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