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.
18 #include <ac/string.h>
25 extern struct entry Entry;
29 extern LDAPMessage *find();
30 extern void * Malloc();
32 static char * get_URL();
33 static int check_URL();
43 void set_updates(); /* routine to modify noBatchUpdates */
45 LDAPMessage *mp; /* returned from find() */
46 char *dn; /* distinguished name */
47 char **rdns; /* for fiddling with the DN */
48 char name[MED_BUF_SIZE]; /* entry to modify */
49 int displayed_choices = 0;
50 static char ans[SMALL_BUF_SIZE]; /* for holding user input */
52 static char printed_warning = 0; /* for use with the */
53 struct attribute no_batch_update_attr;
54 extern char * fetch_boolean_value();
57 int is_a_group; /* TRUE if it is; FALSE otherwise */
59 extern int bind_status;
63 printf("->modify(%s)\n", who);
66 * Require them to bind first if the are modifying a group.
68 if (bind_status == UD_NOT_BOUND) {
69 if (auth((char *) NULL, 1) < 0)
74 * First, decide what entry we are going to modify. If the
75 * user has not included a name on the modify command line,
76 * we will use the person who was last looked up with a find
77 * command. If there is no value there either, we don't know
80 * Once we know who to modify, be sure that they exist, and
85 printf(" Enter the name of the person or\n");
86 printf(" group whose entry you want to modify: ");
89 printf(" Modify whose entry? ");
91 fetch_buffer(name, sizeof(name), stdin);
97 if ((mp = find(who, TRUE)) == NULL) {
98 (void) ldap_msgfree(mp);
99 printf(" Could not locate \"%s\" in the Directory.\n", who);
102 dn = ldap_get_dn(ld, ldap_first_entry(ld, mp));
103 rdns = ldap_explode_dn(dn, TRUE);
105 printf("\n Modifying Directory entry of \"%s\"\n", *rdns);
109 * If verbose mode is turned on and the user has not set a value
110 * for noBatchUpdates, warn them that what they are about to do
111 * may be overwritten automatically by that Stinkbug.
113 no_batch_update_attr.quipu_name = "noBatchUpdates";
114 (void) fetch_boolean_value(dn, no_batch_update_attr);
116 ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ld_errno);
118 if (verbose && !printed_warning && (ld_errno == LDAP_NO_SUCH_ATTRIBUTE)) {
120 printf("\n WARNING!\n");
121 printf(" You are about to make a modification to an LDAP entry\n");
122 printf(" that has its \"automatic updates\" field set to ON.\n");
123 printf(" This means that the entry will be automatically updated\n");
124 printf(" each month from official University sources like the\n");
125 printf(" Personnel Office. With \"automatic updates\" set to ON,\n");
126 printf(" the following fields will be overwritten each month:\n");
127 printf(" Title, home address and phone,\n");
128 printf(" business address and phone\n");
129 printf(" If you modify any of these fields, you may want to change\n");
130 printf(" the \"automatic updates\" field to OFF so that your\n");
131 printf(" changes will not be overwritten. You may change this\n");
132 printf(" setting by choosing \"u\" at the \"Modify what?\" prompt\n");
137 * Current values for user 'who' are being held in 'mp'. We
138 * should parse up that buffer and fill in the Entry structure.
139 * Once we're done with that, we can find out which fields the
140 * user would like to modify.
143 is_a_group = isgroup();
144 (void) ldap_msgfree(mp);
145 printf(" You now need to specify what field you'd like to modify.\n");
147 if ( verbose || !displayed_choices ) {
148 printf("\n Choices are:\n");
149 printf(" -----------------------\n");
150 print_mod_list(is_a_group);
151 printf("\n Pressing Return will cancel the process.\n");
152 displayed_choices = 1;
154 printf("\n Modify what? ");
156 fetch_buffer(ans, sizeof(ans), stdin);
159 perform_action(ans, dn, is_a_group);
160 if ((mp = find(*rdns, TRUE)) == NULL)
163 (void) ldap_msgfree(mp);
166 ldap_value_free(rdns);
170 /* generic routine for changing any field */
171 void change_field(who, attr)
172 char *who; /* DN of entry we are changing */
173 struct attribute attr; /* attribute to change */
176 #define IS_MOD(x) (!strncasecmp(resp, (x), strlen(resp)))
178 char *get_value(); /* routine to extract values */
179 static char buf[MED_BUF_SIZE]; /* for printing things */
180 static char resp[SMALL_BUF_SIZE]; /* for user input */
181 char *prompt, *prompt2, *more;
182 register int i; /* for looping thru values */
184 static LDAPMod *mods[2] = { &mod }; /* passed to ldap_modify */
185 static char *values[MAX_VALUES]; /* passed to ldap_modify */
190 printf("->change_field(%x, %s)\n", attr, who);
193 * If there is no current value associated with the attribute,
194 * then this is the easy case. Collect one (or more) attributes
195 * from the user, and then call ldap_modify_s() to write the changes
196 * to the LDAP server.
198 for (i = 0; i < MAX_VALUES; i++)
200 if (attr.values == (char **) NULL) {
201 printf("\n No current \"%s\"\n", attr.output_string);
202 values[0] = get_value(attr.quipu_name, "Enter a value");
203 if ( values[0] == NULL )
205 mod.mod_op = LDAP_MOD_REPLACE;
206 mod.mod_type = attr.quipu_name;
207 mod.mod_values = values;
208 for (i = 1; i < MAX_VALUES; i++) {
209 printf(" Do you wish to add an additional value? ");
211 fetch_buffer(resp, sizeof(resp), stdin);
212 if ((resp[0] == 'y') || (resp[0] == 'Y'))
213 values[i] = get_value(attr.quipu_name, "Enter an additional value");
218 if (debug & D_MODIFY) {
219 printf(" ld = 0x%x\n", ld);
220 printf(" who = [%s]\n", who);
221 printf(" mods[0]->mod_op = %1d\n", mods[0]->mod_op);
222 printf(" mods[0]->mod_type = %s\n", mods[0]->mod_type);
223 for (i = 0; mods[0]->mod_values[i] != NULL; i++)
224 printf(" mods[0]->mod_values[%1d] = %s\n", i, mods[0]->mod_values[i]);
227 if (ldap_modify_s(ld, who, mods)) {
232 printf(" Modification of '%s' complete.\n", attr.output_string);
233 ldap_uncache_entry( ld, who );
234 for (i--; i > 0; i--)
235 (void) Free(values[i]);
238 * There are values for this attribute already. In this case,
239 * we want to allow the user to delete all existing values,
240 * add additional values to the ones there already, or just
241 * delete some of the values already present. DIXIE does not
242 * handle modifications where the attribute occurs on the LHS
243 * more than once. So to delete entries and add entries, we
244 * need to call ldap_modify() twice.
248 * If the attribute holds values which are DNs, print them
249 * in a most pleasant way.
251 sprintf(buf, "%s: ", attr.output_string);
252 if (!strcmp(attr.quipu_name, "owner"))
253 (void) print_DN(attr);
255 (void) print_values(attr);
258 printf(" You may now:\n");
259 printf(" Add additional values to the existing ones, OR\n");
260 printf(" Clear all values listed above, OR\n");
261 printf(" Delete one of the values listed above, OR\n");
262 printf(" Replace all of the values above with new ones.\n");
264 printf("\n Add, Clear, Delete, or Replace? ");
266 fetch_buffer(resp, sizeof(resp), stdin);
269 * Bail if they just hit the RETURN key.
271 if (resp[0] == '\0') {
273 printf("\n No changes made.\n");
278 * If the want to clear the values, just do it.
280 mod.mod_type = attr.quipu_name;
281 mod.mod_values = values;
282 if (IS_MOD("clear")) {
283 mod.mod_op = LDAP_MOD_DELETE;
284 mod.mod_values = NULL;
285 if ( verbose && !confirm_action( "All existing values will be removed." )) {
286 printf(" Modification halted.\n");
290 if (debug & D_MODIFY) {
291 printf("Clearing attribute '%s'\n", attr.quipu_name);
292 printf("who = [%s]\n", who);
293 printf("mod = [%d] [%s] [%x]\n", mod.mod_op,
294 mod.mod_type, mod.mod_values);
297 if (ldap_modify_s(ld, who, mods)) {
302 printf(" '%s' has been cleared.\n", attr.output_string);
303 ldap_uncache_entry( ld, who );
308 prompt = "Enter the value you wish to add";
309 more = " Add an additional value? ";
310 prompt2 = "Enter another value you wish to add";
311 mod.mod_op = LDAP_MOD_ADD;
313 else if (IS_MOD("delete")) {
314 prompt = "Enter the value you wish to delete";
315 more = " Delete an additional value? ";
316 prompt2 = "Enter another value you wish to delete";
317 mod.mod_op = LDAP_MOD_DELETE;
319 else if (IS_MOD("replace")) {
320 prompt = "Enter the new value";
321 more = " Add an additional value? ";
322 prompt2 = "Enter another value you wish to add";
323 mod.mod_op = LDAP_MOD_REPLACE;
324 if ( verbose && !confirm_action( "All existing values will be overwritten with the new values you are about to enter." )) {
325 printf(" Modification halted.\n");
331 printf(" No changes made.\n");
335 values[0] = get_value(attr.quipu_name, prompt);
336 for (i = 1; i < MAX_VALUES; i++) {
339 fetch_buffer(resp, sizeof(resp), stdin);
340 if ((resp[0] == 'y') || (resp[0] == 'Y'))
341 values[i] = get_value(attr.quipu_name, prompt2);
346 /* if the first value in the value-array is NULL, bail */
347 if (values[0] == NULL) {
349 printf(" No modification made.\n");
353 if (debug & D_MODIFY) {
354 printf(" ld = 0x%x\n", ld);
355 printf(" who = [%s]\n", who);
356 printf(" mods[0]->mod_op = %1d\n", mods[0]->mod_op);
357 printf(" mods[0]->mod_type = %s\n", mods[0]->mod_type);
358 for (i = 0; mods[0]->mod_values[i] != NULL; i++)
359 printf(" mods[0]->mod_values[%1d] = %s\n", i, mods[0]->mod_values[i]);
362 if (ldap_modify_s(ld, who, mods)) {
367 printf(" Modifications to '%s' complete.\n", attr.output_string);
368 ldap_uncache_entry( ld, who );
369 for (i--; i > 0; i--)
370 (void) Free(values[i]);
376 * These are used to size the buffers we use when collecting values that
377 * can cross more than one line.
381 #define MAX_DESC_LINES 24
382 #define INTL_ADDR_LIMIT 30
384 char *get_value(id, prompt)
387 char *cp; /* for the Malloc() */
388 int count; /* line # of new value -- if multiline */
389 int multiline = 0; /* 1 if this value is multiline */
390 static char line[LINE_SIZE]; /* raw line from user */
391 static char buffer[MAX_DESC_LINES * LINE_SIZE]; /* holds ALL of the
395 printf("->get_value(%s, %s)\n", id, prompt);
397 /* if this is a URL, have another routine handle this */
398 if (!strcmp(id, "labeledURL"))
402 * To start with, we have one line of input from the user.
404 * Addresses and multiline description can span multiple lines.
405 * Other attributes may not.
408 (void) memset(buffer, 0, sizeof(buffer));
410 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress") || !strcmp(id, "multiLineDescription") || !strcmp(id, "vacationMessage"))
412 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress"))
415 printf("\n %s:\n", prompt);
420 printf(" %1d: ", count);
424 fetch_buffer(line, sizeof(line), stdin);
430 * Screen out dangerous e-mail addresses of the form:
434 * and addresses that have no '@' symbol at all.
436 if (!strcmp(id, "mail")) {
440 /* if this is a group, don't worry */
444 /* if this address is not @umich.edu, don't worry */
445 /* ...unless there is no '@' at all! */
447 if ((tmp2 = strrchr(tmp, '@')) == NULL) {
449 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 );
455 if (strcasecmp(tmp2, "umich.edu"))
458 /* if not of the form uid@umich.edu, don't worry */
459 if ((i = attr_to_index("uid")) < 0)
461 if (strcasecmp(tmp, *(Entry.attrs[i].values)))
464 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);
468 printf(" Please enter a legal e-mail address (or press RETURN to stop)\n");
475 * If the attribute which we are gathering is a "owner"
476 * then we should lookup the name. The user is going to
477 * either have to change the search base before doing the
478 * modify, or the person is going to have to be within the
479 * scope of the current search base, or they will need to
482 if (!strcmp(id, "owner")) {
483 LDAPMessage *lmp, *elmp;
486 lmp = find(line, FALSE);
487 if (lmp == (LDAPMessage *) NULL) {
488 printf(" Could not find \"%s\" in the Directory\n", line);
490 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);
493 elmp = ldap_first_entry(ld, lmp);
494 if (lmp == (LDAPMessage *) NULL) {
495 ldap_perror(ld, "ldap_first_entry");
498 tmp = ldap_get_dn(ld, elmp);
500 (void) ldap_msgfree(lmp);
504 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress")) {
505 if (strlen(line) > INTL_ADDR_LIMIT) {
506 printf(" The international standard for addresses only allows for 30-character lines\n");
507 printf(" Please re-enter your last line.\n");
513 * Separate lines of multiline attribute values with
514 * dollar signs. Copy this line into the buffer we
515 * use to collect up all of the user-supplied input
516 * lines. If this is not a multiline attribute, we
520 (void) strcat(buffer, "$");
521 (void) strcat(buffer, line);
524 if ((count > MAX_LINES) && (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress"))) {
525 printf(" The international standard for addresses only allows for six lines\n");
529 if ((count > MAX_DESC_LINES) && !strcmp(id, "multiLineDescription")) {
530 printf(" We only allow %d lines of description\n", MAX_DESC_LINES);
535 if (buffer[0] == '\0')
538 if (debug & D_MODIFY)
539 printf(" Value is [%s]\n", buffer);
541 cp = (char *) Malloc((unsigned) (strlen(buffer) + 1));
546 void set_boolean(who, attr)
547 char *who; /* DN of entry we are changing */
548 struct attribute attr; /* boolean attribute to change */
551 extern char * fetch_boolean_value();
552 static char response[16];
553 static char *newsetting[2] = { NULL, NULL };
554 LDAPMod mod, *mods[2];
558 printf("->set_boolean(%s, %s)\n", who, attr.quipu_name);
561 mods[1] = (LDAPMod *) NULL;
562 mod.mod_op = LDAP_MOD_REPLACE;
563 mod.mod_type = attr.quipu_name;
564 mod.mod_values = newsetting;
566 /* fetch the current setting */
567 if ((cp = fetch_boolean_value(who, attr)) == NULL)
569 if (!strcmp(cp, "TRUE"))
570 newsetting[0] = "FALSE";
571 else if (!strcmp(cp, "FALSE"))
572 newsetting[0] = "TRUE";
574 printf(" This field needs to be set to either TRUE or to FALSE.\n");
575 printf(" \"%s\" is not a legal value. Please set this field to either TRUE or to FALSE.\n", cp);
576 newsetting[0] = "FALSE";
579 /* see if they want to change it */
581 printf(" The current value of this field is %s.\n", cp);
582 printf(" Should I change the value of this field to %s?\n",
584 printf(" Please enter Y for yes, N for no, or RETURN to cancel: ");
586 (void) fetch_buffer(response, sizeof(response), stdin);
587 for (s = response; isspace(*s); s++)
589 if ((*s == 'y') || (*s == 'Y')) {
590 if (ldap_modify_s(ld, who, mods)) {
595 printf(" Setting has been changed\n");
596 ldap_uncache_entry(ld, who);
600 printf(" Setting has not been changed\n");
605 void set_updates(who)
609 extern char * fetch_boolean_value();
610 static char response[16];
611 static char value[6];
612 static char *newsetting[2] = { value, NULL };
613 LDAPMod mod, *mods[2];
614 struct attribute attr;
618 printf("->set_updates(%s)\n", who);
621 mods[1] = (LDAPMod *) NULL;
622 mod.mod_op = LDAP_MOD_REPLACE;
623 mod.mod_type = "noBatchUpdates";
624 mod.mod_values = newsetting;
625 /* explain what the implications are */
627 printf("\n By default, updates that are received from the Personnel\n");
628 printf(" Office and the Office of the Registrar are applied to all\n");
629 printf(" entries in the LDAP database each month. Sometimes this\n");
630 printf(" feature is undesirable. For example, if you maintain your\n");
631 printf(" entry in the LDAP database manually, you may not want to\n");
632 printf(" have these updates applied to your entry, possibly overwriting\n");
633 printf(" correct information with out-dated information.\n\n");
636 /* fetch the current setting */
637 attr.quipu_name = "noBatchUpdates";
638 if ((cp = fetch_boolean_value(who, attr)) == NULL)
640 if (!strcmp(cp, "TRUE"))
641 printf(" Automatic updates are currently turned OFF\n");
642 else if (!strcmp(cp, "FALSE"))
643 printf(" Automatic updates are currently turned ON\n");
645 fprintf(stderr, " Unknown update flag -> [%s]\n", cp);
649 /* see if they want to change it */
650 printf("\n Change this setting [no]? ");
652 (void) fetch_buffer(response, sizeof(response), stdin);
653 for (s = response; isspace(*s); s++)
655 if ((*s == 'y') || (*s == 'Y')) {
656 if (!strcmp(cp, "TRUE"))
657 strcpy(value, "FALSE");
659 strcpy(value, "TRUE");
660 if (ldap_modify_s(ld, who, mods)) {
665 printf(" Setting has been changed\n");
666 ldap_uncache_entry( ld, who );
670 printf(" Setting has not been changed\n");
675 print_mod_list(group)
678 register int i, j = 1;
679 extern struct attribute attrlist[];
682 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
683 if (attrlist[i].flags & ATTR_FLAG_GROUP_MOD) {
684 printf(" %2d -> %s\n", j, attrlist[i].output_string);
689 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
690 if (attrlist[i].flags & ATTR_FLAG_PERSON_MOD) {
691 printf(" %2d -> %s\n", j, attrlist[i].output_string);
696 printf(" ? -> Print this list\n\n");
697 printf(" Press the RETURN key without typing a number to quit.\n");
700 printf(" To add nicknames, send mail to x500-nicknames@umich.edu\n");
704 perform_action(choice, dn, group)
710 register int i, j = 1;
711 extern struct attribute attrlist[];
712 extern void mod_addrDN(), change_field(), set_boolean();
714 selection = atoi(choice);
716 printf("\n Choices are:\n");
717 printf(" -----------------------\n");
718 print_mod_list(group);
724 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
725 if (attrlist[i].flags & ATTR_FLAG_GROUP_MOD) {
732 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
733 if (attrlist[i].flags & ATTR_FLAG_PERSON_MOD) {
741 if (attrlist[i].quipu_name == NULL) {
742 printf("\n Choices are:\n");
743 printf(" -----------------------\n");
744 print_mod_list(group);
748 if (attrlist[i].mod_func == change_field)
749 (*attrlist[i].mod_func)(dn, Entry.attrs[attr_to_index(attrlist[i].quipu_name)]);
750 else if (attrlist[i].mod_func == mod_addrDN)
751 (*attrlist[i].mod_func)(dn, i);
752 else if (attrlist[i].mod_func == set_boolean)
753 (*attrlist[i].mod_func)(dn, Entry.attrs[attr_to_index(attrlist[i].quipu_name)]);
755 (*attrlist[i].mod_func)(dn);
759 static char * get_URL()
761 char *rvalue, label[MED_BUF_SIZE], url[MED_BUF_SIZE];
764 printf(" First, enter the URL. (Example: http://www.us.itd.umich.edu/users/).\n");
765 printf(" The URL may be up to %d characters long.\n", MED_BUF_SIZE);
770 (void) fetch_buffer(url, sizeof(url), stdin);
773 if (check_URL(url) == 0)
775 printf(" A URL may not have any spaces or tabs in it. Please re-enter your URL.\n\n");
778 printf("\n Now please enter a descriptive label for this URL\n");
782 (void) fetch_buffer(label, sizeof(label), stdin);
783 } while (label[0] == '\0');
784 rvalue = (char *) Malloc((unsigned) (strlen(url) + 2 + strlen(label)));
785 sprintf(rvalue, "%s %s", url, label);
786 return((char *) rvalue);
789 static check_URL(url)
794 for (cp = url; *cp != '\n' && *cp != '\0'; cp++) {
804 mod_perror( LDAP *ld )
809 ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ld_errno);
812 if (( ld == NULL ) || ( ld_errno != LDAP_UNAVAILABLE &&
813 ld_errno != LDAP_UNWILLING_TO_PERFORM ))
815 ldap_perror( ld, "modify" );
819 fprintf( stderr, "\n modify: failed because part of the online directory is not able\n" );
820 fprintf( stderr, " to be modified right now" );
821 if ( ld_errno == LDAP_UNAVAILABLE ) {
822 fprintf( stderr, " or is temporarily unavailable" );
824 fprintf( stderr, ".\n Please try again later.\n" );