s4-ldb: Implement ldb_msg_diff_ex() function to accept a memory context from client
[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         struct ldb_message *mod;
594         struct ldb_message_element *el;
595         unsigned int i;
596
597         mod = ldb_msg_new(ldb);
598         if (mod == NULL) {
599                 return NULL;
600         }
601
602         mod->dn = msg1->dn;
603         mod->num_elements = 0;
604         mod->elements = NULL;
605
606         msg2 = ldb_msg_canonicalize(ldb, msg2);
607         if (msg2 == NULL) {
608                 talloc_free(mod);
609                 return NULL;
610         }
611         
612         /* look in msg2 to find elements that need to be added
613            or modified */
614         for (i=0;i<msg2->num_elements;i++) {
615                 el = ldb_msg_find_element(msg1, msg2->elements[i].name);
616
617                 if (el && ldb_msg_element_compare(el, &msg2->elements[i]) == 0) {
618                         continue;
619                 }
620
621                 if (ldb_msg_add(mod, 
622                                 &msg2->elements[i],
623                                 el?LDB_FLAG_MOD_REPLACE:LDB_FLAG_MOD_ADD) != LDB_SUCCESS) {
624                         talloc_free(mod);
625                         return NULL;
626                 }
627         }
628
629         /* look in msg1 to find elements that need to be deleted */
630         for (i=0;i<msg1->num_elements;i++) {
631                 el = ldb_msg_find_element(msg2, msg1->elements[i].name);
632                 if (el == NULL) {
633                         if (ldb_msg_add_empty(mod, 
634                                               msg1->elements[i].name,
635                                               LDB_FLAG_MOD_DELETE, NULL) != LDB_SUCCESS) {
636                                 talloc_free(mod);
637                                 return NULL;
638                         }
639                 }
640         }
641
642         return mod;
643 }
644
645 /**
646  * return a ldb_message representing the differences between msg1 and msg2.
647  * If you then use this in a ldb_modify() call it can be used to save edits to a message
648  *
649  * Result message is constructed as follows:
650  * - LDB_FLAG_MOD_ADD     - elements found only in msg2
651  * - LDB_FLAG_MOD_REPLACE - elements in msg2 that have different value in msg1
652  *                          Value for msg2 element is used
653  * - LDB_FLAG_MOD_DELETE  - elements found only in msg2
654  *
655  * @return LDB_SUCCESS or LDB_ERR_OPERATIONS_ERROR
656  */
657 int ldb_msg_diff_ex(struct ldb_context *ldb,
658                     struct ldb_message *msg1,
659                     struct ldb_message *msg2,
660                     TALLOC_CTX *mem_ctx,
661                     struct ldb_message **_msg_out)
662 {
663         int ldb_res;
664         unsigned int i;
665         struct ldb_message *mod;
666         struct ldb_message_element *el;
667         TALLOC_CTX *temp_ctx;
668
669         temp_ctx = talloc_new(mem_ctx);
670         if (!temp_ctx) {
671                 return LDB_ERR_OPERATIONS_ERROR;
672         }
673
674         mod = ldb_msg_new(temp_ctx);
675         if (mod == NULL) {
676                 goto failed;
677         }
678
679         mod->dn = msg1->dn;
680         mod->num_elements = 0;
681         mod->elements = NULL;
682
683         /* canonicalize msg2 so we have no
684          * repeated elements */
685         msg2 = ldb_msg_canonicalize(ldb, msg2);
686         if (msg2 == NULL) {
687                 goto failed;
688         }
689
690         /* steal msg2 into mod context as it is
691          * allocated in ldb's context */
692         talloc_steal(mod, msg2);
693
694         /* look in msg2 to find elements that need to be added
695            or modified */
696         for (i=0;i<msg2->num_elements;i++) {
697                 el = ldb_msg_find_element(msg1, msg2->elements[i].name);
698
699                 if (el && ldb_msg_element_compare(el, &msg2->elements[i]) == 0) {
700                         continue;
701                 }
702
703                 ldb_res = ldb_msg_add(mod,
704                                       &msg2->elements[i],
705                                       el ? LDB_FLAG_MOD_REPLACE : LDB_FLAG_MOD_ADD);
706                 if (ldb_res != LDB_SUCCESS) {
707                         goto failed;
708                 }
709         }
710
711         /* look in msg1 to find elements that need to be deleted */
712         for (i=0;i<msg1->num_elements;i++) {
713                 el = ldb_msg_find_element(msg2, msg1->elements[i].name);
714                 if (el == NULL) {
715                         ldb_res = ldb_msg_add_empty(mod,
716                                                     msg1->elements[i].name,
717                                                     LDB_FLAG_MOD_DELETE, NULL);
718                         if (ldb_res != LDB_SUCCESS) {
719                                 goto failed;
720                         }
721                 }
722         }
723
724         /* steal resulting message into supplied context */
725         talloc_steal(mem_ctx, mod);
726         *_msg_out = mod;
727
728         talloc_free(temp_ctx);
729         return LDB_SUCCESS;
730
731 failed:
732         talloc_free(temp_ctx);
733         return LDB_ERR_OPERATIONS_ERROR;
734 }
735
736
737 int ldb_msg_sanity_check(struct ldb_context *ldb, 
738                          const struct ldb_message *msg)
739 {
740         unsigned int i, j;
741
742         /* basic check on DN */
743         if (msg->dn == NULL) {
744                 /* TODO: return also an error string */
745                 ldb_set_errstring(ldb, "ldb message lacks a DN!");
746                 return LDB_ERR_INVALID_DN_SYNTAX;
747         }
748
749         /* basic syntax checks */
750         for (i = 0; i < msg->num_elements; i++) {
751                 for (j = 0; j < msg->elements[i].num_values; j++) {
752                         if (msg->elements[i].values[j].length == 0) {
753                                 TALLOC_CTX *mem_ctx = talloc_new(ldb);
754                                 /* an attribute cannot be empty */
755                                 /* TODO: return also an error string */
756                                 ldb_asprintf_errstring(ldb, "Element %s has empty attribute in ldb message (%s)!",
757                                                             msg->elements[i].name, 
758                                                             ldb_dn_get_linearized(msg->dn));
759                                 talloc_free(mem_ctx);
760                                 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
761                         }
762                 }
763         }
764
765         return LDB_SUCCESS;
766 }
767
768
769
770
771 /*
772   copy an attribute list. This only copies the array, not the elements
773   (ie. the elements are left as the same pointers)
774 */
775 const char **ldb_attr_list_copy(TALLOC_CTX *mem_ctx, const char * const *attrs)
776 {
777         const char **ret;
778         unsigned int i;
779
780         for (i=0;attrs && attrs[i];i++) /* noop */ ;
781         ret = talloc_array(mem_ctx, const char *, i+1);
782         if (ret == NULL) {
783                 return NULL;
784         }
785         for (i=0;attrs && attrs[i];i++) {
786                 ret[i] = attrs[i];
787         }
788         ret[i] = attrs[i];
789         return ret;
790 }
791
792
793 /*
794   copy an attribute list. This only copies the array, not the elements
795   (ie. the elements are left as the same pointers).  The new attribute is added to the list.
796 */
797 const char **ldb_attr_list_copy_add(TALLOC_CTX *mem_ctx, const char * const *attrs, const char *new_attr)
798 {
799         const char **ret;
800         unsigned int i;
801         bool found = false;
802
803         for (i=0;attrs && attrs[i];i++) {
804                 if (ldb_attr_cmp(attrs[i], new_attr) == 0) {
805                         found = true;
806                 }
807         }
808         if (found) {
809                 return ldb_attr_list_copy(mem_ctx, attrs);
810         }
811         ret = talloc_array(mem_ctx, const char *, i+2);
812         if (ret == NULL) {
813                 return NULL;
814         }
815         for (i=0;attrs && attrs[i];i++) {
816                 ret[i] = attrs[i];
817         }
818         ret[i] = new_attr;
819         ret[i+1] = NULL;
820         return ret;
821 }
822
823
824 /*
825   return 1 if an attribute is in a list of attributes, or 0 otherwise
826 */
827 int ldb_attr_in_list(const char * const *attrs, const char *attr)
828 {
829         unsigned int i;
830         for (i=0;attrs && attrs[i];i++) {
831                 if (ldb_attr_cmp(attrs[i], attr) == 0) {
832                         return 1;
833                 }
834         }
835         return 0;
836 }
837
838
839 /*
840   rename the specified attribute in a search result
841 */
842 int ldb_msg_rename_attr(struct ldb_message *msg, const char *attr, const char *replace)
843 {
844         struct ldb_message_element *el = ldb_msg_find_element(msg, attr);
845         if (el == NULL) {
846                 return LDB_SUCCESS;
847         }
848         el->name = talloc_strdup(msg->elements, replace);
849         if (el->name == NULL) {
850                 return LDB_ERR_OPERATIONS_ERROR;
851         }
852         return LDB_SUCCESS;
853 }
854
855
856 /*
857   copy the specified attribute in a search result to a new attribute
858 */
859 int ldb_msg_copy_attr(struct ldb_message *msg, const char *attr, const char *replace)
860 {
861         struct ldb_message_element *el = ldb_msg_find_element(msg, attr);
862         if (el == NULL) {
863                 return LDB_SUCCESS;
864         }
865         if (ldb_msg_add(msg, el, 0) != 0) {
866                 return LDB_ERR_OPERATIONS_ERROR;
867         }
868         return ldb_msg_rename_attr(msg, attr, replace);
869 }
870
871 /*
872   remove the specified element in a search result
873 */
874 void ldb_msg_remove_element(struct ldb_message *msg, struct ldb_message_element *el)
875 {
876         ptrdiff_t n = (el - msg->elements);
877         if (n >= msg->num_elements) {
878                 /* should we abort() here? */
879                 return;
880         }
881         if (n != msg->num_elements-1) {
882                 memmove(el, el+1, ((msg->num_elements-1) - n)*sizeof(*el));
883         }
884         msg->num_elements--;
885 }
886
887
888 /*
889   remove the specified attribute in a search result
890 */
891 void ldb_msg_remove_attr(struct ldb_message *msg, const char *attr)
892 {
893         struct ldb_message_element *el;
894
895         while ((el = ldb_msg_find_element(msg, attr)) != NULL) {
896                 ldb_msg_remove_element(msg, el);
897         }
898 }
899
900 /*
901   return a LDAP formatted GeneralizedTime string
902 */
903 char *ldb_timestring(TALLOC_CTX *mem_ctx, time_t t)
904 {
905         struct tm *tm = gmtime(&t);
906         char *ts;
907         int r;
908
909         if (!tm) {
910                 return NULL;
911         }
912
913         /* we now excatly how long this string will be */
914         ts = talloc_array(mem_ctx, char, 18);
915
916         /* formatted like: 20040408072012.0Z */
917         r = snprintf(ts, 18,
918                         "%04u%02u%02u%02u%02u%02u.0Z",
919                         tm->tm_year+1900, tm->tm_mon+1,
920                         tm->tm_mday, tm->tm_hour, tm->tm_min,
921                         tm->tm_sec);
922
923         if (r != 17) {
924                 talloc_free(ts);
925                 return NULL;
926         }
927
928         return ts;
929 }
930
931 /*
932   convert a LDAP GeneralizedTime string to a time_t. Return 0 if unable to convert
933 */
934 time_t ldb_string_to_time(const char *s)
935 {
936         struct tm tm;
937         
938         if (s == NULL) return 0;
939         
940         memset(&tm, 0, sizeof(tm));
941         if (sscanf(s, "%04u%02u%02u%02u%02u%02u.0Z",
942                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
943                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
944                 return 0;
945         }
946         tm.tm_year -= 1900;
947         tm.tm_mon -= 1;
948         
949         return timegm(&tm);
950 }
951
952 /*
953   convert a LDAP GeneralizedTime string in ldb_val format to a
954   time_t.
955 */
956 int ldb_val_to_time(const struct ldb_val *v, time_t *t)
957 {
958         struct tm tm;
959
960         if (v == NULL || !v->data || v->length < 17) {
961                 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
962         }
963
964         memset(&tm, 0, sizeof(tm));
965
966         if (sscanf((char *)v->data, "%04u%02u%02u%02u%02u%02u.0Z",
967                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
968                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
969                 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
970         }
971         tm.tm_year -= 1900;
972         tm.tm_mon -= 1;
973
974         *t = timegm(&tm);
975
976         return LDB_SUCCESS;
977 }
978
979 /*
980   return a LDAP formatted UTCTime string
981 */
982 char *ldb_timestring_utc(TALLOC_CTX *mem_ctx, time_t t)
983 {
984         struct tm *tm = gmtime(&t);
985         char *ts;
986         int r;
987
988         if (!tm) {
989                 return NULL;
990         }
991
992         /* we now excatly how long this string will be */
993         ts = talloc_array(mem_ctx, char, 14);
994
995         /* formatted like: 20040408072012.0Z => 040408072012Z */
996         r = snprintf(ts, 14,
997                         "%02u%02u%02u%02u%02u%02uZ",
998                         (tm->tm_year+1900)%100, tm->tm_mon+1,
999                         tm->tm_mday, tm->tm_hour, tm->tm_min,
1000                         tm->tm_sec);
1001
1002         if (r != 13) {
1003                 talloc_free(ts);
1004                 return NULL;
1005         }
1006
1007         return ts;
1008 }
1009
1010 /*
1011   convert a LDAP UTCTime string to a time_t. Return 0 if unable to convert
1012 */
1013 time_t ldb_string_utc_to_time(const char *s)
1014 {
1015         struct tm tm;
1016         
1017         if (s == NULL) return 0;
1018         
1019         memset(&tm, 0, sizeof(tm));
1020         if (sscanf(s, "%02u%02u%02u%02u%02u%02uZ",
1021                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
1022                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
1023                 return 0;
1024         }
1025         if (tm.tm_year < 50) {
1026                 tm.tm_year += 100;
1027         }
1028         tm.tm_mon -= 1;
1029         
1030         return timegm(&tm);
1031 }
1032
1033
1034 /*
1035   dump a set of results to a file. Useful from within gdb
1036 */
1037 void ldb_dump_results(struct ldb_context *ldb, struct ldb_result *result, FILE *f)
1038 {
1039         unsigned int i;
1040
1041         for (i = 0; i < result->count; i++) {
1042                 struct ldb_ldif ldif;
1043                 fprintf(f, "# record %d\n", i+1);
1044                 ldif.changetype = LDB_CHANGETYPE_NONE;
1045                 ldif.msg = result->msgs[i];
1046                 ldb_ldif_write_file(ldb, f, &ldif);
1047         }
1048 }
1049
1050 /*
1051   checks for a string attribute. Returns "1" on match and otherwise "0".
1052 */
1053 int ldb_msg_check_string_attribute(const struct ldb_message *msg,
1054                                    const char *name, const char *value)
1055 {
1056         struct ldb_message_element *el;
1057         struct ldb_val val;
1058         
1059         el = ldb_msg_find_element(msg, name);
1060         if (el == NULL) {
1061                 return 0;
1062         }
1063
1064         val.data = discard_const_p(uint8_t, value);
1065         val.length = strlen(value);
1066
1067         if (ldb_msg_find_val(el, &val)) {
1068                 return 1;
1069         }
1070
1071         return 0;
1072 }
1073