r19725: sync samba3's ldb with samba4
[kai/samba-autobuild/.git] / source3 / lib / ldb / common / ldb_msg.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: ldb message component utility functions
29  *
30  *  Description: functions for manipulating ldb_message structures
31  *
32  *  Author: Andrew Tridgell
33  */
34
35 #include "includes.h"
36 #include "ldb/include/includes.h"
37
38 /*
39   create a new ldb_message in a given memory context (NULL for top level)
40 */
41 struct ldb_message *ldb_msg_new(void *mem_ctx)
42 {
43         return talloc_zero(mem_ctx, struct ldb_message);
44 }
45
46 /*
47   find an element in a message by attribute name
48 */
49 struct ldb_message_element *ldb_msg_find_element(const struct ldb_message *msg, 
50                                                  const char *attr_name)
51 {
52         unsigned int i;
53         for (i=0;i<msg->num_elements;i++) {
54                 if (ldb_attr_cmp(msg->elements[i].name, attr_name) == 0) {
55                         return &msg->elements[i];
56                 }
57         }
58         return NULL;
59 }
60
61 /*
62   see if two ldb_val structures contain exactly the same data
63   return 1 for a match, 0 for a mis-match
64 */
65 int ldb_val_equal_exact(const struct ldb_val *v1, const struct ldb_val *v2)
66 {
67         if (v1->length != v2->length) return 0;
68
69         if (v1->length == 0) return 1;
70
71         if (memcmp(v1->data, v2->data, v1->length) == 0) {
72                 return 1;
73         }
74
75         return 0;
76 }
77
78 /*
79   find a value in an element
80   assumes case sensitive comparison
81 */
82 struct ldb_val *ldb_msg_find_val(const struct ldb_message_element *el, 
83                                  struct ldb_val *val)
84 {
85         unsigned int i;
86         for (i=0;i<el->num_values;i++) {
87                 if (ldb_val_equal_exact(val, &el->values[i])) {
88                         return &el->values[i];
89                 }
90         }
91         return NULL;
92 }
93
94 /*
95   duplicate a ldb_val structure
96 */
97 struct ldb_val ldb_val_dup(void *mem_ctx, const struct ldb_val *v)
98 {
99         struct ldb_val v2;
100         v2.length = v->length;
101         if (v->data == NULL) {
102                 v2.data = NULL;
103                 return v2;
104         }
105
106         /* the +1 is to cope with buggy C library routines like strndup
107            that look one byte beyond */
108         v2.data = talloc_array(mem_ctx, uint8_t, v->length+1);
109         if (!v2.data) {
110                 v2.length = 0;
111                 return v2;
112         }
113
114         memcpy(v2.data, v->data, v->length);
115         ((char *)v2.data)[v->length] = 0;
116         return v2;
117 }
118
119 /*
120   add an empty element to a message
121 */
122 int ldb_msg_add_empty(  struct ldb_message *msg,
123                         const char *attr_name,
124                         int flags,
125                         struct ldb_message_element **return_el)
126 {
127         struct ldb_message_element *els;
128
129         if (! ldb_valid_attr_name(attr_name)) {
130                 return LDB_ERR_OPERATIONS_ERROR;
131         }
132
133         els = talloc_realloc(msg, msg->elements, 
134                              struct ldb_message_element, msg->num_elements+1);
135         if (!els) {
136                 errno = ENOMEM;
137                 return LDB_ERR_OPERATIONS_ERROR;
138         }
139
140         els[msg->num_elements].values = NULL;
141         els[msg->num_elements].num_values = 0;
142         els[msg->num_elements].flags = flags;
143         els[msg->num_elements].name = talloc_strdup(els, attr_name);
144         if (!els[msg->num_elements].name) {
145                 errno = ENOMEM;
146                 return LDB_ERR_OPERATIONS_ERROR;
147         }
148
149         msg->elements = els;
150         msg->num_elements++;
151
152         if (return_el) {
153                 *return_el = &els[msg->num_elements-1];
154         }
155
156         return LDB_SUCCESS;
157 }
158
159 /*
160   add an empty element to a message
161 */
162 int ldb_msg_add(struct ldb_message *msg, 
163                 const struct ldb_message_element *el, 
164                 int flags)
165 {
166         if (ldb_msg_add_empty(msg, el->name, flags, NULL) != 0) {
167                 return LDB_ERR_OPERATIONS_ERROR;
168         }
169
170         msg->elements[msg->num_elements-1] = *el;
171         msg->elements[msg->num_elements-1].flags = flags;
172
173         return LDB_SUCCESS;
174 }
175
176 /*
177   add a value to a message
178 */
179 int ldb_msg_add_value(struct ldb_message *msg, 
180                       const char *attr_name,
181                       const struct ldb_val *val,
182                       struct ldb_message_element **return_el)
183 {
184         struct ldb_message_element *el;
185         struct ldb_val *vals;
186         int ret;
187
188         el = ldb_msg_find_element(msg, attr_name);
189         if (!el) {
190                 ret = ldb_msg_add_empty(msg, attr_name, 0, &el);
191                 if (ret != LDB_SUCCESS) {
192                         return ret;
193                 }
194         }
195
196         vals = talloc_realloc(msg, el->values, struct ldb_val, el->num_values+1);
197         if (!vals) {
198                 errno = ENOMEM;
199                 return LDB_ERR_OPERATIONS_ERROR;
200         }
201         el->values = vals;
202         el->values[el->num_values] = *val;
203         el->num_values++;
204
205         if (return_el) {
206                 *return_el = el;
207         }
208
209         return LDB_SUCCESS;
210 }
211
212
213 /*
214   add a value to a message, stealing it into the 'right' place
215 */
216 int ldb_msg_add_steal_value(struct ldb_message *msg, 
217                             const char *attr_name,
218                             struct ldb_val *val)
219 {
220         int ret;
221         struct ldb_message_element *el;
222
223         ret = ldb_msg_add_value(msg, attr_name, val, &el);
224         if (ret == LDB_SUCCESS) {
225                 talloc_steal(el->values, val->data);
226         }
227         return ret;
228 }
229
230
231 /*
232   add a string element to a message
233 */
234 int ldb_msg_add_string(struct ldb_message *msg, 
235                        const char *attr_name, const char *str)
236 {
237         struct ldb_val val;
238
239         val.data = discard_const_p(uint8_t, str);
240         val.length = strlen(str);
241
242         if (val.length == 0) {
243                 /* allow empty strings as non-existant attributes */
244                 return LDB_SUCCESS;
245         }
246
247         return ldb_msg_add_value(msg, attr_name, &val, NULL);
248 }
249
250 /*
251   add a string element to a message, stealing it into the 'right' place
252 */
253 int ldb_msg_add_steal_string(struct ldb_message *msg, 
254                              const char *attr_name, char *str)
255 {
256         struct ldb_val val;
257
258         val.data = (uint8_t *)str;
259         val.length = strlen(str);
260
261         return ldb_msg_add_steal_value(msg, attr_name, &val);
262 }
263
264 /*
265   add a printf formatted element to a message
266 */
267 int ldb_msg_add_fmt(struct ldb_message *msg, 
268                     const char *attr_name, const char *fmt, ...)
269 {
270         struct ldb_val val;
271         va_list ap;
272         char *str;
273
274         va_start(ap, fmt);
275         str = talloc_vasprintf(msg, fmt, ap);
276         va_end(ap);
277
278         if (str == NULL) return LDB_ERR_OPERATIONS_ERROR;
279
280         val.data   = (uint8_t *)str;
281         val.length = strlen(str);
282
283         return ldb_msg_add_steal_value(msg, attr_name, &val);
284 }
285
286 /*
287   compare two ldb_message_element structures
288   assumes case senistive comparison
289 */
290 int ldb_msg_element_compare(struct ldb_message_element *el1, 
291                             struct ldb_message_element *el2)
292 {
293         unsigned int i;
294
295         if (el1->num_values != el2->num_values) {
296                 return el1->num_values - el2->num_values;
297         }
298
299         for (i=0;i<el1->num_values;i++) {
300                 if (!ldb_msg_find_val(el2, &el1->values[i])) {
301                         return -1;
302                 }
303         }
304
305         return 0;
306 }
307
308 /*
309   compare two ldb_message_element structures
310   comparing by element name
311 */
312 int ldb_msg_element_compare_name(struct ldb_message_element *el1, 
313                                  struct ldb_message_element *el2)
314 {
315         return ldb_attr_cmp(el1->name, el2->name);
316 }
317
318 /*
319   convenience functions to return common types from a message
320   these return the first value if the attribute is multi-valued
321 */
322 const struct ldb_val *ldb_msg_find_ldb_val(const struct ldb_message *msg, const char *attr_name)
323 {
324         struct ldb_message_element *el = ldb_msg_find_element(msg, attr_name);
325         if (!el || el->num_values == 0) {
326                 return NULL;
327         }
328         return &el->values[0];
329 }
330
331 int ldb_msg_find_attr_as_int(const struct ldb_message *msg, 
332                              const char *attr_name,
333                              int default_value)
334 {
335         const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
336         if (!v || !v->data) {
337                 return default_value;
338         }
339         return strtol((const char *)v->data, NULL, 0);
340 }
341
342 unsigned int ldb_msg_find_attr_as_uint(const struct ldb_message *msg, 
343                                        const char *attr_name,
344                                        unsigned int default_value)
345 {
346         const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
347         if (!v || !v->data) {
348                 return default_value;
349         }
350         return strtoul((const char *)v->data, NULL, 0);
351 }
352
353 int64_t ldb_msg_find_attr_as_int64(const struct ldb_message *msg, 
354                                    const char *attr_name,
355                                    int64_t default_value)
356 {
357         const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
358         if (!v || !v->data) {
359                 return default_value;
360         }
361         return strtoll((const char *)v->data, NULL, 0);
362 }
363
364 uint64_t ldb_msg_find_attr_as_uint64(const struct ldb_message *msg, 
365                                      const char *attr_name,
366                                      uint64_t default_value)
367 {
368         const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
369         if (!v || !v->data) {
370                 return default_value;
371         }
372         return strtoull((const char *)v->data, NULL, 0);
373 }
374
375 double ldb_msg_find_attr_as_double(const struct ldb_message *msg, 
376                                    const char *attr_name,
377                                    double default_value)
378 {
379         const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
380         if (!v || !v->data) {
381                 return default_value;
382         }
383         return strtod((const char *)v->data, NULL);
384 }
385
386 int ldb_msg_find_attr_as_bool(const struct ldb_message *msg, 
387                               const char *attr_name,
388                               int default_value)
389 {
390         const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
391         if (!v || !v->data) {
392                 return default_value;
393         }
394         if (strcasecmp((const char *)v->data, "FALSE") == 0) {
395                 return 0;
396         }
397         if (strcasecmp((const char *)v->data, "TRUE") == 0) {
398                 return 1;
399         }
400         return default_value;
401 }
402
403 const char *ldb_msg_find_attr_as_string(const struct ldb_message *msg, 
404                                         const char *attr_name,
405                                         const char *default_value)
406 {
407         const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
408         if (!v || !v->data) {
409                 return default_value;
410         }
411         return (const char *)v->data;
412 }
413
414 struct ldb_dn *ldb_msg_find_attr_as_dn(void *mem_ctx,
415                                        const struct ldb_message *msg,
416                                        const char *attr_name)
417 {
418         const struct ldb_val *v;
419
420         v = ldb_msg_find_ldb_val(msg, attr_name);
421         if (!v || !v->data) {
422                 return NULL;
423         }
424         return ldb_dn_explode(mem_ctx, (const char *)v->data);
425 }
426
427 /*
428   sort the elements of a message by name
429 */
430 void ldb_msg_sort_elements(struct ldb_message *msg)
431 {
432         qsort(msg->elements, msg->num_elements, sizeof(struct ldb_message_element), 
433               (comparison_fn_t)ldb_msg_element_compare_name);
434 }
435
436 /*
437   shallow copy a message - copying only the elements array so that the caller
438   can safely add new elements without changing the message
439 */
440 struct ldb_message *ldb_msg_copy_shallow(TALLOC_CTX *mem_ctx, 
441                                          const struct ldb_message *msg)
442 {
443         struct ldb_message *msg2;
444         int i;
445
446         msg2 = talloc(mem_ctx, struct ldb_message);
447         if (msg2 == NULL) return NULL;
448
449         *msg2 = *msg;
450         msg2->private_data = NULL;
451
452         msg2->elements = talloc_array(msg2, struct ldb_message_element, 
453                                       msg2->num_elements);
454         if (msg2->elements == NULL) goto failed;
455
456         for (i=0;i<msg2->num_elements;i++) {
457                 msg2->elements[i] = msg->elements[i];
458         }
459
460         return msg2;
461
462 failed:
463         talloc_free(msg2);
464         return NULL;
465 }
466
467
468 /*
469   copy a message, allocating new memory for all parts
470 */
471 struct ldb_message *ldb_msg_copy(TALLOC_CTX *mem_ctx, 
472                                  const struct ldb_message *msg)
473 {
474         struct ldb_message *msg2;
475         int i, j;
476
477         msg2 = ldb_msg_copy_shallow(mem_ctx, msg);
478         if (msg2 == NULL) return NULL;
479
480         msg2->dn = ldb_dn_copy(msg2, msg2->dn);
481         if (msg2->dn == NULL) goto failed;
482
483         for (i=0;i<msg2->num_elements;i++) {
484                 struct ldb_message_element *el = &msg2->elements[i];
485                 struct ldb_val *values = el->values;
486                 el->name = talloc_strdup(msg2->elements, el->name);
487                 if (el->name == NULL) goto failed;
488                 el->values = talloc_array(msg2->elements, struct ldb_val, el->num_values);
489                 for (j=0;j<el->num_values;j++) {
490                         el->values[j] = ldb_val_dup(el->values, &values[j]);
491                         if (el->values[j].data == NULL && values[j].length != 0) {
492                                 goto failed;
493                         }
494                 }
495         }
496
497         return msg2;
498
499 failed:
500         talloc_free(msg2);
501         return NULL;
502 }
503
504
505 /*
506   canonicalise a message, merging elements of the same name
507 */
508 struct ldb_message *ldb_msg_canonicalize(struct ldb_context *ldb, 
509                                          const struct ldb_message *msg)
510 {
511         int i;
512         struct ldb_message *msg2;
513
514         msg2 = ldb_msg_copy(ldb, msg);
515         if (msg2 == NULL) return NULL;
516
517         ldb_msg_sort_elements(msg2);
518
519         for (i=1;i<msg2->num_elements;i++) {
520                 struct ldb_message_element *el1 = &msg2->elements[i-1];
521                 struct ldb_message_element *el2 = &msg2->elements[i];
522                 if (ldb_msg_element_compare_name(el1, el2) == 0) {
523                         el1->values = talloc_realloc(msg2->elements, el1->values, struct ldb_val, 
524                                                        el1->num_values + el2->num_values);
525                         if (el1->values == NULL) {
526                                 return NULL;
527                         }
528                         memcpy(el1->values + el1->num_values,
529                                el2->values,
530                                sizeof(struct ldb_val) * el2->num_values);
531                         el1->num_values += el2->num_values;
532                         talloc_free(discard_const_p(char, el2->name));
533                         if (i+1<msg2->num_elements) {
534                                 memmove(el2, el2+1, sizeof(struct ldb_message_element) * 
535                                         (msg2->num_elements - (i+1)));
536                         }
537                         msg2->num_elements--;
538                         i--;
539                 }
540         }
541
542         return msg2;
543 }
544
545
546 /*
547   return a ldb_message representing the differences between msg1 and msg2. If you
548   then use this in a ldb_modify() call it can be used to save edits to a message
549 */
550 struct ldb_message *ldb_msg_diff(struct ldb_context *ldb, 
551                                  struct ldb_message *msg1,
552                                  struct ldb_message *msg2)
553 {
554         struct ldb_message *mod;
555         struct ldb_message_element *el;
556         unsigned int i;
557
558         mod = ldb_msg_new(ldb);
559
560         mod->dn = msg1->dn;
561         mod->num_elements = 0;
562         mod->elements = NULL;
563
564         msg2 = ldb_msg_canonicalize(ldb, msg2);
565         if (msg2 == NULL) {
566                 return NULL;
567         }
568         
569         /* look in msg2 to find elements that need to be added
570            or modified */
571         for (i=0;i<msg2->num_elements;i++) {
572                 el = ldb_msg_find_element(msg1, msg2->elements[i].name);
573
574                 if (el && ldb_msg_element_compare(el, &msg2->elements[i]) == 0) {
575                         continue;
576                 }
577
578                 if (ldb_msg_add(mod, 
579                                 &msg2->elements[i],
580                                 el?LDB_FLAG_MOD_REPLACE:LDB_FLAG_MOD_ADD) != 0) {
581                         return NULL;
582                 }
583         }
584
585         /* look in msg1 to find elements that need to be deleted */
586         for (i=0;i<msg1->num_elements;i++) {
587                 el = ldb_msg_find_element(msg2, msg1->elements[i].name);
588                 if (!el) {
589                         if (ldb_msg_add_empty(mod, 
590                                               msg1->elements[i].name,
591                                               LDB_FLAG_MOD_DELETE, NULL) != 0) {
592                                 return NULL;
593                         }
594                 }
595         }
596
597         return mod;
598 }
599
600 int ldb_msg_sanity_check(struct ldb_context *ldb, 
601                          const struct ldb_message *msg)
602 {
603         int i, j;
604
605         /* basic check on DN */
606         if (msg->dn == NULL) {
607                 /* TODO: return also an error string */
608                 ldb_set_errstring(ldb, "ldb message lacks a DN!");
609                 return LDB_ERR_INVALID_DN_SYNTAX;
610         }
611
612         /* basic syntax checks */
613         for (i = 0; i < msg->num_elements; i++) {
614                 for (j = 0; j < msg->elements[i].num_values; j++) {
615                         if (msg->elements[i].values[j].length == 0) {
616                                 TALLOC_CTX *mem_ctx = talloc_new(ldb);
617                                 /* an attribute cannot be empty */
618                                 /* TODO: return also an error string */
619                                 ldb_asprintf_errstring(ldb, "Element %s has empty attribute in ldb message (%s)!",
620                                                             msg->elements[i].name, 
621                                                             ldb_dn_linearize(mem_ctx, msg->dn));
622                                 talloc_free(mem_ctx);
623                                 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
624                         }
625                 }
626         }
627
628         return LDB_SUCCESS;
629 }
630
631
632
633
634 /*
635   copy an attribute list. This only copies the array, not the elements
636   (ie. the elements are left as the same pointers)
637 */
638 const char **ldb_attr_list_copy(TALLOC_CTX *mem_ctx, const char * const *attrs)
639 {
640         const char **ret;
641         int i;
642         for (i=0;attrs[i];i++) /* noop */ ;
643         ret = talloc_array(mem_ctx, const char *, i+1);
644         if (ret == NULL) {
645                 return NULL;
646         }
647         for (i=0;attrs[i];i++) {
648                 ret[i] = attrs[i];
649         }
650         ret[i] = attrs[i];
651         return ret;
652 }
653
654
655 /*
656   copy an attribute list. This only copies the array, not the elements
657   (ie. the elements are left as the same pointers)
658 */
659 const char **ldb_attr_list_copy_add(TALLOC_CTX *mem_ctx, const char * const *attrs, const char *new_attr)
660 {
661         const char **ret;
662         int i;
663         for (i=0;attrs[i];i++) /* noop */ ;
664         ret = talloc_array(mem_ctx, const char *, i+2);
665         if (ret == NULL) {
666                 return NULL;
667         }
668         for (i=0;attrs[i];i++) {
669                 ret[i] = attrs[i];
670         }
671         ret[i] = new_attr;
672         ret[i+1] = NULL;
673         return ret;
674 }
675
676
677 /*
678   return 1 if an attribute is in a list of attributes, or 0 otherwise
679 */
680 int ldb_attr_in_list(const char * const *attrs, const char *attr)
681 {
682         int i;
683         for (i=0;attrs[i];i++) {
684                 if (ldb_attr_cmp(attrs[i], attr) == 0) {
685                         return 1;
686                 }
687         }
688         return 0;
689 }
690
691
692 /*
693   rename the specified attribute in a search result
694 */
695 int ldb_msg_rename_attr(struct ldb_message *msg, const char *attr, const char *replace)
696 {
697         struct ldb_message_element *el = ldb_msg_find_element(msg, attr);
698         if (el == NULL) {
699                 return LDB_SUCCESS;
700         }
701         el->name = talloc_strdup(msg->elements, replace);
702         if (el->name == NULL) {
703                 return LDB_ERR_OPERATIONS_ERROR;
704         }
705         return LDB_SUCCESS;
706 }
707
708
709 /*
710   copy the specified attribute in a search result to a new attribute
711 */
712 int ldb_msg_copy_attr(struct ldb_message *msg, const char *attr, const char *replace)
713 {
714         struct ldb_message_element *el = ldb_msg_find_element(msg, attr);
715         if (el == NULL) {
716                 return LDB_SUCCESS;
717         }
718         if (ldb_msg_add(msg, el, 0) != 0) {
719                 return LDB_ERR_OPERATIONS_ERROR;
720         }
721         return ldb_msg_rename_attr(msg, attr, replace);
722 }
723
724
725 /*
726   remove the specified attribute in a search result
727 */
728 void ldb_msg_remove_attr(struct ldb_message *msg, const char *attr)
729 {
730         struct ldb_message_element *el = ldb_msg_find_element(msg, attr);
731         if (el) {
732                 int n = (el - msg->elements);
733                 if (n != msg->num_elements-1) {
734                         memmove(el, el+1, ((msg->num_elements-1) - n)*sizeof(*el));
735                 }
736                 msg->num_elements--;
737         }
738 }
739
740 /*
741   return a LDAP formatted time string
742 */
743 char *ldb_timestring(TALLOC_CTX *mem_ctx, time_t t)
744 {
745         struct tm *tm = gmtime(&t);
746
747         if (!tm) {
748                 return NULL;
749         }
750
751         /* formatted like: 20040408072012.0Z */
752         return talloc_asprintf(mem_ctx, 
753                                "%04u%02u%02u%02u%02u%02u.0Z",
754                                tm->tm_year+1900, tm->tm_mon+1,
755                                tm->tm_mday, tm->tm_hour, tm->tm_min,
756                                tm->tm_sec);
757 }
758
759
760 /*
761   convert a LDAP time string to a time_t. Return 0 if unable to convert
762 */
763 time_t ldb_string_to_time(const char *s)
764 {
765         struct tm tm;
766         
767         if (s == NULL) return 0;
768         
769         memset(&tm, 0, sizeof(tm));
770         if (sscanf(s, "%04u%02u%02u%02u%02u%02u", 
771                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
772                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
773                 return 0;
774         }
775         tm.tm_year -= 1900;
776         tm.tm_mon -= 1;
777         
778         return timegm(&tm);
779 }
780
781
782 /*
783   dump a set of results to a file. Useful from within gdb
784 */
785 void ldb_dump_results(struct ldb_context *ldb, struct ldb_result *result, FILE *f)
786 {
787         int i;
788
789         for (i = 0; i < result->count; i++) {
790                 struct ldb_ldif ldif;
791                 fprintf(f, "# record %d\n", i+1);
792                 ldif.changetype = LDB_CHANGETYPE_NONE;
793                 ldif.msg = result->msgs[i];
794                 ldb_ldif_write_file(ldb, f, &ldif);
795         }
796 }
797
798 int ldb_msg_check_string_attribute(const struct ldb_message *msg, const char *name, const char *value)
799 {
800         struct ldb_message_element *el;
801         struct ldb_val val;
802         
803         el = ldb_msg_find_element(msg, name);
804         if (el == NULL)
805                 return 0;
806
807         val.data = discard_const_p(uint8_t, value);
808         val.length = strlen(value);
809
810         if (ldb_msg_find_val(el, &val))
811                 return 1;
812
813         return 0;
814 }