added the rest of the ldb_modify() code, which required a fairly large
[abartlet/samba.git/.git] / source4 / lib / ldb / ldb_tdb / ldb_search.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 search functions
29  *
30  *  Description: functions to search ldb+tdb databases
31  *
32  *  Author: Andrew Tridgell
33  */
34
35 #include "includes.h"
36 #include "ldb_tdb/ldb_tdb.h"
37
38 /*
39   free a message that has all parts separately allocated
40 */
41 static void msg_free_all_parts(struct ldb_message *msg)
42 {
43         int i, j;
44         if (msg->dn) free(msg->dn);
45         for (i=0;i<msg->num_elements;i++) {
46                 if (msg->elements[i].name) free(msg->elements[i].name);
47                 for (j=0;j<msg->elements[i].num_values;j++) {
48                         if (msg->elements[i].values[j].data) 
49                                 free(msg->elements[i].values[j].data);
50                 }
51                 if (msg->elements[i].values) free(msg->elements[i].values);
52         }
53         free(msg->elements);
54         free(msg);
55 }
56
57
58 /*
59   TODO: this should take advantage of the sorted nature of the message
60
61   return index of the attribute, or -1 if not found
62 */
63 int ldb_msg_find_attr(const struct ldb_message *msg, const char *attr)
64 {
65         int i;
66         for (i=0;i<msg->num_elements;i++) {
67                 if (strcmp(msg->elements[i].name, attr) == 0) {
68                         return i;
69                 }
70         }
71         return -1;
72 }
73
74 /*
75   duplicate a ldb_val structure
76 */
77 struct ldb_val ldb_val_dup(const struct ldb_val *v)
78 {
79         struct ldb_val v2;
80         v2.length = v->length;
81         if (v->length == 0) {
82                 v2.data = NULL;
83                 return v2;
84         }
85
86         /* the +1 is to cope with buggy C library routines like strndup
87            that look one byte beyond */
88         v2.data = malloc(v->length+1);
89         if (!v2.data) {
90                 v2.length = 0;
91                 return v2;
92         }
93
94         memcpy(v2.data, v->data, v->length);
95         ((char *)v2.data)[v->length] = 0;
96         return v2;
97 }
98
99
100
101 /*
102   add one element to a message
103 */
104 static int msg_add_element(struct ldb_message *ret, const struct ldb_message_element *el)
105 {
106         int i;
107         struct ldb_message_element *e2, *elnew;
108
109         e2 = realloc_p(ret->elements, struct ldb_message_element, ret->num_elements+1);
110         if (!e2) {
111                 return -1;
112         }
113         ret->elements = e2;
114         
115         elnew = &e2[ret->num_elements];
116
117         elnew->name = strdup(el->name);
118         if (!elnew->name) {
119                 return -1;
120         }
121
122         if (el->num_values) {
123                 elnew->values = malloc_array_p(struct ldb_val, el->num_values);
124                 if (!elnew->values) {
125                         return -1;
126                 }
127         } else {
128                 elnew->values = NULL;
129         }
130
131         for (i=0;i<el->num_values;i++) {
132                 elnew->values[i] = ldb_val_dup(&el->values[i]);
133                 if (elnew->values[i].length != el->values[i].length) {
134                         return -1;
135                 }
136         }
137
138         elnew->num_values = el->num_values;
139
140         ret->num_elements++;
141
142         return 0;
143 }
144
145 /*
146   add all elements from one message into another
147  */
148 static int msg_add_all_elements(struct ldb_message *ret,
149                                 const struct ldb_message *msg)
150 {
151         int i;
152         for (i=0;i<msg->num_elements;i++) {
153                 if (msg_add_element(ret, &msg->elements[i]) != 0) {
154                         return -1;
155                 }
156         }
157
158         return 0;
159 }
160
161
162 /*
163   pull the specified list of attributes from a message
164  */
165 static struct ldb_message *ltdb_pull_attrs(struct ldb_context *ldb, 
166                                            const struct ldb_message *msg, 
167                                            const char **attrs)
168 {
169         struct ldb_message *ret;
170         int i;
171
172         ret = malloc_p(struct ldb_message);
173         if (!ret) {
174                 return NULL;
175         }
176
177         ret->dn = strdup(msg->dn);
178         if (!ret->dn) {
179                 free(ret);
180                 return NULL;
181         }
182
183         ret->num_elements = 0;
184         ret->elements = NULL;
185         ret->private = NULL;
186
187         if (!attrs) {
188                 if (msg_add_all_elements(ret, msg) != 0) {
189                         msg_free_all_parts(ret);
190                         return NULL;
191                 }
192                 return ret;
193         }
194
195         for (i=0;attrs[i];i++) {
196                 int j;
197
198                 if (strcmp(attrs[i], "*") == 0) {
199                         if (msg_add_all_elements(ret, msg) != 0) {
200                                 msg_free_all_parts(ret);
201                                 return NULL;
202                         }
203                         continue;
204                 }
205                 j = ldb_msg_find_attr(msg, attrs[i]);
206                 if (j == -1) {
207                         continue;
208                 }
209                 do {
210                         if (msg_add_element(ret, &msg->elements[j]) != 0) {
211                                 msg_free_all_parts(ret);
212                                 return NULL;                            
213                         }
214                 } while (++j < msg->num_elements && 
215                          strcmp(attrs[i], msg->elements[j].name) == 0);
216         }
217
218         return ret;
219 }
220
221
222
223 /*
224   see if a ldb_val is a wildcard
225 */
226 int ltdb_has_wildcard(const struct ldb_val *val)
227 {
228         if (val->length == 1 && ((char *)val->data)[0] == '*') {
229                 return 1;
230         }
231         return 0;
232 }
233
234
235 /*
236   free the results of a ltdb_search_dn1 search
237 */
238 void ltdb_search_dn1_free(struct ldb_context *ldb, struct ldb_message *msg)
239 {
240         int i;
241         if (msg->dn) free(msg->dn);
242         if (msg->private) free(msg->private);
243         for (i=0;i<msg->num_elements;i++) {
244                 if (msg->elements[i].values) free(msg->elements[i].values);
245         }
246         if (msg->elements) free(msg->elements);
247 }
248
249
250 /*
251   search the database for a single simple dn, returning all attributes
252   in a single message
253
254   return 1 on success, 0 on record-not-found and -1 on error
255 */
256 int ltdb_search_dn1(struct ldb_context *ldb, const char *dn, struct ldb_message *msg)
257 {
258         struct ltdb_private *ltdb = ldb->private;
259         int ret;
260         TDB_DATA tdb_key, tdb_data;
261
262         /* form the key */
263         tdb_key = ltdb_key(dn);
264         if (!tdb_key.dptr) {
265                 return -1;
266         }
267
268         tdb_data = tdb_fetch(ltdb->tdb, tdb_key);
269         free(tdb_key.dptr);
270         if (!tdb_data.dptr) {
271                 return 0;
272         }
273
274         msg->dn = strdup(dn);
275         if (!msg->dn) {
276                 free(tdb_data.dptr);
277                 return -1;
278         }
279         msg->private = tdb_data.dptr;
280         msg->num_elements = 0;
281         msg->elements = NULL;
282
283         ret = ltdb_unpack_data(ldb, &tdb_data, msg);
284         if (ret == -1) {
285                 free(tdb_data.dptr);
286                 return -1;              
287         }
288
289         return 1;
290 }
291
292
293 /*
294   search the database for a single simple dn
295 */
296 int ltdb_search_dn(struct ldb_context *ldb, char *dn,
297                    const char *attrs[], struct ldb_message ***res)
298 {
299         int ret;
300         struct ldb_message msg, *msg2;
301
302         ret = ltdb_search_dn1(ldb, dn, &msg);
303         if (ret != 1) {
304                 return ret;
305         }
306
307         msg2 = ltdb_pull_attrs(ldb, &msg, attrs);
308
309         ltdb_search_dn1_free(ldb, &msg);
310
311         if (!msg2) {
312                 return -1;              
313         }
314
315         *res = malloc_array_p(struct ldb_message *, 2);
316         if (! *res) {
317                 msg_free_all_parts(msg2);
318                 return -1;              
319         }
320
321         (*res)[0] = msg2;
322         (*res)[1] = NULL;
323
324         return 1;
325 }
326
327
328 /*
329   add a set of attributes from a record to a set of results
330   return 0 on success, -1 on failure
331 */
332 int ltdb_add_attr_results(struct ldb_context *ldb, struct ldb_message *msg,
333                           const char *attrs[], 
334                           unsigned int *count, 
335                           struct ldb_message ***res)
336 {
337         struct ldb_message *msg2;
338         struct ldb_message **res2;
339
340         /* pull the attributes that the user wants */
341         msg2 = ltdb_pull_attrs(ldb, msg, attrs);
342         if (!msg2) {
343                 return -1;
344         }
345
346         /* add to the results list */
347         res2 = realloc_p(*res, struct ldb_message *, (*count)+2);
348         if (!res2) {
349                 msg_free_all_parts(msg2);
350                 return -1;
351         }
352
353         (*res) = res2;
354
355         (*res)[*count] = msg2;
356         (*res)[(*count)+1] = NULL;
357         (*count)++;
358
359         return 0;
360 }
361
362
363 /*
364   internal search state during a full db search
365 */
366 struct ltdb_search_info {
367         struct ldb_context *ldb;
368         struct ldb_parse_tree *tree;
369         const char *base;
370         enum ldb_scope scope;
371         const char **attrs;
372         struct ldb_message **msgs;
373         int failures;
374         int count;
375 };
376
377
378 /*
379   search function for a non-indexed search
380  */
381 static int search_func(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, void *state)
382 {
383         struct ltdb_search_info *sinfo = state;
384         struct ldb_message msg;
385         int ret;
386
387         if (key.dsize < 4 || 
388             strncmp(key.dptr, "DN=", 3) != 0) {
389                 return 0;
390         }
391
392         msg.dn = key.dptr + 3;
393
394         /* unpack the record */
395         ret = ltdb_unpack_data(sinfo->ldb, &data, &msg);
396         if (ret == -1) {
397                 sinfo->failures++;
398                 return 0;
399         }
400
401         /* see if it matches the given expression */
402         if (!ldb_message_match(sinfo->ldb, &msg, sinfo->tree, 
403                                sinfo->base, sinfo->scope)) {
404                 ltdb_unpack_data_free(&msg);
405                 return 0;
406         }
407
408         ret = ltdb_add_attr_results(sinfo->ldb, &msg, sinfo->attrs, &sinfo->count, &sinfo->msgs);
409
410         if (ret == -1) {
411                 sinfo->failures++;
412         }
413
414         ltdb_unpack_data_free(&msg);
415
416         return ret;
417 }
418
419
420 /*
421   free a set of search results
422 */
423 int ltdb_search_free(struct ldb_context *ldb, struct ldb_message **msgs)
424 {
425         int i;
426
427         if (!msgs) return 0;
428
429         for (i=0;msgs[i];i++) {
430                 msg_free_all_parts(msgs[i]);
431         }
432
433         free(msgs);
434
435         return 0;
436 }
437
438 /*
439   search the database with a LDAP-like expression.
440   this is the "full search" non-indexed varient
441 */
442 static int ltdb_search_full(struct ldb_context *ldb, 
443                             const char *base,
444                             enum ldb_scope scope,
445                             struct ldb_parse_tree *tree,
446                             const char *attrs[], struct ldb_message ***res)
447 {
448         struct ltdb_private *ltdb = ldb->private;
449         int ret;
450         struct ltdb_search_info sinfo;
451
452         sinfo.tree = tree;
453         sinfo.ldb = ldb;
454         sinfo.scope = scope;
455         sinfo.base = base;
456         sinfo.attrs = attrs;
457         sinfo.msgs = NULL;
458         sinfo.count = 0;
459         sinfo.failures = 0;
460
461         ret = tdb_traverse(ltdb->tdb, search_func, &sinfo);
462
463         if (ret == -1) {
464                 ltdb_search_free(ldb, sinfo.msgs);
465                 return -1;
466         }
467
468         *res = sinfo.msgs;
469         return sinfo.count;
470 }
471
472
473 /*
474   search the database with a LDAP-like expression.
475   choses a search method
476 */
477 int ltdb_search(struct ldb_context *ldb, const char *base,
478                 enum ldb_scope scope, const char *expression,
479                 const char *attrs[], struct ldb_message ***res)
480 {
481         struct ldb_parse_tree *tree;
482         int ret;
483
484         *res = NULL;
485
486         /* form a parse tree for the expression */
487         tree = ldb_parse_tree(expression);
488         if (!tree) {
489                 return -1;
490         }
491
492         if (tree->operation == LDB_OP_SIMPLE && 
493             strcmp(tree->u.simple.attr, "dn") == 0 &&
494             !ltdb_has_wildcard(&tree->u.simple.value)) {
495                 /* yay! its a nice simple one */
496                 ret = ltdb_search_dn(ldb, tree->u.simple.value.data, attrs, res);
497         } else {
498                 ret = ltdb_search_indexed(ldb, base, scope, tree, attrs, res);
499                 if (ret == -1) {
500                         ret = ltdb_search_full(ldb, base, scope, tree, attrs, res);
501                 }
502         }
503
504         ldb_parse_tree_free(tree);
505
506         return ret;
507 }
508