2 * Copyright (c) 1994, Strata Software Limited, Ottawa, Ontario, Canada.
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.
13 * 'saucer' LDAP command-line client source code.
15 * Author: Eric Rosenquist, 1994.
17 * 07-Mar-1999 readline support added: O. Steffensen (oddbjorn@tricknology.org)
24 #include <ac/stdlib.h>
27 # include <readline/readline.h>
28 # ifdef HAVE_READLINE_HISTORY_H
29 # include <readline/history.h>
34 #include <ac/string.h>
35 #include <ac/unistd.h>
41 #define DN_MAXLEN 4096
45 int (*func) (char **, int);
59 char *attrs_null[] = { "0.10", NULL };
61 char default_dn[DN_MAXLEN];
62 char *hostname = "127.0.0.1";
68 int portnum = LDAP_PORT;
70 char true_filter[] = "objectClass=*"; /* Always succeeds */
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);
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);
94 static const char *const binary_attrs[] = {
95 "audio", "jpegPhoto", "personalSignature", "photo"
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]"
111 if (ldap_simple_bind_s(ld, username, credentials) != LDAP_SUCCESS) {
112 ldap_perror(ld, progname);
116 printf("Bound to ldap server as `%s' (%s authentication)\n", username,
117 credentials ? "simple" : "no");
119 puts("Bound anonymously to ldap server");
124 int cmd_help(char **cmdargv, int cmdargc)
129 for (i = 0; i < sizeof(cmdtable) / sizeof(cmdtable[0]); i++)
130 if (strncasecmp(cmdargv[1], cmdtable[i].cmd, strlen(cmdargv[1])) == 0) {
134 cmdargc = 1; /* Command not found - make it display the list of commands */
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");
146 show_syntax(CMD_HELP);
151 int cmd_list(char **cmdargv, int cmdargc)
156 static const char *const opts[] = { "absolute" };
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]))) {
178 show_syntax(CMD_LIST);
182 if (ldap_search(ld, make_dn(dn, relative), LDAP_SCOPE_ONELEVEL,
183 true_filter, attrs_null, 1) == -1) {
184 ldap_perror(ld, progname);
188 if (ldap_result(ld, LDAP_RES_ANY, 1, (struct timeval *)0, &result) == -1) {
189 ldap_perror(ld, progname);
193 display_search_results(result);
198 int cmd_moveto(char **cmdargv, int cmdargc)
204 static const char *const opts[] = { "absolute" };
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]))) {
225 show_syntax(CMD_MOVETO);
230 if (is_whitespace(dn))
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]) {
240 for (rdn = exploded_dn + 1; *rdn; rdn++) {
242 strcat(default_dn, ", ");
243 strcat(default_dn, *rdn);
246 ldap_value_free(exploded_dn);
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)) {
254 strcpy(default_dn, strcat(strcat(strcpy(buf, dn), ", "), default_dn));
256 strcpy(default_dn, dn);
257 ldap_value_free(exploded_dn);
259 puts("Invalid distinguished name.");
264 printf("Distinguished name suffix is `%s'\n", default_dn);
269 int cmd_quit(char **cmdargv, int cmdargc)
274 int cmd_search(char **cmdargv, int cmdargc)
280 static const char *const opts[] = { "absolute", "object", "scope" };
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;
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]))) {
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];
317 if (errflag || !filter) {
318 show_syntax(CMD_SEARCH);
322 if (ldap_search(ld, make_dn(dn, relative), search_scope, filter, attrs_null, 0) == -1) {
323 ldap_perror(ld, progname);
327 if (ldap_result(ld, LDAP_RES_ANY, 1, (struct timeval *)0, &result) == -1) {
328 ldap_perror(ld, progname);
332 display_search_results(result);
337 int cmd_set(char **cmdargv, int cmdargc)
339 static const char *const alias_opts[] = {
340 "never", "search", "find", "always"
344 static const char *const opts[] = {
345 "aliasderef", "sizelimit", "timelimit"
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]))) {
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);
360 j = atoi(cmdargv[i]);
361 ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &j);
367 j = atoi(cmdargv[i]);
368 ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &j);
380 show_syntax(CMD_SET);
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",
388 opt_s, opt_s == 1 ? "y" : "ies",
389 opt_t, opt_t == 1 ? "" : "s");
395 int cmd_show(char **cmdargv, int cmdargc)
400 static const char *const opts[] = { "absolute" };
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]))) {
422 show_syntax(CMD_SHOW);
426 if (ldap_search(ld, make_dn(dn, relative), LDAP_SCOPE_BASE, true_filter, NULL, 0) == -1) {
427 ldap_perror(ld, progname);
431 if (ldap_result(ld, LDAP_RES_ANY, 1, (struct timeval *)0, &result) == -1) {
432 ldap_perror(ld, progname);
436 display_search_results(result);
441 void display_search_results(LDAPMessage *result)
449 for (entry = ldap_first_entry(ld, result); entry; entry = ldap_next_entry(ld, entry)) {
450 if (s = ldap_get_dn(ld, entry)) {
455 /* Make one pass to calculate the length of the longest attribute name */
457 for (s = ldap_first_attribute(ld, entry, &cookie); s; s = ldap_next_attribute(ld, entry, cookie))
458 if ((i = strlen(s)) > maxname)
461 /* Now print the attributes and values */
462 for (s = ldap_first_attribute(ld, entry, &cookie); s; s = ldap_next_attribute(ld, entry, cookie)) {
465 if (table_lookup(s, binary_attrs, sizeof(binary_attrs) / sizeof(binary_attrs[0])) >= 0)
466 continue; /* Skip this attribute - it's binary */
468 printf(" %-*s - ", maxname, s);
470 /* Now print each of the values for the given attribute */
471 if (values = ldap_get_values(ld, entry, s)) {
474 for (val = values; *val; ) {
478 /* Watch out for values that have embedded \n characters */
479 while (nl = strchr(v, '\n')) {
484 printf(" %*s", maxname + 3, "");
489 printf(" %*s", maxname + 3, "");
491 ldap_value_free(values);
497 if (ldap_result2error(ld, result, 0))
498 ldap_perror(ld, progname);
501 int do_command(char *cmd)
507 /* Tokenize the input command, allowing for quoting */
509 cmd = skip_whitespace(cmd);
511 break; /* end of input */
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);
518 puts("Command is missing a trailing quote");
523 cmd = skip_to_whitespace(cmd);
530 printf("cmdargc = %d\n", cmdargc);
531 for (i = 0; i < cmdargc; i++)
535 if (cmdargv[0][0] == '?')
536 return cmd_help(cmdargv, cmdargc);
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);
542 if (!is_whitespace(cmdargv[0])) {
543 printf("Unrecognized command - %s\n", cmdargv[0]);
544 cmd_help(cmdargv, 1);
550 void do_commands(FILE *file)
552 char cmd_buf[BUFSIZ];
553 int tty = isatty(fileno(file));
561 sprintf(prompt, (strlen(default_dn) < 18
563 : "saucer dn=%.15s..> "), default_dn);
564 #ifndef HAVE_READLINE
565 fputs (prompt, stdout);
567 buf = readline (prompt);
577 if (!fgets(cmd_buf, sizeof(cmd_buf), file))
581 status = do_command(buf);
591 int is_whitespace(register char *s)
596 while (*s && isspace((unsigned char) *s))
602 int main(int argc, char **argv)
609 while ((option = getopt(argc, argv, "h:p:u:c:d:")) != EOF)
612 credentials = optarg;
617 ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, &tmp);
618 ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &tmp);
625 portnum = atoi(optarg);
635 fprintf(stderr, "usage: %s [-h host] [-p portnumber] [-u X500UserName]\n\t[-c credentials] [-d debug-level]\n",
637 exit( EXIT_FAILURE );
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 );
662 char *make_dn(char *dn, int relative)
664 static char dn_buf[DN_MAXLEN];
669 if (!default_dn[0] || !relative)
675 return strcat(strcat(strcpy(dn_buf, dn), ", "), default_dn);
678 void show_syntax(unsigned int cmdnum)
680 printf("Syntax: %s %s\n", cmdtable[cmdnum].cmd, cmdtable[cmdnum].help_msg);
683 char *skip_to_char(register char *s, register int c)
688 while (*s && *s != c)
694 char *skip_to_whitespace(register char *s)
699 while (*s && !isspace((unsigned char) *s))
705 char *skip_whitespace(register char *s)
710 while (*s && isspace((unsigned char) *s))
716 int table_lookup(const char *word, const char *const *table, int table_count)
724 wordlen = strlen(word);
726 for (i = 0; i < table_count; i++)
727 if (strncasecmp(word, table[i], wordlen) == 0)
732 FILE *user_tailor(void)
741 struct passwd *pwent;
743 if (pwent = getpwuid(getuid()))
744 strcat(strcpy(rcfile, pwent->pw_dir), "/");
745 strcat(rcfile, ".saucerrc");
748 strcpy(rcfile, "saucer.rc");
751 return fopen(rcfile, "r");