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