]> git.sur5r.net Git - openldap/blob - libraries/libldif/line64.c
"const"ify some static arrays, and related parameters/variables
[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     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                 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( (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                 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
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( char **out, char *t, char *val, int vlen )
202 {
203         unsigned char   *byte, *p, *stop;
204         unsigned char   buf[3];
205         unsigned long   bits;
206         char            *save;
207         int             i, b64, pad, len, savelen;
208         len = 0;
209
210         /* put the type + ": " */
211         for ( p = (unsigned char *) t; *p; p++, len++ ) {
212                 *(*out)++ = *p;
213         }
214         *(*out)++ = ':';
215         len++;
216         save = *out;
217         savelen = len;
218         *(*out)++ = ' ';
219         b64 = 0;
220
221         stop = (unsigned char *) (val + vlen);
222         if ( isascii( val[0] ) && (isspace( val[0] ) || val[0] == ':') ) {
223                 b64 = 1;
224         } else {
225                 for ( byte = (unsigned char *) val; byte < stop;
226                     byte++, len++ ) {
227                         if ( !isascii( *byte ) || !isprint( *byte ) ) {
228                                 b64 = 1;
229                                 break;
230                         }
231                         if ( len > LDIF_LINE_WIDTH ) {
232                                 *(*out)++ = '\n';
233                                 *(*out)++ = ' ';
234                                 len = 1;
235                         }
236                         *(*out)++ = *byte;
237                 }
238         }
239         if ( b64 ) {
240                 *out = save;
241                 *(*out)++ = ':';
242                 *(*out)++ = ' ';
243                 len = savelen + 2;
244                 /* convert to base 64 (3 bytes => 4 base 64 digits) */
245                 for ( byte = (unsigned char *) val; byte < stop - 2;
246                     byte += 3 ) {
247                         bits = (byte[0] & 0xff) << 16;
248                         bits |= (byte[1] & 0xff) << 8;
249                         bits |= (byte[2] & 0xff);
250
251                         for ( i = 0; i < 4; i++, len++, bits <<= 6 ) {
252                                 if ( len > LDIF_LINE_WIDTH ) {
253                                         *(*out)++ = '\n';
254                                         *(*out)++ = ' ';
255                                         len = 1;
256                                 }
257
258                                 /* get b64 digit from high order 6 bits */
259                                 *(*out)++ = nib2b64[ (bits & 0xfc0000L) >> 18 ];
260                         }
261                 }
262
263                 /* add padding if necessary */
264                 if ( byte < stop ) {
265                         for ( i = 0; byte + i < stop; i++ ) {
266                                 buf[i] = byte[i];
267                         }
268                         for ( pad = 0; i < 3; i++, pad++ ) {
269                                 buf[i] = '\0';
270                         }
271                         byte = buf;
272                         bits = (byte[0] & 0xff) << 16;
273                         bits |= (byte[1] & 0xff) << 8;
274                         bits |= (byte[2] & 0xff);
275
276                         for ( i = 0; i < 4; i++, len++, bits <<= 6 ) {
277                                 if ( len > LDIF_LINE_WIDTH ) {
278                                         *(*out)++ = '\n';
279                                         *(*out)++ = ' ';
280                                         len = 1;
281                                 }
282
283                                 if( i + pad < 4 ) {
284                                         /* get b64 digit from low order 6 bits */
285                                         *(*out)++ = nib2b64[ (bits & 0xfc0000L) >> 18 ];
286                                 } else {
287                                         *(*out)++ = '=';
288                                 }
289                         }
290                 }
291         }
292         *(*out)++ = '\n';
293 }
294
295
296 char *
297 ldif_type_and_value( char *type, char *val, int vlen )
298 /*
299  * return malloc'd, zero-terminated LDIF line
300  */
301 {
302     char        *buf, *p;
303     int         tlen;
304
305     tlen = strlen( type );
306     if (( buf = (char *) malloc( LDIF_SIZE_NEEDED( tlen, vlen ) + 1 ))
307                 == NULL )
308         {
309                 lber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug,
310                         "ldif_type_and_value: malloc failed!" );
311                 return NULL;
312     }
313
314     p = buf;
315     ldif_put_type_and_value( &p, type, val, vlen );
316     *p = '\0';
317
318     return( buf );
319 }