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