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