3 * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved.
4 * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
7 * Copyright (c) 1991,1993 Regents of the University of Michigan.
10 * Redistribution and use in source and binary forms are permitted
11 * provided that this notice is preserved and that due credit is given
12 * to the University of Michigan at Ann Arbor. The name of the University
13 * may not be used to endorse or promote products derived from this
14 * software without specific prior written permission. This software
15 * is provided ``as is'' without express or implied warranty.
22 #include <ac/stdlib.h>
25 #include <ac/string.h>
31 static char *get_URL( void );
32 static int check_URL( char *url );
38 LDAPMessage *mp; /* returned from find() */
39 char *dn; /* distinguished name */
40 char **rdns; /* for fiddling with the DN */
41 char name[MED_BUF_SIZE]; /* entry to modify */
42 int displayed_choices = 0;
43 static char ans[SMALL_BUF_SIZE]; /* for holding user input */
45 static char printed_warning = 0; /* for use with the */
46 struct attribute no_batch_update_attr;
49 int is_a_group; /* TRUE if it is; FALSE otherwise */
53 printf("->modify(%s)\n", who);
56 * Require them to bind first if the are modifying a group.
58 if (bind_status == UD_NOT_BOUND) {
59 if (auth((char *) NULL, 1) < 0)
64 * First, decide what entry we are going to modify. If the
65 * user has not included a name on the modify command line,
66 * we will use the person who was last looked up with a find
67 * command. If there is no value there either, we don't know
70 * Once we know who to modify, be sure that they exist, and
75 printf(" Enter the name of the person or\n");
76 printf(" group whose entry you want to modify: ");
79 printf(" Modify whose entry? ");
81 fetch_buffer(name, sizeof(name), stdin);
87 if ((mp = find(who, TRUE)) == NULL) {
88 (void) ldap_msgfree(mp);
89 printf(" Could not locate \"%s\" in the Directory.\n", who);
92 dn = ldap_get_dn(ld, ldap_first_entry(ld, mp));
93 rdns = ldap_explode_dn(dn, TRUE);
95 printf("\n Modifying Directory entry of \"%s\"\n", *rdns);
99 * If verbose mode is turned on and the user has not set a value
100 * for noBatchUpdates, warn them that what they are about to do
101 * may be overwritten automatically by that Stinkbug.
103 no_batch_update_attr.quipu_name = "noBatchUpdates";
104 (void) fetch_boolean_value(dn, no_batch_update_attr);
106 ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ld_errno);
108 if (verbose && !printed_warning && (ld_errno == LDAP_NO_SUCH_ATTRIBUTE)) {
110 printf("\n WARNING!\n");
111 printf(" You are about to make a modification to an LDAP entry\n");
112 printf(" that has its \"automatic updates\" field set to ON.\n");
113 printf(" This means that the entry will be automatically updated\n");
114 printf(" each month from official University sources like the\n");
115 printf(" Personnel Office. With \"automatic updates\" set to ON,\n");
116 printf(" the following fields will be overwritten each month:\n");
117 printf(" Title, home address and phone,\n");
118 printf(" business address and phone\n");
119 printf(" If you modify any of these fields, you may want to change\n");
120 printf(" the \"automatic updates\" field to OFF so that your\n");
121 printf(" changes will not be overwritten. You may change this\n");
122 printf(" setting by choosing \"u\" at the \"Modify what?\" prompt\n");
127 * Current values for user 'who' are being held in 'mp'. We
128 * should parse up that buffer and fill in the Entry structure.
129 * Once we're done with that, we can find out which fields the
130 * user would like to modify.
133 is_a_group = isgroup();
134 (void) ldap_msgfree(mp);
135 printf(" You now need to specify what field you'd like to modify.\n");
137 if ( verbose || !displayed_choices ) {
138 printf("\n Choices are:\n");
139 printf(" -----------------------\n");
140 print_mod_list(is_a_group);
141 printf("\n Pressing Return will cancel the process.\n");
142 displayed_choices = 1;
144 printf("\n Modify what? ");
146 fetch_buffer(ans, sizeof(ans), stdin);
149 perform_action(ans, dn, is_a_group);
150 if ((mp = find(*rdns, TRUE)) == NULL)
153 (void) ldap_msgfree(mp);
156 ldap_value_free(rdns);
160 /* generic routine for changing any field */
163 char *who, /* DN of entry we are changing */
164 int attr_idx /* attribute to change */
167 struct attribute attr = Entry.attrs[attr_to_index(attrlist[attr_idx].quipu_name)];
169 #define IS_MOD(x) (!strncasecmp(resp, (x), strlen(resp)))
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 */
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
376 get_value( char *id, char *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+2)]; /* holds ALL of the
386 printf("->get_value(%s, %s)\n", id, prompt);
388 /* if this is a URL, have another routine handle this */
389 if (!strcmp(id, "labeledURL"))
393 * To start with, we have one line of input from the user.
395 * Addresses and multiline description can span multiple lines.
396 * Other attributes may not.
399 (void) memset(buffer, '\0', sizeof(buffer));
401 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress") || !strcmp(id, "multiLineDescription") || !strcmp(id, "vacationMessage"))
403 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress"))
406 printf("\n %s:\n", prompt);
411 printf(" %1d: ", count);
415 fetch_buffer(line, sizeof(line), stdin);
421 * Screen out dangerous e-mail addresses of the form:
425 * and addresses that have no '@' symbol at all.
427 if (!strcmp(id, "mail")) {
431 /* if this is a group, don't worry */
435 /* if this address is not @umich.edu, don't worry */
436 /* ...unless there is no '@' at all! */
438 if ((tmp2 = strrchr(tmp, '@')) == NULL) {
440 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 );
446 if (strcasecmp(tmp2, "umich.edu"))
449 /* if not of the form uid@umich.edu, don't worry */
450 if ((i = attr_to_index("uid")) < 0)
452 if (strcasecmp(tmp, *(Entry.attrs[i].values)))
455 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);
459 printf(" Please enter a legal e-mail address (or press RETURN to stop)\n");
466 * If the attribute which we are gathering is a "owner"
467 * then we should lookup the name. The user is going to
468 * either have to change the search base before doing the
469 * modify, or the person is going to have to be within the
470 * scope of the current search base, or they will need to
473 if (!strcmp(id, "owner")) {
474 LDAPMessage *lmp, *elmp;
477 lmp = find(line, FALSE);
478 if (lmp == (LDAPMessage *) NULL) {
479 printf(" Could not find \"%s\" in the Directory\n", line);
481 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);
484 elmp = ldap_first_entry(ld, lmp);
485 if (lmp == (LDAPMessage *) NULL) {
486 ldap_perror(ld, "ldap_first_entry");
489 tmp = ldap_get_dn(ld, elmp);
492 (void) ldap_msgfree(lmp);
496 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress")) {
497 if (strlen(line) > INTL_ADDR_LIMIT) {
498 printf(" The international standard for addresses only allows for 30-character lines\n");
499 printf(" Please re-enter your last line.\n");
505 * Separate lines of multiline attribute values with
506 * dollar signs. Copy this line into the buffer we
507 * use to collect up all of the user-supplied input
508 * lines. If this is not a multiline attribute, we
512 (void) strcat(buffer, " $ ");
513 (void) strcat(buffer, line);
516 if ((count > MAX_LINES) && (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress"))) {
517 printf(" The international standard for addresses only allows for six lines\n");
521 if ((count > MAX_DESC_LINES) && !strcmp(id, "multiLineDescription")) {
522 printf(" We only allow %d lines of description\n", MAX_DESC_LINES);
527 if (buffer[0] == '\0')
530 if (debug & D_MODIFY)
531 printf(" Value is [%s]\n", buffer);
533 cp = (char *) Malloc((unsigned) (strlen(buffer) + 1));
540 char *who, /* DN of entry we are changing */
541 int attr_idx /* boolean attribute to change */
544 struct attribute attr = Entry.attrs[attr_to_index(attrlist[attr_idx].quipu_name)];
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((unsigned char)*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");
601 set_updates( char *who, int dummy )
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 LDAP database each month. Sometimes this\n");
624 printf(" feature is undesirable. For example, if you maintain your\n");
625 printf(" entry in the LDAP 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((unsigned char)*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");
670 print_mod_list( int group )
672 register int i, j = 1;
675 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
676 if (attrlist[i].flags & ATTR_FLAG_GROUP_MOD) {
677 printf(" %2d -> %s\n", j, attrlist[i].output_string);
682 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
683 if (attrlist[i].flags & ATTR_FLAG_PERSON_MOD) {
684 printf(" %2d -> %s\n", j, attrlist[i].output_string);
689 printf(" ? -> Print this list\n\n");
690 printf(" Press the RETURN key without typing a number to quit.\n");
693 printf(" To add nicknames, send mail to x500-nicknames@umich.edu\n");
698 perform_action( char *choice, char *dn, int group )
701 register int i, j = 1;
703 selection = atoi(choice);
705 printf("\n Choices are:\n");
706 printf(" -----------------------\n");
707 print_mod_list(group);
713 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
714 if (attrlist[i].flags & ATTR_FLAG_GROUP_MOD) {
721 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
722 if (attrlist[i].flags & ATTR_FLAG_PERSON_MOD) {
730 if (attrlist[i].quipu_name == NULL) {
731 printf("\n Choices are:\n");
732 printf(" -----------------------\n");
733 print_mod_list(group);
737 (*attrlist[i].mod_func)(dn, i);
744 char *rvalue, label[MED_BUF_SIZE], url[MED_BUF_SIZE];
747 printf(" First, enter the URL. (Example: http://www.us.itd.umich.edu/users/).\n");
748 printf(" The URL may be up to %d characters long.\n", MED_BUF_SIZE);
753 (void) fetch_buffer(url, sizeof(url), stdin);
756 if (check_URL(url) == 0)
758 printf(" A URL may not have any spaces or tabs in it. Please re-enter your URL.\n\n");
761 printf("\n Now please enter a descriptive label for this URL\n");
765 (void) fetch_buffer(label, sizeof(label), stdin);
766 } while (label[0] == '\0');
767 rvalue = (char *) Malloc((unsigned) (strlen(url) + 2 + strlen(label)));
768 sprintf(rvalue, "%s %s", url, label);
769 return((char *) rvalue);
773 check_URL( char *url )
777 for (cp = url; *cp != '\n' && *cp != '\0'; cp++) {
778 if (isspace((unsigned char)*cp))
788 mod_perror( LDAP *ld )
790 int ld_errno = LDAP_SUCCESS;
791 char *ld_errtext = NULL;
793 ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ld_errno );
795 if( ld_errno != LDAP_SUCCESS ) {
796 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_errtext );
799 fprintf( stderr, " modify failed: %s (%d)\n",
800 ldap_err2string( ld_errno ), ld_errno );
802 if( ld_errtext != NULL ) {
803 fprintf( stderr, " additional information: %s\n",
807 fprintf( stderr, " Please try again later.\n" );