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.
20 #include <sys/types.h>
26 extern struct entry Entry;
30 extern LDAPMessage *find();
40 void set_updates(); /* routine to modify noBatchUpdates */
42 LDAPMessage *mp; /* returned from find() */
43 char *dn; /* distinguished name */
44 char **rdns; /* for fiddling with the DN */
45 char name[MED_BUF_SIZE]; /* entry to modify */
46 int displayed_choices = 0;
47 static char ans[SMALL_BUF_SIZE]; /* for holding user input */
49 static char printed_warning = 0; /* for use with the */
50 struct attribute no_batch_update_attr;
51 extern char * fetch_boolean_value();
53 int is_a_group; /* TRUE if it is; FALSE otherwise */
55 extern int bind_status;
59 printf("->modify(%s)\n", who);
62 * Require them to bind first if the are modifying a group.
64 if (bind_status == UD_NOT_BOUND) {
65 if (auth((char *) NULL, 1) < 0)
70 * First, decide what entry we are going to modify. If the
71 * user has not included a name on the modify command line,
72 * we will use the person who was last looked up with a find
73 * command. If there is no value there either, we don't know
76 * Once we know who to modify, be sure that they exist, and
81 printf(" Enter the name of the person or\n");
82 printf(" group whose entry you want to modify: ");
85 printf(" Modify whose entry? ");
87 fetch_buffer(name, sizeof(name), stdin);
93 if ((mp = find(who, TRUE)) == NULL) {
94 (void) ldap_msgfree(mp);
95 printf(" Could not locate \"%s\" in the Directory.\n", who);
98 dn = ldap_get_dn(ld, ldap_first_entry(ld, mp));
99 rdns = ldap_explode_dn(dn, TRUE);
101 printf("\n Modifying Directory entry of \"%s\"\n", *rdns);
105 * If verbose mode is turned on and the user has not set a value
106 * for noBatchUpdates, warn them that what they are about to do
107 * may be overwritten automatically by that Stinkbug.
109 no_batch_update_attr.quipu_name = "noBatchUpdates";
110 (void) fetch_boolean_value(dn, no_batch_update_attr);
111 if (verbose && !printed_warning && (ld->ld_errno == LDAP_NO_SUCH_ATTRIBUTE)) {
113 printf("\n WARNING!\n");
114 printf(" You are about to make a modification to an X.500 entry\n");
115 printf(" that has its \"automatic updates\" field set to ON.\n");
116 printf(" This means that the entry will be automatically updated\n");
117 printf(" each month from official University sources like the\n");
118 printf(" Personnel Office. With \"automatic updates\" set to ON,\n");
119 printf(" the following fields will be overwritten each month:\n");
120 printf(" Title, home address and phone,\n");
121 printf(" business address and phone\n");
122 printf(" If you modify any of these fields, you may want to change\n");
123 printf(" the \"automatic updates\" field to OFF so that your\n");
124 printf(" changes will not be overwritten. You may change this\n");
125 printf(" setting by choosing \"u\" at the \"Modify what?\" prompt\n");
130 * Current values for user 'who' are being held in 'mp'. We
131 * should parse up that buffer and fill in the Entry structure.
132 * Once we're done with that, we can find out which fields the
133 * user would like to modify.
136 is_a_group = isgroup();
137 (void) ldap_msgfree(mp);
138 printf(" You now need to specify what field you'd like to modify.\n");
140 if ( verbose || !displayed_choices ) {
141 printf("\n Choices are:\n");
142 printf(" -----------------------\n");
143 print_mod_list(is_a_group);
144 printf("\n Pressing Return will cancel the process.\n");
145 displayed_choices = 1;
147 printf("\n Modify what? ");
149 fetch_buffer(ans, sizeof(ans), stdin);
152 perform_action(ans, dn, is_a_group);
153 if ((mp = find(*rdns, TRUE)) == NULL)
156 (void) ldap_msgfree(mp);
159 ldap_value_free(rdns);
163 /* generic routine for changing any field */
164 void change_field(who, attr)
165 char *who; /* DN of entry we are changing */
166 struct attribute attr; /* attribute to change */
169 #define IS_MOD(x) (!strncasecmp(resp, (x), strlen(resp)))
171 char *get_value(); /* routine to extract values */
172 static char buf[MED_BUF_SIZE]; /* for printing things */
173 static char resp[SMALL_BUF_SIZE]; /* for user input */
174 char *prompt, *prompt2, *more;
175 register int i; /* for looping thru values */
177 static LDAPMod *mods[2] = { &mod }; /* passed to ldap_modify */
178 static char *values[MAX_VALUES]; /* passed to ldap_modify */
183 printf("->change_field(%x, %s)\n", attr, who);
186 * If there is no current value associated with the attribute,
187 * then this is the easy case. Collect one (or more) attributes
188 * from the user, and then call ldap_modify_s() to write the changes
189 * to the LDAP server.
191 for (i = 0; i < MAX_VALUES; i++)
193 if (attr.values == (char **) NULL) {
194 printf("\n No current \"%s\"\n", attr.output_string);
195 values[0] = get_value(attr.quipu_name, "Enter a value");
196 if ( values[0] == NULL )
198 mod.mod_op = LDAP_MOD_REPLACE;
199 mod.mod_type = attr.quipu_name;
200 mod.mod_values = values;
201 for (i = 1; i < MAX_VALUES; i++) {
202 printf(" Do you wish to add an additional value? ");
204 fetch_buffer(resp, sizeof(resp), stdin);
205 if ((resp[0] == 'y') || (resp[0] == 'Y'))
206 values[i] = get_value(attr.quipu_name, "Enter an additional value");
211 if (debug & D_MODIFY) {
212 printf(" ld = 0x%x\n", ld);
213 printf(" who = [%s]\n", who);
214 printf(" mods[0]->mod_op = %1d\n", mods[0]->mod_op);
215 printf(" mods[0]->mod_type = %s\n", mods[0]->mod_type);
216 for (i = 0; mods[0]->mod_values[i] != NULL; i++)
217 printf(" mods[0]->mod_values[%1d] = %s\n", i, mods[0]->mod_values[i]);
220 if (ldap_modify_s(ld, who, mods)) {
225 printf(" Modification of '%s' complete.\n", attr.output_string);
226 ldap_uncache_entry( ld, who );
227 for (i--; i > 0; i--)
228 (void) Free(values[i]);
231 * There are values for this attribute already. In this case,
232 * we want to allow the user to delete all existing values,
233 * add additional values to the ones there already, or just
234 * delete some of the values already present. DIXIE does not
235 * handle modifications where the attribute occurs on the LHS
236 * more than once. So to delete entries and add entries, we
237 * need to call ldap_modify() twice.
241 * If the attribute holds values which are DNs, print them
242 * in a most pleasant way.
244 sprintf(buf, "%s: ", attr.output_string);
245 if (!strcmp(attr.quipu_name, "owner"))
246 (void) print_DN(attr);
248 (void) print_values(attr);
251 printf(" You may now:\n");
252 printf(" Add additional values to the existing ones, OR\n");
253 printf(" Clear all values listed above, OR\n");
254 printf(" Delete one of the values listed above, OR\n");
255 printf(" Replace all of the values above with new ones.\n");
257 printf("\n Add, Clear, Delete, or Replace? ");
259 fetch_buffer(resp, sizeof(resp), stdin);
262 * Bail if they just hit the RETURN key.
264 if (resp[0] == '\0') {
266 printf("\n No changes made.\n");
271 * If the want to clear the values, just do it.
273 mod.mod_type = attr.quipu_name;
274 mod.mod_values = values;
275 if (IS_MOD("clear")) {
276 mod.mod_op = LDAP_MOD_DELETE;
277 mod.mod_values = NULL;
278 if ( verbose && !confirm_action( "All existing values will be removed." )) {
279 printf(" Modification halted.\n");
283 if (debug & D_MODIFY) {
284 printf("Clearing attribute '%s'\n", attr.quipu_name);
285 printf("who = [%s]\n", who);
286 printf("mod = [%d] [%s] [%x]\n", mod.mod_op,
287 mod.mod_type, mod.mod_values);
290 if (ldap_modify_s(ld, who, mods)) {
295 printf(" '%s' has been cleared.\n", attr.output_string);
296 ldap_uncache_entry( ld, who );
301 prompt = "Enter the value you wish to add";
302 more = " Add an additional value? ";
303 prompt2 = "Enter another value you wish to add";
304 mod.mod_op = LDAP_MOD_ADD;
306 else if (IS_MOD("delete")) {
307 prompt = "Enter the value you wish to delete";
308 more = " Delete an additional value? ";
309 prompt2 = "Enter another value you wish to delete";
310 mod.mod_op = LDAP_MOD_DELETE;
312 else if (IS_MOD("replace")) {
313 prompt = "Enter the new value";
314 more = " Add an additional value? ";
315 prompt2 = "Enter another value you wish to add";
316 mod.mod_op = LDAP_MOD_REPLACE;
317 if ( verbose && !confirm_action( "All existing values will be overwritten with the new values you are about to enter." )) {
318 printf(" Modification halted.\n");
324 printf(" No changes made.\n");
328 values[0] = get_value(attr.quipu_name, prompt);
329 for (i = 1; i < MAX_VALUES; i++) {
332 fetch_buffer(resp, sizeof(resp), stdin);
333 if ((resp[0] == 'y') || (resp[0] == 'Y'))
334 values[i] = get_value(attr.quipu_name, prompt2);
339 /* if the first value in the value-array is NULL, bail */
340 if (values[0] == NULL) {
342 printf(" No modification made.\n");
346 if (debug & D_MODIFY) {
347 printf(" ld = 0x%x\n", ld);
348 printf(" who = [%s]\n", who);
349 printf(" mods[0]->mod_op = %1d\n", mods[0]->mod_op);
350 printf(" mods[0]->mod_type = %s\n", mods[0]->mod_type);
351 for (i = 0; mods[0]->mod_values[i] != NULL; i++)
352 printf(" mods[0]->mod_values[%1d] = %s\n", i, mods[0]->mod_values[i]);
355 if (ldap_modify_s(ld, who, mods)) {
360 printf(" Modifications to '%s' complete.\n", attr.output_string);
361 ldap_uncache_entry( ld, who );
362 for (i--; i > 0; i--)
363 (void) Free(values[i]);
369 * These are used to size the buffers we use when collecting values that
370 * can cross more than one line.
374 #define MAX_DESC_LINES 24
375 #define INTL_ADDR_LIMIT 30
377 char *get_value(id, prompt)
380 char *cp; /* for the Malloc() */
381 int count; /* line # of new value -- if multiline */
382 int multiline = 0; /* 1 if this value is multiline */
383 static char line[LINE_SIZE]; /* raw line from user */
384 static char buffer[MAX_DESC_LINES * LINE_SIZE]; /* holds ALL of the
386 extern void * Malloc();
387 static char * get_URL();
391 printf("->get_value(%s, %s)\n", id, prompt);
393 /* if this is a URL, have another routine handle this */
394 if (!strcmp(id, "labeledURL"))
398 * To start with, we have one line of input from the user.
400 * Addresses and multiline description can span multiple lines.
401 * Other attributes may not.
404 (void) memset(buffer, 0, sizeof(buffer));
406 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress") || !strcmp(id, "multiLineDescription") || !strcmp(id, "vacationMessage"))
408 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress"))
411 printf("\n %s:\n", prompt);
416 printf(" %1d: ", count);
420 fetch_buffer(line, sizeof(line), stdin);
426 * Screen out dangerous e-mail addresses of the form:
430 * and addresses that have no '@' symbol at all.
432 if (!strcmp(id, "mail")) {
436 /* if this is a group, don't worry */
440 /* if this address is not @umich.edu, don't worry */
441 /* ...unless there is no '@' at all! */
443 if ((tmp2 = strrchr(tmp, '@')) == NULL) {
445 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 );
451 if (strcasecmp(tmp2, "umich.edu"))
454 /* if not of the form uid@umich.edu, don't worry */
455 if ((i = attr_to_index("uid")) < 0)
457 if (strcasecmp(tmp, *(Entry.attrs[i].values)))
460 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);
464 printf(" Please enter a legal e-mail address (or press RETURN to stop)\n");
471 * If the attribute which we are gathering is a "owner"
472 * then we should lookup the name. The user is going to
473 * either have to change the search base before doing the
474 * modify, or the person is going to have to be within the
475 * scope of the current search base, or they will need to
478 if (!strcmp(id, "owner")) {
479 LDAPMessage *lmp, *elmp;
482 lmp = find(line, FALSE);
483 if (lmp == (LDAPMessage *) NULL) {
484 printf(" Could not find \"%s\" in the Directory\n", line);
486 format("Owners of groups must be valid entries in the X.500 Directory. The name you have typed above could not be found in the X.500 Directory.", 72, 2);
489 elmp = ldap_first_entry(ld, lmp);
490 if (lmp == (LDAPMessage *) NULL) {
491 ldap_perror(ld, "ldap_first_entry");
494 tmp = ldap_get_dn(ld, elmp);
496 (void) ldap_msgfree(lmp);
500 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress")) {
501 if (strlen(line) > INTL_ADDR_LIMIT) {
502 printf(" The international standard for addresses only allows for 30-character lines\n");
503 printf(" Please re-enter your last line.\n");
509 * Separate lines of multiline attribute values with
510 * dollar signs. Copy this line into the buffer we
511 * use to collect up all of the user-supplied input
512 * lines. If this is not a multiline attribute, we
516 (void) strcat(buffer, "$");
517 (void) strcat(buffer, line);
520 if ((count > MAX_LINES) && (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress"))) {
521 printf(" The international standard for addresses only allows for six lines\n");
525 if ((count > MAX_DESC_LINES) && !strcmp(id, "multiLineDescription")) {
526 printf(" We only allow %d lines of description\n", MAX_DESC_LINES);
531 if (buffer[0] == '\0')
534 if (debug & D_MODIFY)
535 printf(" Value is [%s]\n", buffer);
537 cp = (char *) Malloc((unsigned) (strlen(buffer) + 1));
542 void set_boolean(who, attr)
543 char *who; /* DN of entry we are changing */
544 struct attribute attr; /* boolean attribute to change */
547 extern char * fetch_boolean_value();
548 static char response[16];
549 static char *newsetting[2] = { NULL, NULL };
550 LDAPMod mod, *mods[2];
554 printf("->set_boolean(%s, %s)\n", who, attr.quipu_name);
557 mods[1] = (LDAPMod *) NULL;
558 mod.mod_op = LDAP_MOD_REPLACE;
559 mod.mod_type = attr.quipu_name;
560 mod.mod_values = newsetting;
562 /* fetch the current setting */
563 if ((cp = fetch_boolean_value(who, attr)) == NULL)
565 if (!strcmp(cp, "TRUE"))
566 newsetting[0] = "FALSE";
567 else if (!strcmp(cp, "FALSE"))
568 newsetting[0] = "TRUE";
570 printf(" This field needs to be set to either TRUE or to FALSE.\n");
571 printf(" \"%s\" is not a legal value. Please set this field to either TRUE or to FALSE.\n", cp);
572 newsetting[0] = "FALSE";
575 /* see if they want to change it */
577 printf(" The current value of this field is %s.\n", cp);
578 printf(" Should I change the value of this field to %s?\n",
580 printf(" Please enter Y for yes, N for no, or RETURN to cancel: ");
582 (void) fetch_buffer(response, sizeof(response), stdin);
583 for (s = response; isspace(*s); s++)
585 if ((*s == 'y') || (*s == 'Y')) {
586 if (ldap_modify_s(ld, who, mods)) {
591 printf(" Setting has been changed\n");
592 ldap_uncache_entry(ld, who);
596 printf(" Setting has not been changed\n");
601 void set_updates(who)
605 extern char * fetch_boolean_value();
606 static char response[16];
607 static char value[6];
608 static char *newsetting[2] = { value, NULL };
609 LDAPMod mod, *mods[2];
610 struct attribute attr;
614 printf("->set_updates(%s)\n", who);
617 mods[1] = (LDAPMod *) NULL;
618 mod.mod_op = LDAP_MOD_REPLACE;
619 mod.mod_type = "noBatchUpdates";
620 mod.mod_values = newsetting;
621 /* explain what the implications are */
623 printf("\n By default, updates that are received from the Personnel\n");
624 printf(" Office and the Office of the Registrar are applied to all\n");
625 printf(" entries in the X.500 database each month. Sometimes this\n");
626 printf(" feature is undesirable. For example, if you maintain your\n");
627 printf(" entry in the X.500 database manually, you may not want to\n");
628 printf(" have these updates applied to your entry, possibly overwriting\n");
629 printf(" correct information with out-dated information.\n\n");
632 /* fetch the current setting */
633 attr.quipu_name = "noBatchUpdates";
634 if ((cp = fetch_boolean_value(who, attr)) == NULL)
636 if (!strcmp(cp, "TRUE"))
637 printf(" Automatic updates are currently turned OFF\n");
638 else if (!strcmp(cp, "FALSE"))
639 printf(" Automatic updates are currently turned ON\n");
641 fprintf(stderr, " Unknown update flag -> [%s]\n", cp);
645 /* see if they want to change it */
646 printf("\n Change this setting [no]? ");
648 (void) fetch_buffer(response, sizeof(response), stdin);
649 for (s = response; isspace(*s); s++)
651 if ((*s == 'y') || (*s == 'Y')) {
652 if (!strcmp(cp, "TRUE"))
653 strcpy(value, "FALSE");
655 strcpy(value, "TRUE");
656 if (ldap_modify_s(ld, who, mods)) {
661 printf(" Setting has been changed\n");
662 ldap_uncache_entry( ld, who );
666 printf(" Setting has not been changed\n");
671 print_mod_list(group)
674 register int i, j = 1;
675 extern struct attribute attrlist[];
678 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
679 if (attrlist[i].flags & ATTR_FLAG_GROUP_MOD) {
680 printf(" %2d -> %s\n", j, attrlist[i].output_string);
685 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
686 if (attrlist[i].flags & ATTR_FLAG_PERSON_MOD) {
687 printf(" %2d -> %s\n", j, attrlist[i].output_string);
692 printf(" ? -> Print this list\n\n");
693 printf(" Press the RETURN key without typing a number to quit.\n");
696 printf(" To add nicknames, send mail to x500-nicknames@umich.edu\n");
700 perform_action(choice, dn, group)
706 register int i, j = 1;
707 extern struct attribute attrlist[];
708 extern void mod_addrDN(), change_field(), set_boolean();
710 selection = atoi(choice);
712 printf("\n Choices are:\n");
713 printf(" -----------------------\n");
714 print_mod_list(group);
720 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
721 if (attrlist[i].flags & ATTR_FLAG_GROUP_MOD) {
728 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
729 if (attrlist[i].flags & ATTR_FLAG_PERSON_MOD) {
737 if (attrlist[i].quipu_name == NULL) {
738 printf("\n Choices are:\n");
739 printf(" -----------------------\n");
740 print_mod_list(group);
744 if (attrlist[i].mod_func == change_field)
745 (*attrlist[i].mod_func)(dn, Entry.attrs[attr_to_index(attrlist[i].quipu_name)]);
746 else if (attrlist[i].mod_func == mod_addrDN)
747 (*attrlist[i].mod_func)(dn, i);
748 else if (attrlist[i].mod_func == set_boolean)
749 (*attrlist[i].mod_func)(dn, Entry.attrs[attr_to_index(attrlist[i].quipu_name)]);
751 (*attrlist[i].mod_func)(dn);
755 static char * get_URL()
757 char *rvalue, label[MED_BUF_SIZE], url[MED_BUF_SIZE];
758 static int check_URL();
759 extern void * Malloc();
762 printf(" First, enter the URL. (Example: http://www.us.itd.umich.edu/users/).\n");
763 printf(" The URL may be up to %d characters long.\n", MED_BUF_SIZE);
768 (void) fetch_buffer(url, sizeof(url), stdin);
771 if (check_URL(url) == 0)
773 printf(" A URL may not have any spaces or tabs in it. Please re-enter your URL.\n\n");
776 printf("\n Now please enter a descriptive label for this URL\n");
780 (void) fetch_buffer(label, sizeof(label), stdin);
781 } while (label[0] == '\0');
782 rvalue = (char *) Malloc((unsigned) (strlen(url) + 2 + strlen(label)));
783 sprintf(rvalue, "%s %s", url, label);
784 return((char *) rvalue);
787 static check_URL(url)
792 for (cp = url; *cp != '\n' && *cp != '\0'; cp++) {
802 mod_perror( LDAP *ld )
804 if ( ld == NULL || ( ld->ld_errno != LDAP_UNAVAILABLE &&
805 ld->ld_errno != LDAP_UNWILLING_TO_PERFORM )) {
806 ldap_perror( ld, "modify" );
810 fprintf( stderr, "\n modify: failed because part of the online directory is not able\n" );
811 fprintf( stderr, " to be modified right now" );
812 if ( ld->ld_errno == LDAP_UNAVAILABLE ) {
813 fprintf( stderr, " or is temporarily unavailable" );
815 fprintf( stderr, ".\n Please try again later.\n" );