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.
22 #include <sys/types.h>
28 extern struct entry Entry;
32 extern LDAPMessage *find();
42 void set_updates(); /* routine to modify noBatchUpdates */
44 LDAPMessage *mp; /* returned from find() */
45 char *dn; /* distinguished name */
46 char **rdns; /* for fiddling with the DN */
47 char name[MED_BUF_SIZE]; /* entry to modify */
48 int displayed_choices = 0;
49 static char ans[SMALL_BUF_SIZE]; /* for holding user input */
51 static char printed_warning = 0; /* for use with the */
52 struct attribute no_batch_update_attr;
53 extern char * fetch_boolean_value();
55 int is_a_group; /* TRUE if it is; FALSE otherwise */
57 extern int bind_status;
61 printf("->modify(%s)\n", who);
64 * Require them to bind first if the are modifying a group.
66 if (bind_status == UD_NOT_BOUND) {
67 if (auth((char *) NULL, 1) < 0)
72 * First, decide what entry we are going to modify. If the
73 * user has not included a name on the modify command line,
74 * we will use the person who was last looked up with a find
75 * command. If there is no value there either, we don't know
78 * Once we know who to modify, be sure that they exist, and
83 printf(" Enter the name of the person or\n");
84 printf(" group whose entry you want to modify: ");
87 printf(" Modify whose entry? ");
89 fetch_buffer(name, sizeof(name), stdin);
95 if ((mp = find(who, TRUE)) == NULL) {
96 (void) ldap_msgfree(mp);
97 printf(" Could not locate \"%s\" in the Directory.\n", who);
100 dn = ldap_get_dn(ld, ldap_first_entry(ld, mp));
101 rdns = ldap_explode_dn(dn, TRUE);
103 printf("\n Modifying Directory entry of \"%s\"\n", *rdns);
107 * If verbose mode is turned on and the user has not set a value
108 * for noBatchUpdates, warn them that what they are about to do
109 * may be overwritten automatically by that Stinkbug.
111 no_batch_update_attr.quipu_name = "noBatchUpdates";
112 (void) fetch_boolean_value(dn, no_batch_update_attr);
113 if (verbose && !printed_warning && (ld->ld_errno == LDAP_NO_SUCH_ATTRIBUTE)) {
115 printf("\n WARNING!\n");
116 printf(" You are about to make a modification to an LDAP entry\n");
117 printf(" that has its \"automatic updates\" field set to ON.\n");
118 printf(" This means that the entry will be automatically updated\n");
119 printf(" each month from official University sources like the\n");
120 printf(" Personnel Office. With \"automatic updates\" set to ON,\n");
121 printf(" the following fields will be overwritten each month:\n");
122 printf(" Title, home address and phone,\n");
123 printf(" business address and phone\n");
124 printf(" If you modify any of these fields, you may want to change\n");
125 printf(" the \"automatic updates\" field to OFF so that your\n");
126 printf(" changes will not be overwritten. You may change this\n");
127 printf(" setting by choosing \"u\" at the \"Modify what?\" prompt\n");
132 * Current values for user 'who' are being held in 'mp'. We
133 * should parse up that buffer and fill in the Entry structure.
134 * Once we're done with that, we can find out which fields the
135 * user would like to modify.
138 is_a_group = isgroup();
139 (void) ldap_msgfree(mp);
140 printf(" You now need to specify what field you'd like to modify.\n");
142 if ( verbose || !displayed_choices ) {
143 printf("\n Choices are:\n");
144 printf(" -----------------------\n");
145 print_mod_list(is_a_group);
146 printf("\n Pressing Return will cancel the process.\n");
147 displayed_choices = 1;
149 printf("\n Modify what? ");
151 fetch_buffer(ans, sizeof(ans), stdin);
154 perform_action(ans, dn, is_a_group);
155 if ((mp = find(*rdns, TRUE)) == NULL)
158 (void) ldap_msgfree(mp);
161 ldap_value_free(rdns);
165 /* generic routine for changing any field */
166 void change_field(who, attr)
167 char *who; /* DN of entry we are changing */
168 struct attribute attr; /* attribute to change */
171 #define IS_MOD(x) (!strncasecmp(resp, (x), strlen(resp)))
173 char *get_value(); /* routine to extract values */
174 static char buf[MED_BUF_SIZE]; /* for printing things */
175 static char resp[SMALL_BUF_SIZE]; /* for user input */
176 char *prompt, *prompt2, *more;
177 register int i; /* for looping thru values */
179 static LDAPMod *mods[2] = { &mod }; /* passed to ldap_modify */
180 static char *values[MAX_VALUES]; /* passed to ldap_modify */
185 printf("->change_field(%x, %s)\n", attr, who);
188 * If there is no current value associated with the attribute,
189 * then this is the easy case. Collect one (or more) attributes
190 * from the user, and then call ldap_modify_s() to write the changes
191 * to the LDAP server.
193 for (i = 0; i < MAX_VALUES; i++)
195 if (attr.values == (char **) NULL) {
196 printf("\n No current \"%s\"\n", attr.output_string);
197 values[0] = get_value(attr.quipu_name, "Enter a value");
198 if ( values[0] == NULL )
200 mod.mod_op = LDAP_MOD_REPLACE;
201 mod.mod_type = attr.quipu_name;
202 mod.mod_values = values;
203 for (i = 1; i < MAX_VALUES; i++) {
204 printf(" Do you wish to add an additional value? ");
206 fetch_buffer(resp, sizeof(resp), stdin);
207 if ((resp[0] == 'y') || (resp[0] == 'Y'))
208 values[i] = get_value(attr.quipu_name, "Enter an additional value");
213 if (debug & D_MODIFY) {
214 printf(" ld = 0x%x\n", ld);
215 printf(" who = [%s]\n", who);
216 printf(" mods[0]->mod_op = %1d\n", mods[0]->mod_op);
217 printf(" mods[0]->mod_type = %s\n", mods[0]->mod_type);
218 for (i = 0; mods[0]->mod_values[i] != NULL; i++)
219 printf(" mods[0]->mod_values[%1d] = %s\n", i, mods[0]->mod_values[i]);
222 if (ldap_modify_s(ld, who, mods)) {
227 printf(" Modification of '%s' complete.\n", attr.output_string);
228 ldap_uncache_entry( ld, who );
229 for (i--; i > 0; i--)
230 (void) Free(values[i]);
233 * There are values for this attribute already. In this case,
234 * we want to allow the user to delete all existing values,
235 * add additional values to the ones there already, or just
236 * delete some of the values already present. DIXIE does not
237 * handle modifications where the attribute occurs on the LHS
238 * more than once. So to delete entries and add entries, we
239 * need to call ldap_modify() twice.
243 * If the attribute holds values which are DNs, print them
244 * in a most pleasant way.
246 sprintf(buf, "%s: ", attr.output_string);
247 if (!strcmp(attr.quipu_name, "owner"))
248 (void) print_DN(attr);
250 (void) print_values(attr);
253 printf(" You may now:\n");
254 printf(" Add additional values to the existing ones, OR\n");
255 printf(" Clear all values listed above, OR\n");
256 printf(" Delete one of the values listed above, OR\n");
257 printf(" Replace all of the values above with new ones.\n");
259 printf("\n Add, Clear, Delete, or Replace? ");
261 fetch_buffer(resp, sizeof(resp), stdin);
264 * Bail if they just hit the RETURN key.
266 if (resp[0] == '\0') {
268 printf("\n No changes made.\n");
273 * If the want to clear the values, just do it.
275 mod.mod_type = attr.quipu_name;
276 mod.mod_values = values;
277 if (IS_MOD("clear")) {
278 mod.mod_op = LDAP_MOD_DELETE;
279 mod.mod_values = NULL;
280 if ( verbose && !confirm_action( "All existing values will be removed." )) {
281 printf(" Modification halted.\n");
285 if (debug & D_MODIFY) {
286 printf("Clearing attribute '%s'\n", attr.quipu_name);
287 printf("who = [%s]\n", who);
288 printf("mod = [%d] [%s] [%x]\n", mod.mod_op,
289 mod.mod_type, mod.mod_values);
292 if (ldap_modify_s(ld, who, mods)) {
297 printf(" '%s' has been cleared.\n", attr.output_string);
298 ldap_uncache_entry( ld, who );
303 prompt = "Enter the value you wish to add";
304 more = " Add an additional value? ";
305 prompt2 = "Enter another value you wish to add";
306 mod.mod_op = LDAP_MOD_ADD;
308 else if (IS_MOD("delete")) {
309 prompt = "Enter the value you wish to delete";
310 more = " Delete an additional value? ";
311 prompt2 = "Enter another value you wish to delete";
312 mod.mod_op = LDAP_MOD_DELETE;
314 else if (IS_MOD("replace")) {
315 prompt = "Enter the new value";
316 more = " Add an additional value? ";
317 prompt2 = "Enter another value you wish to add";
318 mod.mod_op = LDAP_MOD_REPLACE;
319 if ( verbose && !confirm_action( "All existing values will be overwritten with the new values you are about to enter." )) {
320 printf(" Modification halted.\n");
326 printf(" No changes made.\n");
330 values[0] = get_value(attr.quipu_name, prompt);
331 for (i = 1; i < MAX_VALUES; i++) {
334 fetch_buffer(resp, sizeof(resp), stdin);
335 if ((resp[0] == 'y') || (resp[0] == 'Y'))
336 values[i] = get_value(attr.quipu_name, prompt2);
341 /* if the first value in the value-array is NULL, bail */
342 if (values[0] == NULL) {
344 printf(" No modification made.\n");
348 if (debug & D_MODIFY) {
349 printf(" ld = 0x%x\n", ld);
350 printf(" who = [%s]\n", who);
351 printf(" mods[0]->mod_op = %1d\n", mods[0]->mod_op);
352 printf(" mods[0]->mod_type = %s\n", mods[0]->mod_type);
353 for (i = 0; mods[0]->mod_values[i] != NULL; i++)
354 printf(" mods[0]->mod_values[%1d] = %s\n", i, mods[0]->mod_values[i]);
357 if (ldap_modify_s(ld, who, mods)) {
362 printf(" Modifications to '%s' complete.\n", attr.output_string);
363 ldap_uncache_entry( ld, who );
364 for (i--; i > 0; i--)
365 (void) Free(values[i]);
371 * These are used to size the buffers we use when collecting values that
372 * can cross more than one line.
376 #define MAX_DESC_LINES 24
377 #define INTL_ADDR_LIMIT 30
379 char *get_value(id, prompt)
382 char *cp; /* for the Malloc() */
383 int count; /* line # of new value -- if multiline */
384 int multiline = 0; /* 1 if this value is multiline */
385 static char line[LINE_SIZE]; /* raw line from user */
386 static char buffer[MAX_DESC_LINES * LINE_SIZE]; /* holds ALL of the
388 extern void * Malloc();
389 static char * get_URL();
393 printf("->get_value(%s, %s)\n", id, prompt);
395 /* if this is a URL, have another routine handle this */
396 if (!strcmp(id, "labeledURL"))
400 * To start with, we have one line of input from the user.
402 * Addresses and multiline description can span multiple lines.
403 * Other attributes may not.
406 (void) memset(buffer, 0, sizeof(buffer));
408 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress") || !strcmp(id, "multiLineDescription") || !strcmp(id, "vacationMessage"))
410 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress"))
413 printf("\n %s:\n", prompt);
418 printf(" %1d: ", count);
422 fetch_buffer(line, sizeof(line), stdin);
428 * Screen out dangerous e-mail addresses of the form:
432 * and addresses that have no '@' symbol at all.
434 if (!strcmp(id, "mail")) {
438 /* if this is a group, don't worry */
442 /* if this address is not @umich.edu, don't worry */
443 /* ...unless there is no '@' at all! */
445 if ((tmp2 = strrchr(tmp, '@')) == NULL) {
447 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 );
453 if (strcasecmp(tmp2, "umich.edu"))
456 /* if not of the form uid@umich.edu, don't worry */
457 if ((i = attr_to_index("uid")) < 0)
459 if (strcasecmp(tmp, *(Entry.attrs[i].values)))
462 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);
466 printf(" Please enter a legal e-mail address (or press RETURN to stop)\n");
473 * If the attribute which we are gathering is a "owner"
474 * then we should lookup the name. The user is going to
475 * either have to change the search base before doing the
476 * modify, or the person is going to have to be within the
477 * scope of the current search base, or they will need to
480 if (!strcmp(id, "owner")) {
481 LDAPMessage *lmp, *elmp;
484 lmp = find(line, FALSE);
485 if (lmp == (LDAPMessage *) NULL) {
486 printf(" Could not find \"%s\" in the Directory\n", line);
488 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);
491 elmp = ldap_first_entry(ld, lmp);
492 if (lmp == (LDAPMessage *) NULL) {
493 ldap_perror(ld, "ldap_first_entry");
496 tmp = ldap_get_dn(ld, elmp);
498 (void) ldap_msgfree(lmp);
502 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress")) {
503 if (strlen(line) > INTL_ADDR_LIMIT) {
504 printf(" The international standard for addresses only allows for 30-character lines\n");
505 printf(" Please re-enter your last line.\n");
511 * Separate lines of multiline attribute values with
512 * dollar signs. Copy this line into the buffer we
513 * use to collect up all of the user-supplied input
514 * lines. If this is not a multiline attribute, we
518 (void) strcat(buffer, "$");
519 (void) strcat(buffer, line);
522 if ((count > MAX_LINES) && (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress"))) {
523 printf(" The international standard for addresses only allows for six lines\n");
527 if ((count > MAX_DESC_LINES) && !strcmp(id, "multiLineDescription")) {
528 printf(" We only allow %d lines of description\n", MAX_DESC_LINES);
533 if (buffer[0] == '\0')
536 if (debug & D_MODIFY)
537 printf(" Value is [%s]\n", buffer);
539 cp = (char *) Malloc((unsigned) (strlen(buffer) + 1));
544 void set_boolean(who, attr)
545 char *who; /* DN of entry we are changing */
546 struct attribute attr; /* boolean attribute to change */
549 extern char * fetch_boolean_value();
550 static char response[16];
551 static char *newsetting[2] = { NULL, NULL };
552 LDAPMod mod, *mods[2];
556 printf("->set_boolean(%s, %s)\n", who, attr.quipu_name);
559 mods[1] = (LDAPMod *) NULL;
560 mod.mod_op = LDAP_MOD_REPLACE;
561 mod.mod_type = attr.quipu_name;
562 mod.mod_values = newsetting;
564 /* fetch the current setting */
565 if ((cp = fetch_boolean_value(who, attr)) == NULL)
567 if (!strcmp(cp, "TRUE"))
568 newsetting[0] = "FALSE";
569 else if (!strcmp(cp, "FALSE"))
570 newsetting[0] = "TRUE";
572 printf(" This field needs to be set to either TRUE or to FALSE.\n");
573 printf(" \"%s\" is not a legal value. Please set this field to either TRUE or to FALSE.\n", cp);
574 newsetting[0] = "FALSE";
577 /* see if they want to change it */
579 printf(" The current value of this field is %s.\n", cp);
580 printf(" Should I change the value of this field to %s?\n",
582 printf(" Please enter Y for yes, N for no, or RETURN to cancel: ");
584 (void) fetch_buffer(response, sizeof(response), stdin);
585 for (s = response; isspace(*s); s++)
587 if ((*s == 'y') || (*s == 'Y')) {
588 if (ldap_modify_s(ld, who, mods)) {
593 printf(" Setting has been changed\n");
594 ldap_uncache_entry(ld, who);
598 printf(" Setting has not been changed\n");
603 void set_updates(who)
607 extern char * fetch_boolean_value();
608 static char response[16];
609 static char value[6];
610 static char *newsetting[2] = { value, NULL };
611 LDAPMod mod, *mods[2];
612 struct attribute attr;
616 printf("->set_updates(%s)\n", who);
619 mods[1] = (LDAPMod *) NULL;
620 mod.mod_op = LDAP_MOD_REPLACE;
621 mod.mod_type = "noBatchUpdates";
622 mod.mod_values = newsetting;
623 /* explain what the implications are */
625 printf("\n By default, updates that are received from the Personnel\n");
626 printf(" Office and the Office of the Registrar are applied to all\n");
627 printf(" entries in the LDAP database each month. Sometimes this\n");
628 printf(" feature is undesirable. For example, if you maintain your\n");
629 printf(" entry in the LDAP database manually, you may not want to\n");
630 printf(" have these updates applied to your entry, possibly overwriting\n");
631 printf(" correct information with out-dated information.\n\n");
634 /* fetch the current setting */
635 attr.quipu_name = "noBatchUpdates";
636 if ((cp = fetch_boolean_value(who, attr)) == NULL)
638 if (!strcmp(cp, "TRUE"))
639 printf(" Automatic updates are currently turned OFF\n");
640 else if (!strcmp(cp, "FALSE"))
641 printf(" Automatic updates are currently turned ON\n");
643 fprintf(stderr, " Unknown update flag -> [%s]\n", cp);
647 /* see if they want to change it */
648 printf("\n Change this setting [no]? ");
650 (void) fetch_buffer(response, sizeof(response), stdin);
651 for (s = response; isspace(*s); s++)
653 if ((*s == 'y') || (*s == 'Y')) {
654 if (!strcmp(cp, "TRUE"))
655 strcpy(value, "FALSE");
657 strcpy(value, "TRUE");
658 if (ldap_modify_s(ld, who, mods)) {
663 printf(" Setting has been changed\n");
664 ldap_uncache_entry( ld, who );
668 printf(" Setting has not been changed\n");
673 print_mod_list(group)
676 register int i, j = 1;
677 extern struct attribute attrlist[];
680 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
681 if (attrlist[i].flags & ATTR_FLAG_GROUP_MOD) {
682 printf(" %2d -> %s\n", j, attrlist[i].output_string);
687 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
688 if (attrlist[i].flags & ATTR_FLAG_PERSON_MOD) {
689 printf(" %2d -> %s\n", j, attrlist[i].output_string);
694 printf(" ? -> Print this list\n\n");
695 printf(" Press the RETURN key without typing a number to quit.\n");
698 printf(" To add nicknames, send mail to x500-nicknames@umich.edu\n");
702 perform_action(choice, dn, group)
708 register int i, j = 1;
709 extern struct attribute attrlist[];
710 extern void mod_addrDN(), change_field(), set_boolean();
712 selection = atoi(choice);
714 printf("\n Choices are:\n");
715 printf(" -----------------------\n");
716 print_mod_list(group);
722 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
723 if (attrlist[i].flags & ATTR_FLAG_GROUP_MOD) {
730 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
731 if (attrlist[i].flags & ATTR_FLAG_PERSON_MOD) {
739 if (attrlist[i].quipu_name == NULL) {
740 printf("\n Choices are:\n");
741 printf(" -----------------------\n");
742 print_mod_list(group);
746 if (attrlist[i].mod_func == change_field)
747 (*attrlist[i].mod_func)(dn, Entry.attrs[attr_to_index(attrlist[i].quipu_name)]);
748 else if (attrlist[i].mod_func == mod_addrDN)
749 (*attrlist[i].mod_func)(dn, i);
750 else if (attrlist[i].mod_func == set_boolean)
751 (*attrlist[i].mod_func)(dn, Entry.attrs[attr_to_index(attrlist[i].quipu_name)]);
753 (*attrlist[i].mod_func)(dn);
757 static char * get_URL()
759 char *rvalue, label[MED_BUF_SIZE], url[MED_BUF_SIZE];
760 static int check_URL();
761 extern void * Malloc();
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 )
806 if ( ld == NULL || ( ld->ld_errno != LDAP_UNAVAILABLE &&
807 ld->ld_errno != LDAP_UNWILLING_TO_PERFORM )) {
808 ldap_perror( ld, "modify" );
812 fprintf( stderr, "\n modify: failed because part of the online directory is not able\n" );
813 fprintf( stderr, " to be modified right now" );
814 if ( ld->ld_errno == LDAP_UNAVAILABLE ) {
815 fprintf( stderr, " or is temporarily unavailable" );
817 fprintf( stderr, ".\n Please try again later.\n" );