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