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