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.
19 #include <ac/string.h>
26 static char *get_URL( void );
27 static int check_URL( char *url );
33 LDAPMessage *mp; /* returned from find() */
34 char *dn; /* distinguished name */
35 char **rdns; /* for fiddling with the DN */
36 char name[MED_BUF_SIZE]; /* entry to modify */
37 int displayed_choices = 0;
38 static char ans[SMALL_BUF_SIZE]; /* for holding user input */
40 static char printed_warning = 0; /* for use with the */
41 struct attribute no_batch_update_attr;
44 int is_a_group; /* TRUE if it is; FALSE otherwise */
48 printf("->modify(%s)\n", who);
51 * Require them to bind first if the are modifying a group.
53 if (bind_status == UD_NOT_BOUND) {
54 if (auth((char *) NULL, 1) < 0)
59 * First, decide what entry we are going to modify. If the
60 * user has not included a name on the modify command line,
61 * we will use the person who was last looked up with a find
62 * command. If there is no value there either, we don't know
65 * Once we know who to modify, be sure that they exist, and
70 printf(" Enter the name of the person or\n");
71 printf(" group whose entry you want to modify: ");
74 printf(" Modify whose entry? ");
76 fetch_buffer(name, sizeof(name), stdin);
82 if ((mp = find(who, TRUE)) == NULL) {
83 (void) ldap_msgfree(mp);
84 printf(" Could not locate \"%s\" in the Directory.\n", who);
87 dn = ldap_get_dn(ld, ldap_first_entry(ld, mp));
88 rdns = ldap_explode_dn(dn, TRUE);
90 printf("\n Modifying Directory entry of \"%s\"\n", *rdns);
94 * If verbose mode is turned on and the user has not set a value
95 * for noBatchUpdates, warn them that what they are about to do
96 * may be overwritten automatically by that Stinkbug.
98 no_batch_update_attr.quipu_name = "noBatchUpdates";
99 (void) fetch_boolean_value(dn, no_batch_update_attr);
101 ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ld_errno);
103 if (verbose && !printed_warning && (ld_errno == LDAP_NO_SUCH_ATTRIBUTE)) {
105 printf("\n WARNING!\n");
106 printf(" You are about to make a modification to an LDAP entry\n");
107 printf(" that has its \"automatic updates\" field set to ON.\n");
108 printf(" This means that the entry will be automatically updated\n");
109 printf(" each month from official University sources like the\n");
110 printf(" Personnel Office. With \"automatic updates\" set to ON,\n");
111 printf(" the following fields will be overwritten each month:\n");
112 printf(" Title, home address and phone,\n");
113 printf(" business address and phone\n");
114 printf(" If you modify any of these fields, you may want to change\n");
115 printf(" the \"automatic updates\" field to OFF so that your\n");
116 printf(" changes will not be overwritten. You may change this\n");
117 printf(" setting by choosing \"u\" at the \"Modify what?\" prompt\n");
122 * Current values for user 'who' are being held in 'mp'. We
123 * should parse up that buffer and fill in the Entry structure.
124 * Once we're done with that, we can find out which fields the
125 * user would like to modify.
128 is_a_group = isgroup();
129 (void) ldap_msgfree(mp);
130 printf(" You now need to specify what field you'd like to modify.\n");
132 if ( verbose || !displayed_choices ) {
133 printf("\n Choices are:\n");
134 printf(" -----------------------\n");
135 print_mod_list(is_a_group);
136 printf("\n Pressing Return will cancel the process.\n");
137 displayed_choices = 1;
139 printf("\n Modify what? ");
141 fetch_buffer(ans, sizeof(ans), stdin);
144 perform_action(ans, dn, is_a_group);
145 if ((mp = find(*rdns, TRUE)) == NULL)
148 (void) ldap_msgfree(mp);
151 ldap_value_free(rdns);
155 /* generic routine for changing any field */
158 char *who, /* DN of entry we are changing */
159 int attr_idx /* attribute to change */
162 struct attribute attr = Entry.attrs[attr_to_index(attrlist[attr_idx].quipu_name)];
164 #define IS_MOD(x) (!strncasecmp(resp, (x), strlen(resp)))
166 static char buf[MED_BUF_SIZE]; /* for printing things */
167 static char resp[SMALL_BUF_SIZE]; /* for user input */
168 char *prompt, *prompt2, *more;
169 register int i; /* for looping thru values */
171 static LDAPMod *mods[2] = { &mod }; /* passed to ldap_modify */
172 static char *values[MAX_VALUES]; /* passed to ldap_modify */
176 printf("->change_field(%x, %s)\n", attr, who);
179 * If there is no current value associated with the attribute,
180 * then this is the easy case. Collect one (or more) attributes
181 * from the user, and then call ldap_modify_s() to write the changes
182 * to the LDAP server.
184 for (i = 0; i < MAX_VALUES; i++)
186 if (attr.values == (char **) NULL) {
187 printf("\n No current \"%s\"\n", attr.output_string);
188 values[0] = get_value(attr.quipu_name, "Enter a value");
189 if ( values[0] == NULL )
191 mod.mod_op = LDAP_MOD_REPLACE;
192 mod.mod_type = attr.quipu_name;
193 mod.mod_values = values;
194 for (i = 1; i < MAX_VALUES; i++) {
195 printf(" Do you wish to add an additional value? ");
197 fetch_buffer(resp, sizeof(resp), stdin);
198 if ((resp[0] == 'y') || (resp[0] == 'Y'))
199 values[i] = get_value(attr.quipu_name, "Enter an additional value");
204 if (debug & D_MODIFY) {
205 printf(" ld = 0x%x\n", ld);
206 printf(" who = [%s]\n", who);
207 printf(" mods[0]->mod_op = %1d\n", mods[0]->mod_op);
208 printf(" mods[0]->mod_type = %s\n", mods[0]->mod_type);
209 for (i = 0; mods[0]->mod_values[i] != NULL; i++)
210 printf(" mods[0]->mod_values[%1d] = %s\n", i, mods[0]->mod_values[i]);
213 if (ldap_modify_s(ld, who, mods)) {
218 printf(" Modification of '%s' complete.\n", attr.output_string);
219 ldap_uncache_entry( ld, who );
220 for (i--; i > 0; i--)
221 (void) Free(values[i]);
224 * There are values for this attribute already. In this case,
225 * we want to allow the user to delete all existing values,
226 * add additional values to the ones there already, or just
227 * delete some of the values already present. DIXIE does not
228 * handle modifications where the attribute occurs on the LHS
229 * more than once. So to delete entries and add entries, we
230 * need to call ldap_modify() twice.
234 * If the attribute holds values which are DNs, print them
235 * in a most pleasant way.
237 sprintf(buf, "%s: ", attr.output_string);
238 if (!strcmp(attr.quipu_name, "owner"))
239 (void) print_DN(attr);
241 (void) print_values(attr);
244 printf(" You may now:\n");
245 printf(" Add additional values to the existing ones, OR\n");
246 printf(" Clear all values listed above, OR\n");
247 printf(" Delete one of the values listed above, OR\n");
248 printf(" Replace all of the values above with new ones.\n");
250 printf("\n Add, Clear, Delete, or Replace? ");
252 fetch_buffer(resp, sizeof(resp), stdin);
255 * Bail if they just hit the RETURN key.
257 if (resp[0] == '\0') {
259 printf("\n No changes made.\n");
264 * If the want to clear the values, just do it.
266 mod.mod_type = attr.quipu_name;
267 mod.mod_values = values;
268 if (IS_MOD("clear")) {
269 mod.mod_op = LDAP_MOD_DELETE;
270 mod.mod_values = NULL;
271 if ( verbose && !confirm_action( "All existing values will be removed." )) {
272 printf(" Modification halted.\n");
276 if (debug & D_MODIFY) {
277 printf("Clearing attribute '%s'\n", attr.quipu_name);
278 printf("who = [%s]\n", who);
279 printf("mod = [%d] [%s] [%x]\n", mod.mod_op,
280 mod.mod_type, mod.mod_values);
283 if (ldap_modify_s(ld, who, mods)) {
288 printf(" '%s' has been cleared.\n", attr.output_string);
289 ldap_uncache_entry( ld, who );
294 prompt = "Enter the value you wish to add";
295 more = " Add an additional value? ";
296 prompt2 = "Enter another value you wish to add";
297 mod.mod_op = LDAP_MOD_ADD;
299 else if (IS_MOD("delete")) {
300 prompt = "Enter the value you wish to delete";
301 more = " Delete an additional value? ";
302 prompt2 = "Enter another value you wish to delete";
303 mod.mod_op = LDAP_MOD_DELETE;
305 else if (IS_MOD("replace")) {
306 prompt = "Enter the new value";
307 more = " Add an additional value? ";
308 prompt2 = "Enter another value you wish to add";
309 mod.mod_op = LDAP_MOD_REPLACE;
310 if ( verbose && !confirm_action( "All existing values will be overwritten with the new values you are about to enter." )) {
311 printf(" Modification halted.\n");
317 printf(" No changes made.\n");
321 values[0] = get_value(attr.quipu_name, prompt);
322 for (i = 1; i < MAX_VALUES; i++) {
325 fetch_buffer(resp, sizeof(resp), stdin);
326 if ((resp[0] == 'y') || (resp[0] == 'Y'))
327 values[i] = get_value(attr.quipu_name, prompt2);
332 /* if the first value in the value-array is NULL, bail */
333 if (values[0] == NULL) {
335 printf(" No modification made.\n");
339 if (debug & D_MODIFY) {
340 printf(" ld = 0x%x\n", ld);
341 printf(" who = [%s]\n", who);
342 printf(" mods[0]->mod_op = %1d\n", mods[0]->mod_op);
343 printf(" mods[0]->mod_type = %s\n", mods[0]->mod_type);
344 for (i = 0; mods[0]->mod_values[i] != NULL; i++)
345 printf(" mods[0]->mod_values[%1d] = %s\n", i, mods[0]->mod_values[i]);
348 if (ldap_modify_s(ld, who, mods)) {
353 printf(" Modifications to '%s' complete.\n", attr.output_string);
354 ldap_uncache_entry( ld, who );
355 for (i--; i > 0; i--)
356 (void) Free(values[i]);
362 * These are used to size the buffers we use when collecting values that
363 * can cross more than one line.
367 #define MAX_DESC_LINES 24
368 #define INTL_ADDR_LIMIT 30
371 get_value( char *id, char *prompt )
373 char *cp; /* for the Malloc() */
374 int count; /* line # of new value -- if multiline */
375 int multiline = 0; /* 1 if this value is multiline */
376 static char line[LINE_SIZE]; /* raw line from user */
377 static char buffer[MAX_DESC_LINES * LINE_SIZE]; /* holds ALL of the
381 printf("->get_value(%s, %s)\n", id, prompt);
383 /* if this is a URL, have another routine handle this */
384 if (!strcmp(id, "labeledURL"))
388 * To start with, we have one line of input from the user.
390 * Addresses and multiline description can span multiple lines.
391 * Other attributes may not.
394 (void) memset(buffer, 0, sizeof(buffer));
396 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress") || !strcmp(id, "multiLineDescription") || !strcmp(id, "vacationMessage"))
398 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress"))
401 printf("\n %s:\n", prompt);
406 printf(" %1d: ", count);
410 fetch_buffer(line, sizeof(line), stdin);
416 * Screen out dangerous e-mail addresses of the form:
420 * and addresses that have no '@' symbol at all.
422 if (!strcmp(id, "mail")) {
426 /* if this is a group, don't worry */
430 /* if this address is not @umich.edu, don't worry */
431 /* ...unless there is no '@' at all! */
433 if ((tmp2 = strrchr(tmp, '@')) == NULL) {
435 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 );
441 if (strcasecmp(tmp2, "umich.edu"))
444 /* if not of the form uid@umich.edu, don't worry */
445 if ((i = attr_to_index("uid")) < 0)
447 if (strcasecmp(tmp, *(Entry.attrs[i].values)))
450 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);
454 printf(" Please enter a legal e-mail address (or press RETURN to stop)\n");
461 * If the attribute which we are gathering is a "owner"
462 * then we should lookup the name. The user is going to
463 * either have to change the search base before doing the
464 * modify, or the person is going to have to be within the
465 * scope of the current search base, or they will need to
468 if (!strcmp(id, "owner")) {
469 LDAPMessage *lmp, *elmp;
472 lmp = find(line, FALSE);
473 if (lmp == (LDAPMessage *) NULL) {
474 printf(" Could not find \"%s\" in the Directory\n", line);
476 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);
479 elmp = ldap_first_entry(ld, lmp);
480 if (lmp == (LDAPMessage *) NULL) {
481 ldap_perror(ld, "ldap_first_entry");
484 tmp = ldap_get_dn(ld, elmp);
487 (void) ldap_msgfree(lmp);
491 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress")) {
492 if (strlen(line) > INTL_ADDR_LIMIT) {
493 printf(" The international standard for addresses only allows for 30-character lines\n");
494 printf(" Please re-enter your last line.\n");
500 * Separate lines of multiline attribute values with
501 * dollar signs. Copy this line into the buffer we
502 * use to collect up all of the user-supplied input
503 * lines. If this is not a multiline attribute, we
507 (void) strcat(buffer, "$");
508 (void) strcat(buffer, line);
511 if ((count > MAX_LINES) && (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress"))) {
512 printf(" The international standard for addresses only allows for six lines\n");
516 if ((count > MAX_DESC_LINES) && !strcmp(id, "multiLineDescription")) {
517 printf(" We only allow %d lines of description\n", MAX_DESC_LINES);
522 if (buffer[0] == '\0')
525 if (debug & D_MODIFY)
526 printf(" Value is [%s]\n", buffer);
528 cp = (char *) Malloc((unsigned) (strlen(buffer) + 1));
535 char *who, /* DN of entry we are changing */
536 int attr_idx /* boolean attribute to change */
539 struct attribute attr = Entry.attrs[attr_to_index(attrlist[attr_idx].quipu_name)];
542 static char response[16];
543 static char *newsetting[2] = { NULL, NULL };
544 LDAPMod mod, *mods[2];
548 printf("->set_boolean(%s, %s)\n", who, attr.quipu_name);
551 mods[1] = (LDAPMod *) NULL;
552 mod.mod_op = LDAP_MOD_REPLACE;
553 mod.mod_type = attr.quipu_name;
554 mod.mod_values = newsetting;
556 /* fetch the current setting */
557 if ((cp = fetch_boolean_value(who, attr)) == NULL)
559 if (!strcmp(cp, "TRUE"))
560 newsetting[0] = "FALSE";
561 else if (!strcmp(cp, "FALSE"))
562 newsetting[0] = "TRUE";
564 printf(" This field needs to be set to either TRUE or to FALSE.\n");
565 printf(" \"%s\" is not a legal value. Please set this field to either TRUE or to FALSE.\n", cp);
566 newsetting[0] = "FALSE";
569 /* see if they want to change it */
571 printf(" The current value of this field is %s.\n", cp);
572 printf(" Should I change the value of this field to %s?\n",
574 printf(" Please enter Y for yes, N for no, or RETURN to cancel: ");
576 (void) fetch_buffer(response, sizeof(response), stdin);
577 for (s = response; isspace(*s); s++)
579 if ((*s == 'y') || (*s == 'Y')) {
580 if (ldap_modify_s(ld, who, mods)) {
585 printf(" Setting has been changed\n");
586 ldap_uncache_entry(ld, who);
590 printf(" Setting has not been changed\n");
596 set_updates( char *who, int dummy )
599 static char response[16];
600 static char value[6];
601 static char *newsetting[2] = { value, NULL };
602 LDAPMod mod, *mods[2];
603 struct attribute attr;
607 printf("->set_updates(%s)\n", who);
610 mods[1] = (LDAPMod *) NULL;
611 mod.mod_op = LDAP_MOD_REPLACE;
612 mod.mod_type = "noBatchUpdates";
613 mod.mod_values = newsetting;
614 /* explain what the implications are */
616 printf("\n By default, updates that are received from the Personnel\n");
617 printf(" Office and the Office of the Registrar are applied to all\n");
618 printf(" entries in the LDAP database each month. Sometimes this\n");
619 printf(" feature is undesirable. For example, if you maintain your\n");
620 printf(" entry in the LDAP database manually, you may not want to\n");
621 printf(" have these updates applied to your entry, possibly overwriting\n");
622 printf(" correct information with out-dated information.\n\n");
625 /* fetch the current setting */
626 attr.quipu_name = "noBatchUpdates";
627 if ((cp = fetch_boolean_value(who, attr)) == NULL)
629 if (!strcmp(cp, "TRUE"))
630 printf(" Automatic updates are currently turned OFF\n");
631 else if (!strcmp(cp, "FALSE"))
632 printf(" Automatic updates are currently turned ON\n");
634 fprintf(stderr, " Unknown update flag -> [%s]\n", cp);
638 /* see if they want to change it */
639 printf("\n Change this setting [no]? ");
641 (void) fetch_buffer(response, sizeof(response), stdin);
642 for (s = response; isspace(*s); s++)
644 if ((*s == 'y') || (*s == 'Y')) {
645 if (!strcmp(cp, "TRUE"))
646 strcpy(value, "FALSE");
648 strcpy(value, "TRUE");
649 if (ldap_modify_s(ld, who, mods)) {
654 printf(" Setting has been changed\n");
655 ldap_uncache_entry( ld, who );
659 printf(" Setting has not been changed\n");
665 print_mod_list( int group )
667 register int i, j = 1;
670 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
671 if (attrlist[i].flags & ATTR_FLAG_GROUP_MOD) {
672 printf(" %2d -> %s\n", j, attrlist[i].output_string);
677 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
678 if (attrlist[i].flags & ATTR_FLAG_PERSON_MOD) {
679 printf(" %2d -> %s\n", j, attrlist[i].output_string);
684 printf(" ? -> Print this list\n\n");
685 printf(" Press the RETURN key without typing a number to quit.\n");
688 printf(" To add nicknames, send mail to x500-nicknames@umich.edu\n");
693 perform_action( char *choice, char *dn, int group )
696 register int i, j = 1;
698 selection = atoi(choice);
700 printf("\n Choices are:\n");
701 printf(" -----------------------\n");
702 print_mod_list(group);
708 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
709 if (attrlist[i].flags & ATTR_FLAG_GROUP_MOD) {
716 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
717 if (attrlist[i].flags & ATTR_FLAG_PERSON_MOD) {
725 if (attrlist[i].quipu_name == NULL) {
726 printf("\n Choices are:\n");
727 printf(" -----------------------\n");
728 print_mod_list(group);
732 (*attrlist[i].mod_func)(dn, i);
739 char *rvalue, label[MED_BUF_SIZE], url[MED_BUF_SIZE];
742 printf(" First, enter the URL. (Example: http://www.us.itd.umich.edu/users/).\n");
743 printf(" The URL may be up to %d characters long.\n", MED_BUF_SIZE);
748 (void) fetch_buffer(url, sizeof(url), stdin);
751 if (check_URL(url) == 0)
753 printf(" A URL may not have any spaces or tabs in it. Please re-enter your URL.\n\n");
756 printf("\n Now please enter a descriptive label for this URL\n");
760 (void) fetch_buffer(label, sizeof(label), stdin);
761 } while (label[0] == '\0');
762 rvalue = (char *) Malloc((unsigned) (strlen(url) + 2 + strlen(label)));
763 sprintf(rvalue, "%s %s", url, label);
764 return((char *) rvalue);
768 check_URL( char *url )
772 for (cp = url; *cp != '\n' && *cp != '\0'; cp++) {
783 mod_perror( LDAP *ld )
788 ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ld_errno);
791 if (( ld == NULL ) || ( ld_errno != LDAP_UNAVAILABLE &&
792 ld_errno != LDAP_UNWILLING_TO_PERFORM ))
794 ldap_perror( ld, "modify" );
798 fprintf( stderr, "\n modify: failed because part of the online directory is not able\n" );
799 fprintf( stderr, " to be modified right now" );
800 if ( ld_errno == LDAP_UNAVAILABLE ) {
801 fprintf( stderr, " or is temporarily unavailable" );
803 fprintf( stderr, ".\n Please try again later.\n" );