1 /* -*- show-trailing-whitespace: t; indent-tabs: t -*-
2  * Copyright (c) 2003,2004,2005,2006 David Lichteblau
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18 #define _XOPEN_SOURCE
19 #include <unistd.h>
20 #include "common.h"
21 
22 #define fast_g_string_append_c(gstring, c)				\
23 	do {								\
24 		if ((gstring)->len + 1 >= (gstring)->allocated_len)	\
25 			g_string_append_c((gstring), (c));		\
26 		else {							\
27 			(gstring)->str[(gstring)->len++] = (c);		\
28 			(gstring)->str[(gstring)->len] = 0;		\
29 		}							\
30 	} while (0)
31 
32 static int
read_lhs(FILE * s,GString * lhs)33 read_lhs(FILE *s, GString *lhs)
34 {
35 	int c;
36 
37 	for (;;) {
38 		switch ( c = getc_unlocked(s)) {
39 		case ' ':
40 			if (ferror(s)) syserr();
41 			return 0;
42 		case EOF:
43 			fputs("Error: Unexpected EOF.\n", stderr);
44 			return -1;
45 		case '\n':
46 			fputs("Error: Unexpected EOL.\n", stderr);
47 			return -1;
48 		case 0:
49 			fputs("Error: Null byte not allowed.\n", stderr);
50 			return -1;
51 		default:
52 			fast_g_string_append_c(lhs, c);
53 		}
54 	}
55 }
56 
57 static int
read_backslashed(FILE * s,GString * data)58 read_backslashed(FILE *s, GString *data)
59 {
60 	int c;
61 
62 	for (;;) {
63 		switch ( c = getc_unlocked(s)) {
64 		case '\n':
65 			if (ferror(s)) syserr();
66 			return 0;
67 		case EOF:
68 			goto error;
69 		case '\\':
70 			if ( (c = fgetc(s)) == EOF) goto error;
71 			/* fall through */
72 		default:
73 			fast_g_string_append_c(data, c);
74 		}
75 	}
76 
77 error:
78 	fputs("Error: Unexpected EOF.\n", stderr);
79 	return -1;
80 }
81 
82 static int
read_ldif_attrval(FILE * s,GString * data)83 read_ldif_attrval(FILE *s, GString *data)
84 {
85 	int c;
86 
87 	for (;;)
88 		switch ( c = getc_unlocked(s)) {
89 		case '\n':
90 			if ( (c = fgetc(s)) == ' ') /* folded line */ break;
91 			ungetc(c, s);
92 			if (ferror(s)) syserr();
93 			return 0;
94 		case EOF:
95 			fputs("Error: Unexpected EOF.\n", stderr);
96 			return -1;
97 		default:
98 			fast_g_string_append_c(data, c);
99 		}
100 }
101 
102 static int
read_from_file(GString * data,char * name)103 read_from_file(GString *data, char *name)
104 {
105 	int fd, n;
106 	if ( (fd = open(name, O_RDONLY)) == -1) {
107 		perror("open");
108 		return -1;
109 	}
110 	data->len = 0;
111 	n = 1024;
112 	do {
113 		int olen = data->len;
114 		g_string_set_size(data, data->len + n);
115 		if ( (n = read(fd, data->str + olen, n)) == -1) syserr();
116 		data->len = olen + n;
117 	} while (n > 0);
118 	if (close(fd) == -1) syserr();
119 	return 0;
120 }
121 
122 static int
skip_comment(FILE * s)123 skip_comment(FILE *s)
124 {
125 	int c;
126 
127 	for (;;)
128 		switch ( c = fgetc(s)) {
129 		case EOF:
130 			fputs("Error: Unexpected EOF.\n", stderr);
131 			return -1;
132 		case '\n':
133 			if ( (c = fgetc(s)) == ' ') /* folded line */ break;
134 			ungetc(c, s);
135 			if (ferror(s)) syserr();
136 			return 0;
137 		}
138 }
139 
140 static char *saltbag
141 	= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890./";
142 
143 static char *
cryptdes(char * key)144 cryptdes(char *key)
145 {
146 	unsigned char salt[2];
147 	int fd = open("/dev/random", 2);
148 	if (fd == -1) {
149 		puts("Sorry, crypt not available: Cannot open /dev/random.");
150 		return 0;
151 	}
152 	if (read(fd, salt, 2) != 2) syserr();
153 	close(fd);
154 	salt[0] = saltbag[salt[0] & 63];
155 	salt[1] = saltbag[salt[1] & 63];
156 	return crypt(key, (char *) salt);
157 }
158 
159 static char *
cryptmd5(char * key)160 cryptmd5(char *key)
161 {
162 	char *result;
163 	unsigned char salt[11];
164 	int i;
165 	int fd = open("/dev/random", 2);
166 	if (fd == -1) {
167 		puts("Sorry, MD5 not available: Cannot open /dev/random.");
168 		return 0;
169 	}
170 	salt[0] = '$';
171 	salt[1] = '1';
172 	salt[2] = '$';
173 	if (read(fd, salt + 3, 8) != 8) syserr();
174 	close(fd);
175 	for (i = 3; i < 11; i++)
176 		salt[i] = saltbag[salt[i] & 63];
177 	result = crypt(key, (char *) salt);
178 	if (!result || strlen(result) < 25) {
179 		puts("Sorry, MD5 not available: Are you using the glibc?");
180 		return 0;
181 	}
182 	return result;
183 }
184 
185 /*
186  * Read a line in
187  *   name ' ' (':' encoding)? value '\n'
188  * syntax, skipping comments.  VALUE is parsed according to ENCODING.
189  * Empty NAME is allowed.
190  *
191  * 0: ok
192  * -1: fatal parse error
193  * -2: end of file or empty line
194  */
195 static int
read_line1(FILE * s,GString * name,GString * value)196 read_line1(FILE *s, GString *name, GString *value)
197 {
198 	int c;
199 	char *encoding;
200 
201 	g_string_truncate(name, 0);
202 	g_string_truncate(value, 0);
203 
204 	/* skip comment lines */
205 	do {
206 		c = fgetc(s);
207 		switch (c) {
208 		case EOF:
209 			if (ferror(s)) syserr();
210 			return -2;
211 		case '\n':
212 			return -2;
213 		case '#':
214 			if (skip_comment(s) == -1) return -1;
215 			break;
216 		default:
217 			ungetc(c, s);
218 			c = -1;
219 		}
220 	} while (c != -1);
221 
222 	if (read_lhs(s, name) == -1) return -1;
223 	if ( encoding = memchr(name->str, ':', name->len)) {
224 		encoding++;
225 		name->len = encoding - name->str - 1;
226 		name->str[name->len] = 0;
227 	}
228 
229 	if (!encoding || !strcmp(encoding, ";")) {
230 		if (read_backslashed(s, value) == -1) return -1;
231 	} else if (!*encoding) {
232 		if (read_ldif_attrval(s, value) == -1) return -1;
233 	} else if (!strcmp(encoding, ":")) {
234 		unsigned char *ustr;
235 		int len;
236 		if (read_ldif_attrval(s, value) == -1) return -1;
237 		ustr = (unsigned char *) value->str;;
238 		if ( (len = read_base64(value->str, ustr, value->len)) == -1) {
239 			fputs("Error: Invalid Base64 string.\n", stderr);
240 			return -1;
241 		}
242 		value->len = len;
243 	} else if (!strcmp(encoding, "<")) {
244 		if (read_ldif_attrval(s, value) == -1) return -1;
245 		if (strncmp(value->str, "file://", 7)) {
246 			fputs("Error: Unknown URL scheme.\n", stderr);
247 			return -1;
248 		}
249 		if (read_from_file(value, value->str + 7) == -1)
250 			return -1;
251 	} else if (!strcasecmp(encoding, "crypt")) {
252 		char *hash;
253 		if (read_ldif_attrval(s, value) == -1) return -1;
254 		if ( !(hash = cryptdes(value->str))) return -1;
255 		g_string_assign(value, "{CRYPT}");
256 		g_string_append(value, hash);
257 	} else if (!strcasecmp(encoding, "cryptmd5")) {
258 		char *hash;
259 		if (read_ldif_attrval(s, value) == -1) return -1;
260 		if ( !(hash = cryptmd5(value->str))) return -1;
261 		g_string_assign(value, "{CRYPT}");
262 		g_string_append(value, hash);
263 	} else if (!strcasecmp(encoding, "sha")) {
264 		if (read_ldif_attrval(s, value) == -1) return -1;
265 		g_string_assign(value, "{SHA}");
266 		if (!g_string_append_sha(value, value->str)) return -1;
267 	} else if (!strcasecmp(encoding, "ssha")) {
268 		if (read_ldif_attrval(s, value) == -1) return -1;
269 		g_string_assign(value, "{SSHA}");
270 		if (!g_string_append_ssha(value, value->str)) return -1;
271 	} else if (!strcasecmp(encoding, "md5")) {
272 		if (read_ldif_attrval(s, value) == -1) return -1;
273 		g_string_assign(value, "{MD5}");
274 		if (!g_string_append_md5(value, value->str)) return -1;
275 	} else if (!strcasecmp(encoding, "smd5")) {
276 		if (read_ldif_attrval(s, value) == -1) return -1;
277 		g_string_assign(value, "{SMD5}");
278 		if (!g_string_append_smd5(value, value->str)) return -1;
279 	} else {
280 		char *ptr;
281 		int n = strtol(encoding, &ptr, 10);
282 		if (*ptr) {
283 			fputs("Error: Unknown value encoding.\n", stderr);
284 			return -1;
285 		}
286 		g_string_set_size(value, n);
287 		if (fread(value->str, 1, n, s) != n) syserr();
288 	}
289 	return 0;
290 }
291 
292 
293 /*
294  * Read a line in
295  *   name ' ' (':' encoding)? value '\n'
296  * syntax, skipping comments.  VALUE is parsed according to ENCODING.
297  * Empty NAME is a parse error.
298  *
299  * 0: ok                           if name->len != 0
300  * 0: end of file or empty line    if name->len == 0
301  * -1: parse error
302  */
303 static int
read_line(FILE * s,GString * name,GString * value)304 read_line(FILE *s, GString *name, GString *value)
305 {
306 	int rc = read_line1(s, name, value);
307 	switch (rc) {
308 	case -2:
309 		return 0;
310 	case -1:
311 		return -1;
312 	case 0:
313 		if (!name->len) {
314 			fputs("Error: Space at beginning of line.\n", stderr);
315 			return -1;
316 		}
317 		return 0;
318 	default:
319 		abort();
320 	}
321 }
322 
323 static char *
read_rename_body(FILE * s,GString * tmp1,GString * tmp2,int * deleteoldrdn)324 read_rename_body(FILE *s, GString *tmp1, GString *tmp2, int *deleteoldrdn)
325 {
326 	char *dn;
327 
328 	if (read_line(s, tmp1, tmp2) == -1)
329 		return 0;
330 	if (!tmp1->len) {
331 		fputs("Error: Rename record lacks dn line.\n", stderr);
332 		return 0;
333 	}
334 	*deleteoldrdn = !strcmp(tmp1->str, "replace");
335 	if (!*deleteoldrdn && strcmp(tmp1->str, "add")) {
336 		fputs("Error: Expected 'add' or 'replace' in rename record.\n",
337 		      stderr);
338 		return 0;
339 	}
340 	dn = xdup(tmp2->str);
341 	if (read_line(s, tmp1, tmp2) == -1) {
342 		free(dn);
343 		return 0;
344 	}
345 	if (tmp1->len) {
346 		free(dn);
347 		fputs("Error: Garbage at end of rename record.\n", stderr);
348 		return 0;
349 	}
350 	return dn;
351 }
352 
353 static int
read_nothing(FILE * s,GString * tmp1,GString * tmp2)354 read_nothing(FILE *s, GString *tmp1, GString *tmp2)
355 {
356 	if (read_line(s, tmp1, tmp2) == -1)
357 		return -1;
358 	if (tmp1->len) {
359 		fputs("Error: Garbage at end of record.\n", stderr);
360 		return -1;
361 	}
362 	return 0;
363 }
364 
365 static LDAPMod *
ldapmod4line(char * action,char * ad)366 ldapmod4line(char *action, char *ad)
367 {
368 	LDAPMod *m;
369 	int op;
370 
371  	if (!strcmp(action, "add"))
372 		op = LDAP_MOD_ADD;
373 	else if (!strcmp(action, "delete"))
374 		op = LDAP_MOD_DELETE;
375 	else if (!strcmp(action, "replace"))
376 		op = LDAP_MOD_REPLACE;
377 	else {
378 		fputs("Error: Invalid change marker.\n", stderr);
379 		return 0;
380 	}
381 
382 	m = xalloc(sizeof(LDAPMod));
383 	m->mod_op = op | LDAP_MOD_BVALUES;
384 	m->mod_type = xdup(ad);
385 	return m;
386 }
387 
388 static LDAPMod **
read_modify_body(FILE * s,GString * tmp1,GString * tmp2)389 read_modify_body(FILE *s, GString *tmp1, GString *tmp2)
390 {
391 	LDAPMod **result;
392 	GPtrArray *mods = g_ptr_array_new();
393 	GPtrArray *values;
394 	LDAPMod *m = 0;
395 
396 	for (;;) {
397 		switch (read_line1(s, tmp1, tmp2)) {
398 		case 0:
399 			break;
400 		case -1:
401 			goto error;
402 		case -2:
403 			if (m) {
404 				g_ptr_array_add(values, 0);
405 				m->mod_bvalues = (void *) values->pdata;
406 				g_ptr_array_free(values, 0);
407 				values = 0;
408 			}
409 			goto done;
410 		default:
411 			abort();
412 		}
413 		if (tmp1->len) {
414 			if (m) {
415 				g_ptr_array_add(values, 0);
416 				m->mod_bvalues = (void *) values->pdata;
417 				g_ptr_array_free(values, 0);
418 				values = 0;
419 			}
420 			values = g_ptr_array_new();
421 			if ( !(m = ldapmod4line(tmp1->str, tmp2->str)))
422 				goto error;
423 			g_ptr_array_add(mods, m);
424 		} else
425 			g_ptr_array_add(values, gstring2berval(tmp2));
426 	}
427 done:
428 
429 	g_ptr_array_add(mods, 0);
430 	result = (LDAPMod **) mods->pdata;
431 	g_ptr_array_free(mods, 0);
432 	return result;
433 
434 error:
435 	g_ptr_array_free(mods, 1);
436 	if (values) {
437 		int i;
438 		for (i = 0; i < values->len; i++)
439 			xfree_berval(values->pdata[i]);
440 		g_ptr_array_free(values, 0);
441 	}
442 	return 0;
443 }
444 
445 /*
446  * Lies die erste Zeile eines beliebigen Records nach position `offset' in `s'.
447  * Setze *pos (falls pos != 0).
448  * Liefere 0 bei Erfolg, -1 sonst.
449  * Bei Erfolg:
450  *   - pos ist die exakte Anfangsposition.
451  *   - Setze *key auf den Schluessel (falls key != 0).
452  *   - Setze *dn auf den Distinguished Name (falls dn != 0).
453  * EOF ist kein Fehler und liefert *key = 0 (falls key != 0);
454  */
455 static int
read_header(GString * tmp1,GString * tmp2,FILE * s,long offset,char ** key,char ** dn,long * pos)456 read_header(GString *tmp1, GString *tmp2,
457 	    FILE *s, long offset, char **key, char **dn, long *pos)
458 {
459 	char **rdns = 0;
460 
461 	if (offset != -1)
462 		if (fseek(s, offset, SEEK_SET) == -1) syserr();
463 	do {
464 		if (pos)
465 			if ( (*pos = ftell(s)) == -1) syserr();
466 		if (read_line(s, tmp1, tmp2) == -1) return -1;
467 		if (tmp1->len == 0 && feof(s)) {
468 			if (key) *key = 0;
469 			return 0;
470 		}
471 		if (!strcmp(tmp1->str, "version")) {
472 			if (strcmp(tmp2->str, "ldapvi")) {
473 				fputs("Error: Invalid file format.\n", stderr);
474 				return -1;
475 			}
476 			tmp1->len = 0;
477 		}
478 	} while (!tmp1->len);
479 
480 	rdns = ldap_explode_dn(tmp2->str, 0);
481 	if (!rdns) {
482 		fputs("Error: Invalid distinguished name string.\n", stderr);
483 		return -1;
484 	}
485 
486 	if (key) *key = xdup(tmp1->str);
487 	if (dn) *dn = xdup(tmp2->str);
488 	ldap_value_free(rdns);
489 	return 0;
490 }
491 
492 static int
read_attrval_body(GString * tmp1,GString * tmp2,FILE * s,tentry * entry)493 read_attrval_body(GString *tmp1, GString *tmp2, FILE *s, tentry *entry)
494 {
495 	for (;;) {
496 		tattribute *attribute;
497 
498 		if (read_line(s, tmp1, tmp2) == -1)
499 			return -1;
500 		if (!tmp1->len)
501 			break;
502 		attribute = entry_find_attribute(entry, tmp1->str, 1);
503 		attribute_append_value(attribute, tmp2->str, tmp2->len);
504 	}
505 	return 0;
506 }
507 
508 /*
509  * Lies ein attrval-record nach position `offset' in `s'.
510  * Setze *pos (falls pos != 0).
511  * Liefere 0 bei Erfolg, -1 sonst.
512  * Bei Erfolg:
513  *   - pos ist die exakte Anfangsposition.
514  *   - Setze *entry auf den gelesenen Eintrag (falls entry != 0).
515  *   - Setze *key auf den Schluessel (falls key != 0).
516  * EOF ist kein Fehler und liefert *key = 0 (falls key != 0);
517  */
518 int
read_entry(FILE * s,long offset,char ** key,tentry ** entry,long * pos)519 read_entry(FILE *s, long offset, char **key, tentry **entry, long *pos)
520 {
521 	GString *tmp1 = g_string_new("");
522 	GString *tmp2 = g_string_new("");
523 	char *dn;
524 	char *k = 0;
525 	tentry *e = 0;
526 
527 	int rc = read_header(tmp1, tmp2, s, offset, &k, &dn, pos);
528 	if (rc || !k) goto cleanup;
529 
530 	e = entry_new(dn);
531 	rc = read_attrval_body(tmp1, tmp2, s, e);
532 	if (!rc) {
533 		if (entry) {
534 			*entry = e;
535 			e = 0;
536 		}
537 		if (key) {
538 			*key = k;
539 			k = 0;
540 		}
541 	}
542 
543 cleanup:
544 	if (k) free(k);
545 	if (e) entry_free(e);
546 	g_string_free(tmp1, 1);
547 	g_string_free(tmp2, 1);
548 	return rc;
549 }
550 
551 /*
552  * Lies die erste Zeile eines beliebigen Records nach position `offset' in `s'.
553  * Setze *pos (falls pos != 0).
554  * Liefere 0 bei Erfolg, -1 sonst.
555  * Bei Erfolg:
556  *   - pos ist die exakte Anfangsposition.
557  *   - Setze *key auf den Schluessel (falls key != 0).
558  */
559 int
peek_entry(FILE * s,long offset,char ** key,long * pos)560 peek_entry(FILE *s, long offset, char **key, long *pos)
561 {
562 	GString *tmp1 = g_string_new("");
563 	GString *tmp2 = g_string_new("");
564 
565 	int rc = read_header(tmp1, tmp2, s, offset, key, 0, pos);
566 	g_string_free(tmp1, 1);
567 	g_string_free(tmp2, 1);
568 	return rc;
569 }
570 
571 /*
572  * Lies ein rename-record nach position `offset' in `s'.
573  * Liefere 0 bei Erfolg, -1 sonst.
574  * Bei Erfolg:
575  *   - Setze *dn1 auf den alten DN.
576  *   - Setze *dn2 auf den neuen DN.
577  *   - *deleteoldrdn auf 1 oder 0;
578  */
579 int
read_rename(FILE * s,long offset,char ** dn1,char ** dn2,int * deleteoldrdn)580 read_rename(FILE *s, long offset, char **dn1, char **dn2, int *deleteoldrdn)
581 {
582 	GString *tmp1 = g_string_new("");
583 	GString *tmp2 = g_string_new("");
584 	char *olddn;
585 	char *newdn;
586 
587 	int rc = read_header(tmp1, tmp2, s, offset, 0, &olddn, 0);
588 	if (rc) {
589 		g_string_free(tmp1, 1);
590 		g_string_free(tmp2, 1);
591 		return rc;
592 	}
593 
594 	newdn = read_rename_body(s, tmp1, tmp2, deleteoldrdn);
595 	g_string_free(tmp1, 1);
596 	g_string_free(tmp2, 1);
597 
598 	if (!newdn) {
599 		free(olddn);
600 		return -1;
601 	}
602 	if (dn1) *dn1 = olddn; else free(olddn);
603 	if (dn2) *dn2 = newdn; else free(newdn);
604 	return 0;
605 }
606 
607 int
read_delete(FILE * s,long offset,char ** dn)608 read_delete(FILE *s, long offset, char **dn)
609 {
610 	GString *tmp1 = g_string_new("");
611 	GString *tmp2 = g_string_new("");
612 	char *str;
613 
614 	int rc = read_header(tmp1, tmp2, s, offset, 0, &str, 0);
615 	if (rc) {
616 		g_string_free(tmp1, 1);
617 		g_string_free(tmp2, 1);
618 		return rc;
619 	}
620 
621 	rc = read_nothing(s, tmp1, tmp2);
622 	g_string_free(tmp1, 1);
623 	g_string_free(tmp2, 1);
624 
625 	if (rc == -1)
626 		free(str);
627 	else
628 		*dn = str;
629 	return rc;
630 }
631 
632 /*
633  * Lies ein modify-record nach position `offset' in `s'.
634  * Liefere 0 bei Erfolg, -1 sonst.
635  * Bei Erfolg:
636  *   - Setze *dn auf den DN.
637  *   - Setze *mods auf die Aenderungen.
638  */
639 int
read_modify(FILE * s,long offset,char ** dn,LDAPMod *** mods)640 read_modify(FILE *s, long offset, char **dn, LDAPMod ***mods)
641 {
642 	GString *tmp1 = g_string_new("");
643 	GString *tmp2 = g_string_new("");
644 	char *d;
645 	LDAPMod **m;
646 
647 	int rc = read_header(tmp1, tmp2, s, offset, 0, &d, 0);
648 	if (rc) {
649 		g_string_free(tmp1, 1);
650 		g_string_free(tmp2, 1);
651 		return rc;
652 	}
653 
654 	m = read_modify_body(s, tmp1, tmp2);
655 	g_string_free(tmp1, 1);
656 	g_string_free(tmp2, 1);
657 
658 	if (!m) {
659 		free(d);
660 		return -1;
661 	}
662 	if (dn) *dn = d; else free(d);
663 	if (mods) *mods = m; else ldap_mods_free(m, 1);
664 	return 0;
665 }
666 
667 /*
668  * Parse a complete entry or changerecord and ignore it.  Set *key accordingly.
669  * Leave the stream positioned after the entry.
670  *
671  * Treat EOF as success and set *key to NULL.
672  *
673  * return value:
674  *   0 on success
675  *   -1 on parse error
676  */
677 /*
678  * FIXME: Warum lesen wir hier nicht einfach bis zur naechsten leeren Zeile?
679  */
680 int
skip_entry(FILE * s,long offset,char ** key)681 skip_entry(FILE *s, long offset, char **key)
682 {
683 	GString *tmp1 = g_string_new("");
684 	GString *tmp2 = g_string_new("");
685 	char *k = 0;
686 
687 	int rc = read_header(tmp1, tmp2, s, offset, &k, 0, 0);
688 	if (rc || !k)
689 		;
690 	else if (!strcmp(k, "modify")) {
691 		LDAPMod **mods = read_modify_body(s, tmp1, tmp2);
692 		if (mods)
693 			ldap_mods_free(mods, 1);
694 		else
695 			rc = -1;
696 	} else if (!strcmp(k, "rename")) {
697 		int dor;
698 		char *newdn = read_rename_body(s, tmp1, tmp2, &dor);
699 		if (newdn)
700 			free(newdn);
701 		else
702 			rc = -1;
703 	} else if (!strcmp(k, "delete"))
704 		rc = read_nothing(s, tmp1, tmp2);
705 	else {
706 		tentry *e = entry_new(xdup(""));
707 		rc = read_attrval_body(tmp1, tmp2, s, e);
708 		entry_free(e);
709 	}
710 
711 	if (key) *key = k; else free(k);
712 	g_string_free(tmp1, 1);
713 	g_string_free(tmp2, 1);
714 	return rc;
715 }
716 
717 static int
read_profile_header(GString * tmp1,GString * tmp2,FILE * s,char ** name)718 read_profile_header(GString *tmp1, GString *tmp2, FILE *s, char **name)
719 {
720 	do {
721 		if (read_line(s, tmp1, tmp2) == -1) return -1;
722 		if (tmp1->len == 0 && feof(s)) {
723 			*name = 0;
724 			return 0;
725 		}
726 	} while (!tmp1->len);
727 
728 	if (strcmp(tmp1->str, "profile")) {
729 		fprintf(stderr,
730 			"Error: Expected 'profile' in configuration,"
731 			" found '%s' instead.\n",
732 			tmp1->str);
733 		return -1;
734 	}
735 
736 	*name = xdup(tmp2->str);
737 	return 0;
738 }
739 
740 int
read_profile(FILE * s,tentry ** entry)741 read_profile(FILE *s, tentry **entry)
742 {
743 	GString *tmp1 = g_string_new("");
744 	GString *tmp2 = g_string_new("");
745 	char *name;
746 	tentry *e = 0;
747 
748 	int rc = read_profile_header(tmp1, tmp2, s, &name);
749 	if (rc || !name) goto cleanup;
750 
751 	e = entry_new(name);
752 	rc = read_attrval_body(tmp1, tmp2, s, e);
753 	if (!rc) {
754 		*entry = e;
755 		e = 0;
756 	}
757 
758 cleanup:
759 	if (e) entry_free(e);
760 	g_string_free(tmp1, 1);
761 	g_string_free(tmp2, 1);
762 	return rc;
763 }
764 
765 tparser ldapvi_parser = {
766 	read_entry,
767 	peek_entry,
768 	skip_entry,
769 	read_rename,
770 	read_delete,
771 	read_modify,
772 	print_ldapvi_entry
773 };
774