]> git.sur5r.net Git - openldap/blob - contrib/saucer/main.c
59ea5e4d85a2aae88ac944bad3b20cba7189228c
[openldap] / contrib / saucer / main.c
1 /*
2  * Copyright (c) 1994, Strata Software Limited, Ottawa, Ontario, Canada.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that this notice is preserved and that due credit is given
7  * to Eric Rosenquist and Strata Software Limited. The SSL name
8  * may not be used to endorse or promote products derived from this
9  * software without specific prior written permission. This software
10  * is provided "as is" without express or implied warranty.
11  *
12  *
13  * 'saucer' LDAP command-line client source code.
14  *
15  * Author: Eric Rosenquist, 1994.
16  *
17  * 07-Mar-1999 readline support added: O. Steffensen (oddbjorn@tricknology.org)
18  */
19
20 #include "portable.h"
21
22 #include <stdio.h>
23 #include <stdlib.h>
24
25 #ifdef HAVE_READLINE
26 #  include <readline/readline.h>
27 #  ifdef HAVE_READLINE_HISTORY_H
28 #    include <readline/history.h>
29 #  endif
30 #endif
31
32 #include <ac/ctype.h>
33 #include <ac/string.h>
34 #include <ac/unistd.h>
35
36 #include <lber.h>
37 #include <ldap.h>
38 #include <ldap_log.h>
39
40 #define DN_MAXLEN       4096
41
42 typedef struct {
43         const char      *cmd;
44         int             (*func) (char **, int);
45         const char      *help_msg;
46 } CMDTABLE;
47
48 typedef enum {
49         CMD_HELP,
50         CMD_LIST,
51         CMD_MOVETO,
52         CMD_QUIT,
53         CMD_SEARCH,
54         CMD_SET,
55         CMD_SHOW
56 } COMMAND;
57
58 char            *attrs_null[] = { "0.10", NULL };
59 char            *credentials;
60 char            default_dn[DN_MAXLEN];
61 char            *hostname = "127.0.0.1";
62 LDAP            *ld;
63 extern char     *optarg;
64 extern int      opterr;
65 extern int      optind;
66 int                     option;
67 int                     portnum = LDAP_PORT;
68 char            *progname;
69 char            true_filter[] = "objectClass=*";        /* Always succeeds */
70 char            *username;
71
72 int                     cmd_help(char **cmdargv, int cmdargc);
73 int                     cmd_list(char **cmdargv, int cmdargc);
74 int                     cmd_moveto(char **cmdargv, int cmdargc);
75 int                     cmd_quit(char **cmdargv, int cmdargc);
76 int                     cmd_search(char **cmdargv, int cmdargc);
77 int                     cmd_set(char **cmdargv, int cmdargc);
78 int                     cmd_show(char **cmdargv, int cmdargc);
79
80 int             bind_user(void);
81 void            display_search_results(LDAPMessage *result);
82 int             do_command(char *cmd);
83 void            do_commands(FILE *file);
84 int             is_whitespace(register char *s);
85 char            *make_dn(char *dn, int relative);
86 void            show_syntax(unsigned int cmdnum);
87 char            *skip_to_char(register char *s, register int c);
88 char            *skip_to_whitespace(register char *s);
89 char            *skip_whitespace(register char *s);
90 int             table_lookup(const char *, const char *const *, int);
91 FILE            *user_tailor(void);
92
93 static const char *const binary_attrs[] = {
94         "audio", "jpegPhoto", "personalSignature", "photo"
95 };
96
97 const CMDTABLE  cmdtable[] = {
98         "help"  , cmd_help  , "[command]",
99         "list"  , cmd_list  , "[RDN-or-DN] [-absolute]",
100         "moveto", cmd_moveto, "[RDN-or-DN] [-absolute]",
101         "quit"  , cmd_quit  , "",
102         "search", cmd_search, "<filter> [-object RDN-or-DN] [-absolute]\n\t\t[-scope base|onelevel|subtree]",
103         "set"   , cmd_set   , "[-aliasderef never|search|find|always] [-sizelimit N] [-timelimit seconds]",
104         "show"  , cmd_show  , "[RDN-or-DN] [-absolute]"
105 };
106
107
108 int bind_user(void)
109 {
110         if (ldap_simple_bind_s(ld, username, credentials) != LDAP_SUCCESS) {
111                 ldap_perror(ld, progname);
112                 return 0;
113         }
114         if (username)
115                 printf("Bound to ldap server as `%s' (%s authentication)\n", username,
116                            credentials ? "simple" : "no");
117         else
118                 puts("Bound anonymously to ldap server");
119
120         return 1;
121 }
122
123 int cmd_help(char **cmdargv, int cmdargc)
124 {
125         unsigned int    i;
126
127         if (cmdargc == 2) {
128                 for (i = 0; i < sizeof(cmdtable) / sizeof(cmdtable[0]); i++)
129                         if (strncasecmp(cmdargv[1], cmdtable[i].cmd, strlen(cmdargv[1])) == 0) {
130                                 show_syntax(i);
131                                 return 0;
132                         }
133                 cmdargc = 1;    /* Command not found - make it display the list of commands */
134         }
135
136         if (cmdargc == 1) {
137                 puts("\nType 'help <command>' for help on a particular command.\n\n"
138                          "Supported commands are:");
139                 for (i = 0; i < sizeof(cmdtable) / sizeof(cmdtable[0]); i++)
140                         printf("  %s\n", cmdtable[i].cmd);
141                 puts("\nArguments to commands are separated by whitespace.  Single (')\n"
142                          "or double (\") quotes must be used around arguments that contain\n"
143                          "embedded whitespace characters.\n");
144         } else
145                 show_syntax(CMD_HELP);
146
147         return 0;
148 }
149
150 int cmd_list(char **cmdargv, int cmdargc)
151 {
152         char            *dn      = NULL;
153         int                     errflag  = 0;
154         int                     i;
155         static const char *const opts[]  = { "absolute" };
156         int                     relative = 1;
157         LDAPMessage     *result;
158
159         for (i = 1; i < cmdargc; i++) {
160                 if (cmdargv[i][0] == '-') {
161                         switch (table_lookup(cmdargv[i] + 1, opts, sizeof(opts) / sizeof(opts[0]))) {
162                         case 0:
163                                 relative = 0;
164                                 break;
165                         default:
166                                 errflag = 1;
167                         }
168                 } else {
169                         if (dn)
170                                 errflag = 1;
171                         else
172                                 dn = cmdargv[i];
173                 }
174         }
175
176         if (errflag) {
177                 show_syntax(CMD_LIST);
178                 return 0;
179         }
180
181         if (ldap_search(ld, make_dn(dn, relative), LDAP_SCOPE_ONELEVEL,
182                                         true_filter, attrs_null, 1) == -1) {
183                 ldap_perror(ld, progname);
184                 return 0;
185         }
186
187         if (ldap_result(ld, LDAP_RES_ANY, 1, (struct timeval *)0, &result) == -1) {
188                 ldap_perror(ld, progname);
189                 return 0;
190         }
191
192         display_search_results(result);
193                 
194         return 0;
195 }
196
197 int cmd_moveto(char **cmdargv, int cmdargc)
198 {
199         char            *dn      = NULL;
200         int                     errflag  = 0;
201         char            **exploded_dn;
202         int                     i;
203         static const char *const opts[]  = { "absolute" };
204         int                     relative = 1;
205
206         for (i = 1; i < cmdargc; i++) {
207                 if (cmdargv[i][0] == '-') {
208                         switch (table_lookup(cmdargv[i] + 1, opts, sizeof(opts) / sizeof(opts[0]))) {
209                         case 0:
210                                 relative = 0;
211                                 break;
212                         default:
213                                 errflag = 1;
214                         }
215                 } else {
216                         if (dn)
217                                 errflag = 1;
218                         else
219                                 dn = cmdargv[i];
220                 }
221         }
222
223         if (errflag) {
224                 show_syntax(CMD_MOVETO);
225                 return 0;
226         }
227
228         if (dn) {
229                 if (is_whitespace(dn))
230                         default_dn[0] = 0;
231                 else {
232                         if (strcmp(dn, "..") == 0) {
233                                 /* Move up one level */
234                                 if (exploded_dn = ldap_explode_dn(default_dn, 0)) {
235                                         if (exploded_dn[0]) {
236                                                 char    **rdn;
237
238                                                 default_dn[0] = 0;
239                                                 for (rdn = exploded_dn + 1; *rdn; rdn++) {
240                                                         if (default_dn[0])
241                                                                 strcat(default_dn, ", ");
242                                                         strcat(default_dn, *rdn);
243                                                 }
244                                         }
245                                         ldap_value_free(exploded_dn);
246                                 }
247                         } else {
248                                 /* Use ldap_explode_dn() to parse the string & test its syntax */
249                                 if (exploded_dn = ldap_explode_dn(dn, 1)) {
250                                         if (relative  &&  !is_whitespace(default_dn)) {
251                                                 char    buf[DN_MAXLEN];
252
253                                                 strcpy(default_dn, strcat(strcat(strcpy(buf, dn), ", "), default_dn));
254                                         } else
255                                                 strcpy(default_dn, dn);
256                                         ldap_value_free(exploded_dn);
257                                 } else
258                                         puts("Invalid distinguished name.");
259                         }
260                 }
261         }
262
263         printf("Distinguished name suffix is `%s'\n", default_dn);
264
265         return 0;
266 }
267
268 int cmd_quit(char **cmdargv, int cmdargc)
269 {
270         return 1;
271 }
272
273 int cmd_search(char **cmdargv, int cmdargc)
274 {
275         char            *dn           = NULL;
276         int                     errflag       = 0;
277         char            *filter       = NULL;
278         int                     i, j;
279         static const char *const opts[] = { "absolute", "object", "scope" };
280         int                     relative      = 1;
281         LDAPMessage     *result;
282         static const char *const scope_opts[]= { "base","onelevel","subtree" };
283         static const int scope_vals[] = { LDAP_SCOPE_BASE, LDAP_SCOPE_ONELEVEL, LDAP_SCOPE_SUBTREE };
284         static int      search_scope  = LDAP_SCOPE_ONELEVEL;
285
286         for (i = 1; i < cmdargc; i++) {
287                 if (cmdargv[i][0] == '-') {
288                         switch (table_lookup(cmdargv[i] + 1, opts, sizeof(opts) / sizeof(opts[0]))) {
289                         case 0:
290                                 relative = 0;
291                                 break;
292                         case 1:
293                                 if (++i < cmdargc)
294                                         dn = cmdargv[i];
295                                 else
296                                         errflag = 1;
297                                 break;
298                         case 2:
299                                 if ((++i < cmdargc)  &&
300                                         (j = table_lookup(cmdargv[i], scope_opts, sizeof(scope_opts) / sizeof(scope_opts[0]))) >= 0)
301                                         search_scope = scope_vals[j];
302                                 else
303                                         errflag = 1;
304                                 break;
305                         default:
306                                 errflag = 1;
307                         }
308                 } else {
309                         if (filter)
310                                 errflag = 1;
311                         else
312                                 filter = cmdargv[i];
313                 }
314         }
315
316         if (errflag  ||  !filter) {
317                 show_syntax(CMD_SEARCH);
318                 return 0;
319         }
320
321         if (ldap_search(ld, make_dn(dn, relative), search_scope, filter, attrs_null, 0) == -1) {
322                 ldap_perror(ld, progname);
323                 return 0;
324         }
325
326         if (ldap_result(ld, LDAP_RES_ANY, 1, (struct timeval *)0, &result) == -1) {
327                 ldap_perror(ld, progname);
328                 return 0;
329         }
330
331         display_search_results(result);
332                 
333         return 0;
334 }
335
336 int cmd_set(char **cmdargv, int cmdargc)
337 {
338         static const char *const alias_opts[] = {
339                 "never", "search", "find", "always"
340         };
341         int                     errflag       = 0;
342         int                     i, j;
343         static const char *const opts[] = {
344                 "aliasderef", "sizelimit", "timelimit"
345         };
346
347         for (i = 1; i < cmdargc; i++) {
348                 if (cmdargv[i][0] == '-') {
349                         switch (table_lookup(cmdargv[i] + 1, opts, sizeof(opts) / sizeof(opts[0]))) {
350                         case 0:
351                                 if ((++i < cmdargc)  &&
352                                         (j = table_lookup(cmdargv[i], alias_opts, sizeof(alias_opts) / sizeof(alias_opts[0]))) >= 0)
353                                         ldap_set_option(ld, LDAP_OPT_DEREF, &j);
354                                 else
355                                         errflag = 1;
356                                 break;
357                         case 1:
358                                 if (++i < cmdargc) {
359                                         j = atoi(cmdargv[i]);
360                                         ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &j);
361                                 } else
362                                         errflag = 1;
363                                 break;
364                         case 2:
365                                 if (++i < cmdargc) {
366                                         j = atoi(cmdargv[i]);
367                                         ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &j);
368                                 } else
369                                         errflag = 1;
370                                 break;
371                         default:
372                                 errflag = 1;
373                         }
374                 } else
375                         errflag = 1;
376         }
377
378         if (errflag)
379                 show_syntax(CMD_SET);
380         else {
381                 int opt_a, opt_s, opt_t;
382                 ldap_get_option(ld, LDAP_OPT_DEREF, &opt_a);
383                 ldap_get_option(ld, LDAP_OPT_SIZELIMIT, &opt_s);
384                 ldap_get_option(ld, LDAP_OPT_TIMELIMIT, &opt_t);
385                 printf("Alias dereferencing is %s, Sizelimit is %d entr%s, Timelimit is %d second%s.\n",
386                        alias_opts[opt_a],
387                        opt_s, opt_s == 1 ? "y" : "ies",
388                        opt_t, opt_t == 1 ? ""  : "s");
389         }
390
391         return 0;
392 }
393
394 int cmd_show(char **cmdargv, int cmdargc)
395 {
396         char            *dn      = NULL;
397         int                     errflag  = 0;
398         int                     i;
399         static const char *const opts[] = { "absolute" };
400         int                     relative = 1;
401         LDAPMessage     *result;
402
403         for (i = 1; i < cmdargc; i++) {
404                 if (cmdargv[i][0] == '-') {
405                         switch (table_lookup(cmdargv[i] + 1, opts, sizeof(opts) / sizeof(opts[0]))) {
406                         case 0:
407                                 relative = 0;
408                                 break;
409                         default:
410                                 errflag = 1;
411                         }
412                 } else {
413                         if (dn)
414                                 errflag = 1;
415                         else
416                                 dn = cmdargv[i];
417                 }
418         }
419
420         if (errflag) {
421                 show_syntax(CMD_SHOW);
422                 return 0;
423         }
424
425         if (ldap_search(ld, make_dn(dn, relative), LDAP_SCOPE_BASE, true_filter, NULL, 0) == -1) {
426                 ldap_perror(ld, progname);
427                 return 0;
428         }
429
430         if (ldap_result(ld, LDAP_RES_ANY, 1, (struct timeval *)0, &result) == -1) {
431                 ldap_perror(ld, progname);
432                 return 0;
433         }
434
435         display_search_results(result);
436                 
437         return 0;
438 }
439
440 void display_search_results(LDAPMessage *result)
441 {
442         BerElement      *cookie;
443         int                     i;
444         LDAPMessage     *entry;
445         int                     maxname;
446         char            *s;
447
448         for (entry = ldap_first_entry(ld, result); entry; entry = ldap_next_entry(ld, entry)) {
449                 if (s = ldap_get_dn(ld, entry)) {
450                         printf("  %s\n", s);
451                         ldap_memfree(s);
452                 }
453
454                 /* Make one pass to calculate the length of the longest attribute name */
455                 maxname = 0;
456                 for (s = ldap_first_attribute(ld, entry, &cookie); s; s = ldap_next_attribute(ld, entry, cookie))
457                         if ((i = strlen(s)) > maxname)
458                                 maxname = i;
459
460                 /* Now print the attributes and values */
461                 for (s = ldap_first_attribute(ld, entry, &cookie); s; s = ldap_next_attribute(ld, entry, cookie)) {
462                         char    **values;
463
464                         if (table_lookup(s, binary_attrs, sizeof(binary_attrs) / sizeof(binary_attrs[0])) >= 0)
465                                 continue;       /* Skip this attribute - it's binary */
466
467                         printf("    %-*s - ", maxname, s);
468
469                         /* Now print each of the values for the given attribute */
470                         if (values = ldap_get_values(ld, entry, s)) {
471                                 char    **val;
472
473                                 for (val = values; *val; ) {
474                                         char    *nl;
475                                         char    *v = *val;
476
477                                         /* Watch out for values that have embedded \n characters */
478                                         while (nl = strchr(v, '\n')) {
479                                                 *nl = 0;
480                                                 puts(v);
481                                                 v = nl + 1;
482                                                 if (*v)
483                                                         printf("    %*s", maxname + 3, "");
484                                         }
485                                         if (*v)
486                                                 puts(v);
487                                         if (*++val)
488                                                 printf("    %*s", maxname + 3, "");
489                                 }
490                                 ldap_value_free(values);
491                         } else
492                                 putchar('\n');
493                 }
494         }
495
496         if (ldap_result2error(ld, result, 0))
497                 ldap_perror(ld, progname);
498 }
499
500 int do_command(char *cmd)
501 {
502         char    *cmdargv[128];
503         int             cmdargc = 0;
504         int             i;
505
506         /* Tokenize the input command, allowing for quoting */
507         for (;;) {
508                 cmd = skip_whitespace(cmd);
509                 if (!cmd  ||  !*cmd)
510                         break;  /* end of input */
511
512                 cmdargv[cmdargc++] = cmd;
513                 if (*cmd == '\''  ||  *cmd == '"') {
514                         cmdargv[cmdargc - 1]++;         /* Skip over the opening quote */
515                         cmd = skip_to_char(cmd + 1, *cmd);
516                         if (!cmd  ||  !*cmd) {
517                                 puts("Command is missing a trailing quote");
518                                 return 0;
519                         }
520                         *cmd++ = 0;
521                 } else {
522                         cmd = skip_to_whitespace(cmd);
523                         if (cmd  &&  *cmd)
524                                 *cmd++ = 0;
525                 }
526         }
527
528 #ifdef DEBUG
529         printf("cmdargc = %d\n", cmdargc);
530         for (i = 0; i < cmdargc; i++)
531                 puts(cmdargv[i]);
532 #endif
533         
534         if (cmdargv[0][0] == '?')
535                 return cmd_help(cmdargv, cmdargc);
536
537         for (i = 0; i < sizeof(cmdtable) / sizeof(cmdtable[0]); i++)
538                 if (strncasecmp(cmdargv[0], cmdtable[i].cmd, strlen(cmdargv[0])) == 0)
539                         return (*cmdtable[i].func)(cmdargv, cmdargc);
540
541         if (!is_whitespace(cmdargv[0])) {
542                 printf("Unrecognized command - %s\n", cmdargv[0]);
543                 cmd_help(cmdargv, 1);
544         }
545
546         return 0;
547 }
548
549 void do_commands(FILE *file)
550 {
551         char    cmd_buf[BUFSIZ];
552         int             tty = isatty(fileno(file));
553         char    *buf = cmd_buf;
554         int     status;
555
556         for (;;) {
557                 if (tty)
558                 {
559                         char    prompt[40];
560                         sprintf(prompt, (strlen(default_dn) < 18
561                                          ? "saucer dn=%s> "
562                                          : "saucer dn=%.15s..> "), default_dn);
563 #ifndef HAVE_READLINE
564                         fputs (prompt, stdout);
565 #else
566                         buf = readline (prompt);
567                         if (!buf)
568                                 break;
569                         add_history (buf);
570 #endif
571                 }
572 #ifdef HAVE_READLINE
573                 else
574 #endif
575                 {
576                         if (!fgets(cmd_buf, sizeof(cmd_buf), file))
577                                 break;
578                 }
579
580                 status = do_command(buf);
581 #ifdef HAVE_READLINE
582                 if (tty)
583                         free(buf);
584 #endif
585                 if (status)
586                         break;
587         }
588 }
589
590 int is_whitespace(register char *s)
591 {
592         if (!s)
593                 return 1;
594
595         while (*s  &&  isspace((unsigned char) *s))
596                 ++s;
597
598         return !*s;
599 }
600
601 int main(int argc, char **argv)
602 {
603         int             error_flag = 0;
604         int             tmp;
605         FILE    *rc;
606
607         progname = argv[0];
608         while ((option = getopt(argc, argv, "h:p:u:c:d:")) != EOF)
609                 switch (option) {
610                 case 'c':
611                         credentials = optarg;
612                         break;
613                 case 'd':
614 #ifdef LDAP_DEBUG
615                         tmp = atoi(optarg);
616                         ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, &tmp);
617                         ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &tmp);
618 #endif
619                         break;
620                 case 'h':
621                         hostname = optarg;
622                         break;
623                 case 'p':
624                         portnum = atoi(optarg);
625                         break;
626                 case 'u':
627                         username = optarg;
628                         break;
629                 case '?':
630                         error_flag = 1;
631                 }
632
633         if (error_flag) {
634                 fprintf(stderr, "usage: %s [-h host] [-p portnumber] [-u X500UserName]\n\t[-c credentials] [-d debug-level]\n",
635                                 progname);
636                 exit(2);
637         }
638
639         rc = user_tailor();
640
641         if (!(ld = ldap_open(hostname, portnum))) {
642                 fprintf(stderr, "%s: unable to connect to server at host `%s' on port %d\n",
643                                 progname, hostname, portnum);
644                 exit(2);
645         }
646
647         if (!bind_user())
648                 return 1;
649
650         if (rc) {
651                 do_commands(rc);
652                 fclose(rc);
653         }
654         do_commands(stdin);
655
656         ldap_unbind(ld);
657
658         return 0;
659 }
660
661 char *make_dn(char *dn, int relative)
662 {
663         static char     dn_buf[DN_MAXLEN];
664
665         if (!dn)
666                 dn = "";
667
668         if (!default_dn[0]  ||  !relative)
669                 return dn;
670
671         if (!dn[0])
672                 return default_dn;
673
674         return strcat(strcat(strcpy(dn_buf, dn), ", "), default_dn);
675 }
676
677 void show_syntax(unsigned int cmdnum)
678 {
679         printf("Syntax: %s %s\n", cmdtable[cmdnum].cmd, cmdtable[cmdnum].help_msg);
680 }
681
682 char *skip_to_char(register char *s, register int c)
683 {
684         if (!s)
685                 return s;
686
687         while (*s  &&  *s != c)
688                 ++s;
689
690         return s;
691 }
692
693 char *skip_to_whitespace(register char *s)
694 {
695         if (!s)
696                 return s;
697
698         while (*s  &&  !isspace((unsigned char) *s))
699                 ++s;
700
701         return s;
702 }
703
704 char *skip_whitespace(register char *s)
705 {
706         if (!s)
707                 return s;
708
709         while (*s  &&  isspace((unsigned char) *s))
710                 ++s;
711
712         return s;
713 }
714
715 int table_lookup(const char *word, const char *const *table, int table_count)
716 {
717         register int    i;
718         int                             wordlen;
719
720         if (!word  ||  !*word)
721                 return -1;
722
723         wordlen = strlen(word);
724
725         for (i = 0; i < table_count; i++)
726                 if (strncasecmp(word, table[i], wordlen) == 0)
727                         return i;
728         return -1;
729 }
730
731 FILE *user_tailor(void)
732 {
733         char    rcfile[BUFSIZ];
734
735         rcfile[0] = 0;
736
737 #ifdef unix
738         {
739 #include <pwd.h>
740                 struct passwd   *pwent;
741
742                 if (pwent = getpwuid(getuid()))
743                         strcat(strcpy(rcfile, pwent->pw_dir), "/");
744                 strcat(rcfile, ".saucerrc");
745         }
746 #else
747         strcpy(rcfile, "saucer.rc");
748 #endif
749
750         return fopen(rcfile, "r");
751 }