2 * Copyright (c) 1991,1993 Regents of the University of Michigan.
5 * Redistribution and use in source and binary forms are permitted
6 * provided that this notice is preserved and that due credit is given
7 * to the University of Michigan at Ann Arbor. The name of the University
8 * may not be used to endorse or promote products derived from this
9 * software without specific prior written permission. This software
10 * is provided ``as is'' without express or implied warranty.
17 #include <ac/stdlib.h>
20 #include <ac/string.h>
27 static char *get_URL( void );
28 static int check_URL( char *url );
34 LDAPMessage *mp; /* returned from find() */
35 char *dn; /* distinguished name */
36 char **rdns; /* for fiddling with the DN */
37 char name[MED_BUF_SIZE]; /* entry to modify */
38 int displayed_choices = 0;
39 static char ans[SMALL_BUF_SIZE]; /* for holding user input */
41 static char printed_warning = 0; /* for use with the */
42 struct attribute no_batch_update_attr;
45 int is_a_group; /* TRUE if it is; FALSE otherwise */
49 printf("->modify(%s)\n", who);
52 * Require them to bind first if the are modifying a group.
54 if (bind_status == UD_NOT_BOUND) {
55 if (auth((char *) NULL, 1) < 0)
60 * First, decide what entry we are going to modify. If the
61 * user has not included a name on the modify command line,
62 * we will use the person who was last looked up with a find
63 * command. If there is no value there either, we don't know
66 * Once we know who to modify, be sure that they exist, and
71 printf(" Enter the name of the person or\n");
72 printf(" group whose entry you want to modify: ");
75 printf(" Modify whose entry? ");
77 fetch_buffer(name, sizeof(name), stdin);
83 if ((mp = find(who, TRUE)) == NULL) {
84 (void) ldap_msgfree(mp);
85 printf(" Could not locate \"%s\" in the Directory.\n", who);
88 dn = ldap_get_dn(ld, ldap_first_entry(ld, mp));
89 rdns = ldap_explode_dn(dn, TRUE);
91 printf("\n Modifying Directory entry of \"%s\"\n", *rdns);
95 * If verbose mode is turned on and the user has not set a value
96 * for noBatchUpdates, warn them that what they are about to do
97 * may be overwritten automatically by that Stinkbug.
99 no_batch_update_attr.quipu_name = "noBatchUpdates";
100 (void) fetch_boolean_value(dn, no_batch_update_attr);
102 ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ld_errno);
104 if (verbose && !printed_warning && (ld_errno == LDAP_NO_SUCH_ATTRIBUTE)) {
106 printf("\n WARNING!\n");
107 printf(" You are about to make a modification to an LDAP entry\n");
108 printf(" that has its \"automatic updates\" field set to ON.\n");
109 printf(" This means that the entry will be automatically updated\n");
110 printf(" each month from official University sources like the\n");
111 printf(" Personnel Office. With \"automatic updates\" set to ON,\n");
112 printf(" the following fields will be overwritten each month:\n");
113 printf(" Title, home address and phone,\n");
114 printf(" business address and phone\n");
115 printf(" If you modify any of these fields, you may want to change\n");
116 printf(" the \"automatic updates\" field to OFF so that your\n");
117 printf(" changes will not be overwritten. You may change this\n");
118 printf(" setting by choosing \"u\" at the \"Modify what?\" prompt\n");
123 * Current values for user 'who' are being held in 'mp'. We
124 * should parse up that buffer and fill in the Entry structure.
125 * Once we're done with that, we can find out which fields the
126 * user would like to modify.
129 is_a_group = isgroup();
130 (void) ldap_msgfree(mp);
131 printf(" You now need to specify what field you'd like to modify.\n");
133 if ( verbose || !displayed_choices ) {
134 printf("\n Choices are:\n");
135 printf(" -----------------------\n");
136 print_mod_list(is_a_group);
137 printf("\n Pressing Return will cancel the process.\n");
138 displayed_choices = 1;
140 printf("\n Modify what? ");
142 fetch_buffer(ans, sizeof(ans), stdin);
145 perform_action(ans, dn, is_a_group);
146 if ((mp = find(*rdns, TRUE)) == NULL)
149 (void) ldap_msgfree(mp);
152 ldap_value_free(rdns);
156 /* generic routine for changing any field */
159 char *who, /* DN of entry we are changing */
160 int attr_idx /* attribute to change */
163 struct attribute attr = Entry.attrs[attr_to_index(attrlist[attr_idx].quipu_name)];
165 #define IS_MOD(x) (!strncasecmp(resp, (x), strlen(resp)))
167 static char buf[MED_BUF_SIZE]; /* for printing things */
168 static char resp[SMALL_BUF_SIZE]; /* for user input */
169 char *prompt, *prompt2, *more;
170 register int i; /* for looping thru values */
172 static LDAPMod *mods[2] = { &mod }; /* passed to ldap_modify */
173 static char *values[MAX_VALUES]; /* passed to ldap_modify */
177 printf("->change_field(%x, %s)\n", attr, who);
180 * If there is no current value associated with the attribute,
181 * then this is the easy case. Collect one (or more) attributes
182 * from the user, and then call ldap_modify_s() to write the changes
183 * to the LDAP server.
185 for (i = 0; i < MAX_VALUES; i++)
187 if (attr.values == (char **) NULL) {
188 printf("\n No current \"%s\"\n", attr.output_string);
189 values[0] = get_value(attr.quipu_name, "Enter a value");
190 if ( values[0] == NULL )
192 mod.mod_op = LDAP_MOD_REPLACE;
193 mod.mod_type = attr.quipu_name;
194 mod.mod_values = values;
195 for (i = 1; i < MAX_VALUES; i++) {
196 printf(" Do you wish to add an additional value? ");
198 fetch_buffer(resp, sizeof(resp), stdin);
199 if ((resp[0] == 'y') || (resp[0] == 'Y'))
200 values[i] = get_value(attr.quipu_name, "Enter an additional value");
205 if (debug & D_MODIFY) {
206 printf(" ld = 0x%x\n", ld);
207 printf(" who = [%s]\n", who);
208 printf(" mods[0]->mod_op = %1d\n", mods[0]->mod_op);
209 printf(" mods[0]->mod_type = %s\n", mods[0]->mod_type);
210 for (i = 0; mods[0]->mod_values[i] != NULL; i++)
211 printf(" mods[0]->mod_values[%1d] = %s\n", i, mods[0]->mod_values[i]);
214 if (ldap_modify_s(ld, who, mods)) {
219 printf(" Modification of '%s' complete.\n", attr.output_string);
220 ldap_uncache_entry( ld, who );
221 for (i--; i > 0; i--)
222 (void) Free(values[i]);
225 * There are values for this attribute already. In this case,
226 * we want to allow the user to delete all existing values,
227 * add additional values to the ones there already, or just
228 * delete some of the values already present. DIXIE does not
229 * handle modifications where the attribute occurs on the LHS
230 * more than once. So to delete entries and add entries, we
231 * need to call ldap_modify() twice.
235 * If the attribute holds values which are DNs, print them
236 * in a most pleasant way.
238 sprintf(buf, "%s: ", attr.output_string);
239 if (!strcmp(attr.quipu_name, "owner"))
240 (void) print_DN(attr);
242 (void) print_values(attr);
245 printf(" You may now:\n");
246 printf(" Add additional values to the existing ones, OR\n");
247 printf(" Clear all values listed above, OR\n");
248 printf(" Delete one of the values listed above, OR\n");
249 printf(" Replace all of the values above with new ones.\n");
251 printf("\n Add, Clear, Delete, or Replace? ");
253 fetch_buffer(resp, sizeof(resp), stdin);
256 * Bail if they just hit the RETURN key.
258 if (resp[0] == '\0') {
260 printf("\n No changes made.\n");
265 * If the want to clear the values, just do it.
267 mod.mod_type = attr.quipu_name;
268 mod.mod_values = values;
269 if (IS_MOD("clear")) {
270 mod.mod_op = LDAP_MOD_DELETE;
271 mod.mod_values = NULL;
272 if ( verbose && !confirm_action( "All existing values will be removed." )) {
273 printf(" Modification halted.\n");
277 if (debug & D_MODIFY) {
278 printf("Clearing attribute '%s'\n", attr.quipu_name);
279 printf("who = [%s]\n", who);
280 printf("mod = [%d] [%s] [%x]\n", mod.mod_op,
281 mod.mod_type, mod.mod_values);
284 if (ldap_modify_s(ld, who, mods)) {
289 printf(" '%s' has been cleared.\n", attr.output_string);
290 ldap_uncache_entry( ld, who );
295 prompt = "Enter the value you wish to add";
296 more = " Add an additional value? ";
297 prompt2 = "Enter another value you wish to add";
298 mod.mod_op = LDAP_MOD_ADD;
300 else if (IS_MOD("delete")) {
301 prompt = "Enter the value you wish to delete";
302 more = " Delete an additional value? ";
303 prompt2 = "Enter another value you wish to delete";
304 mod.mod_op = LDAP_MOD_DELETE;
306 else if (IS_MOD("replace")) {
307 prompt = "Enter the new value";
308 more = " Add an additional value? ";
309 prompt2 = "Enter another value you wish to add";
310 mod.mod_op = LDAP_MOD_REPLACE;
311 if ( verbose && !confirm_action( "All existing values will be overwritten with the new values you are about to enter." )) {
312 printf(" Modification halted.\n");
318 printf(" No changes made.\n");
322 values[0] = get_value(attr.quipu_name, prompt);
323 for (i = 1; i < MAX_VALUES; i++) {
326 fetch_buffer(resp, sizeof(resp), stdin);
327 if ((resp[0] == 'y') || (resp[0] == 'Y'))
328 values[i] = get_value(attr.quipu_name, prompt2);
333 /* if the first value in the value-array is NULL, bail */
334 if (values[0] == NULL) {
336 printf(" No modification made.\n");
340 if (debug & D_MODIFY) {
341 printf(" ld = 0x%x\n", ld);
342 printf(" who = [%s]\n", who);
343 printf(" mods[0]->mod_op = %1d\n", mods[0]->mod_op);
344 printf(" mods[0]->mod_type = %s\n", mods[0]->mod_type);
345 for (i = 0; mods[0]->mod_values[i] != NULL; i++)
346 printf(" mods[0]->mod_values[%1d] = %s\n", i, mods[0]->mod_values[i]);
349 if (ldap_modify_s(ld, who, mods)) {
354 printf(" Modifications to '%s' complete.\n", attr.output_string);
355 ldap_uncache_entry( ld, who );
356 for (i--; i > 0; i--)
357 (void) Free(values[i]);
363 * These are used to size the buffers we use when collecting values that
364 * can cross more than one line.
368 #define MAX_DESC_LINES 24
369 #define INTL_ADDR_LIMIT 30
372 get_value( char *id, char *prompt )
374 char *cp; /* for the Malloc() */
375 int count; /* line # of new value -- if multiline */
376 int multiline = 0; /* 1 if this value is multiline */
377 static char line[LINE_SIZE]; /* raw line from user */
378 static char buffer[MAX_DESC_LINES * (LINE_SIZE+2)]; /* holds ALL of the
382 printf("->get_value(%s, %s)\n", id, prompt);
384 /* if this is a URL, have another routine handle this */
385 if (!strcmp(id, "labeledURL"))
389 * To start with, we have one line of input from the user.
391 * Addresses and multiline description can span multiple lines.
392 * Other attributes may not.
395 (void) memset(buffer, 0, sizeof(buffer));
397 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress") || !strcmp(id, "multiLineDescription") || !strcmp(id, "vacationMessage"))
399 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress"))
402 printf("\n %s:\n", prompt);
407 printf(" %1d: ", count);
411 fetch_buffer(line, sizeof(line), stdin);
417 * Screen out dangerous e-mail addresses of the form:
421 * and addresses that have no '@' symbol at all.
423 if (!strcmp(id, "mail")) {
427 /* if this is a group, don't worry */
431 /* if this address is not @umich.edu, don't worry */
432 /* ...unless there is no '@' at all! */
434 if ((tmp2 = strrchr(tmp, '@')) == NULL) {
436 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 );
442 if (strcasecmp(tmp2, "umich.edu"))
445 /* if not of the form uid@umich.edu, don't worry */
446 if ((i = attr_to_index("uid")) < 0)
448 if (strcasecmp(tmp, *(Entry.attrs[i].values)))
451 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);
455 printf(" Please enter a legal e-mail address (or press RETURN to stop)\n");
462 * If the attribute which we are gathering is a "owner"
463 * then we should lookup the name. The user is going to
464 * either have to change the search base before doing the
465 * modify, or the person is going to have to be within the
466 * scope of the current search base, or they will need to
469 if (!strcmp(id, "owner")) {
470 LDAPMessage *lmp, *elmp;
473 lmp = find(line, FALSE);
474 if (lmp == (LDAPMessage *) NULL) {
475 printf(" Could not find \"%s\" in the Directory\n", line);
477 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);
480 elmp = ldap_first_entry(ld, lmp);
481 if (lmp == (LDAPMessage *) NULL) {
482 ldap_perror(ld, "ldap_first_entry");
485 tmp = ldap_get_dn(ld, elmp);
488 (void) ldap_msgfree(lmp);
492 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress")) {
493 if (strlen(line) > INTL_ADDR_LIMIT) {
494 printf(" The international standard for addresses only allows for 30-character lines\n");
495 printf(" Please re-enter your last line.\n");
501 * Separate lines of multiline attribute values with
502 * dollar signs. Copy this line into the buffer we
503 * use to collect up all of the user-supplied input
504 * lines. If this is not a multiline attribute, we
508 (void) strcat(buffer, " $ ");
509 (void) strcat(buffer, line);
512 if ((count > MAX_LINES) && (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress"))) {
513 printf(" The international standard for addresses only allows for six lines\n");
517 if ((count > MAX_DESC_LINES) && !strcmp(id, "multiLineDescription")) {
518 printf(" We only allow %d lines of description\n", MAX_DESC_LINES);
523 if (buffer[0] == '\0')
526 if (debug & D_MODIFY)
527 printf(" Value is [%s]\n", buffer);
529 cp = (char *) Malloc((unsigned) (strlen(buffer) + 1));
536 char *who, /* DN of entry we are changing */
537 int attr_idx /* boolean attribute to change */
540 struct attribute attr = Entry.attrs[attr_to_index(attrlist[attr_idx].quipu_name)];
543 static char response[16];
544 static char *newsetting[2] = { NULL, NULL };
545 LDAPMod mod, *mods[2];
549 printf("->set_boolean(%s, %s)\n", who, attr.quipu_name);
552 mods[1] = (LDAPMod *) NULL;
553 mod.mod_op = LDAP_MOD_REPLACE;
554 mod.mod_type = attr.quipu_name;
555 mod.mod_values = newsetting;
557 /* fetch the current setting */
558 if ((cp = fetch_boolean_value(who, attr)) == NULL)
560 if (!strcmp(cp, "TRUE"))
561 newsetting[0] = "FALSE";
562 else if (!strcmp(cp, "FALSE"))
563 newsetting[0] = "TRUE";
565 printf(" This field needs to be set to either TRUE or to FALSE.\n");
566 printf(" \"%s\" is not a legal value. Please set this field to either TRUE or to FALSE.\n", cp);
567 newsetting[0] = "FALSE";
570 /* see if they want to change it */
572 printf(" The current value of this field is %s.\n", cp);
573 printf(" Should I change the value of this field to %s?\n",
575 printf(" Please enter Y for yes, N for no, or RETURN to cancel: ");
577 (void) fetch_buffer(response, sizeof(response), stdin);
578 for (s = response; isspace((unsigned char)*s); s++)
580 if ((*s == 'y') || (*s == 'Y')) {
581 if (ldap_modify_s(ld, who, mods)) {
586 printf(" Setting has been changed\n");
587 ldap_uncache_entry(ld, who);
591 printf(" Setting has not been changed\n");
597 set_updates( char *who, int dummy )
600 static char response[16];
601 static char value[6];
602 static char *newsetting[2] = { value, NULL };
603 LDAPMod mod, *mods[2];
604 struct attribute attr;
608 printf("->set_updates(%s)\n", who);
611 mods[1] = (LDAPMod *) NULL;
612 mod.mod_op = LDAP_MOD_REPLACE;
613 mod.mod_type = "noBatchUpdates";
614 mod.mod_values = newsetting;
615 /* explain what the implications are */
617 printf("\n By default, updates that are received from the Personnel\n");
618 printf(" Office and the Office of the Registrar are applied to all\n");
619 printf(" entries in the LDAP database each month. Sometimes this\n");
620 printf(" feature is undesirable. For example, if you maintain your\n");
621 printf(" entry in the LDAP database manually, you may not want to\n");
622 printf(" have these updates applied to your entry, possibly overwriting\n");
623 printf(" correct information with out-dated information.\n\n");
626 /* fetch the current setting */
627 attr.quipu_name = "noBatchUpdates";
628 if ((cp = fetch_boolean_value(who, attr)) == NULL)
630 if (!strcmp(cp, "TRUE"))
631 printf(" Automatic updates are currently turned OFF\n");
632 else if (!strcmp(cp, "FALSE"))
633 printf(" Automatic updates are currently turned ON\n");
635 fprintf(stderr, " Unknown update flag -> [%s]\n", cp);
639 /* see if they want to change it */
640 printf("\n Change this setting [no]? ");
642 (void) fetch_buffer(response, sizeof(response), stdin);
643 for (s = response; isspace((unsigned char)*s); s++)
645 if ((*s == 'y') || (*s == 'Y')) {
646 if (!strcmp(cp, "TRUE"))
647 strcpy(value, "FALSE");
649 strcpy(value, "TRUE");
650 if (ldap_modify_s(ld, who, mods)) {
655 printf(" Setting has been changed\n");
656 ldap_uncache_entry( ld, who );
660 printf(" Setting has not been changed\n");
666 print_mod_list( int group )
668 register int i, j = 1;
671 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
672 if (attrlist[i].flags & ATTR_FLAG_GROUP_MOD) {
673 printf(" %2d -> %s\n", j, attrlist[i].output_string);
678 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
679 if (attrlist[i].flags & ATTR_FLAG_PERSON_MOD) {
680 printf(" %2d -> %s\n", j, attrlist[i].output_string);
685 printf(" ? -> Print this list\n\n");
686 printf(" Press the RETURN key without typing a number to quit.\n");
689 printf(" To add nicknames, send mail to x500-nicknames@umich.edu\n");
694 perform_action( char *choice, char *dn, int group )
697 register int i, j = 1;
699 selection = atoi(choice);
701 printf("\n Choices are:\n");
702 printf(" -----------------------\n");
703 print_mod_list(group);
709 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
710 if (attrlist[i].flags & ATTR_FLAG_GROUP_MOD) {
717 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
718 if (attrlist[i].flags & ATTR_FLAG_PERSON_MOD) {
726 if (attrlist[i].quipu_name == NULL) {
727 printf("\n Choices are:\n");
728 printf(" -----------------------\n");
729 print_mod_list(group);
733 (*attrlist[i].mod_func)(dn, i);
740 char *rvalue, label[MED_BUF_SIZE], url[MED_BUF_SIZE];
743 printf(" First, enter the URL. (Example: http://www.us.itd.umich.edu/users/).\n");
744 printf(" The URL may be up to %d characters long.\n", MED_BUF_SIZE);
749 (void) fetch_buffer(url, sizeof(url), stdin);
752 if (check_URL(url) == 0)
754 printf(" A URL may not have any spaces or tabs in it. Please re-enter your URL.\n\n");
757 printf("\n Now please enter a descriptive label for this URL\n");
761 (void) fetch_buffer(label, sizeof(label), stdin);
762 } while (label[0] == '\0');
763 rvalue = (char *) Malloc((unsigned) (strlen(url) + 2 + strlen(label)));
764 sprintf(rvalue, "%s %s", url, label);
765 return((char *) rvalue);
769 check_URL( char *url )
773 for (cp = url; *cp != '\n' && *cp != '\0'; cp++) {
774 if (isspace((unsigned char)*cp))
784 mod_perror( LDAP *ld )
789 ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ld_errno);
792 if (( ld == NULL ) || ( ld_errno != LDAP_UNAVAILABLE &&
793 ld_errno != LDAP_UNWILLING_TO_PERFORM ))
795 ldap_perror( ld, "modify" );
799 fprintf( stderr, "\n modify: failed because part of the online directory is not able\n" );
800 fprintf( stderr, " to be modified right now" );
801 if ( ld_errno == LDAP_UNAVAILABLE ) {
802 fprintf( stderr, " or is temporarily unavailable" );
804 fprintf( stderr, ".\n Please try again later.\n" );