Logo Search packages:      
Sourcecode: ldapvi version File versions  Download package

ldapvi.c

/* -*- show-trailing-whitespace: t; indent-tabs: t -*-
 *
 * Copyright (c) 2003,2004,2005,2006 David Lichteblau
 * Copyright (c) 2006 Perry Nguyen
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#include <curses.h>
#include <signal.h>
#include <term.h>
#include "common.h"

typedef void (*handler_entry)(char *, tentry *, void *);
static void parse_file(
      FILE *, tparser *, thandler *, void *, handler_entry, void *, int);
static void cut_datafile(char *, long, cmdline *);
static int write_file_header(FILE *, cmdline *);
static int rebind(LDAP *, bind_options *, int, char *, int);

static int
compare(tparser *p, thandler *handler, void *userdata, GArray *offsets,
      char *cleanname, char *dataname, long *error_position,
      cmdline *cmdline)
{
      FILE *clean, *data;
      int rc;
      long pos;

      if ( !(clean = fopen(cleanname, "r+"))) syserr();
      if ( !(data = fopen(dataname, "r"))) syserr();
      rc = compare_streams(p, handler, userdata, offsets, clean, data, &pos,
                       error_position);
      if (fclose(clean) == EOF) syserr();
      if (fclose(data) == EOF) syserr();

      if (rc == -2) {
            /* an error has happened */
            int n;

            if (!cmdline) {
                  fputs("oops: unexpected error in handler\n", stderr);
                  exit(1);
            }

            /* remove already-processed entries from the data file */
            cut_datafile(dataname, pos, cmdline);

            /* flag already-processed entries in the offset table */
            for (n = 0; n < offsets->len; n++)
                  if (g_array_index(offsets, long, n) < 0)
                        g_array_index(offsets, long, n) = -1;
      }
      return rc;
}

static void
cleanup(int rc, char *pathname)
{
      DIR *dir;
      struct dirent *entry;
      GString *str = g_string_new(pathname);
      int len;
      struct termios term;

      /*
       * delete temporary directory
       */
      g_string_append(str, "/");
      len = str->len;

      if ( !(dir = opendir(pathname))) syserr();
      while ( (entry = readdir(dir)))
            if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")){
                  g_string_truncate(str, len);
                  g_string_append(str, entry->d_name);
                  if (unlink(str->str) == -1) syserr();
            }
      if (closedir(dir) == -1) syserr();
      if (rmdir(pathname) == -1) syserr();
      g_string_free(str, 1);

      /*
       * reset terminal
       */
      if (tcgetattr(0, &term) == -1)
            /* oh, running without a terminal */
            return;
      term.c_lflag |= ICANON;
      term.c_lflag |= ECHO;
      if (tcsetattr(0, TCSANOW, &term) == -1) syserr();
}

static void
cleanup_signal(int n)
{
      fprintf(stderr, "\nCaught signal %d, exiting...\n", n);
      exit(2);
}

static int
moddn(LDAP *ld, char *old, char *new, int dor, LDAPControl **ctrls)
{
      int rc;
      char **newrdns = ldap_explode_dn(new, 0);
      char **ptr = newrdns;
      char *newrdn = *ptr++;
      GString *newsup = g_string_sized_new(strlen(new));

      if (newrdn) {
            if (*ptr) g_string_append(newsup, *ptr++);
            for (; *ptr; ptr++) {
                  g_string_append_c(newsup, ',');
                  g_string_append(newsup, *ptr);
            }
      } else
            newrdn = "";
      rc = ldap_rename_s(ld, old, newrdn, newsup->str, dor, ctrls, 0);
      g_string_free(newsup, 1);
      ldap_value_free(newrdns);
      return rc;
}


/*****************************************
 * ldapmodify_handler
 */
00139 struct ldapmodify_context {
      LDAP *ld;
      LDAPControl **controls;
      int verbose;
      int noquestions;
      int continuous;
};

static int
ldapmodify_error(struct ldapmodify_context *ctx, char *error)
{
      ldap_perror(ctx->ld, error);
      if (!ctx->continuous)
            return -1;
      fputs("(error ignored)\n", stderr);
      return 0;
}

static int
ldapmodify_change(
      int key, char *labeldn, char *dn, LDAPMod **mods, void *userdata)
{
      struct ldapmodify_context *ctx = userdata;
      LDAP *ld = ctx->ld;
      LDAPControl **ctrls = ctx->controls;
      int verbose = ctx->verbose;

      if (verbose) printf("(modify) %s\n", labeldn);
      if (ldap_modify_ext_s(ld, dn, mods, ctrls, 0))
            return ldapmodify_error(ctx, "ldap_modify");
      return 0;
}

static int
ldapmodify_rename(int key, char *dn1, tentry *modified, void *userdata)
{
      struct ldapmodify_context *ctx = userdata;
      LDAP *ld = ctx->ld;
      LDAPControl **ctrls = ctx->controls;
      int verbose = ctx->verbose;

      char *dn2 = entry_dn(modified);
      int deleteoldrdn = frob_rdn(modified, dn1, FROB_RDN_CHECK) == -1;
      if (verbose) printf("(rename) %s to %s\n", dn1, dn2);
      if (moddn(ld, dn1, dn2, deleteoldrdn, ctrls))
            return ldapmodify_error(ctx, "ldap_rename");
      return 0;
}

static int
ldapmodify_add(int key, char *dn, LDAPMod **mods, void *userdata)
{
      struct ldapmodify_context *ctx = userdata;
      LDAP *ld = ctx->ld;
      LDAPControl **ctrls = ctx->controls;
      int verbose = ctx->verbose;

      if (verbose) printf("(add) %s\n", dn);
      if (ldap_add_ext_s(ld, dn, mods, ctrls, 0))
            return ldapmodify_error(ctx, "ldap_add");
      return 0;
}

static int
ldapmodify_delete(int key, char *dn, void *userdata)
{
      struct ldapmodify_context *ctx = userdata;
      LDAP *ld = ctx->ld;
      LDAPControl **ctrls = ctx->controls;
      int verbose = ctx->verbose;

      if (verbose) printf("(delete) %s\n", dn);
      switch (ldap_delete_ext_s(ld, dn, ctrls, 0)) {
      case 0:
            break;
      case LDAP_NOT_ALLOWED_ON_NONLEAF:
            if (!ctx->noquestions)
                  return -2;
            /* else fall through */
      default:
            return ldapmodify_error(ctx, "ldap_delete");
      }
      return 0;
}

static int
ldapmodify_rename0(
      int key, char *dn1, char *dn2, int deleteoldrdn, void *userdata)
{
      struct ldapmodify_context *ctx = userdata;
      LDAP *ld = ctx->ld;
      LDAPControl **ctrls = ctx->controls;
      int verbose = ctx->verbose;

      if (verbose) printf("(rename) %s to %s\n", dn1, dn2);
      if (moddn(ld, dn1, dn2, deleteoldrdn, ctrls))
            return ldapmodify_error(ctx, "ldap_rename");
      return 0;
}


/*****************************************
 * ldif_handler
 */
static int
ldif_change(int key, char *labeldn, char *dn, LDAPMod **mods, void *userdata)
{
      FILE *s = userdata;
      print_ldif_modify(s, dn, mods);
      return 0;
}

static int
ldif_rename(int key, char *olddn, tentry *modified, void *userdata)
{
      FILE *s = userdata;
      int deleteoldrdn = frob_rdn(modified, olddn, FROB_RDN_CHECK) == -1;
      print_ldif_rename(
            s, olddn, entry_dn(modified),
            deleteoldrdn);
      return 0;
}

static int
ldif_add(int key, char *dn, LDAPMod **mods, void *userdata)
{
      FILE *s = userdata;
      print_ldif_add(s, dn, mods);
      return 0;
}

static int
ldif_delete(int key, char *dn, void *userdata)
{
      FILE *s = userdata;
      print_ldif_delete(s, dn);
      return 0;
}

static int
ldif_rename0(int key, char *dn1, char *dn2, int deleteoldrdn, void *userdata)
{
      FILE *s = userdata;
      print_ldif_rename(s, dn1, dn2, deleteoldrdn);
      return 0;
}

static thandler ldif_handler = {
      ldif_change,
      ldif_rename,
      ldif_add,
      ldif_delete,
      ldif_rename0
};


/*****************************************
 * noop handler
 */
static int
noop_change(int key, char *labeldn, char *dn, LDAPMod **mods, void *userdata)
{
      return 0;
}

static int
noop_rename(int key, char *olddn, tentry *modified, void *userdata)
{
      return 0;
}

static int
noop_add(int key, char *dn, LDAPMod **mods, void *userdata)
{
      return 0;
}

static int
noop_delete(int key, char *dn, void *userdata)
{
      return 0;
}

static int
noop_rename0(int key, char *dn1, char *dn2, int deleteoldrdn, void *userdata)
{
      return 0;
}


/*****************************************
 * forget_deletions_handler
 */
static int
forget_deletion(int key, char *dn, void *userdata)
{
      GArray *deletions = userdata;
      g_array_append_val(deletions, key);
      return 0;
}

static thandler forget_deletions_handler = {
      noop_change,
      noop_rename,
      noop_add,
      forget_deletion,
      noop_rename0
};


/*****************************************
 * vdif_handler
 */
static int
vdif_change(int key, char *labeldn, char *dn, LDAPMod **mods, void *userdata)
{
      FILE *s = userdata;
      print_ldapvi_modify(s, dn, mods);
      return 0;
}

static int
vdif_rename(int key, char *olddn, tentry *modified, void *userdata)
{
      FILE *s = userdata;
      int deleteoldrdn = frob_rdn(modified, olddn, FROB_RDN_CHECK) == -1;
      print_ldapvi_rename(s, olddn, entry_dn(modified), deleteoldrdn);
      return 0;
}

static int
vdif_add(int key, char *dn, LDAPMod **mods, void *userdata)
{
      FILE *s = userdata;
      print_ldapvi_add(s, dn, mods);
      return 0;
}

static int
vdif_delete(int key, char *dn, void *userdata)
{
      FILE *s = userdata;
      print_ldapvi_delete(s, dn);
      return 0;
}

static int
vdif_rename0(int key, char *dn1, char *dn2, int deleteoldrdn, void *userdata)
{
      FILE *s = userdata;
      print_ldapvi_rename(s, dn1, dn2, deleteoldrdn);
      return 0;
}


/*****************************************
 * statistics_handler
 */
00397 struct statistics {
      int nmodify, nadd, ndelete, nrename;
};

static int
statistics_change(
      int key, char *labeldn, char *dn, LDAPMod **mods, void *userdata)
{
      struct statistics *st = userdata;
      st->nmodify++;
      return 0;
}

static int
statistics_rename(int key, char *olddn, tentry *modified, void *userdata)
{
      struct statistics *st = userdata;
      st->nrename++;
      return 0;
}

static int
statistics_add(int key, char *dn, LDAPMod **mods, void *userdata)
{
      struct statistics *st = userdata;
      st->nadd++;
      return 0;
}

static int
statistics_delete(int key, char *dn, void *userdata)
{
      struct statistics *st = userdata;
      st->ndelete++;
      return 0;
}

static int
statistics_rename0(
      int key, char *dn1, char *dn2, int deleteoldrdn, void *userdata)
{
      struct statistics *st = userdata;
      st->nrename++;
      return 0;
}


/* end of handlers
 * **************************************** */

00447 struct rebind_data {
        bind_options bind_options;
      LDAPURLDesc *seen;
};

static void
toggle_sasl(bind_options *bo)
{
      if (bo->authmethod == LDAP_AUTH_SIMPLE) {
            bo->authmethod = LDAP_AUTH_SASL;
            puts("SASL authentication enabled.");
            printf("SASL mechanism: %s (use '*' to change)\n",
                   bo->sasl_mech ? bo->sasl_mech : "(none)");
      } else {
            bo->authmethod = LDAP_AUTH_SIMPLE;
            puts("Simple authentication enabled.");
      }
}

static void
change_mechanism(bind_options *bo)
{
      if (bo->authmethod == LDAP_AUTH_SIMPLE) {
            bo->authmethod = LDAP_AUTH_SASL;
            puts("Switching to SASL authentication.");
      }
      bo->sasl_mech = getline("SASL mechanism", bo->sasl_mech);
}

static int
rebind_callback(
      LDAP *ld, const char *url, ber_tag_t request, int msgid, void *args)
{
      struct rebind_data *rebind_data = args;
      LDAPURLDesc *urld;
      bind_options bo = rebind_data->bind_options;

      printf("Received referral to %s.\n", url);

      if (ldap_url_parse(url, &urld)) {
            puts("Error: Cannot parse URL.");
            return -1;
      }
      if (rebind_data->seen
          && !strcmp(rebind_data->seen->lud_scheme, urld->lud_scheme)
          && !strcmp(rebind_data->seen->lud_host, urld->lud_host)
          && rebind_data->seen->lud_port == urld->lud_port)
            /* confirmed already */
            return 0;

      printf("You are not logged in to %s://%s:%d yet.\n"
             "Type '!' or 'y' to do so.\n",
             urld->lud_scheme, urld->lud_host, urld->lud_port);
      for (;;) {
            bo.dialog = BD_ALWAYS;

            switch (choose("Rebind?", "y!nB*qQ?", "(Type '?' for help.)"))
            {
            case '!':
                        bo.dialog = BD_NEVER;
                  /* fall through */
            case 'y':
                  if (rebind(ld, &bo, 0, 0, 1) == 0) {
                        if (rebind_data->seen)
                              ldap_free_urldesc(rebind_data->seen);
                        rebind_data->seen = urld;
                        return 0;
                  }
                  break;
            case 'n':
                  ldap_free_urldesc(urld);
                  return 0;
            case '*':
                  change_mechanism(&bo);
                  break;
            case 'B':
                  toggle_sasl(&bo);
                  break;
            case 'q':
                  ldap_free_urldesc(urld);
                  return -1;
            case 'Q':
                  exit(0);
            case '?':
                  puts("Commands:\n"
                       "  y -- ask for user name and rebind\n"
                       "  ! -- rebind using cached credentials\n"
                       "  n -- don't login, just continue\n"
                       "  B -- toggle SASL\n"
                       "  * -- set SASL mechanism\n"
                       "  q -- give up\n"
                       "  Q -- give up and exit ldapvi\n"
                       "  ? -- this help");
                  break;
            }
      }
}

static char *
find_user(LDAP *ld, char *filter)
{
      char *dn = 0;
      LDAPMessage *result = 0;
      LDAPMessage *entry = 0;

      if (ldap_bind_s(ld, 0, 0, LDAP_AUTH_SIMPLE)) {
            ldap_perror(ld, "ldap_bind");
            goto cleanup;
      }
      if (ldap_search_s(ld, 0, LDAP_SCOPE_SUBTREE, filter, 0, 0, &result)) {
            ldap_perror(ld, "ldap_search");
            goto cleanup;
      }
      if ( !(entry = ldap_first_entry(ld, result))) {
            puts("User not found.");
            goto cleanup;
      }
      if (ldap_next_entry(ld, result)) {
            puts("More than one entry matched user filter.");
            goto cleanup;
      }
      dn = ldap_get_dn(ld, entry);

cleanup:
      if (result) ldap_msgfree(result);
      return dn;
}

static void
ensure_tmp_directory(char *dir)
{
      if (strcmp(dir, "/tmp/ldapvi-XXXXXX")) return;
      mkdtemp(dir);
      on_exit((on_exit_function) cleanup, dir);
      signal(SIGTERM, cleanup_signal);
      signal(SIGINT, cleanup_signal);
      signal(SIGPIPE, SIG_IGN);
}

static int
rebind_sasl(LDAP *ld, bind_options *bind_options, char *dir, int verbose)
{
      tsasl_defaults *defaults = sasl_defaults_new(bind_options);
      int rc;
      int sasl_mode;

      switch (bind_options->dialog) {
      case BD_NEVER: sasl_mode = LDAP_SASL_QUIET; break;
      case BD_AUTO: sasl_mode = LDAP_SASL_AUTOMATIC; break;
      case BD_ALWAYS: sasl_mode = LDAP_SASL_INTERACTIVE; break;
      default: abort();
      }

      if (dir) {
            ensure_tmp_directory(dir);
            init_sasl_redirection(defaults, append(dir, "/sasl"));
      }

      rc = ldap_sasl_interactive_bind_s(
            ld, bind_options->user, bind_options->sasl_mech, NULL,
            NULL, sasl_mode, ldapvi_sasl_interact, defaults);

      sasl_defaults_free(defaults);
      if (defaults->fd != -1) {
            finish_sasl_redirection(defaults);
            free(defaults->pathname);
      }

      if (rc != LDAP_SUCCESS) {
            ldap_perror(ld, "ldap_sasl_interactive_bind_s");
            return -1;
      }

      if (verbose)
            printf("Bound as authzid=%s, authcid=%s.\n",
                   bind_options->sasl_authzid,
                   bind_options->sasl_authcid);
      return 0;
}

static int
rebind_simple(LDAP *ld, bind_options *bo, int verbose)
{
      if (bo->dialog == BD_ALWAYS
          || (bo->dialog == BD_AUTO && bo->user && !bo->password))
      {
            tdialog d[2];
            init_dialog(d, DIALOG_DEFAULT, "Filter or DN", bo->user);
            init_dialog(d + 1, DIALOG_PASSWORD, "Password", bo->password);
            dialog("--- Login", d, 2, bo->user ? 1 : 0);
            bo->user = d[0].value;
            bo->password = d[1].value;
      }
      if (bo->user && bo->user[0] == '(')
            /* user is a search filter, not a name */
            if ( !(bo->user = find_user(ld, bo->user)))
                  return -1;
      if (ldap_bind_s(ld, bo->user, bo->password, LDAP_AUTH_SIMPLE)) {
            ldap_perror(ld, "ldap_bind");
            return -1;
      }
      if (verbose)
            printf("Bound as %s.\n", bo->user);
      return 0;
}

static int
rebind(LDAP *ld, bind_options *bind_options, int register_callback,
       char *dir, int verbose)
{
      int rc = -1;
      struct rebind_data *rebind_data = xalloc(sizeof(struct rebind_data));

      switch (bind_options->authmethod) {
      case LDAP_AUTH_SASL:
            if (rebind_sasl(ld, bind_options, dir, verbose))
                  return -1;
            break;
      case LDAP_AUTH_SIMPLE:
            if (rebind_simple(ld, bind_options, verbose))
                  return -1;
            break;
      }

      if (register_callback) {
            rebind_data->bind_options = *bind_options;
            rebind_data->bind_options.password
                  = xdup(bind_options->password);
            rebind_data->seen = 0;
            if (ldap_set_rebind_proc(ld, rebind_callback, rebind_data))
                  ldaperr(ld, "ldap_set_rebind_proc");
      }
      return 0;
}

void
init_sasl_arguments(LDAP *ld, bind_options *bind_options)
{
      if (!bind_options->sasl_mech)
            ldap_get_option(ld,
                        LDAP_OPT_X_SASL_MECH,
                        &bind_options->sasl_mech);
      if (!bind_options->sasl_realm)
            ldap_get_option(ld,
                        LDAP_OPT_X_SASL_REALM,
                        &bind_options->sasl_realm);
      if (!bind_options->sasl_authcid)
            ldap_get_option(ld,
                        LDAP_OPT_X_SASL_AUTHCID,
                        &bind_options->sasl_authcid);
      if (!bind_options->sasl_authzid)
            ldap_get_option(ld,
                        LDAP_OPT_X_SASL_AUTHZID,
                        &bind_options->sasl_authzid);
}

static LDAP *
do_connect(char *server, bind_options *bind_options,
         int referrals, int starttls, int tls, int deref, int profileonlyp,
         char *dir)
{
      LDAP *ld = 0;
      int rc = 0;
      int drei = 3;

      if (server && !strstr(server, "://")) {
            char *url = xalloc(strlen(server) + sizeof("ldap://"));
            strcpy(url, "ldap://");
            strcpy(url + 7, server);
            server = url;
      }

      if (ldap_set_option(0, LDAP_OPT_X_TLS_REQUIRE_CERT, (void *) &tls))
            ldaperr(0, "ldap_set_option(LDAP_OPT_X_TLS)");
      if ( rc = ldap_initialize(&ld, server)) {
            fprintf(stderr, "ldap_initialize: %s\n", ldap_err2string(rc));
            exit(1);
      }
      if (!profileonlyp)
            init_sasl_arguments(ld, bind_options);
      if (ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &drei))
            ldaperr(ld, "ldap_set_option(LDAP_OPT_PROTOCOL_VERSION)");
      if (starttls)
            if (ldap_start_tls_s(ld, 0, 0))
                  ldaperr(ld, "ldap_start_tls_s");
      if (rebind(ld, bind_options, 1, dir, 0) == -1) {
            ldap_unbind_s(ld);
            return 0;
      }
      /* after initial bind, always ask interactively (except in '!' rebinds,
       * which are special-cased): */
      bind_options->dialog = BD_ALWAYS;
      if (ldap_set_option(ld, LDAP_OPT_REFERRALS,
                            referrals ? LDAP_OPT_ON : LDAP_OPT_OFF))
            ldaperr(ld, "ldap_set_option(LDAP_OPT_REFERRALS)");
      if (ldap_set_option(ld, LDAP_OPT_DEREF, (void *) &deref))
            ldaperr(ld, "ldap_set_option(LDAP_OPT_DEREF)");

      return ld;
}

/*
 * fixme: brauchen wir das mit dem user?  dann sollten wir hier noch
 * sasl support vorsehen
 *
 * ldapvi-Kommandozeile konnte auch nicht schaden
 */
static int
save_ldif(tparser *parser, GArray *offsets, char *clean, char *data,
        char *server, char *user, int managedsait)
{
      int fd;
      FILE *s;

      GString *name = g_string_sized_new(300);
      g_string_append(name, ",ldapvi-");
      if (gethostname(name->str + name->len, 300 - name->len) == -1)
            syserr();
      name->len = strlen(name->str);
      g_string_sprintfa(name, "-%d.ldif", getpid());

      if ( (fd = open(name->str, O_WRONLY | O_CREAT | O_EXCL, 0600)) == -1) {
            int error = errno;
            fprintf(stderr, "Cannot save %s: ", name->str);
            errno = error;
            perror(0);
            g_string_free(name, 1);
            return 1;
      }
      if ( !(s = fdopen(fd, "w"))) syserr();

      fputs("version: 1\n", s);
      fputs("# to apply these changes using ldapvi, run:\n", s);
      fputs("#   ldapvi --ldapmodify", s);
      if (managedsait)
            fputs(" -M", s);
      if (server) {
            fputs(" -h ", s);
            fputs(server, s);
      }
      fputc(' ', s);
      fputs(name->str, s);
      fputc('\n', s);

      compare(parser, &ldif_handler, s, offsets, clean, data, 0, 0);
      if (fclose(s) == EOF) syserr();

      printf("Your changes have been saved to %s.\n", name->str);
      return 0;
}

static void
view_ldif(tparser *parser, char *dir, GArray *offsets, char *clean, char *data)
{
      FILE *s;
      char *name = append(dir, "/ldif");
      if ( !(s = fopen(name, "w"))) syserr();
      fputs("version: 1\n", s);
      compare(parser, &ldif_handler, s, offsets, clean, data, 0, 0);
      if (fclose(s) == EOF) syserr();
      view(name);
      free(name);
}

static thandler vdif_handler = {
      vdif_change,
      vdif_rename,
      vdif_add,
      vdif_delete,
      vdif_rename0
};

static void
view_vdif(tparser *parser, char *dir, GArray *offsets, char *clean, char *data)
{
      FILE *s;
      char *name = append(dir, "/vdif");

      if ( !(s = fopen(name, "w"))) syserr();
      fputs("version: ldapvi\n", s);
      compare(parser, &vdif_handler, s, offsets, clean, data, 0, 0);
      if (fclose(s) == EOF) syserr();
      view(name);
      free(name);
}

static void
setcolor(int fg)
{
      char *bold = tigetstr("bold");
      char *setaf = tigetstr("setaf");
      if (setaf) putp(tparm(setaf, fg));
      if (bold) putp(bold);
}

static void
print_counter(int color, char *label, int value)
{
      char *sgr0 = tigetstr("sgr0");

      if (value) setcolor(color);
      printf("%s: %d", label, value);
      if (sgr0) putp(sgr0);
}

/* collect statistics.  This comparison step is important
 * for catching syntax errors before real processing starts.
 */
static int
analyze_changes(tparser *p, GArray *offsets, char *clean, char *data,
            cmdline *cmdline)
{
      struct statistics st;
      static thandler statistics_handler = {
            statistics_change,
            statistics_rename,
            statistics_add,
            statistics_delete,
            statistics_rename0
      };
      int rc;
      long pos;

retry:
      memset(&st, 0, sizeof(st));
      rc = compare(
            p, &statistics_handler, &st, offsets, clean, data, &pos, 0);

      /* Success? */
      if (rc == 0) {
            if (!(st.nadd + st.ndelete + st.nmodify + st.nrename)) {
                  if (!cmdline->quiet)
                        puts("No changes.");
                  return 0;
            }
            if (cmdline->quiet)
                  return 1;
            print_counter(COLOR_GREEN, "add", st.nadd);
            fputs(", ", stdout);
            print_counter(COLOR_BLUE, "rename", st.nrename);
            fputs(", ", stdout);
            print_counter(COLOR_YELLOW, "modify", st.nmodify);
            fputs(", ", stdout);
            print_counter(COLOR_RED, "delete", st.ndelete);
            putchar('\n');
            return 1;
      }

      if (cmdline->noninteractive) {
            fputs("Syntax error in noninteractive mode, giving up.\n",
                  stderr);
            exit(1);
      }

      /* Syntax error */
      for (;;) {
            switch (choose("What now?", "eQ?", "(Type '?' for help.)")) {
            case 'e':
                  edit_pos(data, pos);
                  goto retry;
            case 'Q':
                  exit(0);
            case '?':
                  puts("Commands:\n"
                       "  Q -- discard changes and quit\n"
                       "  e -- open editor again\n"
                       "  ? -- this help");
                  break;
            }
      }
}

static void
commit(tparser *p, LDAP *ld, GArray *offsets, char *clean, char *data,
       LDAPControl **ctrls, int verbose, int noquestions, int continuous,
       cmdline *cmdline)
{
      struct ldapmodify_context ctx;
      static thandler ldapmodify_handler = {
            ldapmodify_change,
            ldapmodify_rename,
            ldapmodify_add,
            ldapmodify_delete,
            ldapmodify_rename0
      };
      ctx.ld = ld;
      ctx.controls = ctrls;
      ctx.verbose = verbose;
      ctx.noquestions = noquestions;
      ctx.continuous = continuous;

      switch (compare(p, &ldapmodify_handler, &ctx, offsets, clean, data, 0,
                  cmdline))
      {
      case 0:
            if (!cmdline->quiet)
                  puts("Done.");
            write_ldapvi_history();
            exit(0);
      case -1:
            yourfault("unexpected syntax error!");
      case -2:
            /* user error */
            break;
      default:
            abort();
      }
}

static int
getty(int fd)
{
      if (close(fd) == -1)
            syserr();
      if (open("/dev/tty", O_RDWR) != fd)
            return -1;
      return 0;
}

static int
fixup_streams(FILE **source, FILE **target)
{
      int rc = 0;

      /* find a terminal and restore fds 0, 1 to a sensible value for
       * reading the password.  Save the original streams for later use.*/

      if (!isatty(0)) {
            /* user has redirected stdout */
            int in = dup(fileno(stdin));
            if (in == -1) syserr();
            *source = fdopen(in, "r");
            if (getty(0) == -1) rc = -1;
      }
      if (!isatty(1)) {
            /* user has redirected stdout */
            int out = dup(fileno(stdout));
            if (out == -1) syserr();
            *target = fdopen(out, "w");
            if (getty(1) == -1) rc = -1;
      }
      return rc;
}

static int
ndecimalp(char *str)
{
      char *ptr;
      strtol(str, &ptr, 10);
      return !*ptr;
}

static void
skip(tparser *p, char *dataname, GArray *offsets, cmdline *cmdline)
{
      long pos;
      char *key;
      FILE *s;

      if ( !(s = fopen(dataname, "r"))) syserr();
      p->skip(s, 0, &key);
      if ( (pos = ftell(s)) == -1) syserr();
      if (fclose(s) == EOF) syserr();

      if (key) {
            cut_datafile(dataname, pos, cmdline);
            if (ndecimalp(key))
                  g_array_index(offsets, long, atoi(key)) = -1;
            free(key);
      } else {
                /* Im Normalfall wollen wir einen Eintrag in data
                 * ueberspringen.  Wenn aber in data nichts mehr steht,
                 * sind wir ueber die eigentlichen Aenderungen schon
                 * hinweg und es handelt sich um eine Loeschung.  In
                 * diesem Fall muessen wir nur das Offset aus der
                 * Tabelle entfernen. */
                int n;
                for (n = 0; n < offsets->len; n++)
                        if (g_array_index(offsets, long, n) >= 0) {
                                g_array_remove_index(offsets, n);
                        break;
                  }
      }
}

static tentroid *
entroid_set_entry(LDAP *ld, tentroid *entroid, tentry *entry)
{
      int i;
      tattribute *oc = entry_find_attribute(entry, "objectClass", 0);
      GPtrArray *values;

      if (!oc)
            return 0;

      entroid_reset(entroid);
      values = attribute_values(oc);
      for (i = 0; i < values->len; i++) {
            GArray *av = g_ptr_array_index(values, i);
            LDAPObjectClass *cls;

            {
                  char zero = 0;
                  /* PFUSCH!  die GArrays muessen absolut weg! */
                  g_array_append_val(av, zero);
                  av->len--;
            }

            cls = entroid_request_class(entroid, av->data);
            if (!cls) {
                  g_string_append(entroid->comment, "# ");
                  g_string_append(entroid->comment, entroid->error->str);
                  return entroid;
            }
      }

      if (compute_entroid(entroid) == -1) {
            g_string_append(entroid->comment, "# ");
            g_string_append(entroid->comment, entroid->error->str);
            return entroid;
      }
      return entroid;
}

01071 struct annotation_context {
      LDAP *ld;
      FILE *out;
      tparser *parser;
      tentroid *entroid;
};

static void
annotate_entry(char *key, tentry *entry, void *userdata)
{
      struct annotation_context *ctx = userdata;
      tentroid *entroid = entroid_set_entry(ctx->ld, ctx->entroid, entry);
      ctx->parser->print(ctx->out, entry, key, entroid);
}

static void
rewrite_comments(LDAP *ld, char *dataname, cmdline *cmdline)
{
      FILE *in;
      FILE *out;
      char *tmpname;
      tparser *p = &ldapvi_parser;
      thandler *h = &vdif_handler;
      struct annotation_context ctx;
      int addp = cmdline->ldapmodify_add;
      tschema *schema = schema_new(ld);

      if (!schema) {
            fputs("Error: Failed to read schema.\n", stderr);
            return;
      }

      tmpname = append(dataname, ".tmp");
      if ( !(in = fopen(dataname, "r"))) syserr();
      if ( !(out = fopen(tmpname, "w"))) syserr();

      write_file_header(out, cmdline);
      if (cmdline->ldif) {
            p = &ldif_parser;
            h = &ldif_handler;
      }
      ctx.ld = ld;
      ctx.out = out;
      ctx.entroid = entroid_new(schema);
      ctx.parser = p;
      parse_file(in, p, h, out, annotate_entry, &ctx, addp);

      if (fclose(in) == EOF) syserr();
      if (fclose(out) == EOF) syserr();
      rename(tmpname, dataname);
      free(tmpname);
      schema_free(schema);
}


static void
forget_deletions(tparser *p, GArray *offsets, char *clean, char *data)
{
      int i;
      GArray *deletions = g_array_new(0, 0, sizeof(int));

      compare(p, &forget_deletions_handler, deletions,
            offsets, clean, data, 0, 0);
      for (i = 0; i < deletions->len; i++) {
            int n = g_array_index(deletions, int, i);
            g_array_index(offsets, long, n) = -1;
      }
      g_array_free(deletions, 1);
}

static void
append_sort_control(LDAP *ld, GPtrArray *ctrls, char *keystring)
{
      LDAPControl *ctrl;
      LDAPSortKey **keylist;

      if (ldap_create_sort_keylist(&keylist, keystring))
            ldaperr(ld, "ldap_create_sort_keylist");
      if (ldap_create_sort_control(ld, keylist, 1, &ctrl))
            ldaperr(ld, "ldap_create_sort_keylist");
      g_ptr_array_add(ctrls, ctrl);
}

static GArray *
read_offsets(tparser *p, char *file)
{
      GArray *offsets = g_array_new(0, 0, sizeof(long));
      FILE *s;

      if ( !(s = fopen(file, "r"))) syserr();
      for (;;) {
            long offset;
            char *key, *ptr;
            int n;
            tentry *entry;

            key = 0;
            if (p->entry(s, -1, &key, &entry, &offset) == -1) exit(1);
            if (!key) break;

            n = strtol(key, &ptr, 10);
            if (*ptr) {
                  fprintf(stderr, "Error: Invalid key: `%s'.\n", key);
                  exit(1);
            }
            if (n != offsets->len) {
                  fprintf(stderr, "Error: Unexpected key: `%s'.\n", key);
                  exit(1);
            }
            free(key);
            entry_free(entry);
            g_array_append_val(offsets, offset);
      }
      if (fclose(s) == -1) syserr();

      return offsets;
}

static void
offline_diff(tparser *p, char *a, char *b)
{
      GArray *offsets = read_offsets(p, a);
      compare(p, &ldif_handler, stdout, offsets, a, b, 0, 0);
      g_array_free(offsets, 1);
}

void
write_config(LDAP *ld, FILE *f, cmdline *cmdline)
{
      char *user = cmdline->bind_options.user;
      char *server = cmdline->server;
      int limit;

      if (!f) f = stdout;
      fputs("# ldap.conf(5)\n", f);
      fputs("# edit this as needed and paste into ~/.ldaprc\n", f);

      /* URI/HOST */
      fputc('\n', f);
      fputs("# server name\n", f);
      fputs("# (for parameterless operation, make sure to include at"
            " least this line)\n",
            f);
      if (!server)
            ldap_get_option(ld, LDAP_OPT_URI, &server);
      if (!server)
            ldap_get_option(ld, LDAP_OPT_HOST_NAME, &server);
      if (server)
            if (strstr(server, "://"))
                  fprintf(f, "URI %s\n", server);
            else
                  fprintf(f, "HOST %s\n", server);

      /* BASE */
      fputc('\n', f);
      fputs("# default search base\n", f);
      if (cmdline->basedns->len) {
            GPtrArray *basedns = cmdline->basedns;
            int i;
            if (basedns->len > 1)
                  fputs("### multiple namingcontexts found (uncomment"
                        " one of these lines):\n",
                        f);
            for (i = 0; i < basedns->len; i++) {
                  if (basedns->len > 1) fputc('#', f);
                  fprintf(f, "BASE %s\n", g_ptr_array_index(basedns, i));
            }
      } else {
            if (!cmdline->discover)
                  fputs("### no search base specified, retry with"
                        " --discover?\n",
                        f);
            fputs("#BASE <dn>\n", f);
      }

      /* BINDDN */
      fputc('\n', f);
      fputs("# user to bind as\n", f);
      if (user && user[0] == '(')
            user = find_user(ld, user);
      if (user)
            fprintf(f, "BINDDN %s\n", user);
      else
            fputs("#BINDDN <dn>\n", f);

      /* search options */
      fputc('\n', f);
      fputs("# search parameters (uncomment as needed)\n", f);
      switch (cmdline->deref) {
      case LDAP_DEREF_NEVER:
            fputs("#DEREF never\n", f);
            break;
      case LDAP_DEREF_SEARCHING:
            fputs("#DEREF searcing\n", f);
            break;
      case LDAP_DEREF_FINDING:
            fputs("#DEREF finding\n", f);
            break;
      case LDAP_DEREF_ALWAYS:
            fputs("#DEREF always\n", f);
            break;
      }
      ldap_get_option(ld, LDAP_OPT_SIZELIMIT, &limit);
      fprintf(f, "#SIZELIMIT %d\n", limit);
      ldap_get_option(ld, LDAP_OPT_TIMELIMIT, &limit);
      fprintf(f, "#TIMELIMIT %d\n", limit);
}

static void
add_changerecord(FILE *s, cmdline *cmdline)
{
      switch (cmdline->mode) {
      case ldapvi_mode_delete: {
            char **ptr;
            for (ptr = cmdline->delete_dns; *ptr; ptr++)
                  if (cmdline->ldif)
                        print_ldif_delete(s, *ptr);
                  else
                        print_ldapvi_delete(s, *ptr);
            break;
      }
      case ldapvi_mode_rename:
            if (cmdline->ldif)
                  print_ldif_rename(s,
                                cmdline->rename_old,
                                cmdline->rename_new,
                                cmdline->rename_dor);
            else
                  print_ldapvi_rename(s,
                                  cmdline->rename_old,
                                  cmdline->rename_new,
                                  cmdline->rename_dor);
            break;
      case ldapvi_mode_modrdn:
            if (cmdline->ldif)
                  print_ldif_modrdn(s,
                                cmdline->rename_old,
                                cmdline->rename_new,
                                cmdline->rename_dor);
            else
                  print_ldapvi_modrdn(s,
                                  cmdline->rename_old,
                                  cmdline->rename_new,
                                  cmdline->rename_dor);
            break;
      default:
            abort();
      }
}

static void
add_template(LDAP *ld, FILE *s, GPtrArray *wanted, char *base)
{
      int i;
      tentroid *entroid;
      LDAPObjectClass *cls;
      LDAPAttributeType *at;
      tschema *schema = schema_new(ld);

      if (!schema) {
            fputs("Error: Failed to read schema, giving up.\n", stderr);
            exit(1);
      }

      entroid = entroid_new(schema);
      for (i = 0; i < wanted->len; i++) {
            char *name = g_ptr_array_index(wanted, i);
            cls = entroid_request_class(entroid, name);
            if (!cls) {
                  fputs(entroid->error->str, stderr);
                  exit(1);
            }
            if (cls->oc_kind == LDAP_SCHEMA_ABSTRACT) {
                  g_string_append(entroid->comment,
                              "### NOTE: objectclass is abstract: ");
                  g_string_append(entroid->comment, name);
                  g_string_append_c(entroid->comment, '\n');
            }
      }

      if (compute_entroid(entroid) == -1) {
            fputs(entroid->error->str, stderr);
            exit(1);
      }

      fputc('\n', s);
      fputs(entroid->comment->str, s);
      fprintf(s, "add %s\n", base ? base : "<DN>");

      for (i = 0; i < entroid->classes->len; i++) {
            cls = g_ptr_array_index(entroid->classes, i);
            fprintf(s, "objectClass: %s\n", objectclass_name(cls));
      }
      for (i = 0; i < entroid->must->len; i++) {
            at = g_ptr_array_index(entroid->must, i);
            if (strcmp(at->at_oid, "2.5.4.0"))
                  fprintf(s, "%s: \n", attributetype_name(at));
      }
      for (i = 0; i < entroid->may->len; i++) {
            at = g_ptr_array_index(entroid->may, i);
            if (strcmp(at->at_oid, "2.5.4.0"))
                  fprintf(s, "#%s: \n", attributetype_name(at));
      }

      entroid_free(entroid);
      schema_free(schema);
}

static void
parse_file(FILE *in,
         tparser *p, thandler *h, void *userdata,
         handler_entry hentry, void *entrydata,
         int addp)
{
      char *key = 0;

      for (;;) {
            long pos;

            if (p->peek(in, -1, &key, &pos) == -1) exit(1);
            if (!key) break;

            if (ndecimalp(key)) {
                  tentry *entry;
                  if (p->entry(in, pos, 0, &entry, 0) == -1)
                        exit(1);
                  if (hentry)
                        hentry(key, entry, entrydata);
                  entry_free(entry);
            } else {
                  char *k = key;
                  if (!strcmp(key, "add") && !addp)
                        k = "replace";
                  if (process_immediate(p, h, userdata, in, pos, k) < 0)
                        exit(1);
            }
            free(key);
      }
}

static int
write_file_header(FILE *s, cmdline *cmdline)
{
      int nlines = 0;

      if (print_binary_mode == PRINT_UTF8 && !cmdline->ldif) {
            fputs("# -*- coding: utf-8 -*- vim:encoding=utf-8:\n", s);
            nlines++;
      }
      if (cmdline->ldif) {
            fputs("# " RFC_2849_URL "\n" "# " MANUAL_LDIF_URL "\n", s);
            nlines += 2;
      } else  {
            fputs("# " MANUAL_SYNTAX_URL "\n", s);
            nlines++;
      }

      return nlines;
}

static void
cut_datafile(char *dataname, long pos, cmdline *cmdline)
{
      FILE *in;
      FILE *out;
      char *tmpname = append(dataname, ".tmp");

      if ( !(in = fopen(dataname, "r"))) syserr();
      if ( !(out = fopen(tmpname, "w"))) syserr();
      if (fseek(in, pos, SEEK_SET) == -1) syserr();
      write_file_header(out, cmdline);
      fputc('\n', out);
      fcopy(in, out);
      if (fclose(in) == EOF) syserr();
      if (fclose(out) == EOF) syserr();
      rename(tmpname, dataname);
      free(tmpname);
}

static int
can_seek(FILE *s)
{
      long pos;
      if ( (pos = ftell(s)) == -1) return 0;
      if (fseek(s, pos, SEEK_SET) == -1) return 0;
      return 1;
}

static int
copy_sasl_output(FILE *out, char *sasl)
{
      struct stat st;
      FILE *in;
      int have_sharpsign = 0;
      int line = 0;
      int c;

      if (lstat(sasl, &st) == -1) return;
      if ( !(in = fopen(sasl, "r"))) syserr();

      if (st.st_size > 0) {
            fputs("\n# SASL output:\n", out);
            line += 2;
      }
      for (;;) {
            switch ( c = getc_unlocked(in)) {
            case 0:
            case '\r':
                  break;
            case '\n':
                  if (!have_sharpsign)
                        fputc('#', out);
                  line++;
                  have_sharpsign = 0;
                  fputc(c, out);
                  break;
            case EOF:
                  if (have_sharpsign) {
                        line++;
                        fputc('\n', out);
                  }
                  if (fclose(in) == EOF) syserr();
                  return line;
            default:
                  if (!have_sharpsign) {
                        have_sharpsign = 1;
                        fputs("# ", out);
                  }
                  fputc(c, out);
            }
      }
}

static GArray *
main_write_files(
      LDAP *ld, cmdline *cmdline,
      char *clean, char *data, char *sasl,
      GPtrArray *ctrls,
      FILE *source,
      int *nlines)
{
      FILE *s;
      int line;
      GArray *offsets;

      if ( !(s = fopen(data, "w"))) syserr();
      line = write_file_header(s, cmdline);
      line += copy_sasl_output(s, sasl);

      if (cmdline->mode == ldapvi_mode_in) {
            tparser *p = &ldif_parser;
            thandler *h = &vdif_handler;
            FILE *tmp = 0;

            if (cmdline->in_file) {
                  if ( !(source = fopen(cmdline->in_file, "r+")))
                        syserr();
            } else {
                  if (!source)
                        source = stdin;
                  if (!can_seek(source)) {
                        /* einfach clean als tmpfile nehmen */
                        if ( !(tmp = fopen(clean, "w+"))) syserr();
                        fcopy(source, tmp);
                        if (fseek(tmp, 0, SEEK_SET) == -1) syserr();
                        source = tmp;
                        /* source war stdin, kann offen bleiben */
                  }
            }

            if (cmdline->ldif) h = &ldif_handler;
            if (cmdline->ldapvi) p = &ldapvi_parser;
            parse_file(source, p, h, s, 0, 0, cmdline->ldapmodify_add);

            if (cmdline->in_file)
                  if (fclose(source) == EOF) syserr();
            if (tmp)
                  if (unlink(clean) == -1) syserr();
            if (fclose(s) == EOF) syserr();
            cp("/dev/null", clean, 0, 0);
            offsets = g_array_new(0, 0, sizeof(long));
      } else if (cmdline->classes || cmdline->mode != ldapvi_mode_edit) {
            if (!cmdline->classes)
                  add_changerecord(s, cmdline);
            else if (cmdline->classes->len) {
                  char *base = 0;
                  if (cmdline->basedns->len > 0)
                        base = g_ptr_array_index(cmdline->basedns, 0);
                  add_template(ld, s, cmdline->classes, base);
            } else
                  fputc('\n', s);
            if (fclose(s) == EOF) syserr();
            cp("/dev/null", clean, 0, 0);
            offsets = g_array_new(0, 0, sizeof(long));
      } else {
            offsets = search(s, ld, cmdline, (void *) ctrls->pdata, 0,
                         cmdline->ldif);
            if (fclose(s) == EOF) syserr();
            cp(data, clean, 0, 0);
      }

      *nlines = line;
      return offsets;
}

static int
main_loop(LDAP *ld, cmdline *cmdline,
        tparser *parser, GArray *offsets, char *clean, char *data,
        GPtrArray *ctrls, char *dir)
{
      int changed = 1;
      int continuous = cmdline->continuous;

      for (;;) {
            if (changed)
                  if (!analyze_changes(
                            parser, offsets, clean, data, cmdline))
                  {
                        write_ldapvi_history();
                        return 0;
                  }
            changed = 0;
            switch (choose("Action?",
                         "yYqQvVebB*rsf+?",
                         "(Type '?' for help.)")) {
            case 'Y':
                  continuous = 1;
                  /* fall through */
            case 'y':
                  commit(parser, ld, offsets, clean, data,
                         (void *) ctrls->pdata, cmdline->verbose, 0,
                         continuous, cmdline);
                  changed = 1;
                  break; /* reached only on user error */
            case 'q':
                  if (save_ldif(parser,
                              offsets, clean, data,
                              cmdline->server,
                              cmdline->bind_options.user,
                              cmdline->managedsait))
                        break;
                  write_ldapvi_history();
                  return 0;
            case 'Q':
                  write_ldapvi_history();
                  return 0;
            case 'v':
                  view_ldif(parser, dir, offsets, clean, data);
                  break;
            case 'V':
                  view_vdif(parser, dir, offsets, clean, data);
                  break;
            case 'e':
                  edit(data, 0);
                  changed = 1;
                  break;
            case 'b':
                  rebind(ld, &cmdline->bind_options, 1, 0, 1);
                  changed = 1; /* print stats again */
                  break;
            case '*':
                  change_mechanism(&cmdline->bind_options);
                  puts("Type 'b' to log in.");
                  break;
            case 'B':
                  toggle_sasl(&cmdline->bind_options);
                  puts("Type 'b' to log in.");
                  break;
            case 'r':
                  ldap_unbind_s(ld);
                  ld = do_connect(
                        cmdline->server,
                        &cmdline->bind_options,
                        cmdline->referrals,
                        cmdline->starttls,
                        cmdline->tls,
                        cmdline->deref,
                        1,
                        0);
                  printf("Connected to %s.\n", cmdline->server);
                  changed = 1; /* print stats again */
                  break;
            case 's':
                  skip(parser, data, offsets, cmdline);
                  changed = 1;
                  break;
            case 'f':
                  forget_deletions(parser, offsets, clean, data);
                  changed = 1;
                  break;
            case '+':
                  rewrite_comments(ld, data, cmdline);
                  edit(data, 0);
                  changed = 1;
                  break;
            case 'L' - '@':
                  {
                        char *clear = tigetstr("clear");
                        if (clear && clear != (char *) -1)
                              putp(clear);
                  }
                  break;
            case '?':
                  puts("Commands:\n"
                       "  y -- commit changes\n"
                       "  Y -- commit, ignoring all errors\n"
                       "  q -- save changes as LDIF and quit\n"
                       "  Q -- discard changes and quit\n"
                       "  v -- view changes as LDIF change records\n"
                       "  V -- view changes as ldapvi change records\n"
                       "  e -- open editor again\n"
                       "  b -- show login dialog and rebind\n"
                       "  B -- toggle SASL\n"
                       "  * -- set SASL mechanism\n"
                       "  r -- reconnect to server\n"
                       "  s -- skip one entry\n"
                       "  f -- forget deletions\n"
                       "  + -- rewrite file to include schema comments\n"
                       "  ? -- this help");
                  break;
            }
      }
}

int
main(int argc, const char **argv)
{
      LDAP *ld;
      cmdline cmdline;
      GPtrArray *ctrls = g_ptr_array_new();
      static char dir[] = "/tmp/ldapvi-XXXXXX";
      char *clean;
      char *data;
      char *sasl;
      GArray *offsets;
      FILE *source_stream = 0;
      FILE *target_stream = 0;
      tparser *parser;
      int nlines;

      init_cmdline(&cmdline);

      if (argc >= 2 && !strcmp(argv[1], "--diff")) {
            if (argc != 4) {
                  fputs("wrong number of arguments to --diff\n", stderr);
                  usage(2, 1);
            }
            offline_diff(&ldapvi_parser,
                       (char *) argv[2],
                       (char *) argv[3]);
            exit(0);
      }

      parse_arguments(argc, argv, &cmdline, ctrls);
      if (fixup_streams(&source_stream, &target_stream) == -1)
            cmdline.noninteractive = 1;
      if (cmdline.noninteractive) {
            cmdline.noquestions = 1;
            cmdline.quiet = 1;
            cmdline.bind_options.dialog = BD_NEVER;
      }
      if (cmdline.ldif)
            parser = &ldif_parser;
      else
            parser = &ldapvi_parser;
      read_ldapvi_history();

      setupterm(0, 1, 0);
      ld = do_connect(cmdline.server,
                  &cmdline.bind_options,
                  cmdline.referrals,
                  cmdline.starttls,
                  cmdline.tls,
                  cmdline.deref,
                  cmdline.profileonlyp,
                  dir);
      if (!ld) {
            write_ldapvi_history();
            exit(1);
      }

      if (cmdline.sortkeys)
            append_sort_control(ld, ctrls, cmdline.sortkeys);
      g_ptr_array_add(ctrls, 0);

      if (cmdline.discover) {
            if (cmdline.basedns->len > 0)
                  yourfault("Conflicting options given:"
                          " --base and --discover.");
            discover_naming_contexts(ld, cmdline.basedns);
      }

      if (cmdline.config) {
            write_config(ld, target_stream, &cmdline);
            write_ldapvi_history();
            exit(0);
      }

      if (cmdline.mode == ldapvi_mode_out
          || (cmdline.mode == ldapvi_mode_edit && target_stream))
      {
            if (cmdline.classes)
                  yourfault("Cannot edit entry templates noninteractively.");
            if (!target_stream)
                  target_stream = stdout;
            search(target_stream, ld, &cmdline, (void *) ctrls->pdata, 1,
                   cmdline.mode == ldapvi_mode_out
                   ? !cmdline.ldapvi
                   : cmdline.ldif);
            write_ldapvi_history();
            exit(0);
      }

      ensure_tmp_directory(dir);
      clean = append(dir, "/clean");
      data = append(dir, "/data");
      sasl = append(dir, "/sasl");

      offsets = main_write_files(
            ld, &cmdline, clean, data, sasl, ctrls, source_stream,
            &nlines);

      if (!cmdline.noninteractive) {
            if (target_stream) {
                  FILE *tmp = fopen(data, "r");
                  if (!tmp) syserr();
                  fcopy(tmp, target_stream);
                  write_ldapvi_history();
                  exit(0);
            }
            edit(data, nlines + 1);
      } else if (cmdline.mode == ldapvi_mode_edit)
            yourfault("Cannot edit entries noninteractively.");

      if (cmdline.noquestions) {
            if (!analyze_changes(parser, offsets, clean, data, &cmdline)) {
                  write_ldapvi_history();
                  return 0;
            }
            commit(parser, ld, offsets, clean, data, (void *) ctrls->pdata,
                   cmdline.verbose, 1, cmdline.continuous, &cmdline);
            fputs("Error in noninteractive mode, giving up.\n", stderr);
            return 1;
      }

      return main_loop(
            ld, &cmdline, parser, offsets, clean, data, ctrls, dir);
}

Generated by  Doxygen 1.6.0   Back to index