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();
39 void set_updates(); /* routine to modify noBatchUpdates */
41 LDAPMessage *mp; /* returned from find() */
42 char *dn; /* distinguished name */
43 char **rdns; /* for fiddling with the DN */
44 char name[MED_BUF_SIZE]; /* entry to modify */
45 int displayed_choices = 0;
46 static char ans[SMALL_BUF_SIZE]; /* for holding user input */
48 static char printed_warning = 0; /* for use with the */
49 struct attribute no_batch_update_attr;
50 extern char * fetch_boolean_value();
52 int is_a_group; /* TRUE if it is; FALSE otherwise */
54 extern int bind_status;
58 printf("->modify(%s)\n", who);
61 * Require them to bind first if the are modifying a group.
63 if (bind_status == UD_NOT_BOUND) {
64 if (auth((char *) NULL, 1) < 0)
69 * First, decide what entry we are going to modify. If the
70 * user has not included a name on the modify command line,
71 * we will use the person who was last looked up with a find
72 * command. If there is no value there either, we don't know
75 * Once we know who to modify, be sure that they exist, and
80 printf(" Enter the name of the person or\n");
81 printf(" group whose entry you want to modify: ");
84 printf(" Modify whose entry? ");
86 fetch_buffer(name, sizeof(name), stdin);
92 if ((mp = find(who, TRUE)) == NULL) {
93 (void) ldap_msgfree(mp);
94 printf(" Could not locate \"%s\" in the Directory.\n", who);
97 dn = ldap_get_dn(ld, ldap_first_entry(ld, mp));
98 rdns = ldap_explode_dn(dn, TRUE);
100 printf("\n Modifying Directory entry of \"%s\"\n", *rdns);
104 * If verbose mode is turned on and the user has not set a value
105 * for noBatchUpdates, warn them that what they are about to do
106 * may be overwritten automatically by that Stinkbug.
108 no_batch_update_attr.quipu_name = "noBatchUpdates";
109 (void) fetch_boolean_value(dn, no_batch_update_attr);
110 if (verbose && !printed_warning && (ld->ld_errno == LDAP_NO_SUCH_ATTRIBUTE)) {
112 printf("\n WARNING!\n");
113 printf(" You are about to make a modification to an LDAP entry\n");
114 printf(" that has its \"automatic updates\" field set to ON.\n");
115 printf(" This means that the entry will be automatically updated\n");
116 printf(" each month from official University sources like the\n");
117 printf(" Personnel Office. With \"automatic updates\" set to ON,\n");
118 printf(" the following fields will be overwritten each month:\n");
119 printf(" Title, home address and phone,\n");
120 printf(" business address and phone\n");
121 printf(" If you modify any of these fields, you may want to change\n");
122 printf(" the \"automatic updates\" field to OFF so that your\n");
123 printf(" changes will not be overwritten. You may change this\n");
124 printf(" setting by choosing \"u\" at the \"Modify what?\" prompt\n");
129 * Current values for user 'who' are being held in 'mp'. We
130 * should parse up that buffer and fill in the Entry structure.
131 * Once we're done with that, we can find out which fields the
132 * user would like to modify.
135 is_a_group = isgroup();
136 (void) ldap_msgfree(mp);
137 printf(" You now need to specify what field you'd like to modify.\n");
139 if ( verbose || !displayed_choices ) {
140 printf("\n Choices are:\n");
141 printf(" -----------------------\n");
142 print_mod_list(is_a_group);
143 printf("\n Pressing Return will cancel the process.\n");
144 displayed_choices = 1;
146 printf("\n Modify what? ");
148 fetch_buffer(ans, sizeof(ans), stdin);
151 perform_action(ans, dn, is_a_group);
152 if ((mp = find(*rdns, TRUE)) == NULL)
155 (void) ldap_msgfree(mp);
158 ldap_value_free(rdns);
162 /* generic routine for changing any field */
163 void change_field(who, attr)
164 char *who; /* DN of entry we are changing */
165 struct attribute attr; /* attribute to change */
168 #define IS_MOD(x) (!strncasecmp(resp, (x), strlen(resp)))
170 char *get_value(); /* routine to extract values */
171 static char buf[MED_BUF_SIZE]; /* for printing things */
172 static char resp[SMALL_BUF_SIZE]; /* for user input */
173 char *prompt, *prompt2, *more;
174 register int i; /* for looping thru values */
176 static LDAPMod *mods[2] = { &mod }; /* passed to ldap_modify */
177 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
376 char *get_value(id, 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]; /* holds ALL of the
385 extern void * Malloc();
386 static char * get_URL();
390 printf("->get_value(%s, %s)\n", id, prompt);
392 /* if this is a URL, have another routine handle this */
393 if (!strcmp(id, "labeledURL"))
397 * To start with, we have one line of input from the user.
399 * Addresses and multiline description can span multiple lines.
400 * Other attributes may not.
403 (void) memset(buffer, 0, sizeof(buffer));
405 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress") || !strcmp(id, "multiLineDescription") || !strcmp(id, "vacationMessage"))
407 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress"))
410 printf("\n %s:\n", prompt);
415 printf(" %1d: ", count);
419 fetch_buffer(line, sizeof(line), stdin);
425 * Screen out dangerous e-mail addresses of the form:
429 * and addresses that have no '@' symbol at all.
431 if (!strcmp(id, "mail")) {
435 /* if this is a group, don't worry */
439 /* if this address is not @umich.edu, don't worry */
440 /* ...unless there is no '@' at all! */
442 if ((tmp2 = strrchr(tmp, '@')) == NULL) {
444 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 );
450 if (strcasecmp(tmp2, "umich.edu"))
453 /* if not of the form uid@umich.edu, don't worry */
454 if ((i = attr_to_index("uid")) < 0)
456 if (strcasecmp(tmp, *(Entry.attrs[i].values)))
459 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);
463 printf(" Please enter a legal e-mail address (or press RETURN to stop)\n");
470 * If the attribute which we are gathering is a "owner"
471 * then we should lookup the name. The user is going to
472 * either have to change the search base before doing the
473 * modify, or the person is going to have to be within the
474 * scope of the current search base, or they will need to
477 if (!strcmp(id, "owner")) {
478 LDAPMessage *lmp, *elmp;
481 lmp = find(line, FALSE);
482 if (lmp == (LDAPMessage *) NULL) {
483 printf(" Could not find \"%s\" in the Directory\n", line);
485 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);
488 elmp = ldap_first_entry(ld, lmp);
489 if (lmp == (LDAPMessage *) NULL) {
490 ldap_perror(ld, "ldap_first_entry");
493 tmp = ldap_get_dn(ld, elmp);
495 (void) ldap_msgfree(lmp);
499 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress")) {
500 if (strlen(line) > INTL_ADDR_LIMIT) {
501 printf(" The international standard for addresses only allows for 30-character lines\n");
502 printf(" Please re-enter your last line.\n");
508 * Separate lines of multiline attribute values with
509 * dollar signs. Copy this line into the buffer we
510 * use to collect up all of the user-supplied input
511 * lines. If this is not a multiline attribute, we
515 (void) strcat(buffer, "$");
516 (void) strcat(buffer, line);
519 if ((count > MAX_LINES) && (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress"))) {
520 printf(" The international standard for addresses only allows for six lines\n");
524 if ((count > MAX_DESC_LINES) && !strcmp(id, "multiLineDescription")) {
525 printf(" We only allow %d lines of description\n", MAX_DESC_LINES);
530 if (buffer[0] == '\0')
533 if (debug & D_MODIFY)
534 printf(" Value is [%s]\n", buffer);
536 cp = (char *) Malloc((unsigned) (strlen(buffer) + 1));
541 void set_boolean(who, attr)
542 char *who; /* DN of entry we are changing */
543 struct attribute attr; /* boolean attribute to change */
546 extern char * fetch_boolean_value();
547 static char response[16];
548 static char *newsetting[2] = { NULL, NULL };
549 LDAPMod mod, *mods[2];
553 printf("->set_boolean(%s, %s)\n", who, attr.quipu_name);
556 mods[1] = (LDAPMod *) NULL;
557 mod.mod_op = LDAP_MOD_REPLACE;
558 mod.mod_type = attr.quipu_name;
559 mod.mod_values = newsetting;
561 /* fetch the current setting */
562 if ((cp = fetch_boolean_value(who, attr)) == NULL)
564 if (!strcmp(cp, "TRUE"))
565 newsetting[0] = "FALSE";
566 else if (!strcmp(cp, "FALSE"))
567 newsetting[0] = "TRUE";
569 printf(" This field needs to be set to either TRUE or to FALSE.\n");
570 printf(" \"%s\" is not a legal value. Please set this field to either TRUE or to FALSE.\n", cp);
571 newsetting[0] = "FALSE";
574 /* see if they want to change it */
576 printf(" The current value of this field is %s.\n", cp);
577 printf(" Should I change the value of this field to %s?\n",
579 printf(" Please enter Y for yes, N for no, or RETURN to cancel: ");
581 (void) fetch_buffer(response, sizeof(response), stdin);
582 for (s = response; isspace(*s); s++)
584 if ((*s == 'y') || (*s == 'Y')) {
585 if (ldap_modify_s(ld, who, mods)) {
590 printf(" Setting has been changed\n");
591 ldap_uncache_entry(ld, who);
595 printf(" Setting has not been changed\n");
600 void set_updates(who)
604 extern char * fetch_boolean_value();
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(*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");
670 print_mod_list(group)
673 register int i, j = 1;
674 extern struct attribute attrlist[];
677 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
678 if (attrlist[i].flags & ATTR_FLAG_GROUP_MOD) {
679 printf(" %2d -> %s\n", j, attrlist[i].output_string);
684 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
685 if (attrlist[i].flags & ATTR_FLAG_PERSON_MOD) {
686 printf(" %2d -> %s\n", j, attrlist[i].output_string);
691 printf(" ? -> Print this list\n\n");
692 printf(" Press the RETURN key without typing a number to quit.\n");
695 printf(" To add nicknames, send mail to x500-nicknames@umich.edu\n");
699 perform_action(choice, dn, group)
705 register int i, j = 1;
706 extern struct attribute attrlist[];
707 extern void mod_addrDN(), change_field(), set_boolean();
709 selection = atoi(choice);
711 printf("\n Choices are:\n");
712 printf(" -----------------------\n");
713 print_mod_list(group);
719 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
720 if (attrlist[i].flags & ATTR_FLAG_GROUP_MOD) {
727 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
728 if (attrlist[i].flags & ATTR_FLAG_PERSON_MOD) {
736 if (attrlist[i].quipu_name == NULL) {
737 printf("\n Choices are:\n");
738 printf(" -----------------------\n");
739 print_mod_list(group);
743 if (attrlist[i].mod_func == change_field)
744 (*attrlist[i].mod_func)(dn, Entry.attrs[attr_to_index(attrlist[i].quipu_name)]);
745 else if (attrlist[i].mod_func == mod_addrDN)
746 (*attrlist[i].mod_func)(dn, i);
747 else if (attrlist[i].mod_func == set_boolean)
748 (*attrlist[i].mod_func)(dn, Entry.attrs[attr_to_index(attrlist[i].quipu_name)]);
750 (*attrlist[i].mod_func)(dn);
754 static char * get_URL()
756 char *rvalue, label[MED_BUF_SIZE], url[MED_BUF_SIZE];
757 static int check_URL();
758 extern void * Malloc();
761 printf(" First, enter the URL. (Example: http://www.us.itd.umich.edu/users/).\n");
762 printf(" The URL may be up to %d characters long.\n", MED_BUF_SIZE);
767 (void) fetch_buffer(url, sizeof(url), stdin);
770 if (check_URL(url) == 0)
772 printf(" A URL may not have any spaces or tabs in it. Please re-enter your URL.\n\n");
775 printf("\n Now please enter a descriptive label for this URL\n");
779 (void) fetch_buffer(label, sizeof(label), stdin);
780 } while (label[0] == '\0');
781 rvalue = (char *) Malloc((unsigned) (strlen(url) + 2 + strlen(label)));
782 sprintf(rvalue, "%s %s", url, label);
783 return((char *) rvalue);
786 static check_URL(url)
791 for (cp = url; *cp != '\n' && *cp != '\0'; cp++) {
801 mod_perror( LDAP *ld )
803 if ( ld == NULL || ( ld->ld_errno != LDAP_UNAVAILABLE &&
804 ld->ld_errno != LDAP_UNWILLING_TO_PERFORM )) {
805 ldap_perror( ld, "modify" );
809 fprintf( stderr, "\n modify: failed because part of the online directory is not able\n" );
810 fprintf( stderr, " to be modified right now" );
811 if ( ld->ld_errno == LDAP_UNAVAILABLE ) {
812 fprintf( stderr, " or is temporarily unavailable" );
814 fprintf( stderr, ".\n Please try again later.\n" );