r20189: remove unused struct element
[samba.git] / source4 / lib / ldb / common / ldb_msg.c
1 /* 
2    ldb database library
3
4    Copyright (C) Andrew Tridgell  2004
5
6      ** NOTE! The following LGPL license applies to the ldb
7      ** library. This does NOT imply that all of Samba is released
8      ** under the LGPL
9    
10    This library is free software; you can redistribute it and/or
11    modify it under the terms of the GNU Lesser General Public
12    License as published by the Free Software Foundation; either
13    version 2 of the License, or (at your option) any later version.
14
15    This library is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18    Lesser General Public License for more details.
19
20    You should have received a copy of the GNU Lesser General Public
21    License along with this library; if not, write to the Free Software
22    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23 */
24
25 /*
26  *  Name: ldb
27  *
28  *  Component: ldb message component utility functions
29  *
30  *  Description: functions for manipulating ldb_message structures
31  *
32  *  Author: Andrew Tridgell
33  */
34
35 #include "includes.h"
36 #include "ldb/include/includes.h"
37
38 /*
39   create a new ldb_message in a given memory context (NULL for top level)
40 */
41 struct ldb_message *ldb_msg_new(void *mem_ctx)
42 {
43         return talloc_zero(mem_ctx, struct ldb_message);
44 }
45
46 /*
47   find an element in a message by attribute name
48 */
49 struct ldb_message_element *ldb_msg_find_element(const struct ldb_message *msg, 
50                                                  const char *attr_name)
51 {
52         unsigned int i;
53         for (i=0;i<msg->num_elements;i++) {
54                 if (ldb_attr_cmp(msg->elements[i].name, attr_name) == 0) {
55                         return &msg->elements[i];
56                 }
57         }
58         return NULL;
59 }
60
61 /*
62   see if two ldb_val structures contain exactly the same data
63   return 1 for a match, 0 for a mis-match
64 */
65 int ldb_val_equal_exact(const struct ldb_val *v1, const struct ldb_val *v2)
66 {
67         if (v1->length != v2->length) return 0;
68
69         if (v1->length == 0) return 1;
70
71         if (memcmp(v1->data, v2->data, v1->length) == 0) {
72                 return 1;
73         }
74
75         return 0;
76 }
77
78 /*
79   find a value in an element
80   assumes case sensitive comparison
81 */
82 struct ldb_val *ldb_msg_find_val(const struct ldb_message_element *el, 
83                                  struct ldb_val *val)
84 {
85         unsigned int i;
86         for (i=0;i<el->num_values;i++) {
87                 if (ldb_val_equal_exact(val, &el->values[i])) {
88                         return &el->values[i];
89                 }
90         }
91         return NULL;
92 }
93
94 /*
95   duplicate a ldb_val structure
96 */
97 struct ldb_val ldb_val_dup(void *mem_ctx, const struct ldb_val *v)
98 {
99         struct ldb_val v2;
100         v2.length = v->length;
101         if (v->data == NULL) {
102                 v2.data = NULL;
103                 return v2;
104         }
105
106         /* the +1 is to cope with buggy C library routines like strndup
107            that look one byte beyond */
108         v2.data = talloc_array(mem_ctx, uint8_t, v->length+1);
109         if (!v2.data) {
110                 v2.length = 0;
111                 return v2;
112         }
113
114         memcpy(v2.data, v->data, v->length);
115         ((char *)v2.data)[v->length] = 0;
116         return v2;
117 }
118
119 /*
120   add an empty element to a message
121 */
122 int ldb_msg_add_empty(  struct ldb_message *msg,
123                         const char *attr_name,
124                         int flags,
125                         struct ldb_message_element **return_el)
126 {
127         struct ldb_message_element *els;
128
129         /* 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
459         msg2->elements = talloc_array(msg2, struct ldb_message_element, 
460                                       msg2->num_elements);
461         if (msg2->elements == NULL) goto failed;
462
463         for (i=0;i<msg2->num_elements;i++) {
464                 msg2->elements[i] = msg->elements[i];
465         }
466
467         return msg2;
468
469 failed:
470         talloc_free(msg2);
471         return NULL;
472 }
473
474
475 /*
476   copy a message, allocating new memory for all parts
477 */
478 struct ldb_message *ldb_msg_copy(TALLOC_CTX *mem_ctx, 
479                                  const struct ldb_message *msg)
480 {
481         struct ldb_message *msg2;
482         int i, j;
483
484         msg2 = ldb_msg_copy_shallow(mem_ctx, msg);
485         if (msg2 == NULL) return NULL;
486
487         msg2->dn = ldb_dn_copy(msg2, msg2->dn);
488         if (msg2->dn == NULL) goto failed;
489
490         for (i=0;i<msg2->num_elements;i++) {
491                 struct ldb_message_element *el = &msg2->elements[i];
492                 struct ldb_val *values = el->values;
493                 el->name = talloc_strdup(msg2->elements, el->name);
494                 if (el->name == NULL) goto failed;
495                 el->values = talloc_array(msg2->elements, struct ldb_val, el->num_values);
496                 for (j=0;j<el->num_values;j++) {
497                         el->values[j] = ldb_val_dup(el->values, &values[j]);
498                         if (el->values[j].data == NULL && values[j].length != 0) {
499                                 goto failed;
500                         }
501                 }
502         }
503
504         return msg2;
505
506 failed:
507         talloc_free(msg2);
508         return NULL;
509 }
510
511
512 /*
513   canonicalise a message, merging elements of the same name
514 */
515 struct ldb_message *ldb_msg_canonicalize(struct ldb_context *ldb, 
516                                          const struct ldb_message *msg)
517 {
518         int i;
519         struct ldb_message *msg2;
520
521         msg2 = ldb_msg_copy(ldb, msg);
522         if (msg2 == NULL) return NULL;
523
524         ldb_msg_sort_elements(msg2);
525
526         for (i=1;i<msg2->num_elements;i++) {
527                 struct ldb_message_element *el1 = &msg2->elements[i-1];
528                 struct ldb_message_element *el2 = &msg2->elements[i];
529                 if (ldb_msg_element_compare_name(el1, el2) == 0) {
530                         el1->values = talloc_realloc(msg2->elements, el1->values, struct ldb_val, 
531                                                        el1->num_values + el2->num_values);
532                         if (el1->values == NULL) {
533                                 return NULL;
534                         }
535                         memcpy(el1->values + el1->num_values,
536                                el2->values,
537                                sizeof(struct ldb_val) * el2->num_values);
538                         el1->num_values += el2->num_values;
539                         talloc_free(discard_const_p(char, el2->name));
540                         if (i+1<msg2->num_elements) {
541                                 memmove(el2, el2+1, sizeof(struct ldb_message_element) * 
542                                         (msg2->num_elements - (i+1)));
543                         }
544                         msg2->num_elements--;
545                         i--;
546                 }
547         }
548
549         return msg2;
550 }
551
552
553 /*
554   return a ldb_message representing the differences between msg1 and msg2. If you
555   then use this in a ldb_modify() call it can be used to save edits to a message
556 */
557 struct ldb_message *ldb_msg_diff(struct ldb_context *ldb, 
558                                  struct ldb_message *msg1,
559                                  struct ldb_message *msg2)
560 {
561         struct ldb_message *mod;
562         struct ldb_message_element *el;
563         unsigned int i;
564
565         mod = ldb_msg_new(ldb);
566
567         mod->dn = msg1->dn;
568         mod->num_elements = 0;
569         mod->elements = NULL;
570
571         msg2 = ldb_msg_canonicalize(ldb, msg2);
572         if (msg2 == NULL) {
573                 return NULL;
574         }
575         
576         /* look in msg2 to find elements that need to be added
577            or modified */
578         for (i=0;i<msg2->num_elements;i++) {
579                 el = ldb_msg_find_element(msg1, msg2->elements[i].name);
580
581                 if (el && ldb_msg_element_compare(el, &msg2->elements[i]) == 0) {
582                         continue;
583                 }
584
585                 if (ldb_msg_add(mod, 
586                                 &msg2->elements[i],
587                                 el?LDB_FLAG_MOD_REPLACE:LDB_FLAG_MOD_ADD) != 0) {
588                         return NULL;
589                 }
590         }
591
592         /* look in msg1 to find elements that need to be deleted */
593         for (i=0;i<msg1->num_elements;i++) {
594                 el = ldb_msg_find_element(msg2, msg1->elements[i].name);
595                 if (!el) {
596                         if (ldb_msg_add_empty(mod, 
597                                               msg1->elements[i].name,
598                                               LDB_FLAG_MOD_DELETE, NULL) != 0) {
599                                 return NULL;
600                         }
601                 }
602         }
603
604         return mod;
605 }
606
607 int ldb_msg_sanity_check(struct ldb_context *ldb, 
608                          const struct ldb_message *msg)
609 {
610         int i, j;
611
612         /* basic check on DN */
613         if (msg->dn == NULL) {
614                 /* TODO: return also an error string */
615                 ldb_set_errstring(ldb, "ldb message lacks a DN!");
616                 return LDB_ERR_INVALID_DN_SYNTAX;
617         }
618
619         /* basic syntax checks */
620         for (i = 0; i < msg->num_elements; i++) {
621                 for (j = 0; j < msg->elements[i].num_values; j++) {
622                         if (msg->elements[i].values[j].length == 0) {
623                                 TALLOC_CTX *mem_ctx = talloc_new(ldb);
624                                 /* an attribute cannot be empty */
625                                 /* TODO: return also an error string */
626                                 ldb_asprintf_errstring(ldb, "Element %s has empty attribute in ldb message (%s)!",
627                                                             msg->elements[i].name, 
628                                                             ldb_dn_get_linearized(msg->dn));
629                                 talloc_free(mem_ctx);
630                                 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
631                         }
632                 }
633         }
634
635         return LDB_SUCCESS;
636 }
637
638
639
640
641 /*
642   copy an attribute list. This only copies the array, not the elements
643   (ie. the elements are left as the same pointers)
644 */
645 const char **ldb_attr_list_copy(TALLOC_CTX *mem_ctx, const char * const *attrs)
646 {
647         const char **ret;
648         int i;
649         for (i=0;attrs[i];i++) /* noop */ ;
650         ret = talloc_array(mem_ctx, const char *, i+1);
651         if (ret == NULL) {
652                 return NULL;
653         }
654         for (i=0;attrs[i];i++) {
655                 ret[i] = attrs[i];
656         }
657         ret[i] = attrs[i];
658         return ret;
659 }
660
661
662 /*
663   copy an attribute list. This only copies the array, not the elements
664   (ie. the elements are left as the same pointers).  The new attribute is added to the list.
665 */
666 const char **ldb_attr_list_copy_add(TALLOC_CTX *mem_ctx, const char * const *attrs, const char *new_attr)
667 {
668         const char **ret;
669         int i;
670         for (i=0;attrs[i];i++) /* noop */ ;
671         ret = talloc_array(mem_ctx, const char *, i+2);
672         if (ret == NULL) {
673                 return NULL;
674         }
675         for (i=0;attrs[i];i++) {
676                 ret[i] = attrs[i];
677         }
678         ret[i] = new_attr;
679         ret[i+1] = NULL;
680         return ret;
681 }
682
683
684 /*
685   return 1 if an attribute is in a list of attributes, or 0 otherwise
686 */
687 int ldb_attr_in_list(const char * const *attrs, const char *attr)
688 {
689         int i;
690         for (i=0;attrs[i];i++) {
691                 if (ldb_attr_cmp(attrs[i], attr) == 0) {
692                         return 1;
693                 }
694         }
695         return 0;
696 }
697
698
699 /*
700   rename the specified attribute in a search result
701 */
702 int ldb_msg_rename_attr(struct ldb_message *msg, const char *attr, const char *replace)
703 {
704         struct ldb_message_element *el = ldb_msg_find_element(msg, attr);
705         if (el == NULL) {
706                 return LDB_SUCCESS;
707         }
708         el->name = talloc_strdup(msg->elements, replace);
709         if (el->name == NULL) {
710                 return LDB_ERR_OPERATIONS_ERROR;
711         }
712         return LDB_SUCCESS;
713 }
714
715
716 /*
717   copy the specified attribute in a search result to a new attribute
718 */
719 int ldb_msg_copy_attr(struct ldb_message *msg, const char *attr, const char *replace)
720 {
721         struct ldb_message_element *el = ldb_msg_find_element(msg, attr);
722         if (el == NULL) {
723                 return LDB_SUCCESS;
724         }
725         if (ldb_msg_add(msg, el, 0) != 0) {
726                 return LDB_ERR_OPERATIONS_ERROR;
727         }
728         return ldb_msg_rename_attr(msg, attr, replace);
729 }
730
731
732 /*
733   remove the specified attribute in a search result
734 */
735 void ldb_msg_remove_attr(struct ldb_message *msg, const char *attr)
736 {
737         struct ldb_message_element *el = ldb_msg_find_element(msg, attr);
738         if (el) {
739                 int n = (el - msg->elements);
740                 if (n != msg->num_elements-1) {
741                         memmove(el, el+1, ((msg->num_elements-1) - n)*sizeof(*el));
742                 }
743                 msg->num_elements--;
744         }
745 }
746
747 /*
748   remove the specified element in a search result
749 */
750 void ldb_msg_remove_element(struct ldb_message *msg, struct ldb_message_element *el)
751 {
752         int n = (el - msg->elements);
753         if (n != msg->num_elements-1) {
754                 memmove(el, el+1, ((msg->num_elements-1) - n)*sizeof(*el));
755         }
756         msg->num_elements--;
757 }
758
759 /*
760   return a LDAP formatted time string
761 */
762 char *ldb_timestring(TALLOC_CTX *mem_ctx, time_t t)
763 {
764         struct tm *tm = gmtime(&t);
765         char *ts;
766         int r;
767
768         if (!tm) {
769                 return NULL;
770         }
771
772         /* we now excatly how long this string will be */
773         ts = talloc_array(mem_ctx, char, 18);
774
775         /* formatted like: 20040408072012.0Z */
776         r = snprintf(ts, 18,
777                         "%04u%02u%02u%02u%02u%02u.0Z",
778                         tm->tm_year+1900, tm->tm_mon+1,
779                         tm->tm_mday, tm->tm_hour, tm->tm_min,
780                         tm->tm_sec);
781
782         if (r != 17) {
783                 talloc_free(ts);
784                 return NULL;
785         }
786
787         return ts;
788 }
789
790
791 /*
792   convert a LDAP time string to a time_t. Return 0 if unable to convert
793 */
794 time_t ldb_string_to_time(const char *s)
795 {
796         struct tm tm;
797         
798         if (s == NULL) return 0;
799         
800         memset(&tm, 0, sizeof(tm));
801         if (sscanf(s, "%04u%02u%02u%02u%02u%02u", 
802                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
803                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
804                 return 0;
805         }
806         tm.tm_year -= 1900;
807         tm.tm_mon -= 1;
808         
809         return timegm(&tm);
810 }
811
812
813 /*
814   dump a set of results to a file. Useful from within gdb
815 */
816 void ldb_dump_results(struct ldb_context *ldb, struct ldb_result *result, FILE *f)
817 {
818         int i;
819
820         for (i = 0; i < result->count; i++) {
821                 struct ldb_ldif ldif;
822                 fprintf(f, "# record %d\n", i+1);
823                 ldif.changetype = LDB_CHANGETYPE_NONE;
824                 ldif.msg = result->msgs[i];
825                 ldb_ldif_write_file(ldb, f, &ldif);
826         }
827 }
828
829 int ldb_msg_check_string_attribute(const struct ldb_message *msg, const char *name, const char *value)
830 {
831         struct ldb_message_element *el;
832         struct ldb_val val;
833         
834         el = ldb_msg_find_element(msg, name);
835         if (el == NULL)
836                 return 0;
837
838         val.data = discard_const_p(uint8_t, value);
839         val.length = strlen(value);
840
841         if (ldb_msg_find_val(el, &val))
842                 return 1;
843
844         return 0;
845 }