r7808: fixed the build of ldb after the binary file support in ldif was added
[metze/samba/wip.git] / source4 / lib / ldb / common / ldb_ldif.c
1 /* 
2    ldb database library
3
4    Copyright (C) Andrew Tridgell  2004
5
6      ** NOTE! The following LGPL license applies to the ldb
7      ** library. This does NOT imply that all of Samba is released
8      ** under the LGPL
9    
10    This library is free software; you can redistribute it and/or
11    modify it under the terms of the GNU Lesser General Public
12    License as published by the Free Software Foundation; either
13    version 2 of the License, or (at your option) any later version.
14
15    This library is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18    Lesser General Public License for more details.
19
20    You should have received a copy of the GNU Lesser General Public
21    License along with this library; if not, write to the Free Software
22    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23 */
24
25 /*
26  *  Name: ldb
27  *
28  *  Component: ldif routines
29  *
30  *  Description: ldif pack/unpack routines
31  *
32  *  Author: Andrew Tridgell
33  */
34
35 /*
36   see RFC2849 for the LDIF format definition
37 */
38
39 #include "includes.h"
40 #include "ldb/include/ldb.h"
41 #include "ldb/include/ldb_private.h"
42 #include <ctype.h>
43 #ifdef _SAMBA_BUILD_
44 #include "system/filesys.h"
45 #endif
46
47 /*
48   add to the list of ldif handlers for this ldb context
49 */
50 int ldb_ldif_add_handlers(struct ldb_context *ldb, 
51                           const struct ldb_ldif_handler *handlers, 
52                           unsigned num_handlers)
53 {
54         struct ldb_ldif_handler *h;
55         h = talloc_realloc(ldb, ldb->ldif_handlers,
56                            struct ldb_ldif_handler,
57                            ldb->ldif_num_handlers + num_handlers);
58         if (h == NULL) {
59                 ldb_oom(ldb);
60                 return -1;
61         }
62         ldb->ldif_handlers = h;
63         memcpy(h + ldb->ldif_num_handlers, 
64                handlers, sizeof(*h) * num_handlers);
65         ldb->ldif_num_handlers += num_handlers;
66         return 0;
67 }
68                           
69
70 /*
71   default function for ldif read/write
72 */
73 static int ldb_ldif_default(struct ldb_context *ldb, const struct ldb_val *in, 
74                             struct ldb_val *out)
75 {
76         *out = *in;
77         return 0;
78 }
79
80
81 /*
82   return a function for reading an ldif encoded attributes into a ldb_val
83 */
84 static ldb_ldif_handler_t ldb_ldif_read_fn(struct ldb_context *ldb, const char *attr)
85 {
86         int i;
87         for (i=0;i<ldb->ldif_num_handlers;i++) {
88                 if (ldb_attr_cmp(attr, ldb->ldif_handlers[i].attr) == 0) {
89                         return ldb->ldif_handlers[i].read_fn;
90                 }
91         }
92         return ldb_ldif_default;
93 }
94
95 /*
96   return a function for writing an ldif encoded attribute from a ldb_val
97 */
98 static ldb_ldif_handler_t ldb_ldif_write_fn(struct ldb_context *ldb, const char *attr)
99 {
100         int i;
101         for (i=0;i<ldb->ldif_num_handlers;i++) {
102                 if (ldb_attr_cmp(attr, ldb->ldif_handlers[i].attr) == 0) {
103                         return ldb->ldif_handlers[i].write_fn;
104                 }
105         }
106         return ldb_ldif_default;
107 }
108
109 /*
110   
111 */
112 static int ldb_read_data_file(void *mem_ctx, struct ldb_val *value)
113 {
114         struct stat statbuf;
115         char *buf;
116         int count, size, bytes;
117         int ret;
118         int f;
119
120         f = open(value->data, O_RDONLY);
121         if (f == -1) {
122                 return -1;
123         }
124
125         if (fstat(f, &statbuf) != 0) {
126                 ret = -1;
127                 goto done;
128         }
129
130         if (statbuf.st_size == 0) {
131                 ret = -1;
132                 goto done;
133         }
134
135         value->data = talloc_size(mem_ctx, statbuf.st_size + 1);
136         if (value->data == NULL) {
137                 ret = -1;
138                 goto done;
139         }
140         value->data[statbuf.st_size] = 0;
141
142         count = 0;
143         size = statbuf.st_size;
144         buf = value->data;
145         while (count < statbuf.st_size) {
146                 bytes = read(f, buf, size);
147                 if (bytes == -1) {
148                         talloc_free(value->data);
149                         ret = -1;
150                         goto done;
151                 }
152                 count += bytes;
153                 buf += bytes;
154                 size -= bytes;
155         }
156
157         value->length = statbuf.st_size;
158         ret = statbuf.st_size;
159
160 done:
161         close(f);
162         return ret;
163 }
164
165 /*
166   this base64 decoder was taken from jitterbug (written by tridge).
167   we might need to replace it with a new version
168 */
169 int ldb_base64_decode(char *s)
170 {
171         const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
172         int bit_offset=0, byte_offset, idx, i, n;
173         uint8_t *d = (uint8_t *)s;
174         char *p=NULL;
175
176         n=i=0;
177
178         while (*s && (p=strchr(b64,*s))) {
179                 idx = (int)(p - b64);
180                 byte_offset = (i*6)/8;
181                 bit_offset = (i*6)%8;
182                 d[byte_offset] &= ~((1<<(8-bit_offset))-1);
183                 if (bit_offset < 3) {
184                         d[byte_offset] |= (idx << (2-bit_offset));
185                         n = byte_offset+1;
186                 } else {
187                         d[byte_offset] |= (idx >> (bit_offset-2));
188                         d[byte_offset+1] = 0;
189                         d[byte_offset+1] |= (idx << (8-(bit_offset-2))) & 0xFF;
190                         n = byte_offset+2;
191                 }
192                 s++; i++;
193         }
194         if (bit_offset >= 3) {
195                 n--;
196         }
197
198         if (*s && !p) {
199                 /* the only termination allowed */
200                 if (*s != '=') {
201                         return -1;
202                 }
203         }
204
205         /* null terminate */
206         d[n] = 0;
207         return n;
208 }
209
210
211 /*
212   encode as base64
213   caller frees
214 */
215 char *ldb_base64_encode(void *mem_ctx, const char *buf, int len)
216 {
217         const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
218         int bit_offset, byte_offset, idx, i;
219         const uint8_t *d = (const uint8_t *)buf;
220         int bytes = (len*8 + 5)/6;
221         char *out;
222
223         out = talloc_array(mem_ctx, char, bytes+2);
224         if (!out) return NULL;
225
226         for (i=0;i<bytes;i++) {
227                 byte_offset = (i*6)/8;
228                 bit_offset = (i*6)%8;
229                 if (bit_offset < 3) {
230                         idx = (d[byte_offset] >> (2-bit_offset)) & 0x3F;
231                 } else {
232                         idx = (d[byte_offset] << (bit_offset-2)) & 0x3F;
233                         if (byte_offset+1 < len) {
234                                 idx |= (d[byte_offset+1] >> (8-(bit_offset-2)));
235                         }
236                 }
237                 out[i] = b64[idx];
238         }
239
240         out[i++] = '=';
241         out[i] = 0;
242
243         return out;
244 }
245
246 /*
247   see if a buffer should be base64 encoded
248 */
249 int ldb_should_b64_encode(const struct ldb_val *val)
250 {
251         unsigned int i;
252         uint8_t *p = val->data;
253
254         if (val->length == 0) {
255                 return 0;
256         }
257
258         if (p[0] == ' ' || p[0] == ':') {
259                 return 1;
260         }
261
262         for (i=0; i<val->length; i++) {
263                 if (!isprint(p[i]) || p[i] == '\n') {
264                         return 1;
265                 }
266         }
267         return 0;
268 }
269
270 /* this macro is used to handle the return checking on fprintf_fn() */
271 #define CHECK_RET do { if (ret < 0) return ret; total += ret; } while (0)
272
273 /*
274   write a line folded string onto a file
275 */
276 static int fold_string(int (*fprintf_fn)(void *, const char *, ...), void *private_data,
277                         const char *buf, size_t length, int start_pos)
278 {
279         unsigned int i;
280         int total=0, ret;
281
282         for (i=0;i<length;i++) {
283                 ret = fprintf_fn(private_data, "%c", buf[i]);
284                 CHECK_RET;
285                 if (i != (length-1) && (i + start_pos) % 77 == 0) {
286                         ret = fprintf_fn(private_data, "\n ");
287                         CHECK_RET;
288                 }
289         }
290
291         return total;
292 }
293
294 /*
295   encode as base64 to a file
296 */
297 static int base64_encode_f(struct ldb_context *ldb,
298                            int (*fprintf_fn)(void *, const char *, ...), 
299                            void *private_data,
300                            const char *buf, int len, int start_pos)
301 {
302         char *b = ldb_base64_encode(ldb, buf, len);
303         int ret;
304
305         if (!b) {
306                 return -1;
307         }
308
309         ret = fold_string(fprintf_fn, private_data, b, strlen(b), start_pos);
310
311         talloc_free(b);
312         return ret;
313 }
314
315
316 static const struct {
317         const char *name;
318         enum ldb_changetype changetype;
319 } ldb_changetypes[] = {
320         {"add",    LDB_CHANGETYPE_ADD},
321         {"delete", LDB_CHANGETYPE_DELETE},
322         {"modify", LDB_CHANGETYPE_MODIFY},
323         {NULL, 0}
324 };
325
326 /*
327   write to ldif, using a caller supplied write method
328 */
329 int ldb_ldif_write(struct ldb_context *ldb,
330                    int (*fprintf_fn)(void *, const char *, ...), 
331                    void *private_data,
332                    const struct ldb_ldif *ldif)
333 {
334         unsigned int i, j;
335         int total=0, ret;
336         const struct ldb_message *msg;
337
338         msg = ldif->msg;
339
340         ret = fprintf_fn(private_data, "dn: %s\n", msg->dn);
341         CHECK_RET;
342
343         if (ldif->changetype != LDB_CHANGETYPE_NONE) {
344                 for (i=0;ldb_changetypes[i].name;i++) {
345                         if (ldb_changetypes[i].changetype == ldif->changetype) {
346                                 break;
347                         }
348                 }
349                 if (!ldb_changetypes[i].name) {
350                         ldb_debug(ldb, LDB_DEBUG_ERROR, "Error: Invalid ldif changetype %d\n",
351                                   ldif->changetype);
352                         return -1;
353                 }
354                 ret = fprintf_fn(private_data, "changetype: %s\n", ldb_changetypes[i].name);
355                 CHECK_RET;
356         }
357
358         for (i=0;i<msg->num_elements;i++) {
359                 if (ldif->changetype == LDB_CHANGETYPE_MODIFY) {
360                         switch (msg->elements[i].flags & LDB_FLAG_MOD_MASK) {
361                         case LDB_FLAG_MOD_ADD:
362                                 fprintf_fn(private_data, "add: %s\n", 
363                                            msg->elements[i].name);
364                                 break;
365                         case LDB_FLAG_MOD_DELETE:
366                                 fprintf_fn(private_data, "delete: %s\n", 
367                                            msg->elements[i].name);
368                                 break;
369                         case LDB_FLAG_MOD_REPLACE:
370                                 fprintf_fn(private_data, "replace: %s\n", 
371                                            msg->elements[i].name);
372                                 break;
373                         }
374                 }
375
376                 for (j=0;j<msg->elements[i].num_values;j++) {
377                         ldb_ldif_handler_t write_fn = ldb_ldif_write_fn(ldb, 
378                                                                       msg->elements[i].name);
379                         struct ldb_val v;
380                         ret = write_fn(ldb, &msg->elements[i].values[j], &v);
381                         CHECK_RET;
382                         if (ldb_should_b64_encode(&v)) {
383                                 ret = fprintf_fn(private_data, "%s:: ", 
384                                                  msg->elements[i].name);
385                                 CHECK_RET;
386                                 ret = base64_encode_f(ldb, fprintf_fn, private_data, 
387                                                       v.data, v.length,
388                                                       strlen(msg->elements[i].name)+3);
389                                 CHECK_RET;
390                                 ret = fprintf_fn(private_data, "\n");
391                                 CHECK_RET;
392                         } else {
393                                 ret = fprintf_fn(private_data, "%s: ", msg->elements[i].name);
394                                 CHECK_RET;
395                                 ret = fold_string(fprintf_fn, private_data,
396                                                   v.data, v.length,
397                                                   strlen(msg->elements[i].name)+2);
398                                 CHECK_RET;
399                                 ret = fprintf_fn(private_data, "\n");
400                                 CHECK_RET;
401                         }
402                         if (v.data != msg->elements[i].values[j].data) {
403                                 talloc_free(v.data);
404                         }
405                 }
406                 if (ldif->changetype == LDB_CHANGETYPE_MODIFY) {
407                         fprintf_fn(private_data, "-\n");
408                 }
409         }
410         ret = fprintf_fn(private_data,"\n");
411         CHECK_RET;
412
413         return total;
414 }
415
416 #undef CHECK_RET
417
418
419 /*
420   pull a ldif chunk, which is defined as a piece of data ending in \n\n or EOF
421   this routine removes any RFC2849 continuations and comments
422
423   caller frees
424 */
425 static char *next_chunk(struct ldb_context *ldb, 
426                         int (*fgetc_fn)(void *), void *private_data)
427 {
428         size_t alloc_size=0, chunk_size = 0;
429         char *chunk = NULL;
430         int c;
431         int in_comment = 0;
432
433         while ((c = fgetc_fn(private_data)) != EOF) {
434                 if (chunk_size+1 >= alloc_size) {
435                         char *c2;
436                         alloc_size += 1024;
437                         c2 = talloc_realloc(ldb, chunk, char, alloc_size);
438                         if (!c2) {
439                                 talloc_free(chunk);
440                                 errno = ENOMEM;
441                                 return NULL;
442                         }
443                         chunk = c2;
444                 }
445
446                 if (in_comment) {
447                         if (c == '\n') {
448                                 in_comment = 0;
449                         }
450                         continue;                       
451                 }
452                 
453                 /* handle continuation lines - see RFC2849 */
454                 if (c == ' ' && chunk_size > 1 && chunk[chunk_size-1] == '\n') {
455                         chunk_size--;
456                         continue;
457                 }
458                 
459                 /* chunks are terminated by a double line-feed */
460                 if (c == '\n' && chunk_size > 0 && chunk[chunk_size-1] == '\n') {
461                         chunk[chunk_size-1] = 0;
462                         return chunk;
463                 }
464
465                 if (c == '#' && (chunk_size == 0 || chunk[chunk_size-1] == '\n')) {
466                         in_comment = 1;
467                         continue;
468                 }
469
470                 /* ignore leading blank lines */
471                 if (chunk_size == 0 && c == '\n') {
472                         continue;
473                 }
474
475                 chunk[chunk_size++] = c;
476         }
477
478         if (chunk) {
479                 chunk[chunk_size] = 0;
480         }
481
482         return chunk;
483 }
484
485
486 /* simple ldif attribute parser */
487 static int next_attr(void *mem_ctx, char **s, const char **attr, struct ldb_val *value)
488 {
489         char *p;
490         int base64_encoded = 0;
491         int binary_file = 0;
492
493         if (strncmp(*s, "-\n", 2) == 0) {
494                 value->length = 0;
495                 *attr = "-";
496                 *s += 2;
497                 return 0;
498         }
499
500         p = strchr(*s, ':');
501         if (!p) {
502                 return -1;
503         }
504
505         *p++ = 0;
506
507         if (*p == ':') {
508                 base64_encoded = 1;
509                 p++;
510         }
511
512         if (*p == '<') {
513                 binary_file = 1;
514                 p++;
515         }
516
517         *attr = *s;
518
519         while (*p == ' ' || *p == '\t') {
520                 p++;
521         }
522
523         value->data = p;
524
525         p = strchr(p, '\n');
526
527         if (!p) {
528                 value->length = strlen((char *)value->data);
529                 *s = ((char *)value->data) + value->length;
530         } else {
531                 value->length = p - (char *)value->data;
532                 *s = p+1;
533                 *p = 0;
534         }
535
536         if (base64_encoded) {
537                 int len = ldb_base64_decode(value->data);
538                 if (len == -1) {
539                         /* it wasn't valid base64 data */
540                         return -1;
541                 }
542                 value->length = len;
543         }
544
545         if (binary_file) {
546                 int len = ldb_read_data_file(mem_ctx, value);
547                 if (len == -1) {
548                         /* an error occured hile trying to retrieve the file */
549                         return -1;
550                 }
551         }
552
553         return 0;
554 }
555
556
557 /*
558   free a message from a ldif_read
559 */
560 void ldb_ldif_read_free(struct ldb_context *ldb, struct ldb_ldif *ldif)
561 {
562         talloc_free(ldif);
563 }
564
565 /*
566   add an empty element
567 */
568 static int msg_add_empty(struct ldb_context *ldb,
569                          struct ldb_message *msg, const char *name, unsigned flags)
570 {
571         struct ldb_message_element *el2, *el;
572
573         el2 = talloc_realloc(msg, msg->elements, 
574                                struct ldb_message_element, msg->num_elements+1);
575         if (!el2) {
576                 errno = ENOMEM;
577                 return -1;
578         }
579         
580         msg->elements = el2;
581
582         el = &msg->elements[msg->num_elements];
583         
584         el->name = talloc_strdup(msg->elements, name);
585         el->num_values = 0;
586         el->values = NULL;
587         el->flags = flags;
588
589         if (!el->name) {
590                 errno = ENOMEM;
591                 return -1;
592         }
593
594         msg->num_elements++;
595
596         return 0;
597 }
598
599 /*
600  read from a LDIF source, creating a ldb_message
601 */
602 struct ldb_ldif *ldb_ldif_read(struct ldb_context *ldb,
603                                int (*fgetc_fn)(void *), void *private_data)
604 {
605         struct ldb_ldif *ldif;
606         struct ldb_message *msg;
607         const char *attr=NULL;
608         char *chunk=NULL, *s;
609         struct ldb_val value;
610         unsigned flags = 0;
611
612         value.data = NULL;
613
614         ldif = talloc(ldb, struct ldb_ldif);
615         if (!ldif) return NULL;
616
617         ldif->msg = talloc(ldif, struct ldb_message);
618         if (ldif->msg == NULL) {
619                 talloc_free(ldif);
620                 return NULL;
621         }
622
623         ldif->changetype = LDB_CHANGETYPE_NONE;
624         msg = ldif->msg;
625
626         msg->dn = NULL;
627         msg->elements = NULL;
628         msg->num_elements = 0;
629         msg->private_data = NULL;
630
631         chunk = next_chunk(ldb, fgetc_fn, private_data);
632         if (!chunk) {
633                 goto failed;
634         }
635
636         msg->private_data = chunk;
637         s = chunk;
638
639         if (next_attr(ldif, &s, &attr, &value) != 0) {
640                 goto failed;
641         }
642         
643         /* first line must be a dn */
644         if (ldb_attr_cmp(attr, "dn") != 0) {
645                 ldb_debug(ldb, LDB_DEBUG_ERROR, "Error: First line of ldif must be a dn not '%s'\n", 
646                           attr);
647                 goto failed;
648         }
649
650         msg->dn = value.data;
651
652         while (next_attr(ldif, &s, &attr, &value) == 0) {
653                 ldb_ldif_handler_t read_fn;
654                 struct ldb_message_element *el;
655                 int ret, empty = 0;
656
657                 if (ldb_attr_cmp(attr, "changetype") == 0) {
658                         int i;
659                         for (i=0;ldb_changetypes[i].name;i++) {
660                                 if (ldb_attr_cmp((char *)value.data, ldb_changetypes[i].name) == 0) {
661                                         ldif->changetype = ldb_changetypes[i].changetype;
662                                         break;
663                                 }
664                         }
665                         if (!ldb_changetypes[i].name) {
666                                 ldb_debug(ldb, LDB_DEBUG_ERROR, 
667                                           "Error: Bad ldif changetype '%s'\n",(char *)value.data);
668                         }
669                         flags = 0;
670                         continue;
671                 }
672
673                 if (ldb_attr_cmp(attr, "add") == 0) {
674                         flags = LDB_FLAG_MOD_ADD;
675                         empty = 1;
676                 }
677                 if (ldb_attr_cmp(attr, "delete") == 0) {
678                         flags = LDB_FLAG_MOD_DELETE;
679                         empty = 1;
680                 }
681                 if (ldb_attr_cmp(attr, "replace") == 0) {
682                         flags = LDB_FLAG_MOD_REPLACE;
683                         empty = 1;
684                 }
685                 if (ldb_attr_cmp(attr, "-") == 0) {
686                         flags = 0;
687                         continue;
688                 }
689
690                 if (empty) {
691                         if (msg_add_empty(ldb, msg, (char *)value.data, flags) != 0) {
692                                 goto failed;
693                         }
694                         continue;
695                 }
696                 
697                 el = &msg->elements[msg->num_elements-1];
698
699                 read_fn = ldb_ldif_read_fn(ldb, attr);
700
701                 if (msg->num_elements > 0 && ldb_attr_cmp(attr, el->name) == 0 &&
702                     flags == el->flags) {
703                         /* its a continuation */
704                         el->values = 
705                                 talloc_realloc(msg->elements, el->values, 
706                                                  struct ldb_val, el->num_values+1);
707                         if (!el->values) {
708                                 goto failed;
709                         }
710                         ret = read_fn(ldb, &value, &el->values[el->num_values]);
711                         if (ret != 0) {
712                                 goto failed;
713                         }
714                         if (value.data != el->values[el->num_values].data) {
715                                 talloc_steal(el->values, el->values[el->num_values].data);
716                         }
717                         el->num_values++;
718                 } else {
719                         /* its a new attribute */
720                         msg->elements = talloc_realloc(ldif, msg->elements, 
721                                                          struct ldb_message_element, 
722                                                          msg->num_elements+1);
723                         if (!msg->elements) {
724                                 goto failed;
725                         }
726                         el = &msg->elements[msg->num_elements];
727                         el->flags = flags;
728                         el->name = talloc_strdup(msg->elements, attr);
729                         el->values = talloc(msg->elements, struct ldb_val);
730                         if (!el->values || !el->name) {
731                                 goto failed;
732                         }
733                         el->num_values = 1;
734                         ret = read_fn(ldb, &value, &el->values[0]);
735                         if (ret != 0) {
736                                 goto failed;
737                         }
738                         if (value.data != el->values[0].data) {
739                                 talloc_steal(el->values, el->values[0].data);
740                         }
741                         msg->num_elements++;
742                 }
743         }
744
745         return ldif;
746
747 failed:
748         talloc_free(ldif);
749         return NULL;
750 }
751
752
753
754 /*
755   a wrapper around ldif_read() for reading from FILE*
756 */
757 struct ldif_read_file_state {
758         FILE *f;
759 };
760
761 static int fgetc_file(void *private_data)
762 {
763         struct ldif_read_file_state *state = private_data;
764         return fgetc(state->f);
765 }
766
767 struct ldb_ldif *ldb_ldif_read_file(struct ldb_context *ldb, FILE *f)
768 {
769         struct ldif_read_file_state state;
770         state.f = f;
771         return ldb_ldif_read(ldb, fgetc_file, &state);
772 }
773
774
775 /*
776   a wrapper around ldif_read() for reading from const char*
777 */
778 struct ldif_read_string_state {
779         const char *s;
780 };
781
782 static int fgetc_string(void *private_data)
783 {
784         struct ldif_read_string_state *state = private_data;
785         if (state->s[0] != 0) {
786                 return *state->s++;
787         }
788         return EOF;
789 }
790
791 struct ldb_ldif *ldb_ldif_read_string(struct ldb_context *ldb, const char *s)
792 {
793         struct ldif_read_string_state state;
794         state.s = s;
795         return ldb_ldif_read(ldb, fgetc_string, &state);
796 }
797
798
799 /*
800   wrapper around ldif_write() for a file
801 */
802 struct ldif_write_file_state {
803         FILE *f;
804 };
805
806 static int fprintf_file(void *private_data, const char *fmt, ...) PRINTF_ATTRIBUTE(2, 3);
807
808 static int fprintf_file(void *private_data, const char *fmt, ...)
809 {
810         struct ldif_write_file_state *state = private_data;
811         int ret;
812         va_list ap;
813
814         va_start(ap, fmt);
815         ret = vfprintf(state->f, fmt, ap);
816         va_end(ap);
817         return ret;
818 }
819
820 int ldb_ldif_write_file(struct ldb_context *ldb, FILE *f, const struct ldb_ldif *ldif)
821 {
822         struct ldif_write_file_state state;
823         state.f = f;
824         return ldb_ldif_write(ldb, fprintf_file, &state, ldif);
825 }