3 * Copyright 1998-2002 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.
472 if (!strcmp(id, "owner")) {
473 LDAPMessage *lmp, *elmp;
476 lmp = find(line, FALSE);
477 if (lmp == (LDAPMessage *) NULL) {
478 printf(" Could not find \"%s\" in the Directory\n", line);
480 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);
483 elmp = ldap_first_entry(ld, lmp);
484 if (lmp == (LDAPMessage *) NULL) {
485 ldap_perror(ld, "ldap_first_entry");
488 tmp = ldap_get_dn(ld, elmp);
491 (void) ldap_msgfree(lmp);
495 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress")) {
496 if (strlen(line) > INTL_ADDR_LIMIT) {
497 printf(" The international standard for addresses only allows for 30-character lines\n");
498 printf(" Please re-enter your last line.\n");
504 * Separate lines of multiline attribute values with
505 * dollar signs. Copy this line into the buffer we
506 * use to collect up all of the user-supplied input
507 * lines. If this is not a multiline attribute, we
511 (void) strcat(buffer, " $ ");
512 (void) strcat(buffer, line);
515 if ((count > MAX_LINES) && (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress"))) {
516 printf(" The international standard for addresses only allows for six lines\n");
520 if ((count > MAX_DESC_LINES) && !strcmp(id, "multiLineDescription")) {
521 printf(" We only allow %d lines of description\n", MAX_DESC_LINES);
526 if (buffer[0] == '\0')
529 if (debug & D_MODIFY)
530 printf(" Value is [%s]\n", buffer);
532 cp = (char *) Malloc((unsigned) (strlen(buffer) + 1));
539 char *who, /* DN of entry we are changing */
540 int attr_idx /* boolean attribute to change */
543 struct attribute attr = Entry.attrs[attr_to_index(attrlist[attr_idx].quipu_name)];
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((unsigned char)*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");
600 set_updates( char *who, int dummy )
603 static char response[16];
604 static char value[6];
605 static char *newsetting[2] = { value, NULL };
606 LDAPMod mod, *mods[2];
607 struct attribute attr;
611 printf("->set_updates(%s)\n", who);
614 mods[1] = (LDAPMod *) NULL;
615 mod.mod_op = LDAP_MOD_REPLACE;
616 mod.mod_type = "noBatchUpdates";
617 mod.mod_values = newsetting;
618 /* explain what the implications are */
620 printf("\n By default, updates that are received from the Personnel\n");
621 printf(" Office and the Office of the Registrar are applied to all\n");
622 printf(" entries in the LDAP database each month. Sometimes this\n");
623 printf(" feature is undesirable. For example, if you maintain your\n");
624 printf(" entry in the LDAP database manually, you may not want to\n");
625 printf(" have these updates applied to your entry, possibly overwriting\n");
626 printf(" correct information with out-dated information.\n\n");
629 /* fetch the current setting */
630 attr.quipu_name = "noBatchUpdates";
631 if ((cp = fetch_boolean_value(who, attr)) == NULL)
633 if (!strcmp(cp, "TRUE"))
634 printf(" Automatic updates are currently turned OFF\n");
635 else if (!strcmp(cp, "FALSE"))
636 printf(" Automatic updates are currently turned ON\n");
638 fprintf(stderr, " Unknown update flag -> [%s]\n", cp);
642 /* see if they want to change it */
643 printf("\n Change this setting [no]? ");
645 (void) fetch_buffer(response, sizeof(response), stdin);
646 for (s = response; isspace((unsigned char)*s); s++)
648 if ((*s == 'y') || (*s == 'Y')) {
649 if (!strcmp(cp, "TRUE"))
650 strcpy(value, "FALSE");
652 strcpy(value, "TRUE");
653 if (ldap_modify_s(ld, who, mods)) {
658 printf(" Setting has been changed\n");
659 ldap_uncache_entry( ld, who );
663 printf(" Setting has not been changed\n");
669 print_mod_list( int group )
671 register int i, j = 1;
674 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
675 if (attrlist[i].flags & ATTR_FLAG_GROUP_MOD) {
676 printf(" %2d -> %s\n", j, attrlist[i].output_string);
681 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
682 if (attrlist[i].flags & ATTR_FLAG_PERSON_MOD) {
683 printf(" %2d -> %s\n", j, attrlist[i].output_string);
688 printf(" ? -> Print this list\n\n");
689 printf(" Press the RETURN key without typing a number to quit.\n");
692 printf(" To add nicknames, send mail to x500-nicknames@umich.edu\n");
697 perform_action( char *choice, char *dn, int group )
700 register int i, j = 1;
702 selection = atoi(choice);
704 printf("\n Choices are:\n");
705 printf(" -----------------------\n");
706 print_mod_list(group);
712 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
713 if (attrlist[i].flags & ATTR_FLAG_GROUP_MOD) {
720 for (i = 0; attrlist[i].quipu_name != NULL; i++) {
721 if (attrlist[i].flags & ATTR_FLAG_PERSON_MOD) {
729 if (attrlist[i].quipu_name == NULL) {
730 printf("\n Choices are:\n");
731 printf(" -----------------------\n");
732 print_mod_list(group);
736 (*attrlist[i].mod_func)(dn, i);
743 char *rvalue, label[MED_BUF_SIZE], url[MED_BUF_SIZE];
746 printf(" First, enter the URL. (Example: http://www.us.itd.umich.edu/users/).\n");
747 printf(" The URL may be up to %d characters long.\n", MED_BUF_SIZE);
752 (void) fetch_buffer(url, sizeof(url), stdin);
755 if (check_URL(url) == 0)
757 printf(" A URL may not have any spaces or tabs in it. Please re-enter your URL.\n\n");
760 printf("\n Now please enter a descriptive label for this URL\n");
764 (void) fetch_buffer(label, sizeof(label), stdin);
765 } while (label[0] == '\0');
766 rvalue = (char *) Malloc((unsigned) (strlen(url) + 2 + strlen(label)));
767 sprintf(rvalue, "%s %s", url, label);
768 return((char *) rvalue);
772 check_URL( char *url )
776 for (cp = url; *cp != '\n' && *cp != '\0'; cp++) {
777 if (isspace((unsigned char)*cp))
787 mod_perror( LDAP *ld )
789 int ld_errno = LDAP_SUCCESS;
790 char *ld_errtext = NULL;
792 ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ld_errno );
794 if( ld_errno != LDAP_SUCCESS ) {
795 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_errtext );
798 fprintf( stderr, " modify failed: %s (%d)\n",
799 ldap_err2string( ld_errno ), ld_errno );
801 if( ld_errtext != NULL ) {
802 fprintf( stderr, " additional information: %s\n",
806 fprintf( stderr, " Please try again later.\n" );