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