1 /* -*- show-trailing-whitespace: t; indent-tabs: t -*-
2 *
3 * Copyright (c) 2003,2004,2005,2006 David Lichteblau
4 * Copyright (c) 2006 Perry Nguyen
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20 #include <curses.h>
21 #include <signal.h>
22 #include <term.h>
23 #include "common.h"
24
25 typedef void (*handler_entry)(char *, tentry *, void *);
26 static void parse_file(
27 FILE *, tparser *, thandler *, void *, handler_entry, void *, int);
28 static void cut_datafile(char *, long, cmdline *);
29 static int write_file_header(FILE *, cmdline *);
30 static int rebind(LDAP *, bind_options *, int, char *, int);
31
32 static int
compare(tparser * p,thandler * handler,void * userdata,GArray * offsets,char * cleanname,char * dataname,long * error_position,cmdline * cmdline)33 compare(tparser *p, thandler *handler, void *userdata, GArray *offsets,
34 char *cleanname, char *dataname, long *error_position,
35 cmdline *cmdline)
36 {
37 FILE *clean, *data;
38 int rc;
39 long pos;
40
41 if ( !(clean = fopen(cleanname, "r+"))) syserr();
42 if ( !(data = fopen(dataname, "r"))) syserr();
43 rc = compare_streams(p, handler, userdata, offsets, clean, data, &pos,
44 error_position);
45 if (fclose(clean) == EOF) syserr();
46 if (fclose(data) == EOF) syserr();
47
48 if (rc == -2) {
49 /* an error has happened */
50 int n;
51
52 if (!cmdline) {
53 fputs("oops: unexpected error in handler\n", stderr);
54 exit(1);
55 }
56
57 /* remove already-processed entries from the data file */
58 cut_datafile(dataname, pos, cmdline);
59
60 /* flag already-processed entries in the offset table */
61 for (n = 0; n < offsets->len; n++)
62 if (g_array_index(offsets, long, n) < 0)
63 g_array_index(offsets, long, n) = -1;
64 }
65 return rc;
66 }
67
68 static void
cleanup(int rc,char * pathname)69 cleanup(int rc, char *pathname)
70 {
71 DIR *dir;
72 struct dirent *entry;
73 GString *str = g_string_new(pathname);
74 int len;
75 struct termios term;
76
77 /*
78 * delete temporary directory
79 */
80 g_string_append(str, "/");
81 len = str->len;
82
83 if ( !(dir = opendir(pathname))) syserr();
84 while ( (entry = readdir(dir)))
85 if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")){
86 g_string_truncate(str, len);
87 g_string_append(str, entry->d_name);
88 if (unlink(str->str) == -1) syserr();
89 }
90 if (closedir(dir) == -1) syserr();
91 if (rmdir(pathname) == -1) syserr();
92 g_string_free(str, 1);
93
94 /*
95 * reset terminal
96 */
97 if (tcgetattr(0, &term) == -1)
98 /* oh, running without a terminal */
99 return;
100 term.c_lflag |= ICANON;
101 term.c_lflag |= ECHO;
102 if (tcsetattr(0, TCSANOW, &term) == -1) syserr();
103 }
104
105 static void
cleanup_signal(int n)106 cleanup_signal(int n)
107 {
108 fprintf(stderr, "\nCaught signal %d, exiting...\n", n);
109 exit(2);
110 }
111
112 static int
moddn(LDAP * ld,char * old,char * new,int dor,LDAPControl ** ctrls)113 moddn(LDAP *ld, char *old, char *new, int dor, LDAPControl **ctrls)
114 {
115 int rc;
116 char **newrdns = ldap_explode_dn(new, 0);
117 char **ptr = newrdns;
118 char *newrdn = *ptr++;
119 GString *newsup = g_string_sized_new(strlen(new));
120
121 if (newrdn) {
122 if (*ptr) g_string_append(newsup, *ptr++);
123 for (; *ptr; ptr++) {
124 g_string_append_c(newsup, ',');
125 g_string_append(newsup, *ptr);
126 }
127 } else
128 newrdn = "";
129 rc = ldap_rename_s(ld, old, newrdn, newsup->str, dor, ctrls, 0);
130 g_string_free(newsup, 1);
131 ldap_value_free(newrdns);
132 return rc;
133 }
134
135
136 /*****************************************
137 * ldapmodify_handler
138 */
139 struct ldapmodify_context {
140 LDAP *ld;
141 LDAPControl **controls;
142 int verbose;
143 int noquestions;
144 int continuous;
145 };
146
147 static int
ldapmodify_error(struct ldapmodify_context * ctx,char * error)148 ldapmodify_error(struct ldapmodify_context *ctx, char *error)
149 {
150 ldap_perror(ctx->ld, error);
151 if (!ctx->continuous)
152 return -1;
153 fputs("(error ignored)\n", stderr);
154 return 0;
155 }
156
157 static int
ldapmodify_change(int key,char * labeldn,char * dn,LDAPMod ** mods,void * userdata)158 ldapmodify_change(
159 int key, char *labeldn, char *dn, LDAPMod **mods, void *userdata)
160 {
161 struct ldapmodify_context *ctx = userdata;
162 LDAP *ld = ctx->ld;
163 LDAPControl **ctrls = ctx->controls;
164 int verbose = ctx->verbose;
165
166 if (verbose) printf("(modify) %s\n", labeldn);
167 if (ldap_modify_ext_s(ld, dn, mods, ctrls, 0))
168 return ldapmodify_error(ctx, "ldap_modify");
169 return 0;
170 }
171
172 static int
ldapmodify_rename(int key,char * dn1,tentry * modified,void * userdata)173 ldapmodify_rename(int key, char *dn1, tentry *modified, void *userdata)
174 {
175 struct ldapmodify_context *ctx = userdata;
176 LDAP *ld = ctx->ld;
177 LDAPControl **ctrls = ctx->controls;
178 int verbose = ctx->verbose;
179
180 char *dn2 = entry_dn(modified);
181 int deleteoldrdn = frob_rdn(modified, dn1, FROB_RDN_CHECK) == -1;
182 if (verbose) printf("(rename) %s to %s\n", dn1, dn2);
183 if (moddn(ld, dn1, dn2, deleteoldrdn, ctrls))
184 return ldapmodify_error(ctx, "ldap_rename");
185 return 0;
186 }
187
188 static int
ldapmodify_add(int key,char * dn,LDAPMod ** mods,void * userdata)189 ldapmodify_add(int key, char *dn, LDAPMod **mods, void *userdata)
190 {
191 struct ldapmodify_context *ctx = userdata;
192 LDAP *ld = ctx->ld;
193 LDAPControl **ctrls = ctx->controls;
194 int verbose = ctx->verbose;
195
196 if (verbose) printf("(add) %s\n", dn);
197 if (ldap_add_ext_s(ld, dn, mods, ctrls, 0))
198 return ldapmodify_error(ctx, "ldap_add");
199 return 0;
200 }
201
202 static int
ldapmodify_delete(int key,char * dn,void * userdata)203 ldapmodify_delete(int key, char *dn, void *userdata)
204 {
205 struct ldapmodify_context *ctx = userdata;
206 LDAP *ld = ctx->ld;
207 LDAPControl **ctrls = ctx->controls;
208 int verbose = ctx->verbose;
209
210 if (verbose) printf("(delete) %s\n", dn);
211 switch (ldap_delete_ext_s(ld, dn, ctrls, 0)) {
212 case 0:
213 break;
214 case LDAP_NOT_ALLOWED_ON_NONLEAF:
215 if (!ctx->noquestions)
216 return -2;
217 /* else fall through */
218 default:
219 return ldapmodify_error(ctx, "ldap_delete");
220 }
221 return 0;
222 }
223
224 static int
ldapmodify_rename0(int key,char * dn1,char * dn2,int deleteoldrdn,void * userdata)225 ldapmodify_rename0(
226 int key, char *dn1, char *dn2, int deleteoldrdn, void *userdata)
227 {
228 struct ldapmodify_context *ctx = userdata;
229 LDAP *ld = ctx->ld;
230 LDAPControl **ctrls = ctx->controls;
231 int verbose = ctx->verbose;
232
233 if (verbose) printf("(rename) %s to %s\n", dn1, dn2);
234 if (moddn(ld, dn1, dn2, deleteoldrdn, ctrls))
235 return ldapmodify_error(ctx, "ldap_rename");
236 return 0;
237 }
238
239
240 /*****************************************
241 * ldif_handler
242 */
243 static int
ldif_change(int key,char * labeldn,char * dn,LDAPMod ** mods,void * userdata)244 ldif_change(int key, char *labeldn, char *dn, LDAPMod **mods, void *userdata)
245 {
246 FILE *s = userdata;
247 print_ldif_modify(s, dn, mods);
248 return 0;
249 }
250
251 static int
ldif_rename(int key,char * olddn,tentry * modified,void * userdata)252 ldif_rename(int key, char *olddn, tentry *modified, void *userdata)
253 {
254 FILE *s = userdata;
255 int deleteoldrdn = frob_rdn(modified, olddn, FROB_RDN_CHECK) == -1;
256 print_ldif_rename(
257 s, olddn, entry_dn(modified),
258 deleteoldrdn);
259 return 0;
260 }
261
262 static int
ldif_add(int key,char * dn,LDAPMod ** mods,void * userdata)263 ldif_add(int key, char *dn, LDAPMod **mods, void *userdata)
264 {
265 FILE *s = userdata;
266 print_ldif_add(s, dn, mods);
267 return 0;
268 }
269
270 static int
ldif_delete(int key,char * dn,void * userdata)271 ldif_delete(int key, char *dn, void *userdata)
272 {
273 FILE *s = userdata;
274 print_ldif_delete(s, dn);
275 return 0;
276 }
277
278 static int
ldif_rename0(int key,char * dn1,char * dn2,int deleteoldrdn,void * userdata)279 ldif_rename0(int key, char *dn1, char *dn2, int deleteoldrdn, void *userdata)
280 {
281 FILE *s = userdata;
282 print_ldif_rename(s, dn1, dn2, deleteoldrdn);
283 return 0;
284 }
285
286 static thandler ldif_handler = {
287 ldif_change,
288 ldif_rename,
289 ldif_add,
290 ldif_delete,
291 ldif_rename0
292 };
293
294
295 /*****************************************
296 * noop handler
297 */
298 static int
noop_change(int key,char * labeldn,char * dn,LDAPMod ** mods,void * userdata)299 noop_change(int key, char *labeldn, char *dn, LDAPMod **mods, void *userdata)
300 {
301 return 0;
302 }
303
304 static int
noop_rename(int key,char * olddn,tentry * modified,void * userdata)305 noop_rename(int key, char *olddn, tentry *modified, void *userdata)
306 {
307 return 0;
308 }
309
310 static int
noop_add(int key,char * dn,LDAPMod ** mods,void * userdata)311 noop_add(int key, char *dn, LDAPMod **mods, void *userdata)
312 {
313 return 0;
314 }
315
316 static int
noop_delete(int key,char * dn,void * userdata)317 noop_delete(int key, char *dn, void *userdata)
318 {
319 return 0;
320 }
321
322 static int
noop_rename0(int key,char * dn1,char * dn2,int deleteoldrdn,void * userdata)323 noop_rename0(int key, char *dn1, char *dn2, int deleteoldrdn, void *userdata)
324 {
325 return 0;
326 }
327
328
329 /*****************************************
330 * forget_deletions_handler
331 */
332 static int
forget_deletion(int key,char * dn,void * userdata)333 forget_deletion(int key, char *dn, void *userdata)
334 {
335 GArray *deletions = userdata;
336 g_array_append_val(deletions, key);
337 return 0;
338 }
339
340 static thandler forget_deletions_handler = {
341 noop_change,
342 noop_rename,
343 noop_add,
344 forget_deletion,
345 noop_rename0
346 };
347
348
349 /*****************************************
350 * vdif_handler
351 */
352 static int
vdif_change(int key,char * labeldn,char * dn,LDAPMod ** mods,void * userdata)353 vdif_change(int key, char *labeldn, char *dn, LDAPMod **mods, void *userdata)
354 {
355 FILE *s = userdata;
356 print_ldapvi_modify(s, dn, mods);
357 return 0;
358 }
359
360 static int
vdif_rename(int key,char * olddn,tentry * modified,void * userdata)361 vdif_rename(int key, char *olddn, tentry *modified, void *userdata)
362 {
363 FILE *s = userdata;
364 int deleteoldrdn = frob_rdn(modified, olddn, FROB_RDN_CHECK) == -1;
365 print_ldapvi_rename(s, olddn, entry_dn(modified), deleteoldrdn);
366 return 0;
367 }
368
369 static int
vdif_add(int key,char * dn,LDAPMod ** mods,void * userdata)370 vdif_add(int key, char *dn, LDAPMod **mods, void *userdata)
371 {
372 FILE *s = userdata;
373 print_ldapvi_add(s, dn, mods);
374 return 0;
375 }
376
377 static int
vdif_delete(int key,char * dn,void * userdata)378 vdif_delete(int key, char *dn, void *userdata)
379 {
380 FILE *s = userdata;
381 print_ldapvi_delete(s, dn);
382 return 0;
383 }
384
385 static int
vdif_rename0(int key,char * dn1,char * dn2,int deleteoldrdn,void * userdata)386 vdif_rename0(int key, char *dn1, char *dn2, int deleteoldrdn, void *userdata)
387 {
388 FILE *s = userdata;
389 print_ldapvi_rename(s, dn1, dn2, deleteoldrdn);
390 return 0;
391 }
392
393
394 /*****************************************
395 * statistics_handler
396 */
397 struct statistics {
398 int nmodify, nadd, ndelete, nrename;
399 };
400
401 static int
statistics_change(int key,char * labeldn,char * dn,LDAPMod ** mods,void * userdata)402 statistics_change(
403 int key, char *labeldn, char *dn, LDAPMod **mods, void *userdata)
404 {
405 struct statistics *st = userdata;
406 st->nmodify++;
407 return 0;
408 }
409
410 static int
statistics_rename(int key,char * olddn,tentry * modified,void * userdata)411 statistics_rename(int key, char *olddn, tentry *modified, void *userdata)
412 {
413 struct statistics *st = userdata;
414 st->nrename++;
415 return 0;
416 }
417
418 static int
statistics_add(int key,char * dn,LDAPMod ** mods,void * userdata)419 statistics_add(int key, char *dn, LDAPMod **mods, void *userdata)
420 {
421 struct statistics *st = userdata;
422 st->nadd++;
423 return 0;
424 }
425
426 static int
statistics_delete(int key,char * dn,void * userdata)427 statistics_delete(int key, char *dn, void *userdata)
428 {
429 struct statistics *st = userdata;
430 st->ndelete++;
431 return 0;
432 }
433
434 static int
statistics_rename0(int key,char * dn1,char * dn2,int deleteoldrdn,void * userdata)435 statistics_rename0(
436 int key, char *dn1, char *dn2, int deleteoldrdn, void *userdata)
437 {
438 struct statistics *st = userdata;
439 st->nrename++;
440 return 0;
441 }
442
443
444 /* end of handlers
445 * **************************************** */
446
447 struct rebind_data {
448 bind_options bind_options;
449 LDAPURLDesc *seen;
450 };
451
452 static void
toggle_sasl(bind_options * bo)453 toggle_sasl(bind_options *bo)
454 {
455 if (bo->authmethod == LDAP_AUTH_SIMPLE) {
456 bo->authmethod = LDAP_AUTH_SASL;
457 puts("SASL authentication enabled.");
458 printf("SASL mechanism: %s (use '*' to change)\n",
459 bo->sasl_mech ? bo->sasl_mech : "(none)");
460 } else {
461 bo->authmethod = LDAP_AUTH_SIMPLE;
462 puts("Simple authentication enabled.");
463 }
464 }
465
466 static void
change_mechanism(bind_options * bo)467 change_mechanism(bind_options *bo)
468 {
469 if (bo->authmethod == LDAP_AUTH_SIMPLE) {
470 bo->authmethod = LDAP_AUTH_SASL;
471 puts("Switching to SASL authentication.");
472 }
473 bo->sasl_mech = ldapvi_getline("SASL mechanism", bo->sasl_mech);
474 }
475
476 static int
rebind_callback(LDAP * ld,const char * url,ber_tag_t request,int msgid,void * args)477 rebind_callback(
478 LDAP *ld, const char *url, ber_tag_t request, int msgid, void *args)
479 {
480 struct rebind_data *rebind_data = args;
481 LDAPURLDesc *urld;
482 bind_options bo = rebind_data->bind_options;
483
484 printf("Received referral to %s.\n", url);
485
486 if (ldap_url_parse(url, &urld)) {
487 puts("Error: Cannot parse URL.");
488 return -1;
489 }
490 if (rebind_data->seen
491 && !strcmp(rebind_data->seen->lud_scheme, urld->lud_scheme)
492 && !strcmp(rebind_data->seen->lud_host, urld->lud_host)
493 && rebind_data->seen->lud_port == urld->lud_port)
494 /* confirmed already */
495 return 0;
496
497 printf("You are not logged in to %s://%s:%d yet.\n"
498 "Type '!' or 'y' to do so.\n",
499 urld->lud_scheme, urld->lud_host, urld->lud_port);
500 for (;;) {
501 bo.dialog = BD_ALWAYS;
502
503 switch (choose("Rebind?", "y!nB*qQ?", "(Type '?' for help.)"))
504 {
505 case '!':
506 bo.dialog = BD_NEVER;
507 /* fall through */
508 case 'y':
509 if (rebind(ld, &bo, 0, 0, 1) == 0) {
510 if (rebind_data->seen)
511 ldap_free_urldesc(rebind_data->seen);
512 rebind_data->seen = urld;
513 return 0;
514 }
515 break;
516 case 'n':
517 ldap_free_urldesc(urld);
518 return 0;
519 case '*':
520 change_mechanism(&bo);
521 break;
522 case 'B':
523 toggle_sasl(&bo);
524 break;
525 case 'q':
526 ldap_free_urldesc(urld);
527 return -1;
528 case 'Q':
529 exit(0);
530 case '?':
531 puts("Commands:\n"
532 " y -- ask for user name and rebind\n"
533 " ! -- rebind using cached credentials\n"
534 " n -- don't login, just continue\n"
535 " B -- toggle SASL\n"
536 " * -- set SASL mechanism\n"
537 " q -- give up\n"
538 " Q -- give up and exit ldapvi\n"
539 " ? -- this help");
540 break;
541 }
542 }
543 }
544
545 static char *
find_user(LDAP * ld,char * filter)546 find_user(LDAP *ld, char *filter)
547 {
548 char *dn = 0;
549 LDAPMessage *result = 0;
550 LDAPMessage *entry = 0;
551
552 if (ldap_bind_s(ld, 0, 0, LDAP_AUTH_SIMPLE)) {
553 ldap_perror(ld, "ldap_bind");
554 goto cleanup;
555 }
556 if (ldap_search_s(ld, 0, LDAP_SCOPE_SUBTREE, filter, 0, 0, &result)) {
557 ldap_perror(ld, "ldap_search");
558 goto cleanup;
559 }
560 if ( !(entry = ldap_first_entry(ld, result))) {
561 puts("User not found.");
562 goto cleanup;
563 }
564 if (ldap_next_entry(ld, result)) {
565 puts("More than one entry matched user filter.");
566 goto cleanup;
567 }
568 dn = ldap_get_dn(ld, entry);
569
570 cleanup:
571 if (result) ldap_msgfree(result);
572 return dn;
573 }
574
575 static void
ensure_tmp_directory(char * dir)576 ensure_tmp_directory(char *dir)
577 {
578 if (strcmp(dir, "/tmp/ldapvi-XXXXXX")) return;
579 mkdtemp(dir);
580 on_exit((on_exit_function) cleanup, dir);
581 signal(SIGTERM, cleanup_signal);
582 signal(SIGINT, cleanup_signal);
583 signal(SIGPIPE, SIG_IGN);
584 }
585
586 static int
rebind_sasl(LDAP * ld,bind_options * bind_options,char * dir,int verbose)587 rebind_sasl(LDAP *ld, bind_options *bind_options, char *dir, int verbose)
588 {
589 tsasl_defaults *defaults = sasl_defaults_new(bind_options);
590 int rc;
591 int sasl_mode;
592
593 switch (bind_options->dialog) {
594 case BD_NEVER: sasl_mode = LDAP_SASL_QUIET; break;
595 case BD_AUTO: sasl_mode = LDAP_SASL_AUTOMATIC; break;
596 case BD_ALWAYS: sasl_mode = LDAP_SASL_INTERACTIVE; break;
597 default: abort();
598 }
599
600 if (dir) {
601 ensure_tmp_directory(dir);
602 init_sasl_redirection(defaults, append(dir, "/sasl"));
603 }
604
605 rc = ldap_sasl_interactive_bind_s(
606 ld, bind_options->user, bind_options->sasl_mech, NULL,
607 NULL, sasl_mode, ldapvi_sasl_interact, defaults);
608
609 sasl_defaults_free(defaults);
610 if (defaults->fd != -1) {
611 finish_sasl_redirection(defaults);
612 free(defaults->pathname);
613 }
614
615 if (rc != LDAP_SUCCESS) {
616 ldap_perror(ld, "ldap_sasl_interactive_bind_s");
617 return -1;
618 }
619
620 if (verbose)
621 printf("Bound as authzid=%s, authcid=%s.\n",
622 bind_options->sasl_authzid,
623 bind_options->sasl_authcid);
624 return 0;
625 }
626
627 static int
rebind_simple(LDAP * ld,bind_options * bo,int verbose)628 rebind_simple(LDAP *ld, bind_options *bo, int verbose)
629 {
630 if (bo->dialog == BD_ALWAYS
631 || (bo->dialog == BD_AUTO && bo->user && !bo->password))
632 {
633 tdialog d[2];
634 init_dialog(d, DIALOG_DEFAULT, "Filter or DN", bo->user);
635 init_dialog(d + 1, DIALOG_PASSWORD, "Password", bo->password);
636 dialog("--- Login", d, 2, bo->user ? 1 : 0);
637 bo->user = d[0].value;
638 bo->password = d[1].value;
639 }
640 if (bo->user && bo->user[0] == '(')
641 /* user is a search filter, not a name */
642 if ( !(bo->user = find_user(ld, bo->user)))
643 return -1;
644 if (ldap_bind_s(ld, bo->user, bo->password, LDAP_AUTH_SIMPLE)) {
645 ldap_perror(ld, "ldap_bind");
646 return -1;
647 }
648 if (verbose)
649 printf("Bound as %s.\n", bo->user);
650 return 0;
651 }
652
653 static int
rebind(LDAP * ld,bind_options * bind_options,int register_callback,char * dir,int verbose)654 rebind(LDAP *ld, bind_options *bind_options, int register_callback,
655 char *dir, int verbose)
656 {
657 int rc = -1;
658 struct rebind_data *rebind_data = xalloc(sizeof(struct rebind_data));
659
660 switch (bind_options->authmethod) {
661 case LDAP_AUTH_SASL:
662 if (rebind_sasl(ld, bind_options, dir, verbose))
663 return -1;
664 break;
665 case LDAP_AUTH_SIMPLE:
666 if (rebind_simple(ld, bind_options, verbose))
667 return -1;
668 break;
669 }
670
671 if (register_callback) {
672 rebind_data->bind_options = *bind_options;
673 rebind_data->bind_options.password
674 = xdup(bind_options->password);
675 rebind_data->seen = 0;
676 if (ldap_set_rebind_proc(ld, rebind_callback, rebind_data))
677 ldaperr(ld, "ldap_set_rebind_proc");
678 }
679 return 0;
680 }
681
682 void
init_sasl_arguments(LDAP * ld,bind_options * bind_options)683 init_sasl_arguments(LDAP *ld, bind_options *bind_options)
684 {
685 if (!bind_options->sasl_mech)
686 ldap_get_option(ld,
687 LDAP_OPT_X_SASL_MECH,
688 &bind_options->sasl_mech);
689 if (!bind_options->sasl_realm)
690 ldap_get_option(ld,
691 LDAP_OPT_X_SASL_REALM,
692 &bind_options->sasl_realm);
693 if (!bind_options->sasl_authcid)
694 ldap_get_option(ld,
695 LDAP_OPT_X_SASL_AUTHCID,
696 &bind_options->sasl_authcid);
697 if (!bind_options->sasl_authzid)
698 ldap_get_option(ld,
699 LDAP_OPT_X_SASL_AUTHZID,
700 &bind_options->sasl_authzid);
701 }
702
703 static LDAP *
do_connect(char * server,bind_options * bind_options,int referrals,int starttls,int tls,int deref,int profileonlyp,char * dir)704 do_connect(char *server, bind_options *bind_options,
705 int referrals, int starttls, int tls, int deref, int profileonlyp,
706 char *dir)
707 {
708 LDAP *ld = 0;
709 int rc = 0;
710 int drei = 3;
711
712 if (server && !strstr(server, "://")) {
713 char *url = xalloc(strlen(server) + sizeof("ldap://"));
714 strcpy(url, "ldap://");
715 strcpy(url + 7, server);
716 server = url;
717 }
718
719 if (ldap_set_option(0, LDAP_OPT_X_TLS_REQUIRE_CERT, (void *) &tls))
720 ldaperr(0, "ldap_set_option(LDAP_OPT_X_TLS)");
721 if ( rc = ldap_initialize(&ld, server)) {
722 fprintf(stderr, "ldap_initialize: %s\n", ldap_err2string(rc));
723 exit(1);
724 }
725 if (!profileonlyp)
726 init_sasl_arguments(ld, bind_options);
727 if (ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &drei))
728 ldaperr(ld, "ldap_set_option(LDAP_OPT_PROTOCOL_VERSION)");
729 if (starttls)
730 if (ldap_start_tls_s(ld, 0, 0))
731 ldaperr(ld, "ldap_start_tls_s");
732 if (rebind(ld, bind_options, 1, dir, 0) == -1) {
733 ldap_unbind_s(ld);
734 return 0;
735 }
736 /* after initial bind, always ask interactively (except in '!' rebinds,
737 * which are special-cased): */
738 bind_options->dialog = BD_ALWAYS;
739 if (ldap_set_option(ld, LDAP_OPT_REFERRALS,
740 referrals ? LDAP_OPT_ON : LDAP_OPT_OFF))
741 ldaperr(ld, "ldap_set_option(LDAP_OPT_REFERRALS)");
742 if (ldap_set_option(ld, LDAP_OPT_DEREF, (void *) &deref))
743 ldaperr(ld, "ldap_set_option(LDAP_OPT_DEREF)");
744
745 return ld;
746 }
747
748 /*
749 * fixme: brauchen wir das mit dem user? dann sollten wir hier noch
750 * sasl support vorsehen
751 *
752 * ldapvi-Kommandozeile konnte auch nicht schaden
753 */
754 static int
save_ldif(tparser * parser,GArray * offsets,char * clean,char * data,char * server,char * user,int managedsait)755 save_ldif(tparser *parser, GArray *offsets, char *clean, char *data,
756 char *server, char *user, int managedsait)
757 {
758 int fd;
759 FILE *s;
760
761 GString *name = g_string_sized_new(300);
762 g_string_append(name, ",ldapvi-");
763 if (gethostname(name->str + name->len, 300 - name->len) == -1)
764 syserr();
765 name->len = strlen(name->str);
766 g_string_sprintfa(name, "-%d.ldif", getpid());
767
768 if ( (fd = open(name->str, O_WRONLY | O_CREAT | O_EXCL, 0600)) == -1) {
769 int error = errno;
770 fprintf(stderr, "Cannot save %s: ", name->str);
771 errno = error;
772 perror(0);
773 g_string_free(name, 1);
774 return 1;
775 }
776 if ( !(s = fdopen(fd, "w"))) syserr();
777
778 fputs("version: 1\n", s);
779 fputs("# to apply these changes using ldapvi, run:\n", s);
780 fputs("# ldapvi --ldapmodify", s);
781 if (managedsait)
782 fputs(" -M", s);
783 if (server) {
784 fputs(" -h ", s);
785 fputs(server, s);
786 }
787 fputc(' ', s);
788 fputs(name->str, s);
789 fputc('\n', s);
790
791 compare(parser, &ldif_handler, s, offsets, clean, data, 0, 0);
792 if (fclose(s) == EOF) syserr();
793
794 printf("Your changes have been saved to %s.\n", name->str);
795 return 0;
796 }
797
798 static void
view_ldif(tparser * parser,char * dir,GArray * offsets,char * clean,char * data)799 view_ldif(tparser *parser, char *dir, GArray *offsets, char *clean, char *data)
800 {
801 FILE *s;
802 char *name = append(dir, "/ldif");
803 if ( !(s = fopen(name, "w"))) syserr();
804 fputs("version: 1\n", s);
805 compare(parser, &ldif_handler, s, offsets, clean, data, 0, 0);
806 if (fclose(s) == EOF) syserr();
807 view(name);
808 free(name);
809 }
810
811 static thandler vdif_handler = {
812 vdif_change,
813 vdif_rename,
814 vdif_add,
815 vdif_delete,
816 vdif_rename0
817 };
818
819 static void
view_vdif(tparser * parser,char * dir,GArray * offsets,char * clean,char * data)820 view_vdif(tparser *parser, char *dir, GArray *offsets, char *clean, char *data)
821 {
822 FILE *s;
823 char *name = append(dir, "/vdif");
824
825 if ( !(s = fopen(name, "w"))) syserr();
826 fputs("version: ldapvi\n", s);
827 compare(parser, &vdif_handler, s, offsets, clean, data, 0, 0);
828 if (fclose(s) == EOF) syserr();
829 view(name);
830 free(name);
831 }
832
833 static void
setcolor(int fg)834 setcolor(int fg)
835 {
836 char *bold = tigetstr("bold");
837 char *setaf = tigetstr("setaf");
838 if (setaf) putp(tparm(setaf, fg));
839 if (bold) putp(bold);
840 }
841
842 static void
print_counter(int color,char * label,int value)843 print_counter(int color, char *label, int value)
844 {
845 char *sgr0 = tigetstr("sgr0");
846
847 if (value) setcolor(color);
848 printf("%s: %d", label, value);
849 if (sgr0) putp(sgr0);
850 }
851
852 /* collect statistics. This comparison step is important
853 * for catching syntax errors before real processing starts.
854 */
855 static int
analyze_changes(tparser * p,GArray * offsets,char * clean,char * data,cmdline * cmdline)856 analyze_changes(tparser *p, GArray *offsets, char *clean, char *data,
857 cmdline *cmdline)
858 {
859 struct statistics st;
860 static thandler statistics_handler = {
861 statistics_change,
862 statistics_rename,
863 statistics_add,
864 statistics_delete,
865 statistics_rename0
866 };
867 int rc;
868 long pos;
869
870 retry:
871 memset(&st, 0, sizeof(st));
872 rc = compare(
873 p, &statistics_handler, &st, offsets, clean, data, &pos, 0);
874
875 /* Success? */
876 if (rc == 0) {
877 if (!(st.nadd + st.ndelete + st.nmodify + st.nrename)) {
878 if (!cmdline->quiet)
879 puts("No changes.");
880 return 0;
881 }
882 if (cmdline->quiet)
883 return 1;
884 print_counter(COLOR_GREEN, "add", st.nadd);
885 fputs(", ", stdout);
886 print_counter(COLOR_BLUE, "rename", st.nrename);
887 fputs(", ", stdout);
888 print_counter(COLOR_YELLOW, "modify", st.nmodify);
889 fputs(", ", stdout);
890 print_counter(COLOR_RED, "delete", st.ndelete);
891 putchar('\n');
892 return 1;
893 }
894
895 if (cmdline->noninteractive) {
896 fputs("Syntax error in noninteractive mode, giving up.\n",
897 stderr);
898 exit(1);
899 }
900
901 /* Syntax error */
902 for (;;) {
903 switch (choose("What now?", "eQ?", "(Type '?' for help.)")) {
904 case 'e':
905 edit_pos(data, pos);
906 goto retry;
907 case 'Q':
908 exit(0);
909 case '?':
910 puts("Commands:\n"
911 " Q -- discard changes and quit\n"
912 " e -- open editor again\n"
913 " ? -- this help");
914 break;
915 }
916 }
917 }
918
919 static void
commit(tparser * p,LDAP * ld,GArray * offsets,char * clean,char * data,LDAPControl ** ctrls,int verbose,int noquestions,int continuous,cmdline * cmdline)920 commit(tparser *p, LDAP *ld, GArray *offsets, char *clean, char *data,
921 LDAPControl **ctrls, int verbose, int noquestions, int continuous,
922 cmdline *cmdline)
923 {
924 struct ldapmodify_context ctx;
925 static thandler ldapmodify_handler = {
926 ldapmodify_change,
927 ldapmodify_rename,
928 ldapmodify_add,
929 ldapmodify_delete,
930 ldapmodify_rename0
931 };
932 ctx.ld = ld;
933 ctx.controls = ctrls;
934 ctx.verbose = verbose;
935 ctx.noquestions = noquestions;
936 ctx.continuous = continuous;
937
938 switch (compare(p, &ldapmodify_handler, &ctx, offsets, clean, data, 0,
939 cmdline))
940 {
941 case 0:
942 if (!cmdline->quiet)
943 puts("Done.");
944 write_ldapvi_history();
945 exit(0);
946 case -1:
947 yourfault("unexpected syntax error!");
948 case -2:
949 /* user error */
950 break;
951 default:
952 abort();
953 }
954 }
955
956 static int
getty(int fd)957 getty(int fd)
958 {
959 if (close(fd) == -1)
960 syserr();
961 if (open("/dev/tty", O_RDWR) != fd)
962 return -1;
963 return 0;
964 }
965
966 static int
fixup_streams(FILE ** source,FILE ** target)967 fixup_streams(FILE **source, FILE **target)
968 {
969 int rc = 0;
970
971 /* find a terminal and restore fds 0, 1 to a sensible value for
972 * reading the password. Save the original streams for later use.*/
973
974 if (!isatty(0)) {
975 /* user has redirected stdout */
976 int in = dup(fileno(stdin));
977 if (in == -1) syserr();
978 *source = fdopen(in, "r");
979 if (getty(0) == -1) rc = -1;
980 }
981 if (!isatty(1)) {
982 /* user has redirected stdout */
983 int out = dup(fileno(stdout));
984 if (out == -1) syserr();
985 *target = fdopen(out, "w");
986 if (getty(1) == -1) rc = -1;
987 }
988 return rc;
989 }
990
991 static int
ndecimalp(char * str)992 ndecimalp(char *str)
993 {
994 char *ptr;
995 strtol(str, &ptr, 10);
996 return !*ptr;
997 }
998
999 static void
skip(tparser * p,char * dataname,GArray * offsets,cmdline * cmdline)1000 skip(tparser *p, char *dataname, GArray *offsets, cmdline *cmdline)
1001 {
1002 long pos;
1003 char *key;
1004 FILE *s;
1005
1006 if ( !(s = fopen(dataname, "r"))) syserr();
1007 p->skip(s, 0, &key);
1008 if ( (pos = ftell(s)) == -1) syserr();
1009 if (fclose(s) == EOF) syserr();
1010
1011 if (key) {
1012 cut_datafile(dataname, pos, cmdline);
1013 if (ndecimalp(key))
1014 g_array_index(offsets, long, atoi(key)) = -1;
1015 free(key);
1016 } else {
1017 /* Im Normalfall wollen wir einen Eintrag in data
1018 * ueberspringen. Wenn aber in data nichts mehr steht,
1019 * sind wir ueber die eigentlichen Aenderungen schon
1020 * hinweg und es handelt sich um eine Loeschung. In
1021 * diesem Fall muessen wir nur das Offset aus der
1022 * Tabelle entfernen. */
1023 int n;
1024 for (n = 0; n < offsets->len; n++)
1025 if (g_array_index(offsets, long, n) >= 0) {
1026 g_array_remove_index(offsets, n);
1027 break;
1028 }
1029 }
1030 }
1031
1032 static tentroid *
entroid_set_entry(LDAP * ld,tentroid * entroid,tentry * entry)1033 entroid_set_entry(LDAP *ld, tentroid *entroid, tentry *entry)
1034 {
1035 int i;
1036 tattribute *oc = entry_find_attribute(entry, "objectClass", 0);
1037 GPtrArray *values;
1038
1039 if (!oc)
1040 return 0;
1041
1042 entroid_reset(entroid);
1043 values = attribute_values(oc);
1044 for (i = 0; i < values->len; i++) {
1045 GArray *av = g_ptr_array_index(values, i);
1046 LDAPObjectClass *cls;
1047
1048 {
1049 char zero = 0;
1050 /* PFUSCH! die GArrays muessen absolut weg! */
1051 g_array_append_val(av, zero);
1052 av->len--;
1053 }
1054
1055 cls = entroid_request_class(entroid, av->data);
1056 if (!cls) {
1057 g_string_append(entroid->comment, "# ");
1058 g_string_append(entroid->comment, entroid->error->str);
1059 return entroid;
1060 }
1061 }
1062
1063 if (compute_entroid(entroid) == -1) {
1064 g_string_append(entroid->comment, "# ");
1065 g_string_append(entroid->comment, entroid->error->str);
1066 return entroid;
1067 }
1068 return entroid;
1069 }
1070
1071 struct annotation_context {
1072 LDAP *ld;
1073 FILE *out;
1074 tparser *parser;
1075 tentroid *entroid;
1076 };
1077
1078 static void
annotate_entry(char * key,tentry * entry,void * userdata)1079 annotate_entry(char *key, tentry *entry, void *userdata)
1080 {
1081 struct annotation_context *ctx = userdata;
1082 tentroid *entroid = entroid_set_entry(ctx->ld, ctx->entroid, entry);
1083 ctx->parser->print(ctx->out, entry, key, entroid);
1084 }
1085
1086 static void
rewrite_comments(LDAP * ld,char * dataname,cmdline * cmdline)1087 rewrite_comments(LDAP *ld, char *dataname, cmdline *cmdline)
1088 {
1089 FILE *in;
1090 FILE *out;
1091 char *tmpname;
1092 tparser *p = &ldapvi_parser;
1093 thandler *h = &vdif_handler;
1094 struct annotation_context ctx;
1095 int addp = cmdline->ldapmodify_add;
1096 tschema *schema = schema_new(ld);
1097
1098 if (!schema) {
1099 fputs("Error: Failed to read schema.\n", stderr);
1100 return;
1101 }
1102
1103 tmpname = append(dataname, ".tmp");
1104 if ( !(in = fopen(dataname, "r"))) syserr();
1105 if ( !(out = fopen(tmpname, "w"))) syserr();
1106
1107 write_file_header(out, cmdline);
1108 if (cmdline->ldif) {
1109 p = &ldif_parser;
1110 h = &ldif_handler;
1111 }
1112 ctx.ld = ld;
1113 ctx.out = out;
1114 ctx.entroid = entroid_new(schema);
1115 ctx.parser = p;
1116 parse_file(in, p, h, out, annotate_entry, &ctx, addp);
1117
1118 if (fclose(in) == EOF) syserr();
1119 if (fclose(out) == EOF) syserr();
1120 rename(tmpname, dataname);
1121 free(tmpname);
1122 schema_free(schema);
1123 }
1124
1125
1126 static void
forget_deletions(tparser * p,GArray * offsets,char * clean,char * data)1127 forget_deletions(tparser *p, GArray *offsets, char *clean, char *data)
1128 {
1129 int i;
1130 GArray *deletions = g_array_new(0, 0, sizeof(int));
1131
1132 compare(p, &forget_deletions_handler, deletions,
1133 offsets, clean, data, 0, 0);
1134 for (i = 0; i < deletions->len; i++) {
1135 int n = g_array_index(deletions, int, i);
1136 g_array_index(offsets, long, n) = -1;
1137 }
1138 g_array_free(deletions, 1);
1139 }
1140
1141 static void
append_sort_control(LDAP * ld,GPtrArray * ctrls,char * keystring)1142 append_sort_control(LDAP *ld, GPtrArray *ctrls, char *keystring)
1143 {
1144 LDAPControl *ctrl;
1145 LDAPSortKey **keylist;
1146
1147 if (ldap_create_sort_keylist(&keylist, keystring))
1148 ldaperr(ld, "ldap_create_sort_keylist");
1149 if (ldap_create_sort_control(ld, keylist, 1, &ctrl))
1150 ldaperr(ld, "ldap_create_sort_keylist");
1151 g_ptr_array_add(ctrls, ctrl);
1152 }
1153
1154 static GArray *
read_offsets(tparser * p,char * file)1155 read_offsets(tparser *p, char *file)
1156 {
1157 GArray *offsets = g_array_new(0, 0, sizeof(long));
1158 FILE *s;
1159
1160 if ( !(s = fopen(file, "r"))) syserr();
1161 for (;;) {
1162 long offset;
1163 char *key, *ptr;
1164 int n;
1165 tentry *entry;
1166
1167 key = 0;
1168 if (p->entry(s, -1, &key, &entry, &offset) == -1) exit(1);
1169 if (!key) break;
1170
1171 n = strtol(key, &ptr, 10);
1172 if (*ptr) {
1173 fprintf(stderr, "Error: Invalid key: `%s'.\n", key);
1174 exit(1);
1175 }
1176 if (n != offsets->len) {
1177 fprintf(stderr, "Error: Unexpected key: `%s'.\n", key);
1178 exit(1);
1179 }
1180 free(key);
1181 entry_free(entry);
1182 g_array_append_val(offsets, offset);
1183 }
1184 if (fclose(s) == -1) syserr();
1185
1186 return offsets;
1187 }
1188
1189 static void
offline_diff(tparser * p,char * a,char * b)1190 offline_diff(tparser *p, char *a, char *b)
1191 {
1192 GArray *offsets = read_offsets(p, a);
1193 compare(p, &ldif_handler, stdout, offsets, a, b, 0, 0);
1194 g_array_free(offsets, 1);
1195 }
1196
1197 void
write_config(LDAP * ld,FILE * f,cmdline * cmdline)1198 write_config(LDAP *ld, FILE *f, cmdline *cmdline)
1199 {
1200 char *user = cmdline->bind_options.user;
1201 char *server = cmdline->server;
1202 int limit;
1203
1204 if (!f) f = stdout;
1205 fputs("# ldap.conf(5)\n", f);
1206 fputs("# edit this as needed and paste into ~/.ldaprc\n", f);
1207
1208 /* URI/HOST */
1209 fputc('\n', f);
1210 fputs("# server name\n", f);
1211 fputs("# (for parameterless operation, make sure to include at"
1212 " least this line)\n",
1213 f);
1214 if (!server)
1215 ldap_get_option(ld, LDAP_OPT_URI, &server);
1216 if (!server)
1217 ldap_get_option(ld, LDAP_OPT_HOST_NAME, &server);
1218 if (server)
1219 if (strstr(server, "://"))
1220 fprintf(f, "URI %s\n", server);
1221 else
1222 fprintf(f, "HOST %s\n", server);
1223
1224 /* BASE */
1225 fputc('\n', f);
1226 fputs("# default search base\n", f);
1227 if (cmdline->basedns->len) {
1228 GPtrArray *basedns = cmdline->basedns;
1229 int i;
1230 if (basedns->len > 1)
1231 fputs("### multiple namingcontexts found (uncomment"
1232 " one of these lines):\n",
1233 f);
1234 for (i = 0; i < basedns->len; i++) {
1235 if (basedns->len > 1) fputc('#', f);
1236 fprintf(f, "BASE %s\n", g_ptr_array_index(basedns, i));
1237 }
1238 } else {
1239 if (!cmdline->discover)
1240 fputs("### no search base specified, retry with"
1241 " --discover?\n",
1242 f);
1243 fputs("#BASE <dn>\n", f);
1244 }
1245
1246 /* BINDDN */
1247 fputc('\n', f);
1248 fputs("# user to bind as\n", f);
1249 if (user && user[0] == '(')
1250 user = find_user(ld, user);
1251 if (user)
1252 fprintf(f, "BINDDN %s\n", user);
1253 else
1254 fputs("#BINDDN <dn>\n", f);
1255
1256 /* search options */
1257 fputc('\n', f);
1258 fputs("# search parameters (uncomment as needed)\n", f);
1259 switch (cmdline->deref) {
1260 case LDAP_DEREF_NEVER:
1261 fputs("#DEREF never\n", f);
1262 break;
1263 case LDAP_DEREF_SEARCHING:
1264 fputs("#DEREF searcing\n", f);
1265 break;
1266 case LDAP_DEREF_FINDING:
1267 fputs("#DEREF finding\n", f);
1268 break;
1269 case LDAP_DEREF_ALWAYS:
1270 fputs("#DEREF always\n", f);
1271 break;
1272 }
1273 ldap_get_option(ld, LDAP_OPT_SIZELIMIT, &limit);
1274 fprintf(f, "#SIZELIMIT %d\n", limit);
1275 ldap_get_option(ld, LDAP_OPT_TIMELIMIT, &limit);
1276 fprintf(f, "#TIMELIMIT %d\n", limit);
1277 }
1278
1279 static void
add_changerecord(FILE * s,cmdline * cmdline)1280 add_changerecord(FILE *s, cmdline *cmdline)
1281 {
1282 switch (cmdline->mode) {
1283 case ldapvi_mode_delete: {
1284 char **ptr;
1285 for (ptr = cmdline->delete_dns; *ptr; ptr++)
1286 if (cmdline->ldif)
1287 print_ldif_delete(s, *ptr);
1288 else
1289 print_ldapvi_delete(s, *ptr);
1290 break;
1291 }
1292 case ldapvi_mode_rename:
1293 if (cmdline->ldif)
1294 print_ldif_rename(s,
1295 cmdline->rename_old,
1296 cmdline->rename_new,
1297 cmdline->rename_dor);
1298 else
1299 print_ldapvi_rename(s,
1300 cmdline->rename_old,
1301 cmdline->rename_new,
1302 cmdline->rename_dor);
1303 break;
1304 case ldapvi_mode_modrdn:
1305 if (cmdline->ldif)
1306 print_ldif_modrdn(s,
1307 cmdline->rename_old,
1308 cmdline->rename_new,
1309 cmdline->rename_dor);
1310 else
1311 print_ldapvi_modrdn(s,
1312 cmdline->rename_old,
1313 cmdline->rename_new,
1314 cmdline->rename_dor);
1315 break;
1316 default:
1317 abort();
1318 }
1319 }
1320
1321 static void
add_template(LDAP * ld,FILE * s,GPtrArray * wanted,char * base)1322 add_template(LDAP *ld, FILE *s, GPtrArray *wanted, char *base)
1323 {
1324 int i;
1325 tentroid *entroid;
1326 LDAPObjectClass *cls;
1327 LDAPAttributeType *at;
1328 tschema *schema = schema_new(ld);
1329
1330 if (!schema) {
1331 fputs("Error: Failed to read schema, giving up.\n", stderr);
1332 exit(1);
1333 }
1334
1335 entroid = entroid_new(schema);
1336 for (i = 0; i < wanted->len; i++) {
1337 char *name = g_ptr_array_index(wanted, i);
1338 cls = entroid_request_class(entroid, name);
1339 if (!cls) {
1340 fputs(entroid->error->str, stderr);
1341 exit(1);
1342 }
1343 if (cls->oc_kind == LDAP_SCHEMA_ABSTRACT) {
1344 g_string_append(entroid->comment,
1345 "### NOTE: objectclass is abstract: ");
1346 g_string_append(entroid->comment, name);
1347 g_string_append_c(entroid->comment, '\n');
1348 }
1349 }
1350
1351 if (compute_entroid(entroid) == -1) {
1352 fputs(entroid->error->str, stderr);
1353 exit(1);
1354 }
1355
1356 fputc('\n', s);
1357 fputs(entroid->comment->str, s);
1358 fprintf(s, "add %s\n", base ? base : "<DN>");
1359
1360 for (i = 0; i < entroid->classes->len; i++) {
1361 cls = g_ptr_array_index(entroid->classes, i);
1362 fprintf(s, "objectClass: %s\n", objectclass_name(cls));
1363 }
1364 for (i = 0; i < entroid->must->len; i++) {
1365 at = g_ptr_array_index(entroid->must, i);
1366 if (strcmp(at->at_oid, "2.5.4.0"))
1367 fprintf(s, "%s: \n", attributetype_name(at));
1368 }
1369 for (i = 0; i < entroid->may->len; i++) {
1370 at = g_ptr_array_index(entroid->may, i);
1371 if (strcmp(at->at_oid, "2.5.4.0"))
1372 fprintf(s, "#%s: \n", attributetype_name(at));
1373 }
1374
1375 entroid_free(entroid);
1376 schema_free(schema);
1377 }
1378
1379 static void
parse_file(FILE * in,tparser * p,thandler * h,void * userdata,handler_entry hentry,void * entrydata,int addp)1380 parse_file(FILE *in,
1381 tparser *p, thandler *h, void *userdata,
1382 handler_entry hentry, void *entrydata,
1383 int addp)
1384 {
1385 char *key = 0;
1386
1387 for (;;) {
1388 long pos;
1389
1390 if (p->peek(in, -1, &key, &pos) == -1) exit(1);
1391 if (!key) break;
1392
1393 if (ndecimalp(key)) {
1394 tentry *entry;
1395 if (p->entry(in, pos, 0, &entry, 0) == -1)
1396 exit(1);
1397 if (hentry)
1398 hentry(key, entry, entrydata);
1399 entry_free(entry);
1400 } else {
1401 char *k = key;
1402 if (!strcmp(key, "add") && !addp)
1403 k = "replace";
1404 if (process_immediate(p, h, userdata, in, pos, k) < 0)
1405 exit(1);
1406 }
1407 free(key);
1408 }
1409 }
1410
1411 static int
write_file_header(FILE * s,cmdline * cmdline)1412 write_file_header(FILE *s, cmdline *cmdline)
1413 {
1414 int nlines = 0;
1415
1416 if (print_binary_mode == PRINT_UTF8 && !cmdline->ldif) {
1417 fputs("# -*- coding: utf-8 -*- vim:encoding=utf-8:\n", s);
1418 nlines++;
1419 }
1420 if (cmdline->ldif) {
1421 fputs("# " RFC_2849_URL "\n" "# " MANUAL_LDIF_URL "\n", s);
1422 nlines += 2;
1423 } else {
1424 fputs("# " MANUAL_SYNTAX_URL "\n", s);
1425 nlines++;
1426 }
1427
1428 return nlines;
1429 }
1430
1431 static void
cut_datafile(char * dataname,long pos,cmdline * cmdline)1432 cut_datafile(char *dataname, long pos, cmdline *cmdline)
1433 {
1434 FILE *in;
1435 FILE *out;
1436 char *tmpname = append(dataname, ".tmp");
1437
1438 if ( !(in = fopen(dataname, "r"))) syserr();
1439 if ( !(out = fopen(tmpname, "w"))) syserr();
1440 if (fseek(in, pos, SEEK_SET) == -1) syserr();
1441 write_file_header(out, cmdline);
1442 fputc('\n', out);
1443 fcopy(in, out);
1444 if (fclose(in) == EOF) syserr();
1445 if (fclose(out) == EOF) syserr();
1446 rename(tmpname, dataname);
1447 free(tmpname);
1448 }
1449
1450 static int
can_seek(FILE * s)1451 can_seek(FILE *s)
1452 {
1453 long pos;
1454 if ( (pos = ftell(s)) == -1) return 0;
1455 if (fseek(s, pos, SEEK_SET) == -1) return 0;
1456 return 1;
1457 }
1458
1459 static int
copy_sasl_output(FILE * out,char * sasl)1460 copy_sasl_output(FILE *out, char *sasl)
1461 {
1462 struct stat st;
1463 FILE *in;
1464 int have_sharpsign = 0;
1465 int line = 0;
1466 int c;
1467
1468 if (lstat(sasl, &st) == -1) return 0;
1469 if ( !(in = fopen(sasl, "r"))) syserr();
1470
1471 if (st.st_size > 0) {
1472 fputs("\n# SASL output:\n", out);
1473 line += 2;
1474 }
1475 for (;;) {
1476 switch ( c = getc_unlocked(in)) {
1477 case 0:
1478 case '\r':
1479 break;
1480 case '\n':
1481 if (!have_sharpsign)
1482 fputc('#', out);
1483 line++;
1484 have_sharpsign = 0;
1485 fputc(c, out);
1486 break;
1487 case EOF:
1488 if (have_sharpsign) {
1489 line++;
1490 fputc('\n', out);
1491 }
1492 if (fclose(in) == EOF) syserr();
1493 return line;
1494 default:
1495 if (!have_sharpsign) {
1496 have_sharpsign = 1;
1497 fputs("# ", out);
1498 }
1499 fputc(c, out);
1500 }
1501 }
1502 }
1503
1504 static GArray *
main_write_files(LDAP * ld,cmdline * cmdline,char * clean,char * data,char * sasl,GPtrArray * ctrls,FILE * source,int * nlines)1505 main_write_files(
1506 LDAP *ld, cmdline *cmdline,
1507 char *clean, char *data, char *sasl,
1508 GPtrArray *ctrls,
1509 FILE *source,
1510 int *nlines)
1511 {
1512 FILE *s;
1513 int line;
1514 GArray *offsets;
1515
1516 if ( !(s = fopen(data, "w"))) syserr();
1517 line = write_file_header(s, cmdline);
1518 line += copy_sasl_output(s, sasl);
1519
1520 if (cmdline->mode == ldapvi_mode_in) {
1521 tparser *p = &ldif_parser;
1522 thandler *h = &vdif_handler;
1523 FILE *tmp = 0;
1524
1525 if (cmdline->in_file) {
1526 if ( !(source = fopen(cmdline->in_file, "r+")))
1527 syserr();
1528 } else {
1529 if (!source)
1530 source = stdin;
1531 if (!can_seek(source)) {
1532 /* einfach clean als tmpfile nehmen */
1533 if ( !(tmp = fopen(clean, "w+"))) syserr();
1534 fcopy(source, tmp);
1535 if (fseek(tmp, 0, SEEK_SET) == -1) syserr();
1536 source = tmp;
1537 /* source war stdin, kann offen bleiben */
1538 }
1539 }
1540
1541 if (cmdline->ldif) h = &ldif_handler;
1542 if (cmdline->ldapvi) p = &ldapvi_parser;
1543 parse_file(source, p, h, s, 0, 0, cmdline->ldapmodify_add);
1544
1545 if (cmdline->in_file)
1546 if (fclose(source) == EOF) syserr();
1547 if (tmp)
1548 if (unlink(clean) == -1) syserr();
1549 if (fclose(s) == EOF) syserr();
1550 cp("/dev/null", clean, 0, 0);
1551 offsets = g_array_new(0, 0, sizeof(long));
1552 } else if (cmdline->classes || cmdline->mode != ldapvi_mode_edit) {
1553 if (!cmdline->classes)
1554 add_changerecord(s, cmdline);
1555 else if (cmdline->classes->len) {
1556 char *base = 0;
1557 if (cmdline->basedns->len > 0)
1558 base = g_ptr_array_index(cmdline->basedns, 0);
1559 add_template(ld, s, cmdline->classes, base);
1560 } else
1561 fputc('\n', s);
1562 if (fclose(s) == EOF) syserr();
1563 cp("/dev/null", clean, 0, 0);
1564 offsets = g_array_new(0, 0, sizeof(long));
1565 } else {
1566 offsets = search(s, ld, cmdline, (void *) ctrls->pdata, 0,
1567 cmdline->ldif);
1568 if (fclose(s) == EOF) syserr();
1569 cp(data, clean, 0, 0);
1570 }
1571
1572 *nlines = line;
1573 return offsets;
1574 }
1575
1576 static int
main_loop(LDAP * ld,cmdline * cmdline,tparser * parser,GArray * offsets,char * clean,char * data,GPtrArray * ctrls,char * dir)1577 main_loop(LDAP *ld, cmdline *cmdline,
1578 tparser *parser, GArray *offsets, char *clean, char *data,
1579 GPtrArray *ctrls, char *dir)
1580 {
1581 int changed = 1;
1582 int continuous = cmdline->continuous;
1583
1584 for (;;) {
1585 if (changed)
1586 if (!analyze_changes(
1587 parser, offsets, clean, data, cmdline))
1588 {
1589 write_ldapvi_history();
1590 return 0;
1591 }
1592 changed = 0;
1593 switch (choose("Action?",
1594 "yYqQvVebB*rsf+?",
1595 "(Type '?' for help.)")) {
1596 case 'Y':
1597 continuous = 1;
1598 /* fall through */
1599 case 'y':
1600 commit(parser, ld, offsets, clean, data,
1601 (void *) ctrls->pdata, cmdline->verbose, 0,
1602 continuous, cmdline);
1603 changed = 1;
1604 break; /* reached only on user error */
1605 case 'q':
1606 if (save_ldif(parser,
1607 offsets, clean, data,
1608 cmdline->server,
1609 cmdline->bind_options.user,
1610 cmdline->managedsait))
1611 break;
1612 write_ldapvi_history();
1613 return 0;
1614 case 'Q':
1615 write_ldapvi_history();
1616 return 0;
1617 case 'v':
1618 view_ldif(parser, dir, offsets, clean, data);
1619 break;
1620 case 'V':
1621 view_vdif(parser, dir, offsets, clean, data);
1622 break;
1623 case 'e':
1624 edit(data, 0);
1625 changed = 1;
1626 break;
1627 case 'b':
1628 rebind(ld, &cmdline->bind_options, 1, 0, 1);
1629 changed = 1; /* print stats again */
1630 break;
1631 case '*':
1632 change_mechanism(&cmdline->bind_options);
1633 puts("Type 'b' to log in.");
1634 break;
1635 case 'B':
1636 toggle_sasl(&cmdline->bind_options);
1637 puts("Type 'b' to log in.");
1638 break;
1639 case 'r':
1640 ldap_unbind_s(ld);
1641 ld = do_connect(
1642 cmdline->server,
1643 &cmdline->bind_options,
1644 cmdline->referrals,
1645 cmdline->starttls,
1646 cmdline->tls,
1647 cmdline->deref,
1648 1,
1649 0);
1650 printf("Connected to %s.\n", cmdline->server);
1651 changed = 1; /* print stats again */
1652 break;
1653 case 's':
1654 skip(parser, data, offsets, cmdline);
1655 changed = 1;
1656 break;
1657 case 'f':
1658 forget_deletions(parser, offsets, clean, data);
1659 changed = 1;
1660 break;
1661 case '+':
1662 rewrite_comments(ld, data, cmdline);
1663 edit(data, 0);
1664 changed = 1;
1665 break;
1666 case 'L' - '@':
1667 {
1668 char *clear = tigetstr("clear");
1669 if (clear && clear != (char *) -1)
1670 putp(clear);
1671 }
1672 break;
1673 case '?':
1674 puts("Commands:\n"
1675 " y -- commit changes\n"
1676 " Y -- commit, ignoring all errors\n"
1677 " q -- save changes as LDIF and quit\n"
1678 " Q -- discard changes and quit\n"
1679 " v -- view changes as LDIF change records\n"
1680 " V -- view changes as ldapvi change records\n"
1681 " e -- open editor again\n"
1682 " b -- show login dialog and rebind\n"
1683 " B -- toggle SASL\n"
1684 " * -- set SASL mechanism\n"
1685 " r -- reconnect to server\n"
1686 " s -- skip one entry\n"
1687 " f -- forget deletions\n"
1688 " + -- rewrite file to include schema comments\n"
1689 " ? -- this help");
1690 break;
1691 }
1692 }
1693 }
1694
1695 int
main(int argc,const char ** argv)1696 main(int argc, const char **argv)
1697 {
1698 LDAP *ld;
1699 cmdline cmdline;
1700 GPtrArray *ctrls = g_ptr_array_new();
1701 static char dir[] = "/tmp/ldapvi-XXXXXX";
1702 char *clean;
1703 char *data;
1704 char *sasl;
1705 GArray *offsets;
1706 FILE *source_stream = 0;
1707 FILE *target_stream = 0;
1708 tparser *parser;
1709 int nlines;
1710
1711 init_cmdline(&cmdline);
1712
1713 if (argc >= 2 && !strcmp(argv[1], "--diff")) {
1714 if (argc != 4) {
1715 fputs("wrong number of arguments to --diff\n", stderr);
1716 usage(2, 1);
1717 }
1718 offline_diff(&ldapvi_parser,
1719 (char *) argv[2],
1720 (char *) argv[3]);
1721 exit(0);
1722 }
1723
1724 parse_arguments(argc, argv, &cmdline, ctrls);
1725 if (fixup_streams(&source_stream, &target_stream) == -1)
1726 cmdline.noninteractive = 1;
1727 if (cmdline.noninteractive) {
1728 cmdline.noquestions = 1;
1729 cmdline.quiet = 1;
1730 cmdline.bind_options.dialog = BD_NEVER;
1731 }
1732 if (cmdline.ldif)
1733 parser = &ldif_parser;
1734 else
1735 parser = &ldapvi_parser;
1736 read_ldapvi_history();
1737
1738 setupterm(0, 1, 0);
1739 ld = do_connect(cmdline.server,
1740 &cmdline.bind_options,
1741 cmdline.referrals,
1742 cmdline.starttls,
1743 cmdline.tls,
1744 cmdline.deref,
1745 cmdline.profileonlyp,
1746 dir);
1747 if (!ld) {
1748 write_ldapvi_history();
1749 exit(1);
1750 }
1751
1752 if (cmdline.sortkeys)
1753 append_sort_control(ld, ctrls, cmdline.sortkeys);
1754 g_ptr_array_add(ctrls, 0);
1755
1756 if (cmdline.discover) {
1757 if (cmdline.basedns->len > 0)
1758 yourfault("Conflicting options given:"
1759 " --base and --discover.");
1760 discover_naming_contexts(ld, cmdline.basedns);
1761 }
1762
1763 if (cmdline.config) {
1764 write_config(ld, target_stream, &cmdline);
1765 write_ldapvi_history();
1766 exit(0);
1767 }
1768
1769 if (cmdline.mode == ldapvi_mode_out
1770 || (cmdline.mode == ldapvi_mode_edit && target_stream))
1771 {
1772 if (cmdline.classes)
1773 yourfault("Cannot edit entry templates noninteractively.");
1774 if (!target_stream)
1775 target_stream = stdout;
1776 search(target_stream, ld, &cmdline, (void *) ctrls->pdata, 1,
1777 cmdline.mode == ldapvi_mode_out
1778 ? !cmdline.ldapvi
1779 : cmdline.ldif);
1780 write_ldapvi_history();
1781 exit(0);
1782 }
1783
1784 ensure_tmp_directory(dir);
1785 clean = append(dir, "/clean");
1786 data = append(dir, "/data");
1787 sasl = append(dir, "/sasl");
1788
1789 offsets = main_write_files(
1790 ld, &cmdline, clean, data, sasl, ctrls, source_stream,
1791 &nlines);
1792
1793 if (!cmdline.noninteractive) {
1794 if (target_stream) {
1795 FILE *tmp = fopen(data, "r");
1796 if (!tmp) syserr();
1797 fcopy(tmp, target_stream);
1798 write_ldapvi_history();
1799 exit(0);
1800 }
1801 edit(data, nlines + 1);
1802 } else if (cmdline.mode == ldapvi_mode_edit)
1803 yourfault("Cannot edit entries noninteractively.");
1804
1805 if (cmdline.noquestions) {
1806 if (!analyze_changes(parser, offsets, clean, data, &cmdline)) {
1807 write_ldapvi_history();
1808 return 0;
1809 }
1810 commit(parser, ld, offsets, clean, data, (void *) ctrls->pdata,
1811 cmdline.verbose, 1, cmdline.continuous, &cmdline);
1812 fputs("Error in noninteractive mode, giving up.\n", stderr);
1813 return 1;
1814 }
1815
1816 return main_loop(
1817 ld, &cmdline, parser, offsets, clean, data, ctrls, dir);
1818 }
1819