]> git.sur5r.net Git - openldap/blob - contrib/saucer/main.c
4435e0bfbbaed86d08e6b1ba51af6f284f466b93
[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(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         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         LDAPMessage     *entry;
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                         free(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                         lber_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(2);
638         }
639
640         rc = user_tailor();
641
642         if (!(ld = ldap_open(hostname, portnum))) {
643                 fprintf(stderr, "%s: unable to connect to server at host `%s' on port %d\n",
644                                 progname, hostname, portnum);
645                 exit(2);
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         char            *s;
666
667         if (!dn)
668                 dn = "";
669
670         if (!default_dn[0]  ||  !relative)
671                 return dn;
672
673         if (!dn[0])
674                 return default_dn;
675
676         return strcat(strcat(strcpy(dn_buf, dn), ", "), default_dn);
677 }
678
679 void show_syntax(int cmdnum)
680 {
681         printf("Syntax: %s %s\n", cmdtable[cmdnum].cmd, cmdtable[cmdnum].help_msg);
682 }
683
684 char *skip_to_char(register char *s, register int c)
685 {
686         if (!s)
687                 return s;
688
689         while (*s  &&  *s != c)
690                 ++s;
691
692         return s;
693 }
694
695 char *skip_to_whitespace(register char *s)
696 {
697         if (!s)
698                 return s;
699
700         while (*s  &&  !isspace((unsigned char) *s))
701                 ++s;
702
703         return s;
704 }
705
706 char *skip_whitespace(register char *s)
707 {
708         if (!s)
709                 return s;
710
711         while (*s  &&  isspace((unsigned char) *s))
712                 ++s;
713
714         return s;
715 }
716
717 int table_lookup(const char *word, const char *const *table, int table_count)
718 {
719         register int    i;
720         int                             wordlen;
721
722         if (!word  ||  !*word)
723                 return -1;
724
725         wordlen = strlen(word);
726
727         for (i = 0; i < table_count; i++)
728                 if (strncasecmp(word, table[i], wordlen) == 0)
729                         return i;
730         return -1;
731 }
732
733 FILE *user_tailor(void)
734 {
735         char    rcfile[BUFSIZ];
736
737         rcfile[0] = 0;
738
739 #ifdef unix
740         {
741 #include <pwd.h>
742                 struct passwd   *pwent;
743
744                 if (pwent = getpwuid(getuid()))
745                         strcat(strcpy(rcfile, pwent->pw_dir), "/");
746                 strcat(rcfile, ".saucerrc");
747         }
748 #else
749         strcpy(rcfile, "saucer.rc");
750 #endif
751
752         return fopen(rcfile, "r");
753 }