r19731: Modify the ldb_map infrustructure to always map from requested
[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 2 of the License, or (at your option) any later version.
14
15    This library is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18    Lesser General Public License for more details.
19
20    You should have received a copy of the GNU Lesser General Public
21    License along with this library; if not, write to the Free Software
22    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23 */
24
25 /*
26  *  Name: ldb
27  *
28  *  Component: ldb message component utility functions
29  *
30  *  Description: functions for manipulating ldb_message structures
31  *
32  *  Author: Andrew Tridgell
33  */
34
35 #include "includes.h"
36 #include "ldb/include/includes.h"
37
38 /*
39   create a new ldb_message in a given memory context (NULL for top level)
40 */
41 struct ldb_message *ldb_msg_new(void *mem_ctx)
42 {
43         return talloc_zero(mem_ctx, struct ldb_message);
44 }
45
46 /*
47   find an element in a message by attribute name
48 */
49 struct ldb_message_element *ldb_msg_find_element(const struct ldb_message *msg, 
50                                                  const char *attr_name)
51 {
52         unsigned int i;
53         for (i=0;i<msg->num_elements;i++) {
54                 if (ldb_attr_cmp(msg->elements[i].name, attr_name) == 0) {
55                         return &msg->elements[i];
56                 }
57         }
58         return NULL;
59 }
60
61 /*
62   see if two ldb_val structures contain exactly the same data
63   return 1 for a match, 0 for a mis-match
64 */
65 int ldb_val_equal_exact(const struct ldb_val *v1, const struct ldb_val *v2)
66 {
67         if (v1->length != v2->length) return 0;
68
69         if (v1->length == 0) return 1;
70
71         if (memcmp(v1->data, v2->data, v1->length) == 0) {
72                 return 1;
73         }
74
75         return 0;
76 }
77
78 /*
79   find a value in an element
80   assumes case sensitive comparison
81 */
82 struct ldb_val *ldb_msg_find_val(const struct ldb_message_element *el, 
83                                  struct ldb_val *val)
84 {
85         unsigned int i;
86         for (i=0;i<el->num_values;i++) {
87                 if (ldb_val_equal_exact(val, &el->values[i])) {
88                         return &el->values[i];
89                 }
90         }
91         return NULL;
92 }
93
94 /*
95   duplicate a ldb_val structure
96 */
97 struct ldb_val ldb_val_dup(void *mem_ctx, const struct ldb_val *v)
98 {
99         struct ldb_val v2;
100         v2.length = v->length;
101         if (v->data == NULL) {
102                 v2.data = NULL;
103                 return v2;
104         }
105
106         /* the +1 is to cope with buggy C library routines like strndup
107            that look one byte beyond */
108         v2.data = talloc_array(mem_ctx, uint8_t, v->length+1);
109         if (!v2.data) {
110                 v2.length = 0;
111                 return v2;
112         }
113
114         memcpy(v2.data, v->data, v->length);
115         ((char *)v2.data)[v->length] = 0;
116         return v2;
117 }
118
119 /*
120   add an empty element to a message
121 */
122 int ldb_msg_add_empty(  struct ldb_message *msg,
123                         const char *attr_name,
124                         int flags,
125                         struct ldb_message_element **return_el)
126 {
127         struct ldb_message_element *els;
128
129         if (! ldb_valid_attr_name(attr_name)) {
130                 return LDB_ERR_OPERATIONS_ERROR;
131         }
132
133         els = talloc_realloc(msg, msg->elements, 
134                              struct ldb_message_element, msg->num_elements+1);
135         if (!els) {
136                 errno = ENOMEM;
137                 return LDB_ERR_OPERATIONS_ERROR;
138         }
139
140         els[msg->num_elements].values = NULL;
141         els[msg->num_elements].num_values = 0;
142         els[msg->num_elements].flags = flags;
143         els[msg->num_elements].name = talloc_strdup(els, attr_name);
144         if (!els[msg->num_elements].name) {
145                 errno = ENOMEM;
146                 return LDB_ERR_OPERATIONS_ERROR;
147         }
148
149         msg->elements = els;
150         msg->num_elements++;
151
152         if (return_el) {
153                 *return_el = &els[msg->num_elements-1];
154         }
155
156         return LDB_SUCCESS;
157 }
158
159 /*
160   add an empty element to a message
161 */
162 int ldb_msg_add(struct ldb_message *msg, 
163                 const struct ldb_message_element *el, 
164                 int flags)
165 {
166         if (ldb_msg_add_empty(msg, el->name, flags, NULL) != 0) {
167                 return LDB_ERR_OPERATIONS_ERROR;
168         }
169
170         msg->elements[msg->num_elements-1] = *el;
171         msg->elements[msg->num_elements-1].flags = flags;
172
173         return LDB_SUCCESS;
174 }
175
176 /*
177   add a value to a message
178 */
179 int ldb_msg_add_value(struct ldb_message *msg, 
180                       const char *attr_name,
181                       const struct ldb_val *val,
182                       struct ldb_message_element **return_el)
183 {
184         struct ldb_message_element *el;
185         struct ldb_val *vals;
186         int ret;
187
188         el = ldb_msg_find_element(msg, attr_name);
189         if (!el) {
190                 ret = ldb_msg_add_empty(msg, attr_name, 0, &el);
191                 if (ret != LDB_SUCCESS) {
192                         return ret;
193                 }
194         }
195
196         vals = talloc_realloc(msg, el->values, struct ldb_val, el->num_values+1);
197         if (!vals) {
198                 errno = ENOMEM;
199                 return LDB_ERR_OPERATIONS_ERROR;
200         }
201         el->values = vals;
202         el->values[el->num_values] = *val;
203         el->num_values++;
204
205         if (return_el) {
206                 *return_el = el;
207         }
208
209         return LDB_SUCCESS;
210 }
211
212
213 /*
214   add a value to a message, stealing it into the 'right' place
215 */
216 int ldb_msg_add_steal_value(struct ldb_message *msg, 
217                             const char *attr_name,
218                             struct ldb_val *val)
219 {
220         int ret;
221         struct ldb_message_element *el;
222
223         ret = ldb_msg_add_value(msg, attr_name, val, &el);
224         if (ret == LDB_SUCCESS) {
225                 talloc_steal(el->values, val->data);
226         }
227         return ret;
228 }
229
230
231 /*
232   add a string element to a message
233 */
234 int ldb_msg_add_string(struct ldb_message *msg, 
235                        const char *attr_name, const char *str)
236 {
237         struct ldb_val val;
238
239         val.data = discard_const_p(uint8_t, str);
240         val.length = strlen(str);
241
242         if (val.length == 0) {
243                 /* allow empty strings as non-existant attributes */
244                 return LDB_SUCCESS;
245         }
246
247         return ldb_msg_add_value(msg, attr_name, &val, NULL);
248 }
249
250 /*
251   add a string element to a message, stealing it into the 'right' place
252 */
253 int ldb_msg_add_steal_string(struct ldb_message *msg, 
254                              const char *attr_name, char *str)
255 {
256         struct ldb_val val;
257
258         val.data = (uint8_t *)str;
259         val.length = strlen(str);
260
261         return ldb_msg_add_steal_value(msg, attr_name, &val);
262 }
263
264 /*
265   add a printf formatted element to a message
266 */
267 int ldb_msg_add_fmt(struct ldb_message *msg, 
268                     const char *attr_name, const char *fmt, ...)
269 {
270         struct ldb_val val;
271         va_list ap;
272         char *str;
273
274         va_start(ap, fmt);
275         str = talloc_vasprintf(msg, fmt, ap);
276         va_end(ap);
277
278         if (str == NULL) return LDB_ERR_OPERATIONS_ERROR;
279
280         val.data   = (uint8_t *)str;
281         val.length = strlen(str);
282
283         return ldb_msg_add_steal_value(msg, attr_name, &val);
284 }
285
286 /*
287   compare two ldb_message_element structures
288   assumes case senistive comparison
289 */
290 int ldb_msg_element_compare(struct ldb_message_element *el1, 
291                             struct ldb_message_element *el2)
292 {
293         unsigned int i;
294
295         if (el1->num_values != el2->num_values) {
296                 return el1->num_values - el2->num_values;
297         }
298
299         for (i=0;i<el1->num_values;i++) {
300                 if (!ldb_msg_find_val(el2, &el1->values[i])) {
301                         return -1;
302                 }
303         }
304
305         return 0;
306 }
307
308 /*
309   compare two ldb_message_element structures
310   comparing by element name
311 */
312 int ldb_msg_element_compare_name(struct ldb_message_element *el1, 
313                                  struct ldb_message_element *el2)
314 {
315         return ldb_attr_cmp(el1->name, el2->name);
316 }
317
318 /*
319   convenience functions to return common types from a message
320   these return the first value if the attribute is multi-valued
321 */
322 const struct ldb_val *ldb_msg_find_ldb_val(const struct ldb_message *msg, const char *attr_name)
323 {
324         struct ldb_message_element *el = ldb_msg_find_element(msg, attr_name);
325         if (!el || el->num_values == 0) {
326                 return NULL;
327         }
328         return &el->values[0];
329 }
330
331 int ldb_msg_find_attr_as_int(const struct ldb_message *msg, 
332                              const char *attr_name,
333                              int default_value)
334 {
335         const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
336         if (!v || !v->data) {
337                 return default_value;
338         }
339         return strtol((const char *)v->data, NULL, 0);
340 }
341
342 unsigned int ldb_msg_find_attr_as_uint(const struct ldb_message *msg, 
343                                        const char *attr_name,
344                                        unsigned int default_value)
345 {
346         const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
347         if (!v || !v->data) {
348                 return default_value;
349         }
350         return strtoul((const char *)v->data, NULL, 0);
351 }
352
353 int64_t ldb_msg_find_attr_as_int64(const struct ldb_message *msg, 
354                                    const char *attr_name,
355                                    int64_t default_value)
356 {
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         return strtoll((const char *)v->data, NULL, 0);
362 }
363
364 uint64_t ldb_msg_find_attr_as_uint64(const struct ldb_message *msg, 
365                                      const char *attr_name,
366                                      uint64_t default_value)
367 {
368         const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
369         if (!v || !v->data) {
370                 return default_value;
371         }
372         return strtoull((const char *)v->data, NULL, 0);
373 }
374
375 double ldb_msg_find_attr_as_double(const struct ldb_message *msg, 
376                                    const char *attr_name,
377                                    double default_value)
378 {
379         const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
380         if (!v || !v->data) {
381                 return default_value;
382         }
383         return strtod((const char *)v->data, NULL);
384 }
385
386 int ldb_msg_find_attr_as_bool(const struct ldb_message *msg, 
387                               const char *attr_name,
388                               int default_value)
389 {
390         const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
391         if (!v || !v->data) {
392                 return default_value;
393         }
394         if (strcasecmp((const char *)v->data, "FALSE") == 0) {
395                 return 0;
396         }
397         if (strcasecmp((const char *)v->data, "TRUE") == 0) {
398                 return 1;
399         }
400         return default_value;
401 }
402
403 const char *ldb_msg_find_attr_as_string(const struct ldb_message *msg, 
404                                         const char *attr_name,
405                                         const char *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 (const char *)v->data;
412 }
413
414 struct ldb_dn *ldb_msg_find_attr_as_dn(void *mem_ctx,
415                                        const struct ldb_message *msg,
416                                        const char *attr_name)
417 {
418         const struct ldb_val *v;
419
420         v = ldb_msg_find_ldb_val(msg, attr_name);
421         if (!v || !v->data) {
422                 return NULL;
423         }
424         return ldb_dn_explode(mem_ctx, (const char *)v->data);
425 }
426
427 /*
428   sort the elements of a message by name
429 */
430 void ldb_msg_sort_elements(struct ldb_message *msg)
431 {
432         qsort(msg->elements, msg->num_elements, sizeof(struct ldb_message_element), 
433               (comparison_fn_t)ldb_msg_element_compare_name);
434 }
435
436 /*
437   shallow copy a message - copying only the elements array so that the caller
438   can safely add new elements without changing the message
439 */
440 struct ldb_message *ldb_msg_copy_shallow(TALLOC_CTX *mem_ctx, 
441                                          const struct ldb_message *msg)
442 {
443         struct ldb_message *msg2;
444         int i;
445
446         msg2 = talloc(mem_ctx, struct ldb_message);
447         if (msg2 == NULL) return NULL;
448
449         *msg2 = *msg;
450         msg2->private_data = NULL;
451
452         msg2->elements = talloc_array(msg2, struct ldb_message_element, 
453                                       msg2->num_elements);
454         if (msg2->elements == NULL) goto failed;
455
456         for (i=0;i<msg2->num_elements;i++) {
457                 msg2->elements[i] = msg->elements[i];
458         }
459
460         return msg2;
461
462 failed:
463         talloc_free(msg2);
464         return NULL;
465 }
466
467
468 /*
469   copy a message, allocating new memory for all parts
470 */
471 struct ldb_message *ldb_msg_copy(TALLOC_CTX *mem_ctx, 
472                                  const struct ldb_message *msg)
473 {
474         struct ldb_message *msg2;
475         int i, j;
476
477         msg2 = ldb_msg_copy_shallow(mem_ctx, msg);
478         if (msg2 == NULL) return NULL;
479
480         msg2->dn = ldb_dn_copy(msg2, msg2->dn);
481         if (msg2->dn == NULL) goto failed;
482
483         for (i=0;i<msg2->num_elements;i++) {
484                 struct ldb_message_element *el = &msg2->elements[i];
485                 struct ldb_val *values = el->values;
486                 el->name = talloc_strdup(msg2->elements, el->name);
487                 if (el->name == NULL) goto failed;
488                 el->values = talloc_array(msg2->elements, struct ldb_val, el->num_values);
489                 for (j=0;j<el->num_values;j++) {
490                         el->values[j] = ldb_val_dup(el->values, &values[j]);
491                         if (el->values[j].data == NULL && values[j].length != 0) {
492                                 goto failed;
493                         }
494                 }
495         }
496
497         return msg2;
498
499 failed:
500         talloc_free(msg2);
501         return NULL;
502 }
503
504
505 /*
506   canonicalise a message, merging elements of the same name
507 */
508 struct ldb_message *ldb_msg_canonicalize(struct ldb_context *ldb, 
509                                          const struct ldb_message *msg)
510 {
511         int i;
512         struct ldb_message *msg2;
513
514         msg2 = ldb_msg_copy(ldb, msg);
515         if (msg2 == NULL) return NULL;
516
517         ldb_msg_sort_elements(msg2);
518
519         for (i=1;i<msg2->num_elements;i++) {
520                 struct ldb_message_element *el1 = &msg2->elements[i-1];
521                 struct ldb_message_element *el2 = &msg2->elements[i];
522                 if (ldb_msg_element_compare_name(el1, el2) == 0) {
523                         el1->values = talloc_realloc(msg2->elements, el1->values, struct ldb_val, 
524                                                        el1->num_values + el2->num_values);
525                         if (el1->values == NULL) {
526                                 return NULL;
527                         }
528                         memcpy(el1->values + el1->num_values,
529                                el2->values,
530                                sizeof(struct ldb_val) * el2->num_values);
531                         el1->num_values += el2->num_values;
532                         talloc_free(discard_const_p(char, el2->name));
533                         if (i+1<msg2->num_elements) {
534                                 memmove(el2, el2+1, sizeof(struct ldb_message_element) * 
535                                         (msg2->num_elements - (i+1)));
536                         }
537                         msg2->num_elements--;
538                         i--;
539                 }
540         }
541
542         return msg2;
543 }
544
545
546 /*
547   return a ldb_message representing the differences between msg1 and msg2. If you
548   then use this in a ldb_modify() call it can be used to save edits to a message
549 */
550 struct ldb_message *ldb_msg_diff(struct ldb_context *ldb, 
551                                  struct ldb_message *msg1,
552                                  struct ldb_message *msg2)
553 {
554         struct ldb_message *mod;
555         struct ldb_message_element *el;
556         unsigned int i;
557
558         mod = ldb_msg_new(ldb);
559
560         mod->dn = msg1->dn;
561         mod->num_elements = 0;
562         mod->elements = NULL;
563
564         msg2 = ldb_msg_canonicalize(ldb, msg2);
565         if (msg2 == NULL) {
566                 return NULL;
567         }
568         
569         /* look in msg2 to find elements that need to be added
570            or modified */
571         for (i=0;i<msg2->num_elements;i++) {
572                 el = ldb_msg_find_element(msg1, msg2->elements[i].name);
573
574                 if (el && ldb_msg_element_compare(el, &msg2->elements[i]) == 0) {
575                         continue;
576                 }
577
578                 if (ldb_msg_add(mod, 
579                                 &msg2->elements[i],
580                                 el?LDB_FLAG_MOD_REPLACE:LDB_FLAG_MOD_ADD) != 0) {
581                         return NULL;
582                 }
583         }
584
585         /* look in msg1 to find elements that need to be deleted */
586         for (i=0;i<msg1->num_elements;i++) {
587                 el = ldb_msg_find_element(msg2, msg1->elements[i].name);
588                 if (!el) {
589                         if (ldb_msg_add_empty(mod, 
590                                               msg1->elements[i].name,
591                                               LDB_FLAG_MOD_DELETE, NULL) != 0) {
592                                 return NULL;
593                         }
594                 }
595         }
596
597         return mod;
598 }
599
600 int ldb_msg_sanity_check(struct ldb_context *ldb, 
601                          const struct ldb_message *msg)
602 {
603         int i, j;
604
605         /* basic check on DN */
606         if (msg->dn == NULL) {
607                 /* TODO: return also an error string */
608                 ldb_set_errstring(ldb, "ldb message lacks a DN!");
609                 return LDB_ERR_INVALID_DN_SYNTAX;
610         }
611
612         /* basic syntax checks */
613         for (i = 0; i < msg->num_elements; i++) {
614                 for (j = 0; j < msg->elements[i].num_values; j++) {
615                         if (msg->elements[i].values[j].length == 0) {
616                                 TALLOC_CTX *mem_ctx = talloc_new(ldb);
617                                 /* an attribute cannot be empty */
618                                 /* TODO: return also an error string */
619                                 ldb_asprintf_errstring(ldb, "Element %s has empty attribute in ldb message (%s)!",
620                                                             msg->elements[i].name, 
621                                                             ldb_dn_linearize(mem_ctx, msg->dn));
622                                 talloc_free(mem_ctx);
623                                 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
624                         }
625                 }
626         }
627
628         return LDB_SUCCESS;
629 }
630
631
632
633
634 /*
635   copy an attribute list. This only copies the array, not the elements
636   (ie. the elements are left as the same pointers)
637 */
638 const char **ldb_attr_list_copy(TALLOC_CTX *mem_ctx, const char * const *attrs)
639 {
640         const char **ret;
641         int i;
642         for (i=0;attrs[i];i++) /* noop */ ;
643         ret = talloc_array(mem_ctx, const char *, i+1);
644         if (ret == NULL) {
645                 return NULL;
646         }
647         for (i=0;attrs[i];i++) {
648                 ret[i] = attrs[i];
649         }
650         ret[i] = attrs[i];
651         return ret;
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).  The new attribute is added to the list.
658 */
659 const char **ldb_attr_list_copy_add(TALLOC_CTX *mem_ctx, const char * const *attrs, const char *new_attr)
660 {
661         const char **ret;
662         int i;
663         for (i=0;attrs[i];i++) /* noop */ ;
664         ret = talloc_array(mem_ctx, const char *, i+2);
665         if (ret == NULL) {
666                 return NULL;
667         }
668         for (i=0;attrs[i];i++) {
669                 ret[i] = attrs[i];
670         }
671         ret[i] = new_attr;
672         ret[i+1] = NULL;
673         return ret;
674 }
675
676
677 /*
678   return 1 if an attribute is in a list of attributes, or 0 otherwise
679 */
680 int ldb_attr_in_list(const char * const *attrs, const char *attr)
681 {
682         int i;
683         for (i=0;attrs[i];i++) {
684                 if (ldb_attr_cmp(attrs[i], attr) == 0) {
685                         return 1;
686                 }
687         }
688         return 0;
689 }
690
691
692 /*
693   rename the specified attribute in a search result
694 */
695 int ldb_msg_rename_attr(struct ldb_message *msg, const char *attr, const char *replace)
696 {
697         struct ldb_message_element *el = ldb_msg_find_element(msg, attr);
698         if (el == NULL) {
699                 return LDB_SUCCESS;
700         }
701         el->name = talloc_strdup(msg->elements, replace);
702         if (el->name == NULL) {
703                 return LDB_ERR_OPERATIONS_ERROR;
704         }
705         return LDB_SUCCESS;
706 }
707
708
709 /*
710   copy the specified attribute in a search result to a new attribute
711 */
712 int ldb_msg_copy_attr(struct ldb_message *msg, const char *attr, const char *replace)
713 {
714         struct ldb_message_element *el = ldb_msg_find_element(msg, attr);
715         if (el == NULL) {
716                 return LDB_SUCCESS;
717         }
718         if (ldb_msg_add(msg, el, 0) != 0) {
719                 return LDB_ERR_OPERATIONS_ERROR;
720         }
721         return ldb_msg_rename_attr(msg, attr, replace);
722 }
723
724
725 /*
726   remove the specified attribute in a search result
727 */
728 void ldb_msg_remove_attr(struct ldb_message *msg, const char *attr)
729 {
730         struct ldb_message_element *el = ldb_msg_find_element(msg, attr);
731         if (el) {
732                 int n = (el - msg->elements);
733                 if (n != msg->num_elements-1) {
734                         memmove(el, el+1, ((msg->num_elements-1) - n)*sizeof(*el));
735                 }
736                 msg->num_elements--;
737         }
738 }
739
740 /*
741   remove the specified element in a search result
742 */
743 void ldb_msg_remove_element(struct ldb_message *msg, struct ldb_message_element *el)
744 {
745         int n = (el - msg->elements);
746         if (n != msg->num_elements-1) {
747                 memmove(el, el+1, ((msg->num_elements-1) - n)*sizeof(*el));
748         }
749         msg->num_elements--;
750 }
751
752 /*
753   return a LDAP formatted time string
754 */
755 char *ldb_timestring(TALLOC_CTX *mem_ctx, time_t t)
756 {
757         struct tm *tm = gmtime(&t);
758
759         if (!tm) {
760                 return NULL;
761         }
762
763         /* formatted like: 20040408072012.0Z */
764         return talloc_asprintf(mem_ctx, 
765                                "%04u%02u%02u%02u%02u%02u.0Z",
766                                tm->tm_year+1900, tm->tm_mon+1,
767                                tm->tm_mday, tm->tm_hour, tm->tm_min,
768                                tm->tm_sec);
769 }
770
771
772 /*
773   convert a LDAP time string to a time_t. Return 0 if unable to convert
774 */
775 time_t ldb_string_to_time(const char *s)
776 {
777         struct tm tm;
778         
779         if (s == NULL) return 0;
780         
781         memset(&tm, 0, sizeof(tm));
782         if (sscanf(s, "%04u%02u%02u%02u%02u%02u", 
783                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
784                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
785                 return 0;
786         }
787         tm.tm_year -= 1900;
788         tm.tm_mon -= 1;
789         
790         return timegm(&tm);
791 }
792
793
794 /*
795   dump a set of results to a file. Useful from within gdb
796 */
797 void ldb_dump_results(struct ldb_context *ldb, struct ldb_result *result, FILE *f)
798 {
799         int i;
800
801         for (i = 0; i < result->count; i++) {
802                 struct ldb_ldif ldif;
803                 fprintf(f, "# record %d\n", i+1);
804                 ldif.changetype = LDB_CHANGETYPE_NONE;
805                 ldif.msg = result->msgs[i];
806                 ldb_ldif_write_file(ldb, f, &ldif);
807         }
808 }
809
810 int ldb_msg_check_string_attribute(const struct ldb_message *msg, const char *name, const char *value)
811 {
812         struct ldb_message_element *el;
813         struct ldb_val val;
814         
815         el = ldb_msg_find_element(msg, name);
816         if (el == NULL)
817                 return 0;
818
819         val.data = discard_const_p(uint8_t, value);
820         val.length = strlen(value);
821
822         if (ldb_msg_find_val(el, &val))
823                 return 1;
824
825         return 0;
826 }