3 * Copyright (c) 1991,1993 Regents of the University of Michigan.
6 * Redistribution and use in source and binary forms are permitted
7 * provided that this notice is preserved and that due credit is given
8 * to the University of Michigan at Ann Arbor. The name of the University
9 * may not be used to endorse or promote products derived from this
10 * software without specific prior written permission. This software
11 * is provided ``as is'' without express or implied warranty.
18 #include <ac/stdlib.h>
21 #include <ac/string.h>
28 static char *get_URL( void );
29 static int check_URL( char *url );
35 LDAPMessage *mp; /* returned from find() */
36 char *dn; /* distinguished name */
37 char **rdns; /* for fiddling with the DN */
38 char name[MED_BUF_SIZE]; /* entry to modify */
39 int displayed_choices = 0;
40 static char ans[SMALL_BUF_SIZE]; /* for holding user input */
42 static char printed_warning = 0; /* for use with the */
43 struct attribute no_batch_update_attr;
46 int is_a_group; /* TRUE if it is; FALSE otherwise */
50 printf("->modify(%s)\n", who);
53 * Require them to bind first if the are modifying a group.
55 if (bind_status == UD_NOT_BOUND) {
56 if (auth((char *) NULL, 1) < 0)
61 * First, decide what entry we are going to modify. If the
62 * user has not included a name on the modify command line,
63 * we will use the person who was last looked up with a find
64 * command. If there is no value there either, we don't know
67 * Once we know who to modify, be sure that they exist, and
72 printf(" Enter the name of the person or\n");
73 printf(" group whose entry you want to modify: ");
76 printf(" Modify whose entry? ");
78 fetch_buffer(name, sizeof(name), stdin);
84 if ((mp = find(who, TRUE)) == NULL) {
85 (void) ldap_msgfree(mp);
86 printf(" Could not locate \"%s\" in the Directory.\n", who);
89 dn = ldap_get_dn(ld, ldap_first_entry(ld, mp));
90 rdns = ldap_explode_dn(dn, TRUE);
92 printf("\n Modifying Directory entry of \"%s\"\n", *rdns);
96 * If verbose mode is turned on and the user has not set a value
97 * for noBatchUpdates, warn them that what they are about to do
98 * may be overwritten automatically by that Stinkbug.
100 no_batch_update_attr.quipu_name = "noBatchUpdates";
101 (void) fetch_boolean_value(dn, no_batch_update_attr);
103 ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ld_errno);
105 if (verbose && !printed_warning && (ld_errno == LDAP_NO_SUCH_ATTRIBUTE)) {
107 printf("\n WARNING!\n");
108 printf(" You are about to make a modification to an LDAP entry\n");
109 printf(" that has its \"automatic updates\" field set to ON.\n");
110 printf(" This means that the entry will be automatically updated\n");
111 printf(" each month from official University sources like the\n");
112 printf(" Personnel Office. With \"automatic updates\" set to ON,\n");
113 printf(" the following fields will be overwritten each month:\n");
114 printf(" Title, home address and phone,\n");
115 printf(" business address and phone\n");
116 printf(" If you modify any of these fields, you may want to change\n");
117 printf(" the \"automatic updates\" field to OFF so that your\n");
118 printf(" changes will not be overwritten. You may change this\n");
119 printf(" setting by choosing \"u\" at the \"Modify what?\" prompt\n");
124 * Current values for user 'who' are being held in 'mp'. We
125 * should parse up that buffer and fill in the Entry structure.
126 * Once we're done with that, we can find out which fields the
127 * user would like to modify.
130 is_a_group = isgroup();
131 (void) ldap_msgfree(mp);
132 printf(" You now need to specify what field you'd like to modify.\n");
134 if ( verbose || !displayed_choices ) {
135 printf("\n Choices are:\n");
136 printf(" -----------------------\n");
137 print_mod_list(is_a_group);
138 printf("\n Pressing Return will cancel the process.\n");
139 displayed_choices = 1;
141 printf("\n Modify what? ");
143 fetch_buffer(ans, sizeof(ans), stdin);
146 perform_action(ans, dn, is_a_group);
147 if ((mp = find(*rdns, TRUE)) == NULL)
150 (void) ldap_msgfree(mp);
153 ldap_value_free(rdns);
157 /* generic routine for changing any field */
160 char *who, /* DN of entry we are changing */
161 int attr_idx /* attribute to change */
164 struct attribute attr = Entry.attrs[attr_to_index(attrlist[attr_idx].quipu_name)];
166 #define IS_MOD(x) (!strncasecmp(resp, (x), strlen(resp)))
168 static char buf[MED_BUF_SIZE]; /* for printing things */
169 static char resp[SMALL_BUF_SIZE]; /* for user input */
170 char *prompt, *prompt2, *more;
171 register int i; /* for looping thru values */
173 static LDAPMod *mods[2] = { &mod }; /* passed to ldap_modify */
174 static char *values[MAX_VALUES]; /* passed to ldap_modify */
178 printf("->change_field(%x, %s)\n", attr, who);
181 * If there is no current value associated with the attribute,
182 * then this is the easy case. Collect one (or more) attributes
183 * from the user, and then call ldap_modify_s() to write the changes
184 * to the LDAP server.
186 for (i = 0; i < MAX_VALUES; i++)
188 if (attr.values == (char **) NULL) {
189 printf("\n No current \"%s\"\n", attr.output_string);
190 values[0] = get_value(attr.quipu_name, "Enter a value");
191 if ( values[0] == NULL )
193 mod.mod_op = LDAP_MOD_REPLACE;
194 mod.mod_type = attr.quipu_name;
195 mod.mod_values = values;
196 for (i = 1; i < MAX_VALUES; i++) {
197 printf(" Do you wish to add an additional value? ");
199 fetch_buffer(resp, sizeof(resp), stdin);
200 if ((resp[0] == 'y') || (resp[0] == 'Y'))
201 values[i] = get_value(attr.quipu_name, "Enter an additional value");
206 if (debug & D_MODIFY) {
207 printf(" ld = 0x%x\n", ld);
208 printf(" who = [%s]\n", who);
209 printf(" mods[0]->mod_op = %1d\n", mods[0]->mod_op);
210 printf(" mods[0]->mod_type = %s\n", mods[0]->mod_type);
211 for (i = 0; mods[0]->mod_values[i] != NULL; i++)
212 printf(" mods[0]->mod_values[%1d] = %s\n", i, mods[0]->mod_values[i]);
215 if (ldap_modify_s(ld, who, mods)) {
220 printf(" Modification of '%s' complete.\n", attr.output_string);
221 ldap_uncache_entry( ld, who );
222 for (i--; i > 0; i--)
223 (void) Free(values[i]);
226 * There are values for this attribute already. In this case,
227 * we want to allow the user to delete all existing values,
228 * add additional values to the ones there already, or just
229 * delete some of the values already present. DIXIE does not
230 * handle modifications where the attribute occurs on the LHS
231 * more than once. So to delete entries and add entries, we
232 * need to call ldap_modify() twice.
236 * If the attribute holds values which are DNs, print them
237 * in a most pleasant way.
239 sprintf(buf, "%s: ", attr.output_string);
240 if (!strcmp(attr.quipu_name, "owner"))
241 (void) print_DN(attr);
243 (void) print_values(attr);
246 printf(" You may now:\n");
247 printf(" Add additional values to the existing ones, OR\n");
248 printf(" Clear all values listed above, OR\n");
249 printf(" Delete one of the values listed above, OR\n");
250 printf(" Replace all of the values above with new ones.\n");
252 printf("\n Add, Clear, Delete, or Replace? ");
254 fetch_buffer(resp, sizeof(resp), stdin);
257 * Bail if they just hit the RETURN key.
259 if (resp[0] == '\0') {
261 printf("\n No changes made.\n");
266 * If the want to clear the values, just do it.
268 mod.mod_type = attr.quipu_name;
269 mod.mod_values = values;
270 if (IS_MOD("clear")) {
271 mod.mod_op = LDAP_MOD_DELETE;
272 mod.mod_values = NULL;
273 if ( verbose && !confirm_action( "All existing values will be removed." )) {
274 printf(" Modification halted.\n");
278 if (debug & D_MODIFY) {
279 printf("Clearing attribute '%s'\n", attr.quipu_name);
280 printf("who = [%s]\n", who);
281 printf("mod = [%d] [%s] [%x]\n", mod.mod_op,
282 mod.mod_type, mod.mod_values);
285 if (ldap_modify_s(ld, who, mods)) {
290 printf(" '%s' has been cleared.\n", attr.output_string);
291 ldap_uncache_entry( ld, who );
296 prompt = "Enter the value you wish to add";
297 more = " Add an additional value? ";
298 prompt2 = "Enter another value you wish to add";
299 mod.mod_op = LDAP_MOD_ADD;
301 else if (IS_MOD("delete")) {
302 prompt = "Enter the value you wish to delete";
303 more = " Delete an additional value? ";
304 prompt2 = "Enter another value you wish to delete";
305 mod.mod_op = LDAP_MOD_DELETE;
307 else if (IS_MOD("replace")) {
308 prompt = "Enter the new value";
309 more = " Add an additional value? ";
310 prompt2 = "Enter another value you wish to add";
311 mod.mod_op = LDAP_MOD_REPLACE;
312 if ( verbose && !confirm_action( "All existing values will be overwritten with the new values you are about to enter." )) {
313 printf(" Modification halted.\n");
319 printf(" No changes made.\n");
323 values[0] = get_value(attr.quipu_name, prompt);
324 for (i = 1; i < MAX_VALUES; i++) {
327 fetch_buffer(resp, sizeof(resp), stdin);
328 if ((resp[0] == 'y') || (resp[0] == 'Y'))
329 values[i] = get_value(attr.quipu_name, prompt2);
334 /* if the first value in the value-array is NULL, bail */
335 if (values[0] == NULL) {
337 printf(" No modification made.\n");
341 if (debug & D_MODIFY) {
342 printf(" ld = 0x%x\n", ld);
343 printf(" who = [%s]\n", who);
344 printf(" mods[0]->mod_op = %1d\n", mods[0]->mod_op);
345 printf(" mods[0]->mod_type = %s\n", mods[0]->mod_type);
346 for (i = 0; mods[0]->mod_values[i] != NULL; i++)
347 printf(" mods[0]->mod_values[%1d] = %s\n", i, mods[0]->mod_values[i]);
350 if (ldap_modify_s(ld, who, mods)) {
355 printf(" Modifications to '%s' complete.\n", attr.output_string);
356 ldap_uncache_entry( ld, who );
357 for (i--; i > 0; i--)
358 (void) Free(values[i]);
364 * These are used to size the buffers we use when collecting values that
365 * can cross more than one line.
369 #define MAX_DESC_LINES 24
370 #define INTL_ADDR_LIMIT 30
373 get_value( char *id, char *prompt )
375 char *cp; /* for the Malloc() */
376 int count; /* line # of new value -- if multiline */
377 int multiline = 0; /* 1 if this value is multiline */
378 static char line[LINE_SIZE]; /* raw line from user */
379 static char buffer[MAX_DESC_LINES * (LINE_SIZE+2)]; /* holds ALL of the
383 printf("->get_value(%s, %s)\n", id, prompt);
385 /* if this is a URL, have another routine handle this */
386 if (!strcmp(id, "labeledURL"))
390 * To start with, we have one line of input from the user.
392 * Addresses and multiline description can span multiple lines.
393 * Other attributes may not.
396 (void) memset(buffer, 0, sizeof(buffer));
398 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress") || !strcmp(id, "multiLineDescription") || !strcmp(id, "vacationMessage"))
400 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress"))
403 printf("\n %s:\n", prompt);
408 printf(" %1d: ", count);
412 fetch_buffer(line, sizeof(line), stdin);
418 * Screen out dangerous e-mail addresses of the form:
422 * and addresses that have no '@' symbol at all.
424 if (!strcmp(id, "mail")) {
428 /* if this is a group, don't worry */
432 /* if this address is not @umich.edu, don't worry */
433 /* ...unless there is no '@' at all! */
435 if ((tmp2 = strrchr(tmp, '@')) == NULL) {
437 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 );
443 if (strcasecmp(tmp2, "umich.edu"))
446 /* if not of the form uid@umich.edu, don't worry */
447 if ((i = attr_to_index("uid")) < 0)
449 if (strcasecmp(tmp, *(Entry.attrs[i].values)))
452 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);
456 printf(" Please enter a legal e-mail address (or press RETURN to stop)\n");
463 * If the attribute which we are gathering is a "owner"
464 * then we should lookup the name. The user is going to
465 * either have to change the search base before doing the
466 * modify, or the person is going to have to be within the
467 * scope of the current search base, or they will need to
470 if (!strcmp(id, "owner")) {
471 LDAPMessage *lmp, *elmp;
474 lmp = find(line, FALSE);
475 if (lmp == (LDAPMessage *) NULL) {
476 printf(" Could not find \"%s\" in the Directory\n", line);
478 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);
481 elmp = ldap_first_entry(ld, lmp);
482 if (lmp == (LDAPMessage *) NULL) {
483 ldap_perror(ld, "ldap_first_entry");
486 tmp = ldap_get_dn(ld, elmp);
489 (void) ldap_msgfree(lmp);
493 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress")) {
494 if (strlen(line) > INTL_ADDR_LIMIT) {
495 printf(" The international standard for addresses only allows for 30-character lines\n");
496 printf(" Please re-enter your last line.\n");
502 * Separate lines of multiline attribute values with
503 * dollar signs. Copy this line into the buffer we
504 * use to collect up all of the user-supplied input
505 * lines. If this is not a multiline attribute, we
509 (void) strcat(buffer, " $ ");
510 (void) strcat(buffer, line);
513 if ((count > MAX_LINES) && (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress"))) {
514 printf(" The international standard for addresses only allows for six lines\n");
518 if ((count > MAX_DESC_LINES) && !strcmp(id, "multiLineDescription")) {
519 printf(" We only allow %d lines of description\n", MAX_DESC_LINES);
524 if (buffer[0] == '\0')
527 if (debug & D_MODIFY)
528 printf(" Value is [%s]\n", buffer);
530 cp = (char *) Malloc((unsigned) (strlen(buffer) + 1));
537 char *who, /* DN of entry we are changing */
538 int attr_idx /* boolean attribute to change */
541 struct attribute attr = Entry.attrs[attr_to_index(attrlist[attr_idx].quipu_name)];
544 static char response[16];
545 static char *newsetting[2] = { NULL, NULL };
546 LDAPMod mod, *mods[2];
550 printf("->set_boolean(%s, %s)\n", who, attr.quipu_name);
553 mods[1] = (LDAPMod *) NULL;
554 mod.mod_op = LDAP_MOD_REPLACE;
555 mod.mod_type = attr.quipu_name;
556 mod.mod_values = newsetting;
558 /* fetch the current setting */
559 if ((cp = fetch_boolean_value(who, attr)) == NULL)
561 if (!strcmp(cp, "TRUE"))
562 newsetting[0] = "FALSE";
563 else if (!strcmp(cp, "FALSE"))
564 newsetting[0] = "TRUE";
566 printf(" This field needs to be set to either TRUE or to FALSE.\n");
567 printf(" \"%s\" is not a legal value. Please set this field to either TRUE or to FALSE.\n", cp);
568 newsetting[0] = "FALSE";
571 /* see if they want to change it */
573 printf(" The current value of this field is %s.\n", cp);
574 printf(" Should I change the value of this field to %s?\n",
576 printf(" Please enter Y for yes, N for no, or RETURN to cancel: ");
578 (void) fetch_buffer(response, sizeof(response), stdin);
579 for (s = response; isspace((unsigned char)*s); s++)
581 if ((*s == 'y') || (*s == 'Y')) {
582 if (ldap_modify_s(ld, who, mods)) {
587 printf(" Setting has been changed\n");
588 ldap_uncache_entry(ld, who);
592 printf(" Setting has not been changed\n");
598 set_updates( char *who, int dummy )
601 static char response[16];
602 static char value[6];
603 static char *newsetting[2] = { value, NULL };
604 LDAPMod mod, *mods[2];
605 struct attribute attr;
609 printf("->set_updates(%s)\n", who);
612 mods[1] = (LDAPMod *) NULL;
613 mod.mod_op = LDAP_MOD_REPLACE;
614 mod.mod_type = "noBatchUpdates";
615 mod.mod_values = newsetting;
616 /* explain what the implications are */
618 printf("\n By default, updates that are received from the Personnel\n");
619 printf(" Office and the Office of the Registrar are applied to all\n");
620 printf(" entries in the LDAP database each month. Sometimes this\n");
621 printf(" feature is undesirable. For example, if you maintain your\n");
622 printf(" entry in the LDAP database manually, you may not want to\n");
623 printf(" have these updates applied to your entry, possibly overwriting\n");
624 printf(" correct information with out-dated information.\n\n");
627 /* fetch the current setting */
628 attr.quipu_name = "noBatchUpdates";
629 if ((cp = fetch_boolean_value(who, attr)) == NULL)
631 if (!strcmp(cp, "TRUE"))
632 printf(" Automatic updates are currently turned OFF\n");
633 else if (!strcmp(cp, "FALSE"))
634 printf(" Automatic updates are currently turned ON\n");
636 fprintf(stderr, " Unknown update flag -> [%s]\n", cp);
640 /* see if they want to change it */
641 printf("\n Change this setting [no]? ");
643 (void) fetch_buffer(response, sizeof(response), stdin);
644 for (s = response; isspace((unsigned char)*s); s++)
646 if ((*s == 'y') || (*s == 'Y')) {
647 if (!strcmp(cp, "TRUE"))
648 strcpy(value, "FALSE");
650 strcpy(value, "TRUE");
651 if (ldap_modify_s(ld, who, mods)) {
656 printf(" Setting has been changed\n");
657 ldap_uncache_entry( ld, who );
661 printf(" Setting has not been changed\n");
667 print_mod_list( int group )
669 register int i, j = 1;
672 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
673 if (attrlist[i].flags & ATTR_FLAG_GROUP_MOD) {
674 printf(" %2d -> %s\n", j, attrlist[i].output_string);
679 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
680 if (attrlist[i].flags & ATTR_FLAG_PERSON_MOD) {
681 printf(" %2d -> %s\n", j, attrlist[i].output_string);
686 printf(" ? -> Print this list\n\n");
687 printf(" Press the RETURN key without typing a number to quit.\n");
690 printf(" To add nicknames, send mail to x500-nicknames@umich.edu\n");
695 perform_action( char *choice, char *dn, int group )
698 register int i, j = 1;
700 selection = atoi(choice);
702 printf("\n Choices are:\n");
703 printf(" -----------------------\n");
704 print_mod_list(group);
710 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
711 if (attrlist[i].flags & ATTR_FLAG_GROUP_MOD) {
718 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
719 if (attrlist[i].flags & ATTR_FLAG_PERSON_MOD) {
727 if (attrlist[i].quipu_name == NULL) {
728 printf("\n Choices are:\n");
729 printf(" -----------------------\n");
730 print_mod_list(group);
734 (*attrlist[i].mod_func)(dn, i);
741 char *rvalue, label[MED_BUF_SIZE], url[MED_BUF_SIZE];
744 printf(" First, enter the URL. (Example: http://www.us.itd.umich.edu/users/).\n");
745 printf(" The URL may be up to %d characters long.\n", MED_BUF_SIZE);
750 (void) fetch_buffer(url, sizeof(url), stdin);
753 if (check_URL(url) == 0)
755 printf(" A URL may not have any spaces or tabs in it. Please re-enter your URL.\n\n");
758 printf("\n Now please enter a descriptive label for this URL\n");
762 (void) fetch_buffer(label, sizeof(label), stdin);
763 } while (label[0] == '\0');
764 rvalue = (char *) Malloc((unsigned) (strlen(url) + 2 + strlen(label)));
765 sprintf(rvalue, "%s %s", url, label);
766 return((char *) rvalue);
770 check_URL( char *url )
774 for (cp = url; *cp != '\n' && *cp != '\0'; cp++) {
775 if (isspace((unsigned char)*cp))
785 mod_perror( LDAP *ld )
790 ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ld_errno);
793 if (( ld == NULL ) || ( ld_errno != LDAP_UNAVAILABLE &&
794 ld_errno != LDAP_UNWILLING_TO_PERFORM ))
796 ldap_perror( ld, "modify" );
800 fprintf( stderr, "\n modify: failed because part of the online directory is not able\n" );
801 fprintf( stderr, " to be modified right now" );
802 if ( ld_errno == LDAP_UNAVAILABLE ) {
803 fprintf( stderr, " or is temporarily unavailable" );
805 fprintf( stderr, ".\n Please try again later.\n" );