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>
26 extern struct entry Entry;
30 extern LDAPMessage *find();
31 extern void * Malloc();
33 static char * get_URL();
34 static int check_URL();
44 void set_updates(); /* routine to modify noBatchUpdates */
46 LDAPMessage *mp; /* returned from find() */
47 char *dn; /* distinguished name */
48 char **rdns; /* for fiddling with the DN */
49 char name[MED_BUF_SIZE]; /* entry to modify */
50 int displayed_choices = 0;
51 static char ans[SMALL_BUF_SIZE]; /* for holding user input */
53 static char printed_warning = 0; /* for use with the */
54 struct attribute no_batch_update_attr;
55 extern char * fetch_boolean_value();
58 int is_a_group; /* TRUE if it is; FALSE otherwise */
60 extern int bind_status;
64 printf("->modify(%s)\n", who);
67 * Require them to bind first if the are modifying a group.
69 if (bind_status == UD_NOT_BOUND) {
70 if (auth((char *) NULL, 1) < 0)
75 * First, decide what entry we are going to modify. If the
76 * user has not included a name on the modify command line,
77 * we will use the person who was last looked up with a find
78 * command. If there is no value there either, we don't know
81 * Once we know who to modify, be sure that they exist, and
86 printf(" Enter the name of the person or\n");
87 printf(" group whose entry you want to modify: ");
90 printf(" Modify whose entry? ");
92 fetch_buffer(name, sizeof(name), stdin);
98 if ((mp = find(who, TRUE)) == NULL) {
99 (void) ldap_msgfree(mp);
100 printf(" Could not locate \"%s\" in the Directory.\n", who);
103 dn = ldap_get_dn(ld, ldap_first_entry(ld, mp));
104 rdns = ldap_explode_dn(dn, TRUE);
106 printf("\n Modifying Directory entry of \"%s\"\n", *rdns);
110 * If verbose mode is turned on and the user has not set a value
111 * for noBatchUpdates, warn them that what they are about to do
112 * may be overwritten automatically by that Stinkbug.
114 no_batch_update_attr.quipu_name = "noBatchUpdates";
115 (void) fetch_boolean_value(dn, no_batch_update_attr);
117 ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ld_errno);
119 if (verbose && !printed_warning && (ld_errno == LDAP_NO_SUCH_ATTRIBUTE)) {
121 printf("\n WARNING!\n");
122 printf(" You are about to make a modification to an LDAP entry\n");
123 printf(" that has its \"automatic updates\" field set to ON.\n");
124 printf(" This means that the entry will be automatically updated\n");
125 printf(" each month from official University sources like the\n");
126 printf(" Personnel Office. With \"automatic updates\" set to ON,\n");
127 printf(" the following fields will be overwritten each month:\n");
128 printf(" Title, home address and phone,\n");
129 printf(" business address and phone\n");
130 printf(" If you modify any of these fields, you may want to change\n");
131 printf(" the \"automatic updates\" field to OFF so that your\n");
132 printf(" changes will not be overwritten. You may change this\n");
133 printf(" setting by choosing \"u\" at the \"Modify what?\" prompt\n");
138 * Current values for user 'who' are being held in 'mp'. We
139 * should parse up that buffer and fill in the Entry structure.
140 * Once we're done with that, we can find out which fields the
141 * user would like to modify.
144 is_a_group = isgroup();
145 (void) ldap_msgfree(mp);
146 printf(" You now need to specify what field you'd like to modify.\n");
148 if ( verbose || !displayed_choices ) {
149 printf("\n Choices are:\n");
150 printf(" -----------------------\n");
151 print_mod_list(is_a_group);
152 printf("\n Pressing Return will cancel the process.\n");
153 displayed_choices = 1;
155 printf("\n Modify what? ");
157 fetch_buffer(ans, sizeof(ans), stdin);
160 perform_action(ans, dn, is_a_group);
161 if ((mp = find(*rdns, TRUE)) == NULL)
164 (void) ldap_msgfree(mp);
167 ldap_value_free(rdns);
171 /* generic routine for changing any field */
172 void change_field(who, attr)
173 char *who; /* DN of entry we are changing */
174 struct attribute attr; /* attribute to change */
177 #define IS_MOD(x) (!strncasecmp(resp, (x), strlen(resp)))
179 char *get_value(); /* routine to extract values */
180 static char buf[MED_BUF_SIZE]; /* for printing things */
181 static char resp[SMALL_BUF_SIZE]; /* for user input */
182 char *prompt, *prompt2, *more;
183 register int i; /* for looping thru values */
185 static LDAPMod *mods[2] = { &mod }; /* passed to ldap_modify */
186 static char *values[MAX_VALUES]; /* passed to ldap_modify */
191 printf("->change_field(%x, %s)\n", attr, who);
194 * If there is no current value associated with the attribute,
195 * then this is the easy case. Collect one (or more) attributes
196 * from the user, and then call ldap_modify_s() to write the changes
197 * to the LDAP server.
199 for (i = 0; i < MAX_VALUES; i++)
201 if (attr.values == (char **) NULL) {
202 printf("\n No current \"%s\"\n", attr.output_string);
203 values[0] = get_value(attr.quipu_name, "Enter a value");
204 if ( values[0] == NULL )
206 mod.mod_op = LDAP_MOD_REPLACE;
207 mod.mod_type = attr.quipu_name;
208 mod.mod_values = values;
209 for (i = 1; i < MAX_VALUES; i++) {
210 printf(" Do you wish to add an additional value? ");
212 fetch_buffer(resp, sizeof(resp), stdin);
213 if ((resp[0] == 'y') || (resp[0] == 'Y'))
214 values[i] = get_value(attr.quipu_name, "Enter an additional value");
219 if (debug & D_MODIFY) {
220 printf(" ld = 0x%x\n", ld);
221 printf(" who = [%s]\n", who);
222 printf(" mods[0]->mod_op = %1d\n", mods[0]->mod_op);
223 printf(" mods[0]->mod_type = %s\n", mods[0]->mod_type);
224 for (i = 0; mods[0]->mod_values[i] != NULL; i++)
225 printf(" mods[0]->mod_values[%1d] = %s\n", i, mods[0]->mod_values[i]);
228 if (ldap_modify_s(ld, who, mods)) {
233 printf(" Modification of '%s' complete.\n", attr.output_string);
234 ldap_uncache_entry( ld, who );
235 for (i--; i > 0; i--)
236 (void) Free(values[i]);
239 * There are values for this attribute already. In this case,
240 * we want to allow the user to delete all existing values,
241 * add additional values to the ones there already, or just
242 * delete some of the values already present. DIXIE does not
243 * handle modifications where the attribute occurs on the LHS
244 * more than once. So to delete entries and add entries, we
245 * need to call ldap_modify() twice.
249 * If the attribute holds values which are DNs, print them
250 * in a most pleasant way.
252 sprintf(buf, "%s: ", attr.output_string);
253 if (!strcmp(attr.quipu_name, "owner"))
254 (void) print_DN(attr);
256 (void) print_values(attr);
259 printf(" You may now:\n");
260 printf(" Add additional values to the existing ones, OR\n");
261 printf(" Clear all values listed above, OR\n");
262 printf(" Delete one of the values listed above, OR\n");
263 printf(" Replace all of the values above with new ones.\n");
265 printf("\n Add, Clear, Delete, or Replace? ");
267 fetch_buffer(resp, sizeof(resp), stdin);
270 * Bail if they just hit the RETURN key.
272 if (resp[0] == '\0') {
274 printf("\n No changes made.\n");
279 * If the want to clear the values, just do it.
281 mod.mod_type = attr.quipu_name;
282 mod.mod_values = values;
283 if (IS_MOD("clear")) {
284 mod.mod_op = LDAP_MOD_DELETE;
285 mod.mod_values = NULL;
286 if ( verbose && !confirm_action( "All existing values will be removed." )) {
287 printf(" Modification halted.\n");
291 if (debug & D_MODIFY) {
292 printf("Clearing attribute '%s'\n", attr.quipu_name);
293 printf("who = [%s]\n", who);
294 printf("mod = [%d] [%s] [%x]\n", mod.mod_op,
295 mod.mod_type, mod.mod_values);
298 if (ldap_modify_s(ld, who, mods)) {
303 printf(" '%s' has been cleared.\n", attr.output_string);
304 ldap_uncache_entry( ld, who );
309 prompt = "Enter the value you wish to add";
310 more = " Add an additional value? ";
311 prompt2 = "Enter another value you wish to add";
312 mod.mod_op = LDAP_MOD_ADD;
314 else if (IS_MOD("delete")) {
315 prompt = "Enter the value you wish to delete";
316 more = " Delete an additional value? ";
317 prompt2 = "Enter another value you wish to delete";
318 mod.mod_op = LDAP_MOD_DELETE;
320 else if (IS_MOD("replace")) {
321 prompt = "Enter the new value";
322 more = " Add an additional value? ";
323 prompt2 = "Enter another value you wish to add";
324 mod.mod_op = LDAP_MOD_REPLACE;
325 if ( verbose && !confirm_action( "All existing values will be overwritten with the new values you are about to enter." )) {
326 printf(" Modification halted.\n");
332 printf(" No changes made.\n");
336 values[0] = get_value(attr.quipu_name, prompt);
337 for (i = 1; i < MAX_VALUES; i++) {
340 fetch_buffer(resp, sizeof(resp), stdin);
341 if ((resp[0] == 'y') || (resp[0] == 'Y'))
342 values[i] = get_value(attr.quipu_name, prompt2);
347 /* if the first value in the value-array is NULL, bail */
348 if (values[0] == NULL) {
350 printf(" No modification made.\n");
354 if (debug & D_MODIFY) {
355 printf(" ld = 0x%x\n", ld);
356 printf(" who = [%s]\n", who);
357 printf(" mods[0]->mod_op = %1d\n", mods[0]->mod_op);
358 printf(" mods[0]->mod_type = %s\n", mods[0]->mod_type);
359 for (i = 0; mods[0]->mod_values[i] != NULL; i++)
360 printf(" mods[0]->mod_values[%1d] = %s\n", i, mods[0]->mod_values[i]);
363 if (ldap_modify_s(ld, who, mods)) {
368 printf(" Modifications to '%s' complete.\n", attr.output_string);
369 ldap_uncache_entry( ld, who );
370 for (i--; i > 0; i--)
371 (void) Free(values[i]);
377 * These are used to size the buffers we use when collecting values that
378 * can cross more than one line.
382 #define MAX_DESC_LINES 24
383 #define INTL_ADDR_LIMIT 30
385 char *get_value(id, prompt)
388 char *cp; /* for the Malloc() */
389 int count; /* line # of new value -- if multiline */
390 int multiline = 0; /* 1 if this value is multiline */
391 static char line[LINE_SIZE]; /* raw line from user */
392 static char buffer[MAX_DESC_LINES * LINE_SIZE]; /* holds ALL of the
396 printf("->get_value(%s, %s)\n", id, prompt);
398 /* if this is a URL, have another routine handle this */
399 if (!strcmp(id, "labeledURL"))
403 * To start with, we have one line of input from the user.
405 * Addresses and multiline description can span multiple lines.
406 * Other attributes may not.
409 (void) memset(buffer, 0, sizeof(buffer));
411 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress") || !strcmp(id, "multiLineDescription") || !strcmp(id, "vacationMessage"))
413 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress"))
416 printf("\n %s:\n", prompt);
421 printf(" %1d: ", count);
425 fetch_buffer(line, sizeof(line), stdin);
431 * Screen out dangerous e-mail addresses of the form:
435 * and addresses that have no '@' symbol at all.
437 if (!strcmp(id, "mail")) {
441 /* if this is a group, don't worry */
445 /* if this address is not @umich.edu, don't worry */
446 /* ...unless there is no '@' at all! */
448 if ((tmp2 = strrchr(tmp, '@')) == NULL) {
450 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 );
456 if (strcasecmp(tmp2, "umich.edu"))
459 /* if not of the form uid@umich.edu, don't worry */
460 if ((i = attr_to_index("uid")) < 0)
462 if (strcasecmp(tmp, *(Entry.attrs[i].values)))
465 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);
469 printf(" Please enter a legal e-mail address (or press RETURN to stop)\n");
476 * If the attribute which we are gathering is a "owner"
477 * then we should lookup the name. The user is going to
478 * either have to change the search base before doing the
479 * modify, or the person is going to have to be within the
480 * scope of the current search base, or they will need to
483 if (!strcmp(id, "owner")) {
484 LDAPMessage *lmp, *elmp;
487 lmp = find(line, FALSE);
488 if (lmp == (LDAPMessage *) NULL) {
489 printf(" Could not find \"%s\" in the Directory\n", line);
491 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);
494 elmp = ldap_first_entry(ld, lmp);
495 if (lmp == (LDAPMessage *) NULL) {
496 ldap_perror(ld, "ldap_first_entry");
499 tmp = ldap_get_dn(ld, elmp);
502 (void) ldap_msgfree(lmp);
506 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress")) {
507 if (strlen(line) > INTL_ADDR_LIMIT) {
508 printf(" The international standard for addresses only allows for 30-character lines\n");
509 printf(" Please re-enter your last line.\n");
515 * Separate lines of multiline attribute values with
516 * dollar signs. Copy this line into the buffer we
517 * use to collect up all of the user-supplied input
518 * lines. If this is not a multiline attribute, we
522 (void) strcat(buffer, "$");
523 (void) strcat(buffer, line);
526 if ((count > MAX_LINES) && (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress"))) {
527 printf(" The international standard for addresses only allows for six lines\n");
531 if ((count > MAX_DESC_LINES) && !strcmp(id, "multiLineDescription")) {
532 printf(" We only allow %d lines of description\n", MAX_DESC_LINES);
537 if (buffer[0] == '\0')
540 if (debug & D_MODIFY)
541 printf(" Value is [%s]\n", buffer);
543 cp = (char *) Malloc((unsigned) (strlen(buffer) + 1));
548 void set_boolean(who, attr)
549 char *who; /* DN of entry we are changing */
550 struct attribute attr; /* boolean attribute to change */
553 extern char * fetch_boolean_value();
554 static char response[16];
555 static char *newsetting[2] = { NULL, NULL };
556 LDAPMod mod, *mods[2];
560 printf("->set_boolean(%s, %s)\n", who, attr.quipu_name);
563 mods[1] = (LDAPMod *) NULL;
564 mod.mod_op = LDAP_MOD_REPLACE;
565 mod.mod_type = attr.quipu_name;
566 mod.mod_values = newsetting;
568 /* fetch the current setting */
569 if ((cp = fetch_boolean_value(who, attr)) == NULL)
571 if (!strcmp(cp, "TRUE"))
572 newsetting[0] = "FALSE";
573 else if (!strcmp(cp, "FALSE"))
574 newsetting[0] = "TRUE";
576 printf(" This field needs to be set to either TRUE or to FALSE.\n");
577 printf(" \"%s\" is not a legal value. Please set this field to either TRUE or to FALSE.\n", cp);
578 newsetting[0] = "FALSE";
581 /* see if they want to change it */
583 printf(" The current value of this field is %s.\n", cp);
584 printf(" Should I change the value of this field to %s?\n",
586 printf(" Please enter Y for yes, N for no, or RETURN to cancel: ");
588 (void) fetch_buffer(response, sizeof(response), stdin);
589 for (s = response; isspace(*s); s++)
591 if ((*s == 'y') || (*s == 'Y')) {
592 if (ldap_modify_s(ld, who, mods)) {
597 printf(" Setting has been changed\n");
598 ldap_uncache_entry(ld, who);
602 printf(" Setting has not been changed\n");
607 void set_updates(who)
611 extern char * fetch_boolean_value();
612 static char response[16];
613 static char value[6];
614 static char *newsetting[2] = { value, NULL };
615 LDAPMod mod, *mods[2];
616 struct attribute attr;
620 printf("->set_updates(%s)\n", who);
623 mods[1] = (LDAPMod *) NULL;
624 mod.mod_op = LDAP_MOD_REPLACE;
625 mod.mod_type = "noBatchUpdates";
626 mod.mod_values = newsetting;
627 /* explain what the implications are */
629 printf("\n By default, updates that are received from the Personnel\n");
630 printf(" Office and the Office of the Registrar are applied to all\n");
631 printf(" entries in the LDAP database each month. Sometimes this\n");
632 printf(" feature is undesirable. For example, if you maintain your\n");
633 printf(" entry in the LDAP database manually, you may not want to\n");
634 printf(" have these updates applied to your entry, possibly overwriting\n");
635 printf(" correct information with out-dated information.\n\n");
638 /* fetch the current setting */
639 attr.quipu_name = "noBatchUpdates";
640 if ((cp = fetch_boolean_value(who, attr)) == NULL)
642 if (!strcmp(cp, "TRUE"))
643 printf(" Automatic updates are currently turned OFF\n");
644 else if (!strcmp(cp, "FALSE"))
645 printf(" Automatic updates are currently turned ON\n");
647 fprintf(stderr, " Unknown update flag -> [%s]\n", cp);
651 /* see if they want to change it */
652 printf("\n Change this setting [no]? ");
654 (void) fetch_buffer(response, sizeof(response), stdin);
655 for (s = response; isspace(*s); s++)
657 if ((*s == 'y') || (*s == 'Y')) {
658 if (!strcmp(cp, "TRUE"))
659 strcpy(value, "FALSE");
661 strcpy(value, "TRUE");
662 if (ldap_modify_s(ld, who, mods)) {
667 printf(" Setting has been changed\n");
668 ldap_uncache_entry( ld, who );
672 printf(" Setting has not been changed\n");
677 print_mod_list(group)
680 register int i, j = 1;
681 extern struct attribute attrlist[];
684 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
685 if (attrlist[i].flags & ATTR_FLAG_GROUP_MOD) {
686 printf(" %2d -> %s\n", j, attrlist[i].output_string);
691 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
692 if (attrlist[i].flags & ATTR_FLAG_PERSON_MOD) {
693 printf(" %2d -> %s\n", j, attrlist[i].output_string);
698 printf(" ? -> Print this list\n\n");
699 printf(" Press the RETURN key without typing a number to quit.\n");
702 printf(" To add nicknames, send mail to x500-nicknames@umich.edu\n");
706 perform_action(choice, dn, group)
712 register int i, j = 1;
713 extern struct attribute attrlist[];
714 extern void mod_addrDN(), change_field(), set_boolean();
716 selection = atoi(choice);
718 printf("\n Choices are:\n");
719 printf(" -----------------------\n");
720 print_mod_list(group);
726 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
727 if (attrlist[i].flags & ATTR_FLAG_GROUP_MOD) {
734 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
735 if (attrlist[i].flags & ATTR_FLAG_PERSON_MOD) {
743 if (attrlist[i].quipu_name == NULL) {
744 printf("\n Choices are:\n");
745 printf(" -----------------------\n");
746 print_mod_list(group);
750 if (attrlist[i].mod_func == change_field)
751 (*attrlist[i].mod_func)(dn, Entry.attrs[attr_to_index(attrlist[i].quipu_name)]);
752 else if (attrlist[i].mod_func == mod_addrDN)
753 (*attrlist[i].mod_func)(dn, i);
754 else if (attrlist[i].mod_func == set_boolean)
755 (*attrlist[i].mod_func)(dn, Entry.attrs[attr_to_index(attrlist[i].quipu_name)]);
757 (*attrlist[i].mod_func)(dn);
761 static char * get_URL()
763 char *rvalue, label[MED_BUF_SIZE], url[MED_BUF_SIZE];
766 printf(" First, enter the URL. (Example: http://www.us.itd.umich.edu/users/).\n");
767 printf(" The URL may be up to %d characters long.\n", MED_BUF_SIZE);
772 (void) fetch_buffer(url, sizeof(url), stdin);
775 if (check_URL(url) == 0)
777 printf(" A URL may not have any spaces or tabs in it. Please re-enter your URL.\n\n");
780 printf("\n Now please enter a descriptive label for this URL\n");
784 (void) fetch_buffer(label, sizeof(label), stdin);
785 } while (label[0] == '\0');
786 rvalue = (char *) Malloc((unsigned) (strlen(url) + 2 + strlen(label)));
787 sprintf(rvalue, "%s %s", url, label);
788 return((char *) rvalue);
791 static check_URL(url)
796 for (cp = url; *cp != '\n' && *cp != '\0'; cp++) {
806 mod_perror( LDAP *ld )
811 ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ld_errno);
814 if (( ld == NULL ) || ( ld_errno != LDAP_UNAVAILABLE &&
815 ld_errno != LDAP_UNWILLING_TO_PERFORM ))
817 ldap_perror( ld, "modify" );
821 fprintf( stderr, "\n modify: failed because part of the online directory is not able\n" );
822 fprintf( stderr, " to be modified right now" );
823 if ( ld_errno == LDAP_UNAVAILABLE ) {
824 fprintf( stderr, " or is temporarily unavailable" );
826 fprintf( stderr, ".\n Please try again later.\n" );