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