]> git.sur5r.net Git - openldap/blob - libraries/libldif/line64.c
Initial round 2 memory allocation changes. THIS IS A WORK IN PROGRESS.
[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 const char nib2b64[0x40f] =
24         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
25
26 static const 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     LDAP_CONST 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( (unsigned char) *line ) ) {
66                 line++;
67         }
68         *type = line;
69
70         for ( s = line; *s && *s != ':'; s++ )
71                 ;       /* NULL */
72         if ( *s == '\0' ) {
73                 ber_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( (unsigned char) *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( (unsigned char) *s ) ) {
96                 s++;
97         }
98
99         /* if no value is present, error out */
100         if ( *s == '\0' ) {
101                 ber_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                                         ber_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
180         if ( *next == NULL || **next == '\n' || **next == '\0' ) {
181                 return( NULL );
182         }
183
184         l = *next;
185         while ( (*next = strchr( *next, '\n' )) != NULL ) {
186                 unsigned char c = *(*next + 1);
187                 if ( isspace( c ) && c != '\n' ) {
188                         **next = CONTINUED_LINE_MARKER;
189                         *(*next+1) = CONTINUED_LINE_MARKER;
190                 } else {
191                         *(*next)++ = '\0';
192                         break;
193                 }
194                 (*next)++;
195         }
196
197         return( l );
198 }
199
200 void
201 ldif_put_type_and_value(
202         char **out,
203         LDAP_CONST char *t,
204         LDAP_CONST char *val,
205         int vlen )
206 {
207         unsigned char   *byte, *p, *stop;
208         unsigned char   buf[3];
209         unsigned long   bits;
210         char            *save;
211         int             i, b64, pad, len, savelen;
212         len = 0;
213
214         /* put the type + ": " */
215         for ( p = (unsigned char *) t; *p; p++, len++ ) {
216                 *(*out)++ = *p;
217         }
218         *(*out)++ = ':';
219         len++;
220         save = *out;
221         savelen = len;
222         *(*out)++ = ' ';
223         b64 = 0;
224
225         stop = (unsigned char *) (val + vlen);
226         if ( isascii( val[0] ) && (isspace( val[0] ) || val[0] == ':') ) {
227                 b64 = 1;
228         } else {
229                 for ( byte = (unsigned char *) val; byte < stop;
230                     byte++, len++ ) {
231                         if ( !isascii( *byte ) || !isprint( *byte ) ) {
232                                 b64 = 1;
233                                 break;
234                         }
235                         if ( len > LDIF_LINE_WIDTH ) {
236                                 *(*out)++ = '\n';
237                                 *(*out)++ = ' ';
238                                 len = 1;
239                         }
240                         *(*out)++ = *byte;
241                 }
242         }
243         if ( b64 ) {
244                 *out = save;
245                 *(*out)++ = ':';
246                 *(*out)++ = ' ';
247                 len = savelen + 2;
248                 /* convert to base 64 (3 bytes => 4 base 64 digits) */
249                 for ( byte = (unsigned char *) val; byte < stop - 2;
250                     byte += 3 ) {
251                         bits = (byte[0] & 0xff) << 16;
252                         bits |= (byte[1] & 0xff) << 8;
253                         bits |= (byte[2] & 0xff);
254
255                         for ( i = 0; i < 4; i++, len++, bits <<= 6 ) {
256                                 if ( len > LDIF_LINE_WIDTH ) {
257                                         *(*out)++ = '\n';
258                                         *(*out)++ = ' ';
259                                         len = 1;
260                                 }
261
262                                 /* get b64 digit from high order 6 bits */
263                                 *(*out)++ = nib2b64[ (bits & 0xfc0000L) >> 18 ];
264                         }
265                 }
266
267                 /* add padding if necessary */
268                 if ( byte < stop ) {
269                         for ( i = 0; byte + i < stop; i++ ) {
270                                 buf[i] = byte[i];
271                         }
272                         for ( pad = 0; i < 3; i++, pad++ ) {
273                                 buf[i] = '\0';
274                         }
275                         byte = buf;
276                         bits = (byte[0] & 0xff) << 16;
277                         bits |= (byte[1] & 0xff) << 8;
278                         bits |= (byte[2] & 0xff);
279
280                         for ( i = 0; i < 4; i++, len++, bits <<= 6 ) {
281                                 if ( len > LDIF_LINE_WIDTH ) {
282                                         *(*out)++ = '\n';
283                                         *(*out)++ = ' ';
284                                         len = 1;
285                                 }
286
287                                 if( i + pad < 4 ) {
288                                         /* get b64 digit from low order 6 bits */
289                                         *(*out)++ = nib2b64[ (bits & 0xfc0000L) >> 18 ];
290                                 } else {
291                                         *(*out)++ = '=';
292                                 }
293                         }
294                 }
295         }
296         *(*out)++ = '\n';
297 }
298
299
300 char *
301 ldif_type_and_value( LDAP_CONST char *type, LDAP_CONST char *val, int vlen )
302 /*
303  * return BER malloc'd, zero-terminated LDIF line
304  */
305 {
306     char        *buf, *p;
307     int         tlen;
308
309     tlen = strlen( type );
310     if (( buf = (char *) ber_memalloc( LDIF_SIZE_NEEDED( tlen, vlen ) + 1 ))
311                 == NULL )
312         {
313                 ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug,
314                         "ldif_type_and_value: malloc failed!" );
315                 return NULL;
316     }
317
318     p = buf;
319     ldif_put_type_and_value( &p, type, val, vlen );
320     *p = '\0';
321
322     return( buf );
323 }