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.
21 #include <sys/types.h>
24 extern struct entry Entry;
28 extern LDAPMessage *find();
38 void set_updates(); /* routine to modify noBatchUpdates */
40 LDAPMessage *mp; /* returned from find() */
41 char *dn; /* distinguished name */
42 char **rdns; /* for fiddling with the DN */
43 char name[MED_BUF_SIZE]; /* entry to modify */
44 int displayed_choices = 0;
45 static char ans[SMALL_BUF_SIZE]; /* for holding user input */
47 static char printed_warning = 0; /* for use with the */
48 struct attribute no_batch_update_attr;
49 extern char * fetch_boolean_value();
51 int is_a_group; /* TRUE if it is; FALSE otherwise */
53 extern int bind_status;
57 printf("->modify(%s)\n", who);
60 * Require them to bind first if the are modifying a group.
62 if (bind_status == UD_NOT_BOUND) {
63 if (auth((char *) NULL, 1) < 0)
68 * First, decide what entry we are going to modify. If the
69 * user has not included a name on the modify command line,
70 * we will use the person who was last looked up with a find
71 * command. If there is no value there either, we don't know
74 * Once we know who to modify, be sure that they exist, and
79 printf(" Enter the name of the person or\n");
80 printf(" group whose entry you want to modify: ");
83 printf(" Modify whose entry? ");
85 fetch_buffer(name, sizeof(name), stdin);
91 if ((mp = find(who, TRUE)) == NULL) {
92 (void) ldap_msgfree(mp);
93 printf(" Could not locate \"%s\" in the Directory.\n", who);
96 dn = ldap_get_dn(ld, ldap_first_entry(ld, mp));
97 rdns = ldap_explode_dn(dn, TRUE);
99 printf("\n Modifying Directory entry of \"%s\"\n", *rdns);
103 * If verbose mode is turned on and the user has not set a value
104 * for noBatchUpdates, warn them that what they are about to do
105 * may be overwritten automatically by that Stinkbug.
107 no_batch_update_attr.quipu_name = "noBatchUpdates";
108 (void) fetch_boolean_value(dn, no_batch_update_attr);
109 if (verbose && !printed_warning && (ld->ld_errno == LDAP_NO_SUCH_ATTRIBUTE)) {
111 printf("\n WARNING!\n");
112 printf(" You are about to make a modification to an X.500 entry\n");
113 printf(" that has its \"automatic updates\" field set to ON.\n");
114 printf(" This means that the entry will be automatically updated\n");
115 printf(" each month from official University sources like the\n");
116 printf(" Personnel Office. With \"automatic updates\" set to ON,\n");
117 printf(" the following fields will be overwritten each month:\n");
118 printf(" Title, home address and phone,\n");
119 printf(" business address and phone\n");
120 printf(" If you modify any of these fields, you may want to change\n");
121 printf(" the \"automatic updates\" field to OFF so that your\n");
122 printf(" changes will not be overwritten. You may change this\n");
123 printf(" setting by choosing \"u\" at the \"Modify what?\" prompt\n");
128 * Current values for user 'who' are being held in 'mp'. We
129 * should parse up that buffer and fill in the Entry structure.
130 * Once we're done with that, we can find out which fields the
131 * user would like to modify.
134 is_a_group = isgroup();
135 (void) ldap_msgfree(mp);
136 printf(" You now need to specify what field you'd like to modify.\n");
138 if ( verbose || !displayed_choices ) {
139 printf("\n Choices are:\n");
140 printf(" -----------------------\n");
141 print_mod_list(is_a_group);
142 printf("\n Pressing Return will cancel the process.\n");
143 displayed_choices = 1;
145 printf("\n Modify what? ");
147 fetch_buffer(ans, sizeof(ans), stdin);
150 perform_action(ans, dn, is_a_group);
151 if ((mp = find(*rdns, TRUE)) == NULL)
154 (void) ldap_msgfree(mp);
157 ldap_value_free(rdns);
161 /* generic routine for changing any field */
162 void change_field(who, attr)
163 char *who; /* DN of entry we are changing */
164 struct attribute attr; /* attribute to change */
167 #define IS_MOD(x) (!strncasecmp(resp, (x), strlen(resp)))
169 char *get_value(); /* routine to extract values */
170 static char buf[MED_BUF_SIZE]; /* for printing things */
171 static char resp[SMALL_BUF_SIZE]; /* for user input */
172 char *prompt, *prompt2, *more;
173 register int i; /* for looping thru values */
175 static LDAPMod *mods[2] = { &mod }; /* passed to ldap_modify */
176 static char *values[MAX_VALUES]; /* passed to ldap_modify */
181 printf("->change_field(%x, %s)\n", attr, who);
184 * If there is no current value associated with the attribute,
185 * then this is the easy case. Collect one (or more) attributes
186 * from the user, and then call ldap_modify_s() to write the changes
187 * to the LDAP server.
189 for (i = 0; i < MAX_VALUES; i++)
191 if (attr.values == (char **) NULL) {
192 printf("\n No current \"%s\"\n", attr.output_string);
193 values[0] = get_value(attr.quipu_name, "Enter a value");
194 if ( values[0] == NULL )
196 mod.mod_op = LDAP_MOD_REPLACE;
197 mod.mod_type = attr.quipu_name;
198 mod.mod_values = values;
199 for (i = 1; i < MAX_VALUES; i++) {
200 printf(" Do you wish to add an additional value? ");
202 fetch_buffer(resp, sizeof(resp), stdin);
203 if ((resp[0] == 'y') || (resp[0] == 'Y'))
204 values[i] = get_value(attr.quipu_name, "Enter an additional value");
209 if (debug & D_MODIFY) {
210 printf(" ld = 0x%x\n", ld);
211 printf(" who = [%s]\n", who);
212 printf(" mods[0]->mod_op = %1d\n", mods[0]->mod_op);
213 printf(" mods[0]->mod_type = %s\n", mods[0]->mod_type);
214 for (i = 0; mods[0]->mod_values[i] != NULL; i++)
215 printf(" mods[0]->mod_values[%1d] = %s\n", i, mods[0]->mod_values[i]);
218 if (ldap_modify_s(ld, who, mods)) {
223 printf(" Modification of '%s' complete.\n", attr.output_string);
224 ldap_uncache_entry( ld, who );
225 for (i--; i > 0; i--)
226 (void) Free(values[i]);
229 * There are values for this attribute already. In this case,
230 * we want to allow the user to delete all existing values,
231 * add additional values to the ones there already, or just
232 * delete some of the values already present. DIXIE does not
233 * handle modifications where the attribute occurs on the LHS
234 * more than once. So to delete entries and add entries, we
235 * need to call ldap_modify() twice.
239 * If the attribute holds values which are DNs, print them
240 * in a most pleasant way.
242 sprintf(buf, "%s: ", attr.output_string);
243 if (!strcmp(attr.quipu_name, "owner"))
244 (void) print_DN(attr);
246 (void) print_values(attr);
249 printf(" You may now:\n");
250 printf(" Add additional values to the existing ones, OR\n");
251 printf(" Clear all values listed above, OR\n");
252 printf(" Delete one of the values listed above, OR\n");
253 printf(" Replace all of the values above with new ones.\n");
255 printf("\n Add, Clear, Delete, or Replace? ");
257 fetch_buffer(resp, sizeof(resp), stdin);
260 * Bail if they just hit the RETURN key.
262 if (resp[0] == '\0') {
264 printf("\n No changes made.\n");
269 * If the want to clear the values, just do it.
271 mod.mod_type = attr.quipu_name;
272 mod.mod_values = values;
273 if (IS_MOD("clear")) {
274 mod.mod_op = LDAP_MOD_DELETE;
275 mod.mod_values = NULL;
276 if ( verbose && !confirm_action( "All existing values will be removed." )) {
277 printf(" Modification halted.\n");
281 if (debug & D_MODIFY) {
282 printf("Clearing attribute '%s'\n", attr.quipu_name);
283 printf("who = [%s]\n", who);
284 printf("mod = [%d] [%s] [%x]\n", mod.mod_op,
285 mod.mod_type, mod.mod_values);
288 if (ldap_modify_s(ld, who, mods)) {
293 printf(" '%s' has been cleared.\n", attr.output_string);
294 ldap_uncache_entry( ld, who );
299 prompt = "Enter the value you wish to add";
300 more = " Add an additional value? ";
301 prompt2 = "Enter another value you wish to add";
302 mod.mod_op = LDAP_MOD_ADD;
304 else if (IS_MOD("delete")) {
305 prompt = "Enter the value you wish to delete";
306 more = " Delete an additional value? ";
307 prompt2 = "Enter another value you wish to delete";
308 mod.mod_op = LDAP_MOD_DELETE;
310 else if (IS_MOD("replace")) {
311 prompt = "Enter the new value";
312 more = " Add an additional value? ";
313 prompt2 = "Enter another value you wish to add";
314 mod.mod_op = LDAP_MOD_REPLACE;
315 if ( verbose && !confirm_action( "All existing values will be overwritten with the new values you are about to enter." )) {
316 printf(" Modification halted.\n");
322 printf(" No changes made.\n");
326 values[0] = get_value(attr.quipu_name, prompt);
327 for (i = 1; i < MAX_VALUES; i++) {
330 fetch_buffer(resp, sizeof(resp), stdin);
331 if ((resp[0] == 'y') || (resp[0] == 'Y'))
332 values[i] = get_value(attr.quipu_name, prompt2);
337 /* if the first value in the value-array is NULL, bail */
338 if (values[0] == NULL) {
340 printf(" No modification made.\n");
344 if (debug & D_MODIFY) {
345 printf(" ld = 0x%x\n", ld);
346 printf(" who = [%s]\n", who);
347 printf(" mods[0]->mod_op = %1d\n", mods[0]->mod_op);
348 printf(" mods[0]->mod_type = %s\n", mods[0]->mod_type);
349 for (i = 0; mods[0]->mod_values[i] != NULL; i++)
350 printf(" mods[0]->mod_values[%1d] = %s\n", i, mods[0]->mod_values[i]);
353 if (ldap_modify_s(ld, who, mods)) {
358 printf(" Modifications to '%s' complete.\n", attr.output_string);
359 ldap_uncache_entry( ld, who );
360 for (i--; i > 0; i--)
361 (void) Free(values[i]);
367 * These are used to size the buffers we use when collecting values that
368 * can cross more than one line.
372 #define MAX_DESC_LINES 24
373 #define INTL_ADDR_LIMIT 30
375 char *get_value(id, prompt)
378 char *cp; /* for the Malloc() */
379 int count; /* line # of new value -- if multiline */
380 int multiline = 0; /* 1 if this value is multiline */
381 static char line[LINE_SIZE]; /* raw line from user */
382 static char buffer[MAX_DESC_LINES * LINE_SIZE]; /* holds ALL of the
384 extern void * Malloc();
385 static char * get_URL();
389 printf("->get_value(%s, %s)\n", id, prompt);
391 /* if this is a URL, have another routine handle this */
392 if (!strcmp(id, "labeledURL"))
396 * To start with, we have one line of input from the user.
398 * Addresses and multiline description can span multiple lines.
399 * Other attributes may not.
402 (void) memset(buffer, 0, sizeof(buffer));
404 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress") || !strcmp(id, "multiLineDescription") || !strcmp(id, "vacationMessage"))
406 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress"))
409 printf("\n %s:\n", prompt);
414 printf(" %1d: ", count);
418 fetch_buffer(line, sizeof(line), stdin);
424 * Screen out dangerous e-mail addresses of the form:
428 * and addresses that have no '@' symbol at all.
430 if (!strcmp(id, "mail")) {
434 /* if this is a group, don't worry */
438 /* if this address is not @umich.edu, don't worry */
439 /* ...unless there is no '@' at all! */
441 if ((tmp2 = strrchr(tmp, '@')) == NULL) {
443 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 );
449 if (strcasecmp(tmp2, "umich.edu"))
452 /* if not of the form uid@umich.edu, don't worry */
453 if ((i = attr_to_index("uid")) < 0)
455 if (strcasecmp(tmp, *(Entry.attrs[i].values)))
458 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);
462 printf(" Please enter a legal e-mail address (or press RETURN to stop)\n");
469 * If the attribute which we are gathering is a "owner"
470 * then we should lookup the name. The user is going to
471 * either have to change the search base before doing the
472 * modify, or the person is going to have to be within the
473 * scope of the current search base, or they will need to
476 if (!strcmp(id, "owner")) {
477 LDAPMessage *lmp, *elmp;
480 lmp = find(line, FALSE);
481 if (lmp == (LDAPMessage *) NULL) {
482 printf(" Could not find \"%s\" in the Directory\n", line);
484 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);
487 elmp = ldap_first_entry(ld, lmp);
488 if (lmp == (LDAPMessage *) NULL) {
489 ldap_perror(ld, "ldap_first_entry");
492 tmp = ldap_get_dn(ld, elmp);
494 (void) ldap_msgfree(lmp);
498 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress")) {
499 if (strlen(line) > INTL_ADDR_LIMIT) {
500 printf(" The international standard for addresses only allows for 30-character lines\n");
501 printf(" Please re-enter your last line.\n");
507 * Separate lines of multiline attribute values with
508 * dollar signs. Copy this line into the buffer we
509 * use to collect up all of the user-supplied input
510 * lines. If this is not a multiline attribute, we
514 (void) strcat(buffer, "$");
515 (void) strcat(buffer, line);
518 if ((count > MAX_LINES) && (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress"))) {
519 printf(" The international standard for addresses only allows for six lines\n");
523 if ((count > MAX_DESC_LINES) && !strcmp(id, "multiLineDescription")) {
524 printf(" We only allow %d lines of description\n", MAX_DESC_LINES);
529 if (buffer[0] == '\0')
532 if (debug & D_MODIFY)
533 printf(" Value is [%s]\n", buffer);
535 cp = (char *) Malloc((unsigned) (strlen(buffer) + 1));
540 void set_boolean(who, attr)
541 char *who; /* DN of entry we are changing */
542 struct attribute attr; /* boolean attribute to change */
545 extern char * fetch_boolean_value();
546 static char response[16];
547 static char *newsetting[2] = { NULL, NULL };
548 LDAPMod mod, *mods[2];
552 printf("->set_boolean(%s, %s)\n", who, attr.quipu_name);
555 mods[1] = (LDAPMod *) NULL;
556 mod.mod_op = LDAP_MOD_REPLACE;
557 mod.mod_type = attr.quipu_name;
558 mod.mod_values = newsetting;
560 /* fetch the current setting */
561 if ((cp = fetch_boolean_value(who, attr)) == NULL)
563 if (!strcmp(cp, "TRUE"))
564 newsetting[0] = "FALSE";
565 else if (!strcmp(cp, "FALSE"))
566 newsetting[0] = "TRUE";
568 printf(" This field needs to be set to either TRUE or to FALSE.\n");
569 printf(" \"%s\" is not a legal value. Please set this field to either TRUE or to FALSE.\n", cp);
570 newsetting[0] = "FALSE";
573 /* see if they want to change it */
575 printf(" The current value of this field is %s.\n", cp);
576 printf(" Should I change the value of this field to %s?\n",
578 printf(" Please enter Y for yes, N for no, or RETURN to cancel: ");
580 (void) fetch_buffer(response, sizeof(response), stdin);
581 for (s = response; isspace(*s); s++)
583 if ((*s == 'y') || (*s == 'Y')) {
584 if (ldap_modify_s(ld, who, mods)) {
589 printf(" Setting has been changed\n");
590 ldap_uncache_entry(ld, who);
594 printf(" Setting has not been changed\n");
599 void set_updates(who)
603 extern char * fetch_boolean_value();
604 static char response[16];
605 static char value[6];
606 static char *newsetting[2] = { value, NULL };
607 LDAPMod mod, *mods[2];
608 struct attribute attr;
612 printf("->set_updates(%s)\n", who);
615 mods[1] = (LDAPMod *) NULL;
616 mod.mod_op = LDAP_MOD_REPLACE;
617 mod.mod_type = "noBatchUpdates";
618 mod.mod_values = newsetting;
619 /* explain what the implications are */
621 printf("\n By default, updates that are received from the Personnel\n");
622 printf(" Office and the Office of the Registrar are applied to all\n");
623 printf(" entries in the X.500 database each month. Sometimes this\n");
624 printf(" feature is undesirable. For example, if you maintain your\n");
625 printf(" entry in the X.500 database manually, you may not want to\n");
626 printf(" have these updates applied to your entry, possibly overwriting\n");
627 printf(" correct information with out-dated information.\n\n");
630 /* fetch the current setting */
631 attr.quipu_name = "noBatchUpdates";
632 if ((cp = fetch_boolean_value(who, attr)) == NULL)
634 if (!strcmp(cp, "TRUE"))
635 printf(" Automatic updates are currently turned OFF\n");
636 else if (!strcmp(cp, "FALSE"))
637 printf(" Automatic updates are currently turned ON\n");
639 fprintf(stderr, " Unknown update flag -> [%s]\n", cp);
643 /* see if they want to change it */
644 printf("\n Change this setting [no]? ");
646 (void) fetch_buffer(response, sizeof(response), stdin);
647 for (s = response; isspace(*s); s++)
649 if ((*s == 'y') || (*s == 'Y')) {
650 if (!strcmp(cp, "TRUE"))
651 strcpy(value, "FALSE");
653 strcpy(value, "TRUE");
654 if (ldap_modify_s(ld, who, mods)) {
659 printf(" Setting has been changed\n");
660 ldap_uncache_entry( ld, who );
664 printf(" Setting has not been changed\n");
669 print_mod_list(group)
672 register int i, j = 1;
673 extern struct attribute attrlist[];
676 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
677 if (attrlist[i].flags & ATTR_FLAG_GROUP_MOD) {
678 printf(" %2d -> %s\n", j, attrlist[i].output_string);
683 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
684 if (attrlist[i].flags & ATTR_FLAG_PERSON_MOD) {
685 printf(" %2d -> %s\n", j, attrlist[i].output_string);
690 printf(" ? -> Print this list\n\n");
691 printf(" Press the RETURN key without typing a number to quit.\n");
694 printf(" To add nicknames, send mail to x500-nicknames@umich.edu\n");
698 perform_action(choice, dn, group)
704 register int i, j = 1;
705 extern struct attribute attrlist[];
706 extern void mod_addrDN(), change_field(), set_boolean();
708 selection = atoi(choice);
710 printf("\n Choices are:\n");
711 printf(" -----------------------\n");
712 print_mod_list(group);
718 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
719 if (attrlist[i].flags & ATTR_FLAG_GROUP_MOD) {
726 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
727 if (attrlist[i].flags & ATTR_FLAG_PERSON_MOD) {
735 if (attrlist[i].quipu_name == NULL) {
736 printf("\n Choices are:\n");
737 printf(" -----------------------\n");
738 print_mod_list(group);
742 if (attrlist[i].mod_func == change_field)
743 (*attrlist[i].mod_func)(dn, Entry.attrs[attr_to_index(attrlist[i].quipu_name)]);
744 else if (attrlist[i].mod_func == mod_addrDN)
745 (*attrlist[i].mod_func)(dn, i);
746 else if (attrlist[i].mod_func == set_boolean)
747 (*attrlist[i].mod_func)(dn, Entry.attrs[attr_to_index(attrlist[i].quipu_name)]);
749 (*attrlist[i].mod_func)(dn);
753 static char * get_URL()
755 char *rvalue, label[MED_BUF_SIZE], url[MED_BUF_SIZE];
756 static int check_URL();
757 extern void * Malloc();
760 printf(" First, enter the URL. (Example: http://www.us.itd.umich.edu/users/).\n");
761 printf(" The URL may be up to %d characters long.\n", MED_BUF_SIZE);
766 (void) fetch_buffer(url, sizeof(url), stdin);
769 if (check_URL(url) == 0)
771 printf(" A URL may not have any spaces or tabs in it. Please re-enter your URL.\n\n");
774 printf("\n Now please enter a descriptive label for this URL\n");
778 (void) fetch_buffer(label, sizeof(label), stdin);
779 } while (label[0] == '\0');
780 rvalue = (char *) Malloc((unsigned) (strlen(url) + 2 + strlen(label)));
781 sprintf(rvalue, "%s %s", url, label);
782 return((char *) rvalue);
785 static check_URL(url)
790 for (cp = url; *cp != '\n' && *cp != '\0'; cp++) {
800 mod_perror( LDAP *ld )
802 if ( ld == NULL || ( ld->ld_errno != LDAP_UNAVAILABLE &&
803 ld->ld_errno != LDAP_UNWILLING_TO_PERFORM )) {
804 ldap_perror( ld, "modify" );
808 fprintf( stderr, "\n modify: failed because part of the online directory is not able\n" );
809 fprintf( stderr, " to be modified right now" );
810 if ( ld->ld_errno == LDAP_UNAVAILABLE ) {
811 fprintf( stderr, " or is temporarily unavailable" );
813 fprintf( stderr, ".\n Please try again later.\n" );