r12983: - fix using a DIRSYNC cookie from the command line
[kamenim/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], "dirsync:", 8) == 0) {
75                         struct ldb_dirsync_control *control;
76                         const char *p;
77                         char cookie[1024];
78                         int crit, flags, max_attrs, ret;
79                        
80                         cookie[0] = '\0';
81                         p = &(control_strings[i][8]);
82                         ret = sscanf(p, "%d:%d:%d:%1023[^$]", &crit, &flags, &max_attrs, cookie);
83
84                         if ((ret < 3) || (crit < 0) || (crit > 1) || (flags < 0) || (max_attrs < 0)) {
85                                 fprintf(stderr, "invalid dirsync control syntax\n");
86                                 return NULL;
87                         }
88
89                         /* w2k3 seems to ignore the parameter,
90                          * but w2k sends a wrong cookie when this value is to small
91                          * this would cause looping forever, while getting
92                          * the same data and same cookie forever
93                          */
94                         if (max_attrs == 0) max_attrs = 0x0FFFFFFF;
95
96                         ctrl[i] = talloc(ctrl, struct ldb_control);
97                         ctrl[i]->oid = LDB_CONTROL_DIRSYNC_OID;
98                         ctrl[i]->critical = crit;
99                         control = talloc(ctrl[i], struct ldb_dirsync_control);
100                         control->flags = flags;
101                         control->max_attributes = max_attrs;
102                         if (*cookie) {
103                                 control->cookie_len = ldb_base64_decode(cookie);
104                                 control->cookie = talloc_memdup(control, cookie, control->cookie_len);
105                         } else {
106                                 control->cookie = NULL;
107                                 control->cookie_len = 0;
108                         }
109                         ctrl[i]->data = control;
110
111                         continue;
112                 }
113
114                 if (strncmp(control_strings[i], "asq:", 4) == 0) {
115                         struct ldb_asq_control *control;
116                         const char *p;
117                         char attr[256];
118                         int crit, ret;
119
120                         attr[0] = '\0';
121                         p = &(control_strings[i][4]);
122                         ret = sscanf(p, "%d:%255[^$]", &crit, attr);
123                         if ((ret != 2) || (crit < 0) || (crit > 1) || (attr[0] == '\0')) {
124                                 fprintf(stderr, "invalid asq control syntax\n");
125                                 return NULL;
126                         }
127
128                         ctrl[i] = talloc(ctrl, struct ldb_control);
129                         ctrl[i]->oid = LDB_CONTROL_ASQ_OID;
130                         ctrl[i]->critical = crit;
131                         control = talloc(ctrl[i], struct ldb_asq_control);
132                         control->request = 1;
133                         control->source_attribute = talloc_strdup(control, attr);
134                         control->src_attr_len = strlen(attr);
135                         ctrl[i]->data = control;
136
137                         continue;
138                 }
139
140                 if (strncmp(control_strings[i], "extended_dn:", 12) == 0) {
141                         struct ldb_extended_dn_control *control;
142                         const char *p;
143                         int crit, type, ret;
144
145                         p = &(control_strings[i][12]);
146                         ret = sscanf(p, "%d:%d", &crit, &type);
147                         if ((ret != 2) || (crit < 0) || (crit > 1) || (type < 0) || (type > 1)) {
148                                 fprintf(stderr, "invalid extended_dn control syntax\n");
149                                 return NULL;
150                         }
151
152                         ctrl[i] = talloc(ctrl, struct ldb_control);
153                         ctrl[i]->oid = LDB_CONTROL_EXTENDED_DN_OID;
154                         ctrl[i]->critical = crit;
155                         control = talloc(ctrl[i], struct ldb_extended_dn_control);
156                         control->type = type;
157                         ctrl[i]->data = control;
158
159                         continue;
160                 }
161
162                 if (strncmp(control_strings[i], "paged_results:", 14) == 0) {
163                         struct ldb_paged_control *control;
164                         const char *p;
165                         int crit, size, ret;
166                        
167                         p = &(control_strings[i][14]);
168                         ret = sscanf(p, "%d:%d", &crit, &size);
169
170                         if ((ret != 2) || (crit < 0) || (crit > 1) || (size < 0)) {
171                                 fprintf(stderr, "invalid paged_results control syntax\n");
172                                 return NULL;
173                         }
174
175                         ctrl[i] = talloc(ctrl, struct ldb_control);
176                         ctrl[i]->oid = LDB_CONTROL_PAGED_RESULTS_OID;
177                         ctrl[i]->critical = crit;
178                         control = talloc(ctrl[i], struct ldb_paged_control);
179                         control->size = size;
180                         control->cookie = NULL;
181                         control->cookie_len = 0;
182                         ctrl[i]->data = control;
183
184                         continue;
185                 }
186
187                 if (strncmp(control_strings[i], "server_sort:", 12) == 0) {
188                         struct ldb_server_sort_control **control;
189                         const char *p;
190                         char attr[256];
191                         char rule[128];
192                         int crit, rev, ret;
193
194                         attr[0] = '\0';
195                         rule[0] = '\0';
196                         p = &(control_strings[i][12]);
197                         ret = sscanf(p, "%d:%d:%255[^:]:%127[^:]", &crit, &rev, attr, rule);
198                         if ((ret < 3) || (crit < 0) || (crit > 1) || (rev < 0 ) || (rev > 1) ||attr[0] == '\0') {
199                                 fprintf(stderr, "invalid server_sort control syntax\n");
200                                 return NULL;
201                         }
202                         ctrl[i] = talloc(ctrl, struct ldb_control);
203                         ctrl[i]->oid = LDB_CONTROL_SERVER_SORT_OID;
204                         ctrl[i]->critical = crit;
205                         control = talloc_array(ctrl[i], struct ldb_server_sort_control *, 2);
206                         control[0] = talloc(control, struct ldb_server_sort_control);
207                         control[0]->attributeName = talloc_strdup(control, attr);
208                         if (rule[0])
209                                 control[0]->orderingRule = talloc_strdup(control, rule);
210                         else
211                                 control[0]->orderingRule = NULL;
212                         control[0]->reverse = rev;
213                         control[1] = NULL;
214                         ctrl[i]->data = control;
215
216                         continue;
217                 }
218
219                 /* no controls matched, throw an error */
220                 fprintf(stderr, "Invalid control name\n");
221                 return NULL;
222         }
223
224         ctrl[i] = NULL;
225
226         return ctrl;
227 }
228
229 /* this function check controls reply and determines if more
230  * processing is needed setting up the request controls correctly
231  *
232  * returns:
233  *      -1 error
234  *      0 all ok
235  *      1 all ok, more processing required
236  */
237 static int handle_controls_reply(struct ldb_control **reply, struct ldb_control **request)
238 {
239         int i, j;
240         int ret = 0;
241
242         if (reply == NULL || request == NULL) return -1;
243         
244         for (i = 0; reply[i]; i++) {
245                 if (strcmp(LDB_CONTROL_ASQ_OID, reply[i]->oid) == 0) {
246                         struct ldb_asq_control *rep_control;
247
248                         rep_control = talloc_get_type(reply[i]->data, struct ldb_asq_control);
249
250                         /* check the result */
251                         if (rep_control->result != 0) {
252                                 fprintf(stderr, "Warning: ASQ not performed with error: %d\n", rep_control->result);
253                         }
254
255                         continue;
256                 }
257                 if (strcmp(LDB_CONTROL_PAGED_RESULTS_OID, reply[i]->oid) == 0) {
258                         struct ldb_paged_control *rep_control, *req_control;
259
260                         rep_control = talloc_get_type(reply[i]->data, struct ldb_paged_control);
261                         if (rep_control->cookie_len == 0) /* we are done */
262                                 break;
263
264                         /* more processing required */
265                         /* let's fill in the request control with the new cookie */
266
267                         for (j = 0; request[j]; j++) {
268                                 if (strcmp(LDB_CONTROL_PAGED_RESULTS_OID, request[j]->oid) == 0)
269                                         break;
270                         }
271                         /* if there's a reply control we must find a request
272                          * control matching it */
273                         if (! request[j]) return -1;
274
275                         req_control = talloc_get_type(request[j]->data, struct ldb_paged_control);
276
277                         if (req_control->cookie)
278                                 talloc_free(req_control->cookie);
279                         req_control->cookie = talloc_memdup(req_control,
280                                                             rep_control->cookie,
281                                                             rep_control->cookie_len);
282                         req_control->cookie_len = rep_control->cookie_len;
283
284                         ret = 1;
285
286                         continue;
287                 }
288
289                 if (strcmp(LDB_CONTROL_SORT_RESP_OID, reply[i]->oid) == 0) {
290                         struct ldb_sort_resp_control *rep_control;
291
292                         rep_control = talloc_get_type(reply[i]->data, struct ldb_sort_resp_control);
293
294                         /* check we have a matching control in the request */
295                         for (j = 0; request[j]; j++) {
296                                 if (strcmp(LDB_CONTROL_SERVER_SORT_OID, request[j]->oid) == 0)
297                                         break;
298                         }
299                         if (! request[j]) {
300                                 fprintf(stderr, "Warning Server Sort reply received but no request found\n");
301                                 continue;
302                         }
303
304                         /* check the result */
305                         if (rep_control->result != 0) {
306                                 fprintf(stderr, "Warning: Sorting not performed with error: %d\n", rep_control->result);
307                         }
308
309                         continue;
310                 }
311
312                 if (strcmp(LDB_CONTROL_DIRSYNC_OID, reply[i]->oid) == 0) {
313                         struct ldb_dirsync_control *rep_control, *req_control;
314                         char *cookie;
315
316                         rep_control = talloc_get_type(reply[i]->data, struct ldb_dirsync_control);
317                         if (rep_control->cookie_len == 0) /* we are done */
318                                 break;
319
320                         /* more processing required */
321                         /* let's fill in the request control with the new cookie */
322
323                         for (j = 0; request[j]; j++) {
324                                 if (strcmp(LDB_CONTROL_DIRSYNC_OID, request[j]->oid) == 0)
325                                         break;
326                         }
327                         /* if there's a reply control we must find a request
328                          * control matching it */
329                         if (! request[j]) return -1;
330
331                         req_control = talloc_get_type(request[j]->data, struct ldb_dirsync_control);
332
333                         if (req_control->cookie)
334                                 talloc_free(req_control->cookie);
335                         req_control->cookie = talloc_memdup(req_control, 
336                                                             rep_control->cookie,
337                                                             rep_control->cookie_len);
338                         req_control->cookie_len = rep_control->cookie_len;
339
340                         cookie = ldb_base64_encode(req_control, rep_control->cookie, rep_control->cookie_len);
341                         printf("# DIRSYNC cookie returned was:\n# %s\n", cookie);
342
343                         ret = 1;
344
345                         continue;
346                 }
347
348                 /* no controls matched, throw a warning */
349                 fprintf(stderr, "Unknown reply control oid: %s\n", reply[i]->oid);
350         }
351
352         return ret;
353 }
354
355
356 static int do_search(struct ldb_context *ldb,
357                      const struct ldb_dn *basedn,
358                      struct ldb_cmdline *options,
359                      const char *expression,
360                      const char * const *attrs)
361 {
362         int ret, i;
363         int loop = 0;
364         int total = 0;
365         struct ldb_request req;
366         struct ldb_result *result = NULL;
367
368         req.operation = LDB_REQ_SEARCH;
369         req.op.search.base = basedn;
370         req.op.search.scope = options->scope;
371         req.op.search.tree = ldb_parse_tree(ldb, expression);
372         req.op.search.attrs = attrs;
373         req.op.search.res = NULL;
374         req.controls = parse_controls(ldb, options->controls);
375         if (options->controls != NULL && req.controls == NULL) return -1;
376         req.creds = NULL;
377
378         do {
379                 loop = 0;
380
381                 ret = ldb_request(ldb, &req);
382                 if (ret != LDB_SUCCESS) {
383                         printf("search failed - %s\n", ldb_errstring(ldb));
384                         if (req.op.search.res && req.op.search.res->controls) {
385                                 
386                         /*      TODO: handle_control */
387                         }
388                         return -1;
389                 }
390
391                 result = req.op.search.res;
392                 printf("# returned %d records\n", result->count);
393
394                 if (options->sorted) {
395                         ldb_qsort(result->msgs, ret, sizeof(struct ldb_message *),
396                                   ldb, (ldb_qsort_cmp_fn_t)do_compare_msg);
397                 }
398
399                 for (i = 0; i < result->count; i++, total++) {
400                         struct ldb_ldif ldif;
401                         printf("# record %d\n", total + 1);
402
403                         ldif.changetype = LDB_CHANGETYPE_NONE;
404                         ldif.msg = result->msgs[i];
405
406                         if (options->sorted) {
407                                 /*
408                                  * Ensure attributes are always returned in the same
409                                  * order.  For testing, this makes comparison of old
410                                  * vs. new much easier.
411                                  */
412                                 ldb_msg_sort_elements(ldif.msg);
413                         }
414         
415                         ldb_ldif_write_file(ldb, stdout, &ldif);
416                 }
417
418                 if (result->controls) {
419                         if (handle_controls_reply(result->controls, req.controls) == 1)
420                                 loop = 1;
421                 }
422
423                 if (result) {
424                         ret = talloc_free(result);
425                         if (ret == -1) {
426                                 fprintf(stderr, "talloc_free failed\n");
427                                 exit(1);
428                         }
429                 }
430
431                 req.op.search.res = NULL;
432                 
433         } while(loop);
434
435         return 0;
436 }
437
438  int main(int argc, const char **argv)
439 {
440         struct ldb_context *ldb;
441         struct ldb_dn *basedn = NULL;
442         const char * const * attrs = NULL;
443         struct ldb_cmdline *options;
444         int ret = -1;
445         const char *expression = "(|(objectClass=*)(distinguishedName=*))";
446
447         ldb = ldb_init(NULL);
448
449         options = ldb_cmdline_process(ldb, argc, argv, usage);
450
451         /* the check for '=' is for compatibility with ldapsearch */
452         if (!options->interactive &&
453             options->argc > 0 && 
454             strchr(options->argv[0], '=')) {
455                 expression = options->argv[0];
456                 options->argv++;
457                 options->argc--;
458         }
459
460         if (options->argc > 0) {
461                 attrs = (const char * const *)(options->argv);
462         }
463
464         if (options->basedn != NULL) {
465                 basedn = ldb_dn_explode(ldb, options->basedn);
466                 if (basedn == NULL) {
467                         fprintf(stderr, "Invalid Base DN format\n");
468                         exit(1);
469                 }
470         }
471
472         if (options->interactive) {
473                 char line[1024];
474                 while (fgets(line, sizeof(line), stdin)) {
475                         if (do_search(ldb, basedn, options, line, attrs) == -1) {
476                                 ret = -1;
477                         }
478                 }
479         } else {
480                 ret = do_search(ldb, basedn, options, expression, attrs);
481         }
482
483         talloc_free(ldb);
484         return ret;
485 }