bf607fd4e6dcd6d542f237559fd98478257a7ff8
[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 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 "ldb_private.h"
35
36 /*
37   create a new ldb_message in a given memory context (NULL for top level)
38 */
39 struct ldb_message *ldb_msg_new(void *mem_ctx)
40 {
41         return talloc_zero(mem_ctx, struct ldb_message);
42 }
43
44 /*
45   find an element in a message by attribute name
46 */
47 struct ldb_message_element *ldb_msg_find_element(const struct ldb_message *msg, 
48                                                  const char *attr_name)
49 {
50         unsigned int i;
51         for (i=0;i<msg->num_elements;i++) {
52                 if (ldb_attr_cmp(msg->elements[i].name, attr_name) == 0) {
53                         return &msg->elements[i];
54                 }
55         }
56         return NULL;
57 }
58
59 /*
60   see if two ldb_val structures contain exactly the same data
61   return 1 for a match, 0 for a mis-match
62 */
63 int ldb_val_equal_exact(const struct ldb_val *v1, const struct ldb_val *v2)
64 {
65         if (v1->length != v2->length) return 0;
66
67         if (v1->length == 0) return 1;
68
69         if (memcmp(v1->data, v2->data, v1->length) == 0) {
70                 return 1;
71         }
72
73         return 0;
74 }
75
76 /*
77   find a value in an element
78   assumes case sensitive comparison
79 */
80 struct ldb_val *ldb_msg_find_val(const struct ldb_message_element *el, 
81                                  struct ldb_val *val)
82 {
83         unsigned int i;
84         for (i=0;i<el->num_values;i++) {
85                 if (ldb_val_equal_exact(val, &el->values[i])) {
86                         return &el->values[i];
87                 }
88         }
89         return NULL;
90 }
91
92 /*
93   duplicate a ldb_val structure
94 */
95 struct ldb_val ldb_val_dup(void *mem_ctx, const struct ldb_val *v)
96 {
97         struct ldb_val v2;
98         v2.length = v->length;
99         if (v->data == NULL) {
100                 v2.data = NULL;
101                 return v2;
102         }
103
104         /* the +1 is to cope with buggy C library routines like strndup
105            that look one byte beyond */
106         v2.data = talloc_array(mem_ctx, uint8_t, v->length+1);
107         if (!v2.data) {
108                 v2.length = 0;
109                 return v2;
110         }
111
112         memcpy(v2.data, v->data, v->length);
113         ((char *)v2.data)[v->length] = 0;
114         return v2;
115 }
116
117 /*
118   add an empty element to a message
119 */
120 int ldb_msg_add_empty(  struct ldb_message *msg,
121                         const char *attr_name,
122                         int flags,
123                         struct ldb_message_element **return_el)
124 {
125         struct ldb_message_element *els;
126
127         els = talloc_realloc(msg, msg->elements, 
128                              struct ldb_message_element, msg->num_elements+1);
129         if (!els) {
130                 errno = ENOMEM;
131                 return LDB_ERR_OPERATIONS_ERROR;
132         }
133
134         els[msg->num_elements].values = NULL;
135         els[msg->num_elements].num_values = 0;
136         els[msg->num_elements].flags = flags;
137         els[msg->num_elements].name = talloc_strdup(els, attr_name);
138         if (!els[msg->num_elements].name) {
139                 errno = ENOMEM;
140                 return LDB_ERR_OPERATIONS_ERROR;
141         }
142
143         msg->elements = els;
144         msg->num_elements++;
145
146         if (return_el) {
147                 *return_el = &els[msg->num_elements-1];
148         }
149
150         return LDB_SUCCESS;
151 }
152
153 /*
154   add an empty element to a message
155 */
156 int ldb_msg_add(struct ldb_message *msg, 
157                 const struct ldb_message_element *el, 
158                 int flags)
159 {
160         /* We have to copy this, just in case *el is a pointer into
161          * what ldb_msg_add_empty() is about to realloc() */
162         struct ldb_message_element el_copy = *el;
163         if (ldb_msg_add_empty(msg, el->name, flags, NULL) != 0) {
164                 return LDB_ERR_OPERATIONS_ERROR;
165         }
166
167         msg->elements[msg->num_elements-1] = el_copy;
168         msg->elements[msg->num_elements-1].flags = flags;
169
170         return LDB_SUCCESS;
171 }
172
173 /*
174   add a value to a message
175 */
176 int ldb_msg_add_value(struct ldb_message *msg, 
177                       const char *attr_name,
178                       const struct ldb_val *val,
179                       struct ldb_message_element **return_el)
180 {
181         struct ldb_message_element *el;
182         struct ldb_val *vals;
183         int ret;
184
185         el = ldb_msg_find_element(msg, attr_name);
186         if (!el) {
187                 ret = ldb_msg_add_empty(msg, attr_name, 0, &el);
188                 if (ret != LDB_SUCCESS) {
189                         return ret;
190                 }
191         }
192
193         vals = talloc_realloc(msg, el->values, struct ldb_val, el->num_values+1);
194         if (!vals) {
195                 errno = ENOMEM;
196                 return LDB_ERR_OPERATIONS_ERROR;
197         }
198         el->values = vals;
199         el->values[el->num_values] = *val;
200         el->num_values++;
201
202         if (return_el) {
203                 *return_el = el;
204         }
205
206         return LDB_SUCCESS;
207 }
208
209
210 /*
211   add a value to a message, stealing it into the 'right' place
212 */
213 int ldb_msg_add_steal_value(struct ldb_message *msg, 
214                             const char *attr_name,
215                             struct ldb_val *val)
216 {
217         int ret;
218         struct ldb_message_element *el;
219
220         ret = ldb_msg_add_value(msg, attr_name, val, &el);
221         if (ret == LDB_SUCCESS) {
222                 talloc_steal(el->values, val->data);
223         }
224         return ret;
225 }
226
227
228 /*
229   add a string element to a message
230 */
231 int ldb_msg_add_string(struct ldb_message *msg, 
232                        const char *attr_name, const char *str)
233 {
234         struct ldb_val val;
235
236         val.data = discard_const_p(uint8_t, str);
237         val.length = strlen(str);
238
239         if (val.length == 0) {
240                 /* allow empty strings as non-existant attributes */
241                 return LDB_SUCCESS;
242         }
243
244         return ldb_msg_add_value(msg, attr_name, &val, NULL);
245 }
246
247 /*
248   add a string element to a message, stealing it into the 'right' place
249 */
250 int ldb_msg_add_steal_string(struct ldb_message *msg, 
251                              const char *attr_name, char *str)
252 {
253         struct ldb_val val;
254
255         val.data = (uint8_t *)str;
256         val.length = strlen(str);
257
258         return ldb_msg_add_steal_value(msg, attr_name, &val);
259 }
260
261 /*
262   add a printf formatted element to a message
263 */
264 int ldb_msg_add_fmt(struct ldb_message *msg, 
265                     const char *attr_name, const char *fmt, ...)
266 {
267         struct ldb_val val;
268         va_list ap;
269         char *str;
270
271         va_start(ap, fmt);
272         str = talloc_vasprintf(msg, fmt, ap);
273         va_end(ap);
274
275         if (str == NULL) return LDB_ERR_OPERATIONS_ERROR;
276
277         val.data   = (uint8_t *)str;
278         val.length = strlen(str);
279
280         return ldb_msg_add_steal_value(msg, attr_name, &val);
281 }
282
283 /*
284   compare two ldb_message_element structures
285   assumes case senistive comparison
286 */
287 int ldb_msg_element_compare(struct ldb_message_element *el1, 
288                             struct ldb_message_element *el2)
289 {
290         unsigned int i;
291
292         if (el1->num_values != el2->num_values) {
293                 return el1->num_values - el2->num_values;
294         }
295
296         for (i=0;i<el1->num_values;i++) {
297                 if (!ldb_msg_find_val(el2, &el1->values[i])) {
298                         return -1;
299                 }
300         }
301
302         return 0;
303 }
304
305 /*
306   compare two ldb_message_element structures
307   comparing by element name
308 */
309 int ldb_msg_element_compare_name(struct ldb_message_element *el1, 
310                                  struct ldb_message_element *el2)
311 {
312         return ldb_attr_cmp(el1->name, el2->name);
313 }
314
315 /*
316   convenience functions to return common types from a message
317   these return the first value if the attribute is multi-valued
318 */
319 const struct ldb_val *ldb_msg_find_ldb_val(const struct ldb_message *msg, 
320                                            const char *attr_name)
321 {
322         struct ldb_message_element *el = ldb_msg_find_element(msg, attr_name);
323         if (!el || el->num_values == 0) {
324                 return NULL;
325         }
326         return &el->values[0];
327 }
328
329 int ldb_msg_find_attr_as_int(const struct ldb_message *msg, 
330                              const char *attr_name,
331                              int default_value)
332 {
333         const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
334         if (!v || !v->data) {
335                 return default_value;
336         }
337         return strtol((const char *)v->data, NULL, 0);
338 }
339
340 unsigned int ldb_msg_find_attr_as_uint(const struct ldb_message *msg, 
341                                        const char *attr_name,
342                                        unsigned int default_value)
343 {
344         const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
345         if (!v || !v->data) {
346                 return default_value;
347         }
348         return strtoul((const char *)v->data, NULL, 0);
349 }
350
351 int64_t ldb_msg_find_attr_as_int64(const struct ldb_message *msg, 
352                                    const char *attr_name,
353                                    int64_t default_value)
354 {
355         const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
356         if (!v || !v->data) {
357                 return default_value;
358         }
359         return strtoll((const char *)v->data, NULL, 0);
360 }
361
362 uint64_t ldb_msg_find_attr_as_uint64(const struct ldb_message *msg, 
363                                      const char *attr_name,
364                                      uint64_t default_value)
365 {
366         const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
367         if (!v || !v->data) {
368                 return default_value;
369         }
370         return strtoull((const char *)v->data, NULL, 0);
371 }
372
373 double ldb_msg_find_attr_as_double(const struct ldb_message *msg, 
374                                    const char *attr_name,
375                                    double default_value)
376 {
377         const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
378         if (!v || !v->data) {
379                 return default_value;
380         }
381         return strtod((const char *)v->data, NULL);
382 }
383
384 int ldb_msg_find_attr_as_bool(const struct ldb_message *msg, 
385                               const char *attr_name,
386                               int default_value)
387 {
388         const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
389         if (!v || !v->data) {
390                 return default_value;
391         }
392         if (v->length == 5 && strncasecmp((const char *)v->data, "FALSE", 5) == 0) {
393                 return 0;
394         }
395         if (v->length == 4 && strncasecmp((const char *)v->data, "TRUE", 4) == 0) {
396                 return 1;
397         }
398         return default_value;
399 }
400
401 const char *ldb_msg_find_attr_as_string(const struct ldb_message *msg, 
402                                         const char *attr_name,
403                                         const char *default_value)
404 {
405         const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
406         if (!v || !v->data) {
407                 return default_value;
408         }
409         return (const char *)v->data;
410 }
411
412 struct ldb_dn *ldb_msg_find_attr_as_dn(struct ldb_context *ldb,
413                                        void *mem_ctx,
414                                        const struct ldb_message *msg,
415                                        const char *attr_name)
416 {
417         struct ldb_dn *res_dn;
418         const struct ldb_val *v;
419
420         v = ldb_msg_find_ldb_val(msg, attr_name);
421         if (!v || !v->data) {
422                 return NULL;
423         }
424         res_dn = ldb_dn_from_ldb_val(mem_ctx, ldb, v);
425         if ( ! ldb_dn_validate(res_dn)) {
426                 talloc_free(res_dn);
427                 return NULL;
428         }
429         return res_dn;
430 }
431
432 /*
433   sort the elements of a message by name
434 */
435 void ldb_msg_sort_elements(struct ldb_message *msg)
436 {
437         qsort(msg->elements, msg->num_elements, sizeof(struct ldb_message_element), 
438               (comparison_fn_t)ldb_msg_element_compare_name);
439 }
440
441 /*
442   shallow copy a message - copying only the elements array so that the caller
443   can safely add new elements without changing the message
444 */
445 struct ldb_message *ldb_msg_copy_shallow(TALLOC_CTX *mem_ctx, 
446                                          const struct ldb_message *msg)
447 {
448         struct ldb_message *msg2;
449         int i;
450
451         msg2 = talloc(mem_ctx, struct ldb_message);
452         if (msg2 == NULL) return NULL;
453
454         *msg2 = *msg;
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         if (mod == NULL) {
564                 return NULL;
565         }
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                 talloc_free(mod);
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) != LDB_SUCCESS) {
589                         talloc_free(mod);
590                         return NULL;
591                 }
592         }
593
594         /* look in msg1 to find elements that need to be deleted */
595         for (i=0;i<msg1->num_elements;i++) {
596                 el = ldb_msg_find_element(msg2, msg1->elements[i].name);
597                 if (el == NULL) {
598                         if (ldb_msg_add_empty(mod, 
599                                               msg1->elements[i].name,
600                                               LDB_FLAG_MOD_DELETE, NULL) != LDB_SUCCESS) {
601                                 talloc_free(mod);
602                                 return NULL;
603                         }
604                 }
605         }
606
607         return mod;
608 }
609
610 int ldb_msg_sanity_check(struct ldb_context *ldb, 
611                          const struct ldb_message *msg)
612 {
613         int i, j;
614
615         /* basic check on DN */
616         if (msg->dn == NULL) {
617                 /* TODO: return also an error string */
618                 ldb_set_errstring(ldb, "ldb message lacks a DN!");
619                 return LDB_ERR_INVALID_DN_SYNTAX;
620         }
621
622         /* basic syntax checks */
623         for (i = 0; i < msg->num_elements; i++) {
624                 for (j = 0; j < msg->elements[i].num_values; j++) {
625                         if (msg->elements[i].values[j].length == 0) {
626                                 TALLOC_CTX *mem_ctx = talloc_new(ldb);
627                                 /* an attribute cannot be empty */
628                                 /* TODO: return also an error string */
629                                 ldb_asprintf_errstring(ldb, "Element %s has empty attribute in ldb message (%s)!",
630                                                             msg->elements[i].name, 
631                                                             ldb_dn_get_linearized(msg->dn));
632                                 talloc_free(mem_ctx);
633                                 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
634                         }
635                 }
636         }
637
638         return LDB_SUCCESS;
639 }
640
641
642
643
644 /*
645   copy an attribute list. This only copies the array, not the elements
646   (ie. the elements are left as the same pointers)
647 */
648 const char **ldb_attr_list_copy(TALLOC_CTX *mem_ctx, const char * const *attrs)
649 {
650         const char **ret;
651         int i;
652         for (i=0;attrs && attrs[i];i++) /* noop */ ;
653         ret = talloc_array(mem_ctx, const char *, i+1);
654         if (ret == NULL) {
655                 return NULL;
656         }
657         for (i=0;attrs && attrs[i];i++) {
658                 ret[i] = attrs[i];
659         }
660         ret[i] = attrs[i];
661         return ret;
662 }
663
664
665 /*
666   copy an attribute list. This only copies the array, not the elements
667   (ie. the elements are left as the same pointers).  The new attribute is added to the list.
668 */
669 const char **ldb_attr_list_copy_add(TALLOC_CTX *mem_ctx, const char * const *attrs, const char *new_attr)
670 {
671         const char **ret;
672         int i;
673         bool found = false;
674         for (i=0;attrs && attrs[i];i++) {
675                 if (ldb_attr_cmp(attrs[i], new_attr) == 0) {
676                         found = true;
677                 }
678         }
679         if (found) {
680                 return ldb_attr_list_copy(mem_ctx, attrs);
681         }
682         ret = talloc_array(mem_ctx, const char *, i+2);
683         if (ret == NULL) {
684                 return NULL;
685         }
686         for (i=0;attrs && attrs[i];i++) {
687                 ret[i] = attrs[i];
688         }
689         ret[i] = new_attr;
690         ret[i+1] = NULL;
691         return ret;
692 }
693
694
695 /*
696   return 1 if an attribute is in a list of attributes, or 0 otherwise
697 */
698 int ldb_attr_in_list(const char * const *attrs, const char *attr)
699 {
700         int i;
701         for (i=0;attrs && attrs[i];i++) {
702                 if (ldb_attr_cmp(attrs[i], attr) == 0) {
703                         return 1;
704                 }
705         }
706         return 0;
707 }
708
709
710 /*
711   rename the specified attribute in a search result
712 */
713 int ldb_msg_rename_attr(struct ldb_message *msg, const char *attr, const char *replace)
714 {
715         struct ldb_message_element *el = ldb_msg_find_element(msg, attr);
716         if (el == NULL) {
717                 return LDB_SUCCESS;
718         }
719         el->name = talloc_strdup(msg->elements, replace);
720         if (el->name == NULL) {
721                 return LDB_ERR_OPERATIONS_ERROR;
722         }
723         return LDB_SUCCESS;
724 }
725
726
727 /*
728   copy the specified attribute in a search result to a new attribute
729 */
730 int ldb_msg_copy_attr(struct ldb_message *msg, const char *attr, const char *replace)
731 {
732         struct ldb_message_element *el = ldb_msg_find_element(msg, attr);
733         if (el == NULL) {
734                 return LDB_SUCCESS;
735         }
736         if (ldb_msg_add(msg, el, 0) != 0) {
737                 return LDB_ERR_OPERATIONS_ERROR;
738         }
739         return ldb_msg_rename_attr(msg, attr, replace);
740 }
741
742 /*
743   remove the specified element in a search result
744 */
745 void ldb_msg_remove_element(struct ldb_message *msg, struct ldb_message_element *el)
746 {
747         int n = (el - msg->elements);
748         if (n >= msg->num_elements) {
749                 /* should we abort() here? */
750                 return;
751         }
752         if (n != msg->num_elements-1) {
753                 memmove(el, el+1, ((msg->num_elements-1) - n)*sizeof(*el));
754         }
755         msg->num_elements--;
756 }
757
758
759 /*
760   remove the specified attribute in a search result
761 */
762 void ldb_msg_remove_attr(struct ldb_message *msg, const char *attr)
763 {
764         struct ldb_message_element *el = ldb_msg_find_element(msg, attr);
765         if (el) {
766                 ldb_msg_remove_element(msg, el);
767         }
768 }
769
770 /*
771   return a LDAP formatted GeneralizedTime string
772 */
773 char *ldb_timestring(TALLOC_CTX *mem_ctx, time_t t)
774 {
775         struct tm *tm = gmtime(&t);
776         char *ts;
777         int r;
778
779         if (!tm) {
780                 return NULL;
781         }
782
783         /* we now excatly how long this string will be */
784         ts = talloc_array(mem_ctx, char, 18);
785
786         /* formatted like: 20040408072012.0Z */
787         r = snprintf(ts, 18,
788                         "%04u%02u%02u%02u%02u%02u.0Z",
789                         tm->tm_year+1900, tm->tm_mon+1,
790                         tm->tm_mday, tm->tm_hour, tm->tm_min,
791                         tm->tm_sec);
792
793         if (r != 17) {
794                 talloc_free(ts);
795                 return NULL;
796         }
797
798         return ts;
799 }
800
801 /*
802   convert a LDAP GeneralizedTime string to a time_t. Return 0 if unable to convert
803 */
804 time_t ldb_string_to_time(const char *s)
805 {
806         struct tm tm;
807         
808         if (s == NULL) return 0;
809         
810         memset(&tm, 0, sizeof(tm));
811         if (sscanf(s, "%04u%02u%02u%02u%02u%02u", 
812                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
813                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
814                 return 0;
815         }
816         tm.tm_year -= 1900;
817         tm.tm_mon -= 1;
818         
819         return timegm(&tm);
820 }
821
822 /*
823   return a LDAP formatted UTCTime string
824 */
825 char *ldb_timestring_utc(TALLOC_CTX *mem_ctx, time_t t)
826 {
827         struct tm *tm = gmtime(&t);
828         char *ts;
829         int r;
830
831         if (!tm) {
832                 return NULL;
833         }
834
835         /* we now excatly how long this string will be */
836         ts = talloc_array(mem_ctx, char, 14);
837
838         /* formatted like: 20040408072012.0Z => 040408072012Z */
839         r = snprintf(ts, 14,
840                         "%02u%02u%02u%02u%02u%02uZ",
841                         (tm->tm_year+1900)%100, tm->tm_mon+1,
842                         tm->tm_mday, tm->tm_hour, tm->tm_min,
843                         tm->tm_sec);
844
845         if (r != 13) {
846                 talloc_free(ts);
847                 return NULL;
848         }
849
850         return ts;
851 }
852
853 /*
854   convert a LDAP UTCTime string to a time_t. Return 0 if unable to convert
855 */
856 time_t ldb_string_utc_to_time(const char *s)
857 {
858         struct tm tm;
859         
860         if (s == NULL) return 0;
861         
862         memset(&tm, 0, sizeof(tm));
863         if (sscanf(s, "%02u%02u%02u%02u%02u%02u", 
864                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
865                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
866                 return 0;
867         }
868         if (tm.tm_year < 50) {
869                 tm.tm_year += 100;
870         }
871         tm.tm_mon -= 1;
872         
873         return timegm(&tm);
874 }
875
876
877 /*
878   dump a set of results to a file. Useful from within gdb
879 */
880 void ldb_dump_results(struct ldb_context *ldb, struct ldb_result *result, FILE *f)
881 {
882         int i;
883
884         for (i = 0; i < result->count; i++) {
885                 struct ldb_ldif ldif;
886                 fprintf(f, "# record %d\n", i+1);
887                 ldif.changetype = LDB_CHANGETYPE_NONE;
888                 ldif.msg = result->msgs[i];
889                 ldb_ldif_write_file(ldb, f, &ldif);
890         }
891 }
892
893 int ldb_msg_check_string_attribute(const struct ldb_message *msg, const char *name, const char *value)
894 {
895         struct ldb_message_element *el;
896         struct ldb_val val;
897         
898         el = ldb_msg_find_element(msg, name);
899         if (el == NULL) {
900                 return 0;
901         }
902
903         val.data = discard_const_p(uint8_t, value);
904         val.length = strlen(value);
905
906         if (ldb_msg_find_val(el, &val)) {
907                 return 1;
908         }
909
910         return 0;
911 }
912