]> git.sur5r.net Git - openldap/blob - libraries/libldif/line64.c
b4f8968e7424f85429c0062d295792e956ffe23b
[openldap] / libraries / libldif / line64.c
1 /* line64.c - routines for dealing with the slapd line format */
2
3 #include "portable.h"
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <ctype.h>
8
9 #include <ac/string.h>
10 #include <ac/socket.h>
11 #include <ac/time.h>
12
13 int ldif_debug = 0;
14
15 #include "ldap_log.h"
16 #include "lber_pvt.h"
17 #include "ldif.h"
18
19 #define RIGHT2                  0x03
20 #define RIGHT4                  0x0f
21 #define CONTINUED_LINE_MARKER   '\001'
22
23 static char nib2b64[0x40f] =
24         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
25
26 static unsigned char b642nib[0x80] = {
27         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
28         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
29         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
30         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
31         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
32         0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
33         0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
34         0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
35         0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
36         0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
37         0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
38         0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
39         0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
40         0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
41         0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
42         0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
43 };
44
45 /*
46  * ldif_parse_line - takes a line of the form "type:[:] value" and splits it
47  * into components "type" and "value".  if a double colon separates type from
48  * value, then value is encoded in base 64, and parse_line un-decodes it
49  * (in place) before returning.
50  */
51
52 int
53 ldif_parse_line(
54     char        *line,
55     char        **type,
56     char        **value,
57     int         *vlen
58 )
59 {
60         char    *p, *s, *d, *byte, *stop;
61         char    nib;
62         int     i, b64;
63
64         /* skip any leading space */
65         while ( isspace( *line ) ) {
66                 line++;
67         }
68         *type = line;
69
70         for ( s = line; *s && *s != ':'; s++ )
71                 ;       /* NULL */
72         if ( *s == '\0' ) {
73                 lber_pvt_log_printf( LDAP_DEBUG_PARSE, ldif_debug,
74                         "ldif_parse_line missing ':'\n");
75                 return( -1 );
76         }
77
78         /* trim any space between type and : */
79         for ( p = s - 1; p > line && isspace( *p ); p-- ) {
80                 *p = '\0';
81         }
82         *s++ = '\0';
83
84         /* check for double : - indicates base 64 encoded value */
85         if ( *s == ':' ) {
86                 s++;
87                 b64 = 1;
88
89         /* single : - normally encoded value */
90         } else {
91                 b64 = 0;
92         }
93
94         /* skip space between : and value */
95         while ( isspace( *s ) ) {
96                 s++;
97         }
98
99         /* if no value is present, error out */
100         if ( *s == '\0' ) {
101                 lber_pvt_log_printf( LDAP_DEBUG_PARSE, ldif_debug,
102                         "ldif_parse_line missing value\n");
103                 return( -1 );
104         }
105
106         /* check for continued line markers that should be deleted */
107         for ( p = s, d = s; *p; p++ ) {
108                 if ( *p != CONTINUED_LINE_MARKER )
109                         *d++ = *p;
110         }
111         *d = '\0';
112
113         *value = s;
114         if ( b64 ) {
115                 stop = strchr( s, '\0' );
116                 byte = s;
117                 for ( p = s, *vlen = 0; p < stop; p += 4, *vlen += 3 ) {
118                         for ( i = 0; i < 4; i++ ) {
119                                 if ( p[i] != '=' && (p[i] & 0x80 ||
120                                     b642nib[ p[i] & 0x7f ] > 0x3f) ) {
121                                         lber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug,
122 "ldif_parse_line: invalid base 64 encoding char (%c) 0x%x\n",
123                                             p[i], p[i] );
124                                         return( -1 );
125                                 }
126                         }
127
128                         /* first digit */
129                         nib = b642nib[ p[0] & 0x7f ];
130                         byte[0] = nib << 2;
131                         /* second digit */
132                         nib = b642nib[ p[1] & 0x7f ];
133                         byte[0] |= nib >> 4;
134                         byte[1] = (nib & RIGHT4) << 4;
135                         /* third digit */
136                         if ( p[2] == '=' ) {
137                                 *vlen += 1;
138                                 break;
139                         }
140                         nib = b642nib[ p[2] & 0x7f ];
141                         byte[1] |= nib >> 2;
142                         byte[2] = (nib & RIGHT2) << 6;
143                         /* fourth digit */
144                         if ( p[3] == '=' ) {
145                                 *vlen += 2;
146                                 break;
147                         }
148                         nib = b642nib[ p[3] & 0x7f ];
149                         byte[2] |= nib;
150
151                         byte += 3;
152                 }
153                 s[ *vlen ] = '\0';
154         } else {
155                 *vlen = (int) (d - s);
156         }
157
158         return( 0 );
159 }
160
161 /*
162  * ldif_getline - return the next "line" (minus newline) of input from a
163  * string buffer of lines separated by newlines, terminated by \n\n
164  * or \0.  this routine handles continued lines, bundling them into
165  * a single big line before returning.  if a line begins with a white
166  * space character, it is a continuation of the previous line. the white
167  * space character (nb: only one char), and preceeding newline are changed
168  * into CONTINUED_LINE_MARKER chars, to be deleted later by the
169  * str_parse_line() routine above.
170  *
171  * it takes a pointer to a pointer to the buffer on the first call,
172  * which it updates and must be supplied on subsequent calls.
173  */
174
175 char *
176 ldif_getline( char **next )
177 {
178         char    *l;
179         char    c;
180
181         if ( *next == NULL || **next == '\n' || **next == '\0' ) {
182                 return( NULL );
183         }
184
185         l = *next;
186         while ( (*next = strchr( *next, '\n' )) != NULL ) {
187                 c = *(*next + 1);
188                 if ( isspace( c ) && c != '\n' ) {
189                         **next = CONTINUED_LINE_MARKER;
190                         *(*next+1) = CONTINUED_LINE_MARKER;
191                 } else {
192                         *(*next)++ = '\0';
193                         break;
194                 }
195                 (*next)++;
196         }
197
198         return( l );
199 }
200
201 void
202 ldif_put_type_and_value( char **out, char *t, char *val, int vlen )
203 {
204         unsigned char   *byte, *p, *stop;
205         unsigned char   buf[3];
206         unsigned long   bits;
207         char            *save;
208         int             i, b64, pad, len, savelen;
209         len = 0;
210
211         /* put the type + ": " */
212         for ( p = (unsigned char *) t; *p; p++, len++ ) {
213                 *(*out)++ = *p;
214         }
215         *(*out)++ = ':';
216         len++;
217         save = *out;
218         savelen = len;
219         *(*out)++ = ' ';
220         b64 = 0;
221
222         stop = (unsigned char *) (val + vlen);
223         if ( isascii( val[0] ) && (isspace( val[0] ) || val[0] == ':') ) {
224                 b64 = 1;
225         } else {
226                 for ( byte = (unsigned char *) val; byte < stop;
227                     byte++, len++ ) {
228                         if ( !isascii( *byte ) || !isprint( *byte ) ) {
229                                 b64 = 1;
230                                 break;
231                         }
232                         if ( len > LINE_WIDTH ) {
233                                 *(*out)++ = '\n';
234                                 *(*out)++ = ' ';
235                                 len = 1;
236                         }
237                         *(*out)++ = *byte;
238                 }
239         }
240         if ( b64 ) {
241                 *out = save;
242                 *(*out)++ = ':';
243                 *(*out)++ = ' ';
244                 len = savelen + 2;
245                 /* convert to base 64 (3 bytes => 4 base 64 digits) */
246                 for ( byte = (unsigned char *) val; byte < stop - 2;
247                     byte += 3 ) {
248                         bits = (byte[0] & 0xff) << 16;
249                         bits |= (byte[1] & 0xff) << 8;
250                         bits |= (byte[2] & 0xff);
251
252                         for ( i = 0; i < 4; i++, len++, bits <<= 6 ) {
253                                 if ( len > LINE_WIDTH ) {
254                                         *(*out)++ = '\n';
255                                         *(*out)++ = ' ';
256                                         len = 1;
257                                 }
258
259                                 /* get b64 digit from high order 6 bits */
260                                 *(*out)++ = nib2b64[ (bits & 0xfc0000L) >> 18 ];
261                         }
262                 }
263
264                 /* add padding if necessary */
265                 if ( byte < stop ) {
266                         for ( i = 0; byte + i < stop; i++ ) {
267                                 buf[i] = byte[i];
268                         }
269                         for ( pad = 0; i < 3; i++, pad++ ) {
270                                 buf[i] = '\0';
271                         }
272                         byte = buf;
273                         bits = (byte[0] & 0xff) << 16;
274                         bits |= (byte[1] & 0xff) << 8;
275                         bits |= (byte[2] & 0xff);
276
277                         for ( i = 0; i < 4; i++, len++, bits <<= 6 ) {
278                                 if ( len > LINE_WIDTH ) {
279                                         *(*out)++ = '\n';
280                                         *(*out)++ = ' ';
281                                         len = 1;
282                                 }
283
284                                 if( i + pad < 4 ) {
285                                         /* get b64 digit from low order 6 bits */
286                                         *(*out)++ = nib2b64[ (bits & 0xfc0000L) >> 18 ];
287                                 } else {
288                                         *(*out)++ = '=';
289                                 }
290                         }
291                 }
292         }
293         *(*out)++ = '\n';
294 }
295
296
297 char *
298 ldif_type_and_value( char *type, char *val, int vlen )
299 /*
300  * return malloc'd, zero-terminated LDIF line
301  */
302 {
303     char        *buf, *p;
304     int         tlen;
305
306     tlen = strlen( type );
307     if (( buf = (char *) malloc( LDIF_SIZE_NEEDED( tlen, vlen ) + 1 ))
308                 == NULL )
309         {
310                 lber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug,
311                         "ldif_type_and_value: malloc failed!" );
312                 return NULL;
313     }
314
315     p = buf;
316     ldif_put_type_and_value( &p, type, val, vlen );
317     *p = '\0';
318
319     return( buf );
320 }