3 * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved.
4 * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
7 * Copyright (c) 1991,1993 Regents of the University of Michigan.
10 * Redistribution and use in source and binary forms are permitted
11 * provided that this notice is preserved and that due credit is given
12 * to the University of Michigan at Ann Arbor. The name of the University
13 * may not be used to endorse or promote products derived from this
14 * software without specific prior written permission. This software
15 * is provided ``as is'' without express or implied warranty.
22 #include <ac/stdlib.h>
25 #include <ac/string.h>
32 static char *get_URL( void );
33 static int check_URL( char *url );
39 LDAPMessage *mp; /* returned from find() */
40 char *dn; /* distinguished name */
41 char **rdns; /* for fiddling with the DN */
42 char name[MED_BUF_SIZE]; /* entry to modify */
43 int displayed_choices = 0;
44 static char ans[SMALL_BUF_SIZE]; /* for holding user input */
46 static char printed_warning = 0; /* for use with the */
47 struct attribute no_batch_update_attr;
50 int is_a_group; /* TRUE if it is; FALSE otherwise */
54 printf("->modify(%s)\n", who);
57 * Require them to bind first if the are modifying a group.
59 if (bind_status == UD_NOT_BOUND) {
60 if (auth((char *) NULL, 1) < 0)
65 * First, decide what entry we are going to modify. If the
66 * user has not included a name on the modify command line,
67 * we will use the person who was last looked up with a find
68 * command. If there is no value there either, we don't know
71 * Once we know who to modify, be sure that they exist, and
76 printf(" Enter the name of the person or\n");
77 printf(" group whose entry you want to modify: ");
80 printf(" Modify whose entry? ");
82 fetch_buffer(name, sizeof(name), stdin);
88 if ((mp = find(who, TRUE)) == NULL) {
89 (void) ldap_msgfree(mp);
90 printf(" Could not locate \"%s\" in the Directory.\n", who);
93 dn = ldap_get_dn(ld, ldap_first_entry(ld, mp));
94 rdns = ldap_explode_dn(dn, TRUE);
96 printf("\n Modifying Directory entry of \"%s\"\n", *rdns);
100 * If verbose mode is turned on and the user has not set a value
101 * for noBatchUpdates, warn them that what they are about to do
102 * may be overwritten automatically by that Stinkbug.
104 no_batch_update_attr.quipu_name = "noBatchUpdates";
105 (void) fetch_boolean_value(dn, no_batch_update_attr);
107 ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ld_errno);
109 if (verbose && !printed_warning && (ld_errno == LDAP_NO_SUCH_ATTRIBUTE)) {
111 printf("\n WARNING!\n");
112 printf(" You are about to make a modification to an LDAP entry\n");
113 printf(" that has its \"automatic updates\" field set to ON.\n");
114 printf(" This means that the entry will be automatically updated\n");
115 printf(" each month from official University sources like the\n");
116 printf(" Personnel Office. With \"automatic updates\" set to ON,\n");
117 printf(" the following fields will be overwritten each month:\n");
118 printf(" Title, home address and phone,\n");
119 printf(" business address and phone\n");
120 printf(" If you modify any of these fields, you may want to change\n");
121 printf(" the \"automatic updates\" field to OFF so that your\n");
122 printf(" changes will not be overwritten. You may change this\n");
123 printf(" setting by choosing \"u\" at the \"Modify what?\" prompt\n");
128 * Current values for user 'who' are being held in 'mp'. We
129 * should parse up that buffer and fill in the Entry structure.
130 * Once we're done with that, we can find out which fields the
131 * user would like to modify.
134 is_a_group = isgroup();
135 (void) ldap_msgfree(mp);
136 printf(" You now need to specify what field you'd like to modify.\n");
138 if ( verbose || !displayed_choices ) {
139 printf("\n Choices are:\n");
140 printf(" -----------------------\n");
141 print_mod_list(is_a_group);
142 printf("\n Pressing Return will cancel the process.\n");
143 displayed_choices = 1;
145 printf("\n Modify what? ");
147 fetch_buffer(ans, sizeof(ans), stdin);
150 perform_action(ans, dn, is_a_group);
151 if ((mp = find(*rdns, TRUE)) == NULL)
154 (void) ldap_msgfree(mp);
157 ldap_value_free(rdns);
161 /* generic routine for changing any field */
164 char *who, /* DN of entry we are changing */
165 int attr_idx /* attribute to change */
168 struct attribute attr = Entry.attrs[attr_to_index(attrlist[attr_idx].quipu_name)];
170 #define IS_MOD(x) (!strncasecmp(resp, (x), strlen(resp)))
172 static char buf[MED_BUF_SIZE]; /* for printing things */
173 static char resp[SMALL_BUF_SIZE]; /* for user input */
174 char *prompt, *prompt2, *more;
175 register int i; /* for looping thru values */
177 static LDAPMod *mods[2] = { &mod }; /* passed to ldap_modify */
178 static char *values[MAX_VALUES]; /* passed to ldap_modify */
182 printf("->change_field(%x, %s)\n", attr, who);
185 * If there is no current value associated with the attribute,
186 * then this is the easy case. Collect one (or more) attributes
187 * from the user, and then call ldap_modify_s() to write the changes
188 * to the LDAP server.
190 for (i = 0; i < MAX_VALUES; i++)
192 if (attr.values == (char **) NULL) {
193 printf("\n No current \"%s\"\n", attr.output_string);
194 values[0] = get_value(attr.quipu_name, "Enter a value");
195 if ( values[0] == NULL )
197 mod.mod_op = LDAP_MOD_REPLACE;
198 mod.mod_type = attr.quipu_name;
199 mod.mod_values = values;
200 for (i = 1; i < MAX_VALUES; i++) {
201 printf(" Do you wish to add an additional value? ");
203 fetch_buffer(resp, sizeof(resp), stdin);
204 if ((resp[0] == 'y') || (resp[0] == 'Y'))
205 values[i] = get_value(attr.quipu_name, "Enter an additional value");
210 if (debug & D_MODIFY) {
211 printf(" ld = 0x%x\n", ld);
212 printf(" who = [%s]\n", who);
213 printf(" mods[0]->mod_op = %1d\n", mods[0]->mod_op);
214 printf(" mods[0]->mod_type = %s\n", mods[0]->mod_type);
215 for (i = 0; mods[0]->mod_values[i] != NULL; i++)
216 printf(" mods[0]->mod_values[%1d] = %s\n", i, mods[0]->mod_values[i]);
219 if (ldap_modify_s(ld, who, mods)) {
224 printf(" Modification of '%s' complete.\n", attr.output_string);
225 ldap_uncache_entry( ld, who );
226 for (i--; i > 0; i--)
227 (void) Free(values[i]);
230 * There are values for this attribute already. In this case,
231 * we want to allow the user to delete all existing values,
232 * add additional values to the ones there already, or just
233 * delete some of the values already present. DIXIE does not
234 * handle modifications where the attribute occurs on the LHS
235 * more than once. So to delete entries and add entries, we
236 * need to call ldap_modify() twice.
240 * If the attribute holds values which are DNs, print them
241 * in a most pleasant way.
243 sprintf(buf, "%s: ", attr.output_string);
244 if (!strcmp(attr.quipu_name, "owner"))
245 (void) print_DN(attr);
247 (void) print_values(attr);
250 printf(" You may now:\n");
251 printf(" Add additional values to the existing ones, OR\n");
252 printf(" Clear all values listed above, OR\n");
253 printf(" Delete one of the values listed above, OR\n");
254 printf(" Replace all of the values above with new ones.\n");
256 printf("\n Add, Clear, Delete, or Replace? ");
258 fetch_buffer(resp, sizeof(resp), stdin);
261 * Bail if they just hit the RETURN key.
263 if (resp[0] == '\0') {
265 printf("\n No changes made.\n");
270 * If the want to clear the values, just do it.
272 mod.mod_type = attr.quipu_name;
273 mod.mod_values = values;
274 if (IS_MOD("clear")) {
275 mod.mod_op = LDAP_MOD_DELETE;
276 mod.mod_values = NULL;
277 if ( verbose && !confirm_action( "All existing values will be removed." )) {
278 printf(" Modification halted.\n");
282 if (debug & D_MODIFY) {
283 printf("Clearing attribute '%s'\n", attr.quipu_name);
284 printf("who = [%s]\n", who);
285 printf("mod = [%d] [%s] [%x]\n", mod.mod_op,
286 mod.mod_type, mod.mod_values);
289 if (ldap_modify_s(ld, who, mods)) {
294 printf(" '%s' has been cleared.\n", attr.output_string);
295 ldap_uncache_entry( ld, who );
300 prompt = "Enter the value you wish to add";
301 more = " Add an additional value? ";
302 prompt2 = "Enter another value you wish to add";
303 mod.mod_op = LDAP_MOD_ADD;
305 else if (IS_MOD("delete")) {
306 prompt = "Enter the value you wish to delete";
307 more = " Delete an additional value? ";
308 prompt2 = "Enter another value you wish to delete";
309 mod.mod_op = LDAP_MOD_DELETE;
311 else if (IS_MOD("replace")) {
312 prompt = "Enter the new value";
313 more = " Add an additional value? ";
314 prompt2 = "Enter another value you wish to add";
315 mod.mod_op = LDAP_MOD_REPLACE;
316 if ( verbose && !confirm_action( "All existing values will be overwritten with the new values you are about to enter." )) {
317 printf(" Modification halted.\n");
323 printf(" No changes made.\n");
327 values[0] = get_value(attr.quipu_name, prompt);
328 for (i = 1; i < MAX_VALUES; i++) {
331 fetch_buffer(resp, sizeof(resp), stdin);
332 if ((resp[0] == 'y') || (resp[0] == 'Y'))
333 values[i] = get_value(attr.quipu_name, prompt2);
338 /* if the first value in the value-array is NULL, bail */
339 if (values[0] == NULL) {
341 printf(" No modification made.\n");
345 if (debug & D_MODIFY) {
346 printf(" ld = 0x%x\n", ld);
347 printf(" who = [%s]\n", who);
348 printf(" mods[0]->mod_op = %1d\n", mods[0]->mod_op);
349 printf(" mods[0]->mod_type = %s\n", mods[0]->mod_type);
350 for (i = 0; mods[0]->mod_values[i] != NULL; i++)
351 printf(" mods[0]->mod_values[%1d] = %s\n", i, mods[0]->mod_values[i]);
354 if (ldap_modify_s(ld, who, mods)) {
359 printf(" Modifications to '%s' complete.\n", attr.output_string);
360 ldap_uncache_entry( ld, who );
361 for (i--; i > 0; i--)
362 (void) Free(values[i]);
368 * These are used to size the buffers we use when collecting values that
369 * can cross more than one line.
373 #define MAX_DESC_LINES 24
374 #define INTL_ADDR_LIMIT 30
377 get_value( char *id, char *prompt )
379 char *cp; /* for the Malloc() */
380 int count; /* line # of new value -- if multiline */
381 int multiline = 0; /* 1 if this value is multiline */
382 static char line[LINE_SIZE]; /* raw line from user */
383 static char buffer[MAX_DESC_LINES * (LINE_SIZE+2)]; /* holds ALL of the
387 printf("->get_value(%s, %s)\n", id, prompt);
389 /* if this is a URL, have another routine handle this */
390 if (!strcmp(id, "labeledURL"))
394 * To start with, we have one line of input from the user.
396 * Addresses and multiline description can span multiple lines.
397 * Other attributes may not.
400 (void) memset(buffer, '\0', sizeof(buffer));
402 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress") || !strcmp(id, "multiLineDescription") || !strcmp(id, "vacationMessage"))
404 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress"))
407 printf("\n %s:\n", prompt);
412 printf(" %1d: ", count);
416 fetch_buffer(line, sizeof(line), stdin);
422 * Screen out dangerous e-mail addresses of the form:
426 * and addresses that have no '@' symbol at all.
428 if (!strcmp(id, "mail")) {
432 /* if this is a group, don't worry */
436 /* if this address is not @umich.edu, don't worry */
437 /* ...unless there is no '@' at all! */
439 if ((tmp2 = strrchr(tmp, '@')) == NULL) {
441 format("The address you entered is not a valid e-mail address. E-mail addresses should be of the form \"local@domain\", e.g. bjensen@b.imap.itd.umich.edu", 75, 2 );
447 if (strcasecmp(tmp2, "umich.edu"))
450 /* if not of the form uid@umich.edu, don't worry */
451 if ((i = attr_to_index("uid")) < 0)
453 if (strcasecmp(tmp, *(Entry.attrs[i].values)))
456 format("An e-mail address of the form uniqname@umich.edu is not the form that you want registered in the Directory. This form is the one to use on business cards, for example, but the Directory should contain your real e-mail address; that is, the address where you really read your mail.", 75, 2);
460 printf(" Please enter a legal e-mail address (or press RETURN to stop)\n");
467 * If the attribute which we are gathering is a "owner"
468 * then we should lookup the name. The user is going to
469 * either have to change the search base before doing the
470 * modify, or the person is going to have to be within the
471 * scope of the current search base, or they will need to
474 if (!strcmp(id, "owner")) {
475 LDAPMessage *lmp, *elmp;
478 lmp = find(line, FALSE);
479 if (lmp == (LDAPMessage *) NULL) {
480 printf(" Could not find \"%s\" in the Directory\n", line);
482 format("Owners of groups must be valid entries in the LDAP Directory. The name you have typed above could not be found in the LDAP Directory.", 72, 2);
485 elmp = ldap_first_entry(ld, lmp);
486 if (lmp == (LDAPMessage *) NULL) {
487 ldap_perror(ld, "ldap_first_entry");
490 tmp = ldap_get_dn(ld, elmp);
493 (void) ldap_msgfree(lmp);
497 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress")) {
498 if (strlen(line) > INTL_ADDR_LIMIT) {
499 printf(" The international standard for addresses only allows for 30-character lines\n");
500 printf(" Please re-enter your last line.\n");
506 * Separate lines of multiline attribute values with
507 * dollar signs. Copy this line into the buffer we
508 * use to collect up all of the user-supplied input
509 * lines. If this is not a multiline attribute, we
513 (void) strcat(buffer, " $ ");
514 (void) strcat(buffer, line);
517 if ((count > MAX_LINES) && (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress"))) {
518 printf(" The international standard for addresses only allows for six lines\n");
522 if ((count > MAX_DESC_LINES) && !strcmp(id, "multiLineDescription")) {
523 printf(" We only allow %d lines of description\n", MAX_DESC_LINES);
528 if (buffer[0] == '\0')
531 if (debug & D_MODIFY)
532 printf(" Value is [%s]\n", buffer);
534 cp = (char *) Malloc((unsigned) (strlen(buffer) + 1));
541 char *who, /* DN of entry we are changing */
542 int attr_idx /* boolean attribute to change */
545 struct attribute attr = Entry.attrs[attr_to_index(attrlist[attr_idx].quipu_name)];
548 static char response[16];
549 static char *newsetting[2] = { NULL, NULL };
550 LDAPMod mod, *mods[2];
554 printf("->set_boolean(%s, %s)\n", who, attr.quipu_name);
557 mods[1] = (LDAPMod *) NULL;
558 mod.mod_op = LDAP_MOD_REPLACE;
559 mod.mod_type = attr.quipu_name;
560 mod.mod_values = newsetting;
562 /* fetch the current setting */
563 if ((cp = fetch_boolean_value(who, attr)) == NULL)
565 if (!strcmp(cp, "TRUE"))
566 newsetting[0] = "FALSE";
567 else if (!strcmp(cp, "FALSE"))
568 newsetting[0] = "TRUE";
570 printf(" This field needs to be set to either TRUE or to FALSE.\n");
571 printf(" \"%s\" is not a legal value. Please set this field to either TRUE or to FALSE.\n", cp);
572 newsetting[0] = "FALSE";
575 /* see if they want to change it */
577 printf(" The current value of this field is %s.\n", cp);
578 printf(" Should I change the value of this field to %s?\n",
580 printf(" Please enter Y for yes, N for no, or RETURN to cancel: ");
582 (void) fetch_buffer(response, sizeof(response), stdin);
583 for (s = response; isspace((unsigned char)*s); s++)
585 if ((*s == 'y') || (*s == 'Y')) {
586 if (ldap_modify_s(ld, who, mods)) {
591 printf(" Setting has been changed\n");
592 ldap_uncache_entry(ld, who);
596 printf(" Setting has not been changed\n");
602 set_updates( char *who, int dummy )
605 static char response[16];
606 static char value[6];
607 static char *newsetting[2] = { value, NULL };
608 LDAPMod mod, *mods[2];
609 struct attribute attr;
613 printf("->set_updates(%s)\n", who);
616 mods[1] = (LDAPMod *) NULL;
617 mod.mod_op = LDAP_MOD_REPLACE;
618 mod.mod_type = "noBatchUpdates";
619 mod.mod_values = newsetting;
620 /* explain what the implications are */
622 printf("\n By default, updates that are received from the Personnel\n");
623 printf(" Office and the Office of the Registrar are applied to all\n");
624 printf(" entries in the LDAP database each month. Sometimes this\n");
625 printf(" feature is undesirable. For example, if you maintain your\n");
626 printf(" entry in the LDAP database manually, you may not want to\n");
627 printf(" have these updates applied to your entry, possibly overwriting\n");
628 printf(" correct information with out-dated information.\n\n");
631 /* fetch the current setting */
632 attr.quipu_name = "noBatchUpdates";
633 if ((cp = fetch_boolean_value(who, attr)) == NULL)
635 if (!strcmp(cp, "TRUE"))
636 printf(" Automatic updates are currently turned OFF\n");
637 else if (!strcmp(cp, "FALSE"))
638 printf(" Automatic updates are currently turned ON\n");
640 fprintf(stderr, " Unknown update flag -> [%s]\n", cp);
644 /* see if they want to change it */
645 printf("\n Change this setting [no]? ");
647 (void) fetch_buffer(response, sizeof(response), stdin);
648 for (s = response; isspace((unsigned char)*s); s++)
650 if ((*s == 'y') || (*s == 'Y')) {
651 if (!strcmp(cp, "TRUE"))
652 strcpy(value, "FALSE");
654 strcpy(value, "TRUE");
655 if (ldap_modify_s(ld, who, mods)) {
660 printf(" Setting has been changed\n");
661 ldap_uncache_entry( ld, who );
665 printf(" Setting has not been changed\n");
671 print_mod_list( int group )
673 register int i, j = 1;
676 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
677 if (attrlist[i].flags & ATTR_FLAG_GROUP_MOD) {
678 printf(" %2d -> %s\n", j, attrlist[i].output_string);
683 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
684 if (attrlist[i].flags & ATTR_FLAG_PERSON_MOD) {
685 printf(" %2d -> %s\n", j, attrlist[i].output_string);
690 printf(" ? -> Print this list\n\n");
691 printf(" Press the RETURN key without typing a number to quit.\n");
694 printf(" To add nicknames, send mail to x500-nicknames@umich.edu\n");
699 perform_action( char *choice, char *dn, int group )
702 register int i, j = 1;
704 selection = atoi(choice);
706 printf("\n Choices are:\n");
707 printf(" -----------------------\n");
708 print_mod_list(group);
714 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
715 if (attrlist[i].flags & ATTR_FLAG_GROUP_MOD) {
722 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
723 if (attrlist[i].flags & ATTR_FLAG_PERSON_MOD) {
731 if (attrlist[i].quipu_name == NULL) {
732 printf("\n Choices are:\n");
733 printf(" -----------------------\n");
734 print_mod_list(group);
738 (*attrlist[i].mod_func)(dn, i);
745 char *rvalue, label[MED_BUF_SIZE], url[MED_BUF_SIZE];
748 printf(" First, enter the URL. (Example: http://www.us.itd.umich.edu/users/).\n");
749 printf(" The URL may be up to %d characters long.\n", MED_BUF_SIZE);
754 (void) fetch_buffer(url, sizeof(url), stdin);
757 if (check_URL(url) == 0)
759 printf(" A URL may not have any spaces or tabs in it. Please re-enter your URL.\n\n");
762 printf("\n Now please enter a descriptive label for this URL\n");
766 (void) fetch_buffer(label, sizeof(label), stdin);
767 } while (label[0] == '\0');
768 rvalue = (char *) Malloc((unsigned) (strlen(url) + 2 + strlen(label)));
769 sprintf(rvalue, "%s %s", url, label);
770 return((char *) rvalue);
774 check_URL( char *url )
778 for (cp = url; *cp != '\n' && *cp != '\0'; cp++) {
779 if (isspace((unsigned char)*cp))
789 mod_perror( LDAP *ld )
791 int ld_errno = LDAP_SUCCESS;
792 char *ld_errtext = NULL;
794 ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ld_errno );
796 if( ld_errno != LDAP_SUCCESS ) {
797 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_errtext );
800 fprintf( stderr, " modify failed: %s (%d)\n",
801 ldap_err2string( ld_errno ), ld_errno );
803 if( ld_errtext != NULL ) {
804 fprintf( stderr, " additional information: %s\n",
808 fprintf( stderr, " Please try again later.\n" );