r12833: complete ldbsearch support for controls
[samba.git] / source4 / lib / ldb / tools / ldbsearch.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: ldbsearch
29  *
30  *  Description: utility for ldb search - modelled on ldapsearch
31  *
32  *  Author: Andrew Tridgell
33  */
34
35 #include "includes.h"
36 #include "ldb/include/includes.h"
37 #include "ldb/tools/cmdline.h"
38
39 static void usage(void)
40 {
41         printf("Usage: ldbsearch <options> <expression> <attrs...>\n");
42         printf("Options:\n");
43         printf("  -H ldb_url       choose the database (or $LDB_URL)\n");
44         printf("  -s base|sub|one  choose search scope\n");
45         printf("  -b basedn        choose baseDN\n");
46         printf("  -i               read search expressions from stdin\n");
47         printf("  -S               sort returned attributes\n");
48         printf("  -o options       pass options like modules to activate\n");
49         printf("              e.g: -o modules:timestamps\n");
50         exit(1);
51 }
52
53 static int do_compare_msg(struct ldb_message **el1,
54                           struct ldb_message **el2,
55                           void *opaque)
56 {
57         struct ldb_context *ldb = talloc_get_type(opaque, struct ldb_context);
58         return ldb_dn_compare(ldb, (*el1)->dn, (*el2)->dn);
59 }
60
61 static struct ldb_control **parse_controls(void *mem_ctx, char **control_strings)
62 {
63         int i;
64         struct ldb_control **ctrl;
65
66         if (control_strings == NULL || control_strings[0] == NULL)
67                 return NULL;
68
69         for (i = 0; control_strings[i]; i++);
70
71         ctrl = talloc_array(mem_ctx, struct ldb_control *, i + 1);
72
73         for (i = 0; control_strings[i]; i++) {
74                 if (strncmp(control_strings[i], "extended_dn:", 12) == 0) {
75                         struct ldb_extended_dn_control *control;
76                         const char *p;
77                         int crit, type, ret;
78
79                         p = &(control_strings[i][12]);
80                         ret = sscanf(p, "%d:%d", &crit, &type);
81                         if ((ret != 2) || (crit < 0) || (crit > 1) || (type < 0) || (type > 1)) {
82                                 fprintf(stderr, "invalid extended_dn control syntax\n");
83                                 return NULL;
84                         }
85
86                         ctrl[i] = talloc(ctrl, struct ldb_control);
87                         ctrl[i]->oid = LDB_CONTROL_EXTENDED_DN_OID;
88                         ctrl[i]->critical = crit;
89                         control = talloc(ctrl[i], struct ldb_extended_dn_control);
90                         control->type = type;
91                         ctrl[i]->data = control;
92
93                         continue;
94                 }
95
96                 if (strncmp(control_strings[i], "paged_results:", 14) == 0) {
97                         struct ldb_paged_control *control;
98                         const char *p;
99                         int crit, size, ret;
100                        
101                         p = &(control_strings[i][14]);
102                         ret = sscanf(p, "%d:%d", &crit, &size);
103
104                         if ((ret != 2) || (crit < 0) || (crit > 1) || (size < 0)) {
105                                 fprintf(stderr, "invalid paged_results control syntax\n");
106                                 return NULL;
107                         }
108
109                         ctrl[i] = talloc(ctrl, struct ldb_control);
110                         ctrl[i]->oid = LDB_CONTROL_PAGED_RESULTS_OID;
111                         ctrl[i]->critical = crit;
112                         control = talloc(ctrl[i], struct ldb_paged_control);
113                         control->size = size;
114                         control->cookie = NULL;
115                         control->cookie_len = 0;
116                         ctrl[i]->data = control;
117
118                         continue;
119                 }
120
121                 if (strncmp(control_strings[i], "server_sort:", 12) == 0) {
122                         struct ldb_server_sort_control **control;
123                         const char *p;
124                         char attr[256];
125                         char rule[128];
126                         int crit, rev, ret;
127
128                         attr[0] = '\0';
129                         rule[0] = '\0';
130                         p = &(control_strings[i][12]);
131                         ret = sscanf(p, "%d:%d:%255[^:]:%127[^:]", &crit, &rev, attr, rule);
132                         if ((ret < 3) || (crit < 0) || (crit > 1) || (rev < 0 ) || (rev > 1) ||attr[0] == '\0') {
133                                 fprintf(stderr, "invalid server_sort control syntax\n");
134                                 return NULL;
135                         }
136                         ctrl[i] = talloc(ctrl, struct ldb_control);
137                         ctrl[i]->oid = LDB_CONTROL_SERVER_SORT_OID;
138                         ctrl[i]->critical = crit;
139                         control = talloc_array(ctrl[i], struct ldb_server_sort_control *, 2);
140                         control[0] = talloc(control, struct ldb_server_sort_control);
141                         control[0]->attributeName = talloc_strdup(control, attr);
142                         if (rule[0])
143                                 control[0]->orderingRule = talloc_strdup(control, rule);
144                         else
145                                 control[0]->orderingRule = NULL;
146                         control[0]->reverse = rev;
147                         control[1] = NULL;
148                         ctrl[i]->data = control;
149
150                         continue;
151                 }
152
153                 /* no controls matched, throw an error */
154                 fprintf(stderr, "Invalid control name\n");
155                 return NULL;
156         }
157
158         ctrl[i] = NULL;
159
160         return ctrl;
161 }
162
163 /* this function check controls reply and determines if more
164  * processing is needed setting up the request controls correctly
165  *
166  * returns:
167  *      -1 error
168  *      0 all ok
169  *      1 all ok, more processing required
170  */
171 static int handle_controls_reply(struct ldb_control **reply, struct ldb_control **request)
172 {
173         int i, j;
174         int ret = 0;
175
176         if (reply == NULL || request == NULL) return -1;
177         
178         for (i = 0; reply[i]; i++) {
179                 if (strcmp(LDB_CONTROL_PAGED_RESULTS_OID, reply[i]->oid) == 0) {
180                         struct ldb_paged_control *rep_control, *req_control;
181
182                         rep_control = talloc_get_type(reply[i]->data, struct ldb_paged_control);
183                         if (rep_control->cookie_len == 0) /* we are done */
184                                 break;
185
186                         /* more processing required */
187                         /* let's fill in the request control with the new cookie */
188
189                         for (j = 0; request[j]; j++) {
190                                 if (strcmp(LDB_CONTROL_PAGED_RESULTS_OID, request[j]->oid) == 0)
191                                         break;
192                         }
193                         /* if there's a reply control we must find a request
194                          * control matching it */
195                         if (! request[j]) return -1;
196
197                         req_control = talloc_get_type(request[j]->data, struct ldb_paged_control);
198
199                         if (req_control->cookie)
200                                 talloc_free(req_control->cookie);
201                         req_control->cookie = talloc_memdup(req_control,
202                                                             rep_control->cookie,
203                                                             rep_control->cookie_len);
204                         req_control->cookie_len = rep_control->cookie_len;
205
206                         ret = 1;
207
208                         continue;
209                 }
210
211                 if (strcmp(LDB_CONTROL_SORT_RESP_OID, reply[i]->oid) == 0) {
212                         struct ldb_sort_resp_control *rep_control;
213
214                         rep_control = talloc_get_type(reply[i]->data, struct ldb_sort_resp_control);
215
216                         /* check we have a matching control in the request */
217                         for (j = 0; request[j]; j++) {
218                                 if (strcmp(LDB_CONTROL_SERVER_SORT_OID, request[j]->oid) == 0)
219                                         break;
220                         }
221                         if (! request[j]) {
222                                 fprintf(stderr, "Warning Server Sort reply received but no request found\n");
223                                 continue;
224                         }
225
226                         /* check the result */
227                         if (rep_control->result != 0) {
228                                 fprintf(stderr, "Warning: Sorting not performed with error: %d\n", rep_control->result);
229                         }
230
231                         continue;
232                 }
233
234                 /* no controls matched, throw a warning */
235                 fprintf(stderr, "Unknown reply control oid: %s\n", reply[i]->oid);
236         }
237
238         return ret;
239 }
240
241
242 static int do_search(struct ldb_context *ldb,
243                      const struct ldb_dn *basedn,
244                      struct ldb_cmdline *options,
245                      const char *expression,
246                      const char * const *attrs)
247 {
248         int ret, i;
249         int loop = 0;
250         int total = 0;
251         struct ldb_request req;
252         struct ldb_result *result = NULL;
253
254         req.operation = LDB_REQ_SEARCH;
255         req.op.search.base = basedn;
256         req.op.search.scope = options->scope;
257         req.op.search.tree = ldb_parse_tree(ldb, expression);
258         req.op.search.attrs = attrs;
259         req.op.search.res = NULL;
260         req.controls = parse_controls(ldb, options->controls);
261         if (options->controls != NULL && req.controls == NULL) return -1;
262         req.creds = NULL;
263
264         do {
265                 loop = 0;
266
267                 ret = ldb_request(ldb, &req);
268                 if (ret != LDB_SUCCESS) {
269                         printf("search failed - %s\n", ldb_errstring(ldb));
270                         return -1;
271                 }
272
273                 result = req.op.search.res;
274                 printf("# returned %d records\n", result->count);
275
276                 if (options->sorted) {
277                         ldb_qsort(result->msgs, ret, sizeof(struct ldb_message *),
278                                   ldb, (ldb_qsort_cmp_fn_t)do_compare_msg);
279                 }
280
281                 for (i = 0; i < result->count; i++, total++) {
282                         struct ldb_ldif ldif;
283                         printf("# record %d\n", total + 1);
284
285                         ldif.changetype = LDB_CHANGETYPE_NONE;
286                         ldif.msg = result->msgs[i];
287
288                         if (options->sorted) {
289                                 /*
290                                  * Ensure attributes are always returned in the same
291                                  * order.  For testing, this makes comparison of old
292                                  * vs. new much easier.
293                                  */
294                                 ldb_msg_sort_elements(ldif.msg);
295                         }
296         
297                         ldb_ldif_write_file(ldb, stdout, &ldif);
298                 }
299
300                 if (result->controls) {
301                         if (handle_controls_reply(result->controls, req.controls) == 1)
302                                 loop = 1;
303                 }
304
305                 if (result) {
306                         ret = talloc_free(result);
307                         if (ret == -1) {
308                                 fprintf(stderr, "talloc_free failed\n");
309                                 exit(1);
310                         }
311                 }
312
313                 req.op.search.res = NULL;
314                 
315         } while(loop);
316
317         return 0;
318 }
319
320  int main(int argc, const char **argv)
321 {
322         struct ldb_context *ldb;
323         struct ldb_dn *basedn = NULL;
324         const char * const * attrs = NULL;
325         struct ldb_cmdline *options;
326         int ret = -1;
327         const char *expression = "(objectclass=*)";
328
329         ldb = ldb_init(NULL);
330
331         options = ldb_cmdline_process(ldb, argc, argv, usage);
332
333         /* the check for '=' is for compatibility with ldapsearch */
334         if (!options->interactive &&
335             options->argc > 0 && 
336             strchr(options->argv[0], '=')) {
337                 expression = options->argv[0];
338                 options->argv++;
339                 options->argc--;
340         }
341
342         if (options->argc > 0) {
343                 attrs = (const char * const *)(options->argv);
344         }
345
346         if (options->basedn != NULL) {
347                 basedn = ldb_dn_explode(ldb, options->basedn);
348                 if (basedn == NULL) {
349                         fprintf(stderr, "Invalid Base DN format\n");
350                         exit(1);
351                 }
352         }
353
354         if (options->interactive) {
355                 char line[1024];
356                 while (fgets(line, sizeof(line), stdin)) {
357                         if (do_search(ldb, basedn, options, line, attrs) == -1) {
358                                 ret = -1;
359                         }
360                 }
361         } else {
362                 ret = do_search(ldb, basedn, options, expression, attrs);
363         }
364
365         talloc_free(ldb);
366         return ret;
367 }