r4427: - added ldb_msg_*() functions for sorting, comparing and copying messages
[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/ldb.h"
37 #include "ldb/include/ldb_private.h"
38
39
40 /*
41   find an element in a message by attribute name
42 */
43 struct ldb_message_element *ldb_msg_find_element(const struct ldb_message *msg, 
44                                                  const char *attr_name)
45 {
46         unsigned int i;
47         for (i=0;i<msg->num_elements;i++) {
48                 if (ldb_attr_cmp(msg->elements[i].name, attr_name) == 0) {
49                         return &msg->elements[i];
50                 }
51         }
52         return NULL;
53 }
54
55 /*
56   see if two ldb_val structures contain exactly the same data
57   return 1 for a match, 0 for a mis-match
58 */
59 int ldb_val_equal_exact(const struct ldb_val *v1, const struct ldb_val *v2)
60 {
61         if (v1->length != v2->length) return 0;
62
63         if (v1->length == 0) return 1;
64
65         if (memcmp(v1->data, v2->data, v1->length) == 0) {
66                 return 1;
67         }
68
69         return 0;
70 }
71
72 /*
73   find a value in an element
74   assumes case sensitive comparison
75 */
76 struct ldb_val *ldb_msg_find_val(const struct ldb_message_element *el, 
77                                  struct ldb_val *val)
78 {
79         unsigned int i;
80         for (i=0;i<el->num_values;i++) {
81                 if (ldb_val_equal_exact(val, &el->values[i])) {
82                         return &el->values[i];
83                 }
84         }
85         return NULL;
86 }
87
88 /*
89   duplicate a ldb_val structure
90 */
91 struct ldb_val ldb_val_dup(struct ldb_context *ldb,
92                            const struct ldb_val *v)
93 {
94         struct ldb_val v2;
95         v2.length = v->length;
96         if (v->length == 0) {
97                 v2.data = NULL;
98                 return v2;
99         }
100
101         /* the +1 is to cope with buggy C library routines like strndup
102            that look one byte beyond */
103         v2.data = ldb_malloc(ldb, v->length+1);
104         if (!v2.data) {
105                 v2.length = 0;
106                 return v2;
107         }
108
109         memcpy(v2.data, v->data, v->length);
110         ((char *)v2.data)[v->length] = 0;
111         return v2;
112 }
113
114 /*
115   add an empty element to a message
116 */
117 int ldb_msg_add_empty(struct ldb_context *ldb,
118                       struct ldb_message *msg, const char *attr_name, int flags)
119 {
120         struct ldb_message_element *els;
121
122         els = ldb_realloc_p(ldb, msg->elements, 
123                             struct ldb_message_element, msg->num_elements+1);
124         if (!els) {
125                 errno = ENOMEM;
126                 return -1;
127         }
128
129         els[msg->num_elements].values = NULL;
130         els[msg->num_elements].num_values = 0;
131         els[msg->num_elements].flags = flags;
132         els[msg->num_elements].name = ldb_strdup(ldb, attr_name);
133         if (!els[msg->num_elements].name) {
134                 return -1;
135         }
136
137         msg->elements = els;
138         msg->num_elements++;
139
140         return 0;
141 }
142
143 /*
144   add an empty element to a message
145 */
146 int ldb_msg_add(struct ldb_context *ldb,
147                 struct ldb_message *msg, 
148                 const struct ldb_message_element *el, 
149                 int flags)
150 {
151         if (ldb_msg_add_empty(ldb, msg, el->name, flags) != 0) {
152                 return -1;
153         }
154
155         msg->elements[msg->num_elements-1] = *el;
156         msg->elements[msg->num_elements-1].flags = flags;
157
158         return 0;
159 }
160
161 /*
162   add a value to a message
163 */
164 int ldb_msg_add_value(struct ldb_context *ldb,
165                       struct ldb_message *msg, 
166                       const char *attr_name,
167                       struct ldb_val *val)
168 {
169         struct ldb_message_element *el;
170         struct ldb_val *vals;
171
172         el = ldb_msg_find_element(msg, attr_name);
173         if (!el) {
174                 ldb_msg_add_empty(ldb, msg, attr_name, 0);
175                 el = ldb_msg_find_element(msg, attr_name);
176         }
177         if (!el) {
178                 return -1;
179         }
180
181         vals = ldb_realloc_p(ldb, el->values, struct ldb_val, el->num_values+1);
182         if (!vals) {
183                 errno = ENOMEM;
184                 return -1;
185         }
186         el->values = vals;
187         el->values[el->num_values] = *val;
188         el->num_values++;
189
190         return 0;
191 }
192
193
194 /*
195   add a string element to a message
196 */
197 int ldb_msg_add_string(struct ldb_context *ldb, struct ldb_message *msg, 
198                        const char *attr_name, char *str)
199 {
200         struct ldb_val val;
201
202         val.data = str;
203         val.length = strlen(str);
204
205         return ldb_msg_add_value(ldb, msg, attr_name, &val);
206 }
207
208 /*
209   compare two ldb_message_element structures
210   assumes case senistive comparison
211 */
212 int ldb_msg_element_compare(struct ldb_message_element *el1, 
213                             struct ldb_message_element *el2)
214 {
215         unsigned int i;
216
217         if (el1->num_values != el2->num_values) {
218                 return el1->num_values - el2->num_values;
219         }
220
221         for (i=0;i<el1->num_values;i++) {
222                 if (!ldb_msg_find_val(el2, &el1->values[i])) {
223                         return -1;
224                 }
225         }
226
227         return 0;
228 }
229
230 /*
231   compare two ldb_message_element structures
232   comparing by element name
233 */
234 int ldb_msg_element_compare_name(struct ldb_message_element *el1, 
235                                  struct ldb_message_element *el2)
236 {
237         return ldb_attr_cmp(el1->name, el2->name);
238 }
239
240 /*
241   convenience functions to return common types from a message
242   these return the first value if the attribute is multi-valued
243 */
244 const struct ldb_val *ldb_msg_find_ldb_val(const struct ldb_message *msg, const char *attr_name)
245 {
246         struct ldb_message_element *el = ldb_msg_find_element(msg, attr_name);
247         if (!el || el->num_values == 0) {
248                 return NULL;
249         }
250         return &el->values[0];
251 }
252
253 int ldb_msg_find_int(const struct ldb_message *msg, 
254                      const char *attr_name,
255                      int default_value)
256 {
257         const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
258         if (!v || !v->data) {
259                 return default_value;
260         }
261         return strtol(v->data, NULL, 0);
262 }
263
264 unsigned int ldb_msg_find_uint(const struct ldb_message *msg, 
265                                const char *attr_name,
266                                unsigned int default_value)
267 {
268         const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
269         if (!v || !v->data) {
270                 return default_value;
271         }
272         return strtoul(v->data, NULL, 0);
273 }
274
275 int64_t ldb_msg_find_int64(const struct ldb_message *msg, 
276                            const char *attr_name,
277                            int64_t default_value)
278 {
279         const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
280         if (!v || !v->data) {
281                 return default_value;
282         }
283         return strtoll(v->data, NULL, 0);
284 }
285
286 uint64_t ldb_msg_find_uint64(const struct ldb_message *msg, 
287                              const char *attr_name,
288                              uint64_t default_value)
289 {
290         const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
291         if (!v || !v->data) {
292                 return default_value;
293         }
294         return strtoull(v->data, NULL, 0);
295 }
296
297 double ldb_msg_find_double(const struct ldb_message *msg, 
298                            const char *attr_name,
299                            double default_value)
300 {
301         const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
302         if (!v || !v->data) {
303                 return default_value;
304         }
305         return strtod(v->data, NULL);
306 }
307
308 const char *ldb_msg_find_string(const struct ldb_message *msg, 
309                                 const char *attr_name,
310                                 const char *default_value)
311 {
312         const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
313         if (!v || !v->data) {
314                 return default_value;
315         }
316         return v->data;
317 }
318
319
320 /*
321   sort the elements of a message by name
322 */
323 void ldb_msg_sort_elements(struct ldb_message *msg)
324 {
325         qsort(msg->elements, msg->num_elements, sizeof(struct ldb_message_element), 
326               (comparison_fn_t)ldb_msg_element_compare_name);
327 }
328
329
330 /*
331   free a message created using ldb_msg_copy
332 */
333 void ldb_msg_free(struct ldb_context *ldb, struct ldb_message *msg)
334 {
335         int i, j;
336
337         for (i=0;i<msg->num_elements;i++) {
338                 struct ldb_message_element *el = &msg->elements[i];
339                 for (j=0;j<el->num_values;j++) {
340                         ldb_free(ldb, el->values[j].data);
341                 }
342                 if (el->values) ldb_free(ldb, el->values);
343                 ldb_free(ldb, el->name);
344         }
345         if (msg->elements) ldb_free(ldb, msg->elements);
346         ldb_free(ldb, msg->dn);
347         ldb_free(ldb, msg);
348 }
349
350 /*
351   copy a message, allocating new memory for all parts
352 */
353 struct ldb_message *ldb_msg_copy(struct ldb_context *ldb, 
354                                  const struct ldb_message *msg)
355 {
356         struct ldb_message *msg2;
357         int i, j;
358
359         msg2 = ldb_malloc_p(ldb, struct ldb_message);
360         if (msg2 == NULL) return NULL;
361
362         msg2->elements = NULL;
363         msg2->num_elements = 0;
364         msg2->private_data = NULL;
365
366         msg2->dn = ldb_strdup(ldb, msg->dn);
367         if (msg2->dn == NULL) goto failed;
368
369         msg2->elements = ldb_malloc_array_p(ldb, struct ldb_message_element, msg->num_elements);
370         if (msg2->elements == NULL) goto failed;
371
372         for (i=0;i<msg->num_elements;i++) {
373                 struct ldb_message_element *el1 = &msg->elements[i];
374                 struct ldb_message_element *el2 = &msg2->elements[i];
375
376                 el2->flags = el1->flags;
377                 el2->num_values = 0;
378                 el2->values = NULL;
379                 el2->name = ldb_strdup(ldb, el1->name);
380                 if (el2->name == NULL) goto failed;
381                 el2->values = ldb_malloc_array_p(ldb, struct ldb_val, el1->num_values);
382                 for (j=0;j<el1->num_values;j++) {
383                         el2->values[j] = ldb_val_dup(ldb, &el1->values[j]);
384                         if (el2->values[j].data == NULL &&
385                             el1->values[j].length != 0) {
386                                 goto failed;
387                         }
388                         el2->num_values++;
389                 }
390
391                 msg2->num_elements++;
392         }
393
394         return msg2;
395
396 failed:
397         ldb_msg_free(ldb, msg2);
398         return NULL;
399 }
400
401
402 /*
403   canonicalise a message, merging elements of the same name
404 */
405 struct ldb_message *ldb_msg_canonicalize(struct ldb_context *ldb, 
406                                          const struct ldb_message *msg)
407 {
408         int i;
409         struct ldb_message *msg2;
410
411         msg2 = ldb_msg_copy(ldb, msg);
412         if (msg2 == NULL) return NULL;
413
414         ldb_msg_sort_elements(msg2);
415
416         for (i=1;i<msg2->num_elements;i++) {
417                 struct ldb_message_element *el1 = &msg2->elements[i-1];
418                 struct ldb_message_element *el2 = &msg2->elements[i];
419                 if (ldb_msg_element_compare_name(el1, el2) == 0) {
420                         el1->values = ldb_realloc_p(ldb, el1->values, struct ldb_val, 
421                                                     el1->num_values + el2->num_values);
422                         if (el1->values == NULL) {
423                                 return NULL;
424                         }
425                         memcpy(el1->values + el1->num_values,
426                                el2->values,
427                                sizeof(struct ldb_val) * el2->num_values);
428                         el1->num_values += el2->num_values;
429                         ldb_free(ldb, el2->name);
430                         ldb_free(ldb, el2->values);
431                         if (i+1<msg2->num_elements) {
432                                 memmove(el2, el2+1, sizeof(struct ldb_message_element) * 
433                                         (msg2->num_elements - (i+1)));
434                         }
435                         msg2->num_elements--;
436                         i--;
437                 }
438         }
439
440         return msg2;
441 }