line64.c 7.31 KB
Newer Older
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1
2
/* line64.c - routines for dealing with the slapd line format */

3
4
#include "portable.h"

Kurt Zeilenga's avatar
Kurt Zeilenga committed
5
#include <stdio.h>
Kurt Zeilenga's avatar
Kurt Zeilenga committed
6
7
8

#include <ac/stdlib.h>
#include <ac/ctype.h>
9
10
11
12
13

#include <ac/string.h>
#include <ac/socket.h>
#include <ac/time.h>

14
15
int ldif_debug = 0;

16
#include "ldap_log.h"
17
#include "lber_pvt.h"
Kurt Zeilenga's avatar
Kurt Zeilenga committed
18
19
20
21
22
23
#include "ldif.h"

#define RIGHT2			0x03
#define RIGHT4			0x0f
#define CONTINUED_LINE_MARKER	'\001'

24
static const char nib2b64[0x40] =
Kurt Zeilenga's avatar
Kurt Zeilenga committed
25
26
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

27
static const unsigned char b642nib[0x80] = {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
	0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
	0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
	0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
	0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
	0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
	0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
	0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
};

/*
47
 * ldif_parse_line - takes a line of the form "type:[:] value" and splits it
Kurt Zeilenga's avatar
Kurt Zeilenga committed
48
49
50
51
52
53
 * into components "type" and "value".  if a double colon separates type from
 * value, then value is encoded in base 64, and parse_line un-decodes it
 * (in place) before returning.
 */

int
54
ldif_parse_line(
Kurt Zeilenga's avatar
Kurt Zeilenga committed
55
    LDAP_CONST char	*line,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
56
57
58
59
60
61
62
63
64
65
    char	**type,
    char	**value,
    int		*vlen
)
{
	char	*p, *s, *d, *byte, *stop;
	char	nib;
	int	i, b64;

	/* skip any leading space */
66
	while ( isspace( (unsigned char) *line ) ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
67
68
69
70
71
72
73
		line++;
	}
	*type = line;

	for ( s = line; *s && *s != ':'; s++ )
		;	/* NULL */
	if ( *s == '\0' ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
74
		ber_pvt_log_printf( LDAP_DEBUG_PARSE, ldif_debug,
75
			"ldif_parse_line missing ':'\n");
Kurt Zeilenga's avatar
Kurt Zeilenga committed
76
77
78
79
		return( -1 );
	}

	/* trim any space between type and : */
80
	for ( p = s - 1; p > line && isspace( (unsigned char) *p ); p-- ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
		*p = '\0';
	}
	*s++ = '\0';

	/* check for double : - indicates base 64 encoded value */
	if ( *s == ':' ) {
		s++;
		b64 = 1;

	/* single : - normally encoded value */
	} else {
		b64 = 0;
	}

	/* skip space between : and value */
96
	while ( isspace( (unsigned char) *s ) ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
97
98
99
100
101
		s++;
	}

	/* if no value is present, error out */
	if ( *s == '\0' ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
102
		ber_pvt_log_printf( LDAP_DEBUG_PARSE, ldif_debug,
103
			"ldif_parse_line missing value\n");
Kurt Zeilenga's avatar
Kurt Zeilenga committed
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
		return( -1 );
	}

	/* check for continued line markers that should be deleted */
	for ( p = s, d = s; *p; p++ ) {
		if ( *p != CONTINUED_LINE_MARKER )
			*d++ = *p;
	}
	*d = '\0';

	*value = s;
	if ( b64 ) {
		stop = strchr( s, '\0' );
		byte = s;
		for ( p = s, *vlen = 0; p < stop; p += 4, *vlen += 3 ) {
119
			for ( i = 0; i < 4; i++ ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
120
121
				if ( p[i] != '=' && (p[i] & 0x80 ||
				    b642nib[ p[i] & 0x7f ] > 0x3f) ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
122
					ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug,
123
124
"ldif_parse_line: invalid base 64 encoding char (%c) 0x%x\n",
					    p[i], p[i] );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
					return( -1 );
				}
			}

			/* first digit */
			nib = b642nib[ p[0] & 0x7f ];
			byte[0] = nib << 2;
			/* second digit */
			nib = b642nib[ p[1] & 0x7f ];
			byte[0] |= nib >> 4;
			byte[1] = (nib & RIGHT4) << 4;
			/* third digit */
			if ( p[2] == '=' ) {
				*vlen += 1;
				break;
			}
			nib = b642nib[ p[2] & 0x7f ];
			byte[1] |= nib >> 2;
			byte[2] = (nib & RIGHT2) << 6;
			/* fourth digit */
			if ( p[3] == '=' ) {
				*vlen += 2;
				break;
			}
			nib = b642nib[ p[3] & 0x7f ];
			byte[2] |= nib;

			byte += 3;
		}
		s[ *vlen ] = '\0';
	} else {
		*vlen = (int) (d - s);
	}

	return( 0 );
}

/*
163
 * ldif_getline - return the next "line" (minus newline) of input from a
Kurt Zeilenga's avatar
Kurt Zeilenga committed
164
165
166
167
168
169
170
171
172
173
174
175
176
 * string buffer of lines separated by newlines, terminated by \n\n
 * or \0.  this routine handles continued lines, bundling them into
 * a single big line before returning.  if a line begins with a white
 * space character, it is a continuation of the previous line. the white
 * space character (nb: only one char), and preceeding newline are changed
 * into CONTINUED_LINE_MARKER chars, to be deleted later by the
 * str_parse_line() routine above.
 *
 * it takes a pointer to a pointer to the buffer on the first call,
 * which it updates and must be supplied on subsequent calls.
 */

char *
177
ldif_getline( char **next )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
178
{
179
	char *line;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
180

181
182
183
184
185
186
	do {
		if ( *next == NULL || **next == '\n' || **next == '\0' ) {
			return( NULL );
		}

		line = *next;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
187

188
189
		while ( (*next = strchr( *next, '\n' )) != NULL ) {
			unsigned char c = *(*next + 1);
Kurt Zeilenga's avatar
Kurt Zeilenga committed
190
191

			if ( !isspace( c ) || c == '\n' ) {
192
193
194
				*(*next)++ = '\0';
				break;
			}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
195
196
197

			**next = CONTINUED_LINE_MARKER;
			*(*next+1) = CONTINUED_LINE_MARKER;
198
			(*next)++;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
199
		}
200
	} while( *line == '#' );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
201

202
	return( line );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
203
204
205
}

void
Kurt Zeilenga's avatar
Kurt Zeilenga committed
206
207
208
209
210
ldif_put_type_and_value(
	char **out,
	LDAP_CONST char *t,
	LDAP_CONST char *val,
	int vlen )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
{
	unsigned char	*byte, *p, *stop;
	unsigned char	buf[3];
	unsigned long	bits;
	char		*save;
	int		i, b64, pad, len, savelen;
	len = 0;

	/* put the type + ": " */
	for ( p = (unsigned char *) t; *p; p++, len++ ) {
		*(*out)++ = *p;
	}
	*(*out)++ = ':';
	len++;
	save = *out;
	savelen = len;
	*(*out)++ = ' ';
	b64 = 0;

	stop = (unsigned char *) (val + vlen);
Hallvard Furuseth's avatar
Hallvard Furuseth committed
231
	if ( isascii( val[0] ) && (isspace( val[0] ) || val[0] == ':') ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
232
233
234
235
236
237
238
239
		b64 = 1;
	} else {
		for ( byte = (unsigned char *) val; byte < stop;
		    byte++, len++ ) {
			if ( !isascii( *byte ) || !isprint( *byte ) ) {
				b64 = 1;
				break;
			}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
240
			if ( len > LDIF_LINE_WIDTH ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
				*(*out)++ = '\n';
				*(*out)++ = ' ';
				len = 1;
			}
			*(*out)++ = *byte;
		}
	}
	if ( b64 ) {
		*out = save;
		*(*out)++ = ':';
		*(*out)++ = ' ';
		len = savelen + 2;
		/* convert to base 64 (3 bytes => 4 base 64 digits) */
		for ( byte = (unsigned char *) val; byte < stop - 2;
		    byte += 3 ) {
			bits = (byte[0] & 0xff) << 16;
			bits |= (byte[1] & 0xff) << 8;
			bits |= (byte[2] & 0xff);

			for ( i = 0; i < 4; i++, len++, bits <<= 6 ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
261
				if ( len > LDIF_LINE_WIDTH ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
					*(*out)++ = '\n';
					*(*out)++ = ' ';
					len = 1;
				}

				/* get b64 digit from high order 6 bits */
				*(*out)++ = nib2b64[ (bits & 0xfc0000L) >> 18 ];
			}
		}

		/* add padding if necessary */
		if ( byte < stop ) {
			for ( i = 0; byte + i < stop; i++ ) {
				buf[i] = byte[i];
			}
			for ( pad = 0; i < 3; i++, pad++ ) {
				buf[i] = '\0';
			}
			byte = buf;
			bits = (byte[0] & 0xff) << 16;
			bits |= (byte[1] & 0xff) << 8;
			bits |= (byte[2] & 0xff);

			for ( i = 0; i < 4; i++, len++, bits <<= 6 ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
286
				if ( len > LDIF_LINE_WIDTH ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
287
288
289
290
291
					*(*out)++ = '\n';
					*(*out)++ = ' ';
					len = 1;
				}

292
293
294
295
296
297
				if( i + pad < 4 ) {
					/* get b64 digit from low order 6 bits */
					*(*out)++ = nib2b64[ (bits & 0xfc0000L) >> 18 ];
				} else {
					*(*out)++ = '=';
				}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
298
299
300
301
302
303
304
305
			}
		}
	}
	*(*out)++ = '\n';
}


char *
Kurt Zeilenga's avatar
Kurt Zeilenga committed
306
ldif_type_and_value( LDAP_CONST char *type, LDAP_CONST char *val, int vlen )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
307
/*
308
 * return BER malloc'd, zero-terminated LDIF line
Kurt Zeilenga's avatar
Kurt Zeilenga committed
309
310
311
312
313
314
 */
{
    char	*buf, *p;
    int		tlen;

    tlen = strlen( type );
315
    if (( buf = (char *) ber_memalloc( LDIF_SIZE_NEEDED( tlen, vlen ) + 1 ))
316
317
		== NULL )
	{
Kurt Zeilenga's avatar
Kurt Zeilenga committed
318
		ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug,
319
320
			"ldif_type_and_value: malloc failed!" );
		return NULL;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
321
322
323
    }

    p = buf;
324
    ldif_put_type_and_value( &p, type, val, vlen );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
325
326
327
328
    *p = '\0';

    return( buf );
}