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