s4-ldb: added a warning about ldb_msg_add_dn
[kamenim/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 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         if (v1->data == v2->data) return 1;
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 DN element to a message
263   WARNING: this uses the linearized string from the dn, and does not
264   copy the string.
265 */
266 int ldb_msg_add_dn(struct ldb_message *msg, const char *attr_name,
267                    struct ldb_dn *dn)
268 {
269         return ldb_msg_add_string(msg, attr_name, ldb_dn_get_linearized(dn));
270 }
271
272 /*
273   add a printf formatted element to a message
274 */
275 int ldb_msg_add_fmt(struct ldb_message *msg, 
276                     const char *attr_name, const char *fmt, ...)
277 {
278         struct ldb_val val;
279         va_list ap;
280         char *str;
281
282         va_start(ap, fmt);
283         str = talloc_vasprintf(msg, fmt, ap);
284         va_end(ap);
285
286         if (str == NULL) return LDB_ERR_OPERATIONS_ERROR;
287
288         val.data   = (uint8_t *)str;
289         val.length = strlen(str);
290
291         return ldb_msg_add_steal_value(msg, attr_name, &val);
292 }
293
294 /*
295   compare two ldb_message_element structures
296   assumes case senistive comparison
297 */
298 int ldb_msg_element_compare(struct ldb_message_element *el1, 
299                             struct ldb_message_element *el2)
300 {
301         unsigned int i;
302
303         if (el1->num_values != el2->num_values) {
304                 return el1->num_values - el2->num_values;
305         }
306
307         for (i=0;i<el1->num_values;i++) {
308                 if (!ldb_msg_find_val(el2, &el1->values[i])) {
309                         return -1;
310                 }
311         }
312
313         return 0;
314 }
315
316 /*
317   compare two ldb_message_element structures
318   comparing by element name
319 */
320 int ldb_msg_element_compare_name(struct ldb_message_element *el1, 
321                                  struct ldb_message_element *el2)
322 {
323         return ldb_attr_cmp(el1->name, el2->name);
324 }
325
326 /*
327   convenience functions to return common types from a message
328   these return the first value if the attribute is multi-valued
329 */
330 const struct ldb_val *ldb_msg_find_ldb_val(const struct ldb_message *msg, 
331                                            const char *attr_name)
332 {
333         struct ldb_message_element *el = ldb_msg_find_element(msg, attr_name);
334         if (!el || el->num_values == 0) {
335                 return NULL;
336         }
337         return &el->values[0];
338 }
339
340 int ldb_msg_find_attr_as_int(const struct ldb_message *msg, 
341                              const char *attr_name,
342                              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 strtol((const char *)v->data, NULL, 0);
349 }
350
351 unsigned int ldb_msg_find_attr_as_uint(const struct ldb_message *msg, 
352                                        const char *attr_name,
353                                        unsigned int 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 strtoul((const char *)v->data, NULL, 0);
360 }
361
362 int64_t ldb_msg_find_attr_as_int64(const struct ldb_message *msg, 
363                                    const char *attr_name,
364                                    int64_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 strtoll((const char *)v->data, NULL, 0);
371 }
372
373 uint64_t ldb_msg_find_attr_as_uint64(const struct ldb_message *msg, 
374                                      const char *attr_name,
375                                      uint64_t 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 strtoull((const char *)v->data, NULL, 0);
382 }
383
384 double ldb_msg_find_attr_as_double(const struct ldb_message *msg, 
385                                    const char *attr_name,
386                                    double 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         return strtod((const char *)v->data, NULL);
393 }
394
395 int ldb_msg_find_attr_as_bool(const struct ldb_message *msg, 
396                               const char *attr_name,
397                               int default_value)
398 {
399         const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
400         if (!v || !v->data) {
401                 return default_value;
402         }
403         if (v->length == 5 && strncasecmp((const char *)v->data, "FALSE", 5) == 0) {
404                 return 0;
405         }
406         if (v->length == 4 && strncasecmp((const char *)v->data, "TRUE", 4) == 0) {
407                 return 1;
408         }
409         return default_value;
410 }
411
412 const char *ldb_msg_find_attr_as_string(const struct ldb_message *msg, 
413                                         const char *attr_name,
414                                         const char *default_value)
415 {
416         const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
417         if (!v || !v->data) {
418                 return default_value;
419         }
420         return (const char *)v->data;
421 }
422
423 struct ldb_dn *ldb_msg_find_attr_as_dn(struct ldb_context *ldb,
424                                        void *mem_ctx,
425                                        const struct ldb_message *msg,
426                                        const char *attr_name)
427 {
428         struct ldb_dn *res_dn;
429         const struct ldb_val *v;
430
431         v = ldb_msg_find_ldb_val(msg, attr_name);
432         if (!v || !v->data) {
433                 return NULL;
434         }
435         res_dn = ldb_dn_from_ldb_val(mem_ctx, ldb, v);
436         if ( ! ldb_dn_validate(res_dn)) {
437                 talloc_free(res_dn);
438                 return NULL;
439         }
440         return res_dn;
441 }
442
443 /*
444   sort the elements of a message by name
445 */
446 void ldb_msg_sort_elements(struct ldb_message *msg)
447 {
448         qsort(msg->elements, msg->num_elements, sizeof(struct ldb_message_element), 
449               (comparison_fn_t)ldb_msg_element_compare_name);
450 }
451
452 /*
453   shallow copy a message - copying only the elements array so that the caller
454   can safely add new elements without changing the message
455 */
456 struct ldb_message *ldb_msg_copy_shallow(TALLOC_CTX *mem_ctx, 
457                                          const struct ldb_message *msg)
458 {
459         struct ldb_message *msg2;
460         int i;
461
462         msg2 = talloc(mem_ctx, struct ldb_message);
463         if (msg2 == NULL) return NULL;
464
465         *msg2 = *msg;
466
467         msg2->elements = talloc_array(msg2, struct ldb_message_element, 
468                                       msg2->num_elements);
469         if (msg2->elements == NULL) goto failed;
470
471         for (i=0;i<msg2->num_elements;i++) {
472                 msg2->elements[i] = msg->elements[i];
473         }
474
475         return msg2;
476
477 failed:
478         talloc_free(msg2);
479         return NULL;
480 }
481
482
483 /*
484   copy a message, allocating new memory for all parts
485 */
486 struct ldb_message *ldb_msg_copy(TALLOC_CTX *mem_ctx, 
487                                  const struct ldb_message *msg)
488 {
489         struct ldb_message *msg2;
490         int i, j;
491
492         msg2 = ldb_msg_copy_shallow(mem_ctx, msg);
493         if (msg2 == NULL) return NULL;
494
495         msg2->dn = ldb_dn_copy(msg2, msg2->dn);
496         if (msg2->dn == NULL) goto failed;
497
498         for (i=0;i<msg2->num_elements;i++) {
499                 struct ldb_message_element *el = &msg2->elements[i];
500                 struct ldb_val *values = el->values;
501                 el->name = talloc_strdup(msg2->elements, el->name);
502                 if (el->name == NULL) goto failed;
503                 el->values = talloc_array(msg2->elements, struct ldb_val, el->num_values);
504                 for (j=0;j<el->num_values;j++) {
505                         el->values[j] = ldb_val_dup(el->values, &values[j]);
506                         if (el->values[j].data == NULL && values[j].length != 0) {
507                                 goto failed;
508                         }
509                 }
510         }
511
512         return msg2;
513
514 failed:
515         talloc_free(msg2);
516         return NULL;
517 }
518
519
520 /*
521   canonicalise a message, merging elements of the same name
522 */
523 struct ldb_message *ldb_msg_canonicalize(struct ldb_context *ldb, 
524                                          const struct ldb_message *msg)
525 {
526         int i;
527         struct ldb_message *msg2;
528
529         msg2 = ldb_msg_copy(ldb, msg);
530         if (msg2 == NULL) return NULL;
531
532         ldb_msg_sort_elements(msg2);
533
534         for (i=1;i<msg2->num_elements;i++) {
535                 struct ldb_message_element *el1 = &msg2->elements[i-1];
536                 struct ldb_message_element *el2 = &msg2->elements[i];
537                 if (ldb_msg_element_compare_name(el1, el2) == 0) {
538                         el1->values = talloc_realloc(msg2->elements, el1->values, struct ldb_val, 
539                                                        el1->num_values + el2->num_values);
540                         if (el1->num_values + el2->num_values > 0 && el1->values == NULL) {
541                                 return NULL;
542                         }
543                         memcpy(el1->values + el1->num_values,
544                                el2->values,
545                                sizeof(struct ldb_val) * el2->num_values);
546                         el1->num_values += el2->num_values;
547                         talloc_free(discard_const_p(char, el2->name));
548                         if (i+1<msg2->num_elements) {
549                                 memmove(el2, el2+1, sizeof(struct ldb_message_element) * 
550                                         (msg2->num_elements - (i+1)));
551                         }
552                         msg2->num_elements--;
553                         i--;
554                 }
555         }
556
557         return msg2;
558 }
559
560
561 /*
562   return a ldb_message representing the differences between msg1 and msg2. If you
563   then use this in a ldb_modify() call it can be used to save edits to a message
564 */
565 struct ldb_message *ldb_msg_diff(struct ldb_context *ldb, 
566                                  struct ldb_message *msg1,
567                                  struct ldb_message *msg2)
568 {
569         struct ldb_message *mod;
570         struct ldb_message_element *el;
571         unsigned int i;
572
573         mod = ldb_msg_new(ldb);
574         if (mod == NULL) {
575                 return NULL;
576         }
577
578         mod->dn = msg1->dn;
579         mod->num_elements = 0;
580         mod->elements = NULL;
581
582         msg2 = ldb_msg_canonicalize(ldb, msg2);
583         if (msg2 == NULL) {
584                 talloc_free(mod);
585                 return NULL;
586         }
587         
588         /* look in msg2 to find elements that need to be added
589            or modified */
590         for (i=0;i<msg2->num_elements;i++) {
591                 el = ldb_msg_find_element(msg1, msg2->elements[i].name);
592
593                 if (el && ldb_msg_element_compare(el, &msg2->elements[i]) == 0) {
594                         continue;
595                 }
596
597                 if (ldb_msg_add(mod, 
598                                 &msg2->elements[i],
599                                 el?LDB_FLAG_MOD_REPLACE:LDB_FLAG_MOD_ADD) != LDB_SUCCESS) {
600                         talloc_free(mod);
601                         return NULL;
602                 }
603         }
604
605         /* look in msg1 to find elements that need to be deleted */
606         for (i=0;i<msg1->num_elements;i++) {
607                 el = ldb_msg_find_element(msg2, msg1->elements[i].name);
608                 if (el == NULL) {
609                         if (ldb_msg_add_empty(mod, 
610                                               msg1->elements[i].name,
611                                               LDB_FLAG_MOD_DELETE, NULL) != LDB_SUCCESS) {
612                                 talloc_free(mod);
613                                 return NULL;
614                         }
615                 }
616         }
617
618         return mod;
619 }
620
621 int ldb_msg_sanity_check(struct ldb_context *ldb, 
622                          const struct ldb_message *msg)
623 {
624         int i, j;
625
626         /* basic check on DN */
627         if (msg->dn == NULL) {
628                 /* TODO: return also an error string */
629                 ldb_set_errstring(ldb, "ldb message lacks a DN!");
630                 return LDB_ERR_INVALID_DN_SYNTAX;
631         }
632
633         /* basic syntax checks */
634         for (i = 0; i < msg->num_elements; i++) {
635                 for (j = 0; j < msg->elements[i].num_values; j++) {
636                         if (msg->elements[i].values[j].length == 0) {
637                                 TALLOC_CTX *mem_ctx = talloc_new(ldb);
638                                 /* an attribute cannot be empty */
639                                 /* TODO: return also an error string */
640                                 ldb_asprintf_errstring(ldb, "Element %s has empty attribute in ldb message (%s)!",
641                                                             msg->elements[i].name, 
642                                                             ldb_dn_get_linearized(msg->dn));
643                                 talloc_free(mem_ctx);
644                                 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
645                         }
646                 }
647         }
648
649         return LDB_SUCCESS;
650 }
651
652
653
654
655 /*
656   copy an attribute list. This only copies the array, not the elements
657   (ie. the elements are left as the same pointers)
658 */
659 const char **ldb_attr_list_copy(TALLOC_CTX *mem_ctx, const char * const *attrs)
660 {
661         const char **ret;
662         int i;
663         for (i=0;attrs && attrs[i];i++) /* noop */ ;
664         ret = talloc_array(mem_ctx, const char *, i+1);
665         if (ret == NULL) {
666                 return NULL;
667         }
668         for (i=0;attrs && attrs[i];i++) {
669                 ret[i] = attrs[i];
670         }
671         ret[i] = attrs[i];
672         return ret;
673 }
674
675
676 /*
677   copy an attribute list. This only copies the array, not the elements
678   (ie. the elements are left as the same pointers).  The new attribute is added to the list.
679 */
680 const char **ldb_attr_list_copy_add(TALLOC_CTX *mem_ctx, const char * const *attrs, const char *new_attr)
681 {
682         const char **ret;
683         int i;
684         bool found = false;
685         for (i=0;attrs && attrs[i];i++) {
686                 if (ldb_attr_cmp(attrs[i], new_attr) == 0) {
687                         found = true;
688                 }
689         }
690         if (found) {
691                 return ldb_attr_list_copy(mem_ctx, attrs);
692         }
693         ret = talloc_array(mem_ctx, const char *, i+2);
694         if (ret == NULL) {
695                 return NULL;
696         }
697         for (i=0;attrs && attrs[i];i++) {
698                 ret[i] = attrs[i];
699         }
700         ret[i] = new_attr;
701         ret[i+1] = NULL;
702         return ret;
703 }
704
705
706 /*
707   return 1 if an attribute is in a list of attributes, or 0 otherwise
708 */
709 int ldb_attr_in_list(const char * const *attrs, const char *attr)
710 {
711         int i;
712         for (i=0;attrs && attrs[i];i++) {
713                 if (ldb_attr_cmp(attrs[i], attr) == 0) {
714                         return 1;
715                 }
716         }
717         return 0;
718 }
719
720
721 /*
722   rename the specified attribute in a search result
723 */
724 int ldb_msg_rename_attr(struct ldb_message *msg, const char *attr, const char *replace)
725 {
726         struct ldb_message_element *el = ldb_msg_find_element(msg, attr);
727         if (el == NULL) {
728                 return LDB_SUCCESS;
729         }
730         el->name = talloc_strdup(msg->elements, replace);
731         if (el->name == NULL) {
732                 return LDB_ERR_OPERATIONS_ERROR;
733         }
734         return LDB_SUCCESS;
735 }
736
737
738 /*
739   copy the specified attribute in a search result to a new attribute
740 */
741 int ldb_msg_copy_attr(struct ldb_message *msg, const char *attr, const char *replace)
742 {
743         struct ldb_message_element *el = ldb_msg_find_element(msg, attr);
744         if (el == NULL) {
745                 return LDB_SUCCESS;
746         }
747         if (ldb_msg_add(msg, el, 0) != 0) {
748                 return LDB_ERR_OPERATIONS_ERROR;
749         }
750         return ldb_msg_rename_attr(msg, attr, replace);
751 }
752
753 /*
754   remove the specified element in a search result
755 */
756 void ldb_msg_remove_element(struct ldb_message *msg, struct ldb_message_element *el)
757 {
758         int n = (el - msg->elements);
759         if (n >= msg->num_elements) {
760                 /* should we abort() here? */
761                 return;
762         }
763         if (n != msg->num_elements-1) {
764                 memmove(el, el+1, ((msg->num_elements-1) - n)*sizeof(*el));
765         }
766         msg->num_elements--;
767 }
768
769
770 /*
771   remove the specified attribute in a search result
772 */
773 void ldb_msg_remove_attr(struct ldb_message *msg, const char *attr)
774 {
775         struct ldb_message_element *el = ldb_msg_find_element(msg, attr);
776         if (el) {
777                 ldb_msg_remove_element(msg, el);
778         }
779 }
780
781 /*
782   return a LDAP formatted GeneralizedTime string
783 */
784 char *ldb_timestring(TALLOC_CTX *mem_ctx, time_t t)
785 {
786         struct tm *tm = gmtime(&t);
787         char *ts;
788         int r;
789
790         if (!tm) {
791                 return NULL;
792         }
793
794         /* we now excatly how long this string will be */
795         ts = talloc_array(mem_ctx, char, 18);
796
797         /* formatted like: 20040408072012.0Z */
798         r = snprintf(ts, 18,
799                         "%04u%02u%02u%02u%02u%02u.0Z",
800                         tm->tm_year+1900, tm->tm_mon+1,
801                         tm->tm_mday, tm->tm_hour, tm->tm_min,
802                         tm->tm_sec);
803
804         if (r != 17) {
805                 talloc_free(ts);
806                 return NULL;
807         }
808
809         return ts;
810 }
811
812 /*
813   convert a LDAP GeneralizedTime string to a time_t. Return 0 if unable to convert
814 */
815 time_t ldb_string_to_time(const char *s)
816 {
817         struct tm tm;
818         
819         if (s == NULL) return 0;
820         
821         memset(&tm, 0, sizeof(tm));
822         if (sscanf(s, "%04u%02u%02u%02u%02u%02u", 
823                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
824                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
825                 return 0;
826         }
827         tm.tm_year -= 1900;
828         tm.tm_mon -= 1;
829         
830         return timegm(&tm);
831 }
832
833 /*
834   return a LDAP formatted UTCTime string
835 */
836 char *ldb_timestring_utc(TALLOC_CTX *mem_ctx, time_t t)
837 {
838         struct tm *tm = gmtime(&t);
839         char *ts;
840         int r;
841
842         if (!tm) {
843                 return NULL;
844         }
845
846         /* we now excatly how long this string will be */
847         ts = talloc_array(mem_ctx, char, 14);
848
849         /* formatted like: 20040408072012.0Z => 040408072012Z */
850         r = snprintf(ts, 14,
851                         "%02u%02u%02u%02u%02u%02uZ",
852                         (tm->tm_year+1900)%100, tm->tm_mon+1,
853                         tm->tm_mday, tm->tm_hour, tm->tm_min,
854                         tm->tm_sec);
855
856         if (r != 13) {
857                 talloc_free(ts);
858                 return NULL;
859         }
860
861         return ts;
862 }
863
864 /*
865   convert a LDAP UTCTime string to a time_t. Return 0 if unable to convert
866 */
867 time_t ldb_string_utc_to_time(const char *s)
868 {
869         struct tm tm;
870         
871         if (s == NULL) return 0;
872         
873         memset(&tm, 0, sizeof(tm));
874         if (sscanf(s, "%02u%02u%02u%02u%02u%02u", 
875                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
876                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
877                 return 0;
878         }
879         if (tm.tm_year < 50) {
880                 tm.tm_year += 100;
881         }
882         tm.tm_mon -= 1;
883         
884         return timegm(&tm);
885 }
886
887
888 /*
889   dump a set of results to a file. Useful from within gdb
890 */
891 void ldb_dump_results(struct ldb_context *ldb, struct ldb_result *result, FILE *f)
892 {
893         int i;
894
895         for (i = 0; i < result->count; i++) {
896                 struct ldb_ldif ldif;
897                 fprintf(f, "# record %d\n", i+1);
898                 ldif.changetype = LDB_CHANGETYPE_NONE;
899                 ldif.msg = result->msgs[i];
900                 ldb_ldif_write_file(ldb, f, &ldif);
901         }
902 }
903
904 /*
905   checks for a string attribute. Returns "1" on match and otherwise "0".
906 */
907 int ldb_msg_check_string_attribute(const struct ldb_message *msg,
908                                    const char *name, const char *value)
909 {
910         struct ldb_message_element *el;
911         struct ldb_val val;
912         
913         el = ldb_msg_find_element(msg, name);
914         if (el == NULL) {
915                 return 0;
916         }
917
918         val.data = discard_const_p(uint8_t, value);
919         val.length = strlen(value);
920
921         if (ldb_msg_find_val(el, &val)) {
922                 return 1;
923         }
924
925         return 0;
926 }
927