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