]> git.sur5r.net Git - openldap/blob - clients/ud/mod.c
Fixed ldapconfig.h.edit/Make-template to generate ldapconfig.h correctly.
[openldap] / clients / ud / mod.c
1 /*
2  * Copyright (c) 1991,1993  Regents of the University of Michigan.
3  * All rights reserved.
4  *
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.
11  */
12
13 #include <stdio.h>
14 #include <string.h>
15 #include <ctype.h>
16 #include <time.h>
17 #ifndef __STDC__
18 #include <memory.h>
19 #endif
20 #include <sys/types.h>
21
22 #include <lber.h>
23 #include <ldap.h>
24 #include "ud.h"
25
26 extern struct entry Entry; 
27 extern int verbose;
28 extern LDAP *ld;
29
30 extern LDAPMessage *find();
31
32 #ifdef DEBUG
33 extern int debug;
34 #endif
35
36 modify(who)
37 char *who;
38 {
39 #ifdef UOFM
40         void set_updates();     /* routine to modify noBatchUpdates */
41 #endif
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 */
48 #ifdef UOFM
49         static char printed_warning = 0;        /* for use with the */
50         struct attribute no_batch_update_attr;
51         extern char * fetch_boolean_value();
52 #endif
53         int is_a_group;         /* TRUE if it is; FALSE otherwise */
54         extern void Free();
55         extern int bind_status;
56
57 #ifdef DEBUG
58         if (debug & D_TRACE)
59                 printf("->modify(%s)\n", who);
60 #endif
61         /*
62          *  Require them to bind first if the are modifying a group.
63          */
64         if (bind_status == UD_NOT_BOUND) {
65                 if (auth((char *) NULL, 1) < 0)
66                         return;
67         }
68
69         /*
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
74          *  who to modify.
75          *
76          *  Once we know who to modify, be sure that they exist, and
77          *  parse out their DN.
78          */
79         if (who == NULL) {
80                 if (verbose) {
81                         printf("  Enter the name of the person or\n");
82                         printf("  group whose entry you want to modify: ");
83                 }
84                 else
85                         printf("  Modify whose entry? ");
86                 fflush(stdout);
87                 fetch_buffer(name, sizeof(name), stdin);
88                 if (name[0] != '\0')
89                         who = name;
90                 else
91                         return;
92         }
93         if ((mp = find(who, TRUE)) == NULL) {
94                 (void) ldap_msgfree(mp);
95                 printf("  Could not locate \"%s\" in the Directory.\n", who);
96                 return;
97         }
98         dn = ldap_get_dn(ld, ldap_first_entry(ld, mp));
99         rdns = ldap_explode_dn(dn, TRUE);
100         if (verbose)
101                 printf("\n  Modifying Directory entry of \"%s\"\n", *rdns);
102
103 #ifdef UOFM
104         /*
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.
108          */
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)) {
112                 printed_warning = 1;
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");
126         }
127 #endif
128
129         /*
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.
134          */
135         parse_answer(mp);
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");
139         for (;;) {
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;
146                 }
147                 printf("\n  Modify what? ");
148                 fflush(stdout);
149                 fetch_buffer(ans, sizeof(ans), stdin);
150                 if (ans[0] == '\0')
151                         break;
152                 perform_action(ans, dn, is_a_group);
153                 if ((mp = find(*rdns, TRUE)) == NULL)
154                         break;
155                 parse_answer(mp);
156                 (void) ldap_msgfree(mp);
157         }
158         (void) Free(dn);
159         ldap_value_free(rdns);
160         return;
161 }
162
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 */
167 {
168
169 #define IS_MOD(x)       (!strncasecmp(resp, (x), strlen(resp)))
170                                 
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 */
176         static LDAPMod mod;
177         static LDAPMod *mods[2] = { &mod };     /* passed to ldap_modify */
178         static char *values[MAX_VALUES];        /* passed to ldap_modify */
179         extern void Free();
180
181 #ifdef DEBUG
182         if (debug & D_TRACE)
183                 printf("->change_field(%x, %s)\n", attr, who);
184 #endif
185         /*
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.
190          */
191         for (i = 0; i < MAX_VALUES; i++)
192                 values[i] = NULL;
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 )
197                         return;
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? ");
203                         fflush(stdout);
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");
207                         else
208                                 break;
209                 }
210 #ifdef DEBUG
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]);
218                 }
219 #endif
220                 if (ldap_modify_s(ld, who, mods)) {
221                         mod_perror(ld);
222                         return;
223                 }
224                 else if (verbose)
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]);
229         }
230         /*
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.
238          */
239         else {
240                 /*
241                  *  If the attribute holds values which are DNs, print them
242                  *  in a most pleasant way.
243                  */
244                 sprintf(buf, "%s:  ", attr.output_string);
245                 if (!strcmp(attr.quipu_name, "owner"))
246                         (void) print_DN(attr);
247                 else
248                         (void) print_values(attr);
249
250                 if (verbose) {
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");
256                 }
257                 printf("\n  Add, Clear, Delete, or Replace? ");
258                 fflush(stdout);
259                 fetch_buffer(resp, sizeof(resp), stdin);
260
261                 /*
262                  *  Bail if they just hit the RETURN key.
263                  */
264                 if (resp[0] == '\0') {
265                         if (verbose)
266                                 printf("\n  No changes made.\n");
267                         return;
268                 }
269
270                 /*
271                  *  If the want to clear the values, just do it.
272                  */
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");
280                                 return;
281                         }
282 #ifdef DEBUG
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);
288                         }
289 #endif
290                         if (ldap_modify_s(ld, who, mods)) {
291                                 mod_perror(ld);
292                                 return;
293                         }
294                         else if (verbose)
295                                 printf("  '%s' has been cleared.\n", attr.output_string);
296                         ldap_uncache_entry( ld,  who );
297                         return;
298                 }
299
300                 if (IS_MOD("add")) {
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;
305                 }
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;
311                 }
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");
319                                 return;
320                         }
321
322                 }
323                 else {
324                         printf("  No changes made.\n");
325                         return;
326                 }
327
328                 values[0] = get_value(attr.quipu_name, prompt);
329                 for (i = 1; i < MAX_VALUES; i++) {
330                         printf(more);
331                         fflush(stdout);
332                         fetch_buffer(resp, sizeof(resp), stdin);
333                         if ((resp[0] == 'y') || (resp[0] == 'Y'))
334                                 values[i] = get_value(attr.quipu_name, prompt2);
335                         else
336                                 break;
337                 }
338
339                 /* if the first value in the value-array is NULL, bail */
340                 if (values[0] == NULL) {
341                         if (verbose)
342                                 printf("  No modification made.\n");
343                         return;
344                 }
345 #ifdef DEBUG
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]);
353                 }
354 #endif
355                 if (ldap_modify_s(ld, who, mods)) {
356                         mod_perror(ld);
357                         return;
358                 }
359                 else if (verbose)
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]);
364         }
365         return;
366 }
367
368 /*
369  *  These are used to size the buffers we use when collecting values that
370  *  can cross more than one line.
371  */
372 #define LINE_SIZE       80
373 #define MAX_LINES        6
374 #define MAX_DESC_LINES  24
375 #define INTL_ADDR_LIMIT 30
376
377 char *get_value(id, prompt)
378 char *id, *prompt;
379 {
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 
385                                                            lines we get */
386         extern void * Malloc();
387         static char * get_URL();
388
389 #ifdef DEBUG
390         if (debug & D_TRACE)
391                 printf("->get_value(%s, %s)\n", id, prompt);
392 #endif
393         /* if this is a URL, have another routine handle this */
394         if (!strcmp(id, "labeledURL"))
395                 return(get_URL());
396
397         /*
398          *  To start with, we have one line of input from the user.
399          *
400          *  Addresses and multiline description can span multiple lines.
401          *  Other attributes may not.
402          */
403         count = 1;
404         (void) memset(buffer, 0, sizeof(buffer));
405 #ifdef UOFM
406         if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress") || !strcmp(id, "multiLineDescription") || !strcmp(id, "vacationMessage")) 
407 #else
408         if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress"))
409 #endif
410                 multiline = 1;
411         printf("\n  %s:\n", prompt);
412
413         /* fetch lines */
414         for (;;) {
415                 if (multiline)
416                         printf(" %1d: ", count);
417                 else
418                         printf("  > ");
419                 fflush(stdout);
420                 fetch_buffer(line, sizeof(line), stdin);
421
422                 if (line[0] == '\0')
423                         break;
424 #ifdef UOFM
425                 /*
426                  *  Screen out dangerous e-mail addresses of the form:
427                  *
428                  *              user@umich.edu
429                  *
430                  * and addresses that have no '@' symbol at all.
431                  */
432                 if (!strcmp(id, "mail")) {
433                         int i;
434                         char *tmp, *tmp2;
435
436                         /* if this is a group, don't worry */
437                         if (isgroup())
438                                 goto mail_is_good;
439
440                         /* if this address is not @umich.edu, don't worry */
441                         /* ...unless there is no '@' at all! */
442                         tmp = strdup(line);
443                         if ((tmp2 = strrchr(tmp, '@')) == NULL) {
444                         printf("\n");
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 );
446                                 goto mail_is_bad;
447                         }
448                         
449                         *tmp2 = '\0';
450                         tmp2++;
451                         if (strcasecmp(tmp2, "umich.edu"))
452                                 goto mail_is_good;
453
454                         /* if not of the form uid@umich.edu, don't worry */
455                         if ((i = attr_to_index("uid")) < 0)
456                                 goto mail_is_good;
457                         if (strcasecmp(tmp, *(Entry.attrs[i].values)))
458                                 goto mail_is_good;
459                         printf("\n");
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);
461
462 mail_is_bad:
463                         printf("\n");
464                         printf("  Please enter a legal e-mail address (or press RETURN to stop)\n");
465                         continue;
466                 }
467 mail_is_good:
468 #endif
469
470                 /*
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
476                  *  type in a UFN.
477                  */
478                 if (!strcmp(id, "owner")) {
479                         LDAPMessage *lmp, *elmp;
480                         char *tmp;
481                         
482                         lmp = find(line, FALSE);
483                         if (lmp == (LDAPMessage *) NULL) {
484                                 printf("  Could not find \"%s\" in the Directory\n", line);
485                                 if (verbose) 
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);
487                                 return(NULL);
488                         }
489                         elmp = ldap_first_entry(ld, lmp);
490                         if (lmp == (LDAPMessage *) NULL) {
491                                 ldap_perror(ld, "ldap_first_entry");
492                                 return(NULL);
493                         }
494                         tmp = ldap_get_dn(ld, elmp);
495                         strcpy(buffer, tmp);
496                         (void) ldap_msgfree(lmp);
497                         break;
498                 }
499
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");
504                                 continue;
505                         }
506                 }
507
508                 /*
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
513                  *  are done.
514                  */
515                 if (count++ > 1)
516                         (void) strcat(buffer, "$");
517                 (void) strcat(buffer, line);
518                 if (!multiline)
519                         break;
520                 if ((count > MAX_LINES) && (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress"))) {
521                         printf("  The international standard for addresses only allows for six lines\n");
522                         break;
523                 }
524 #ifdef UOFM
525                 if ((count > MAX_DESC_LINES) && !strcmp(id, "multiLineDescription")) {
526                         printf("  We only allow %d lines of description\n", MAX_DESC_LINES);
527                         break;
528                 }
529 #endif
530         }
531         if (buffer[0] == '\0')
532                 return(NULL);
533 #ifdef DEBUG
534         if (debug & D_MODIFY)
535                 printf("  Value is [%s]\n", buffer);
536 #endif
537         cp = (char *) Malloc((unsigned) (strlen(buffer) + 1));
538         strcpy(cp, buffer);
539         return(cp);
540 }
541
542 void set_boolean(who, attr)
543 char *who;                      /* DN of entry we are changing */
544 struct attribute attr;          /* boolean attribute to change */
545 {
546         char *cp, *s;
547         extern char * fetch_boolean_value();
548         static char response[16];
549         static char *newsetting[2] = { NULL, NULL };
550         LDAPMod mod, *mods[2];
551
552 #ifdef DEBUG
553         if (debug & D_TRACE)
554                 printf("->set_boolean(%s, %s)\n", who, attr.quipu_name);
555 #endif
556         mods[0] = &mod;
557         mods[1] = (LDAPMod *) NULL;
558         mod.mod_op = LDAP_MOD_REPLACE;
559         mod.mod_type = attr.quipu_name;
560         mod.mod_values = newsetting;
561
562         /* fetch the current setting */
563         if ((cp = fetch_boolean_value(who, attr)) == NULL)
564                 return;
565         if (!strcmp(cp, "TRUE"))
566                 newsetting[0] = "FALSE";
567         else if (!strcmp(cp, "FALSE"))
568                 newsetting[0] = "TRUE";
569         else {
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";
573         }
574
575         /* see if they want to change it */
576         printf("\n");
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", 
579                                                         newsetting[0]);
580         printf("  Please enter Y for yes, N for no, or RETURN to cancel:  ");
581         fflush(stdout);
582         (void) fetch_buffer(response, sizeof(response), stdin);
583         for (s = response; isspace(*s); s++)
584                         ;
585         if ((*s == 'y') || (*s == 'Y')) {
586                 if (ldap_modify_s(ld, who, mods)) {
587                         mod_perror(ld);
588                         return;
589                 }
590                 else if (verbose)
591                         printf("  Setting has been changed\n");
592                 ldap_uncache_entry(ld, who);
593                 return;
594         }
595         if (verbose)
596                 printf("  Setting has not been changed\n");
597 }
598
599 #ifdef UOFM
600
601 void set_updates(who)
602 char *who;
603 {
604         char *cp, *s;
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;
611
612 #ifdef DEBUG
613         if (debug & D_TRACE)
614                 printf("->set_updates(%s)\n", who);
615 #endif
616         mods[0] = &mod;
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 */
622         if (verbose) {
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");
630         }
631
632         /* fetch the current setting */
633         attr.quipu_name = "noBatchUpdates";
634         if ((cp = fetch_boolean_value(who, attr)) == NULL)
635                 return;
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");
640         else {
641                 fprintf(stderr, "  Unknown update flag -> [%s]\n", cp);
642                 return;
643         }
644
645         /* see if they want to change it */
646         printf("\n  Change this setting [no]? ");
647         fflush(stdout);
648         (void) fetch_buffer(response, sizeof(response), stdin);
649         for (s = response; isspace(*s); s++)
650                         ;
651         if ((*s == 'y') || (*s == 'Y')) {
652                 if (!strcmp(cp, "TRUE"))
653                         strcpy(value, "FALSE");
654                 else
655                         strcpy(value, "TRUE");
656                 if (ldap_modify_s(ld, who, mods)) {
657                         mod_perror(ld);
658                         return;
659                 }
660                 else if (verbose)
661                         printf("  Setting has been changed\n");
662                 ldap_uncache_entry( ld, who );
663                 return;
664         }
665         if (verbose)
666                 printf("  Setting has not been changed\n");
667 }
668
669 #endif
670
671 print_mod_list(group)
672 int group;
673 {
674         register int i, j = 1;
675         extern struct attribute attrlist[];
676
677         if (group == TRUE) {
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);
681                         j++;
682                 }
683             }
684         } else {
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);
688                         j++;
689                 }
690             }
691         }
692         printf("   ? ->  Print this list\n\n");
693         printf("  Press the RETURN key without typing a number to quit.\n");
694 #ifdef UOFM
695         if (group == FALSE)
696                 printf("  To add nicknames, send mail to x500-nicknames@umich.edu\n");
697 #endif
698 }
699                         
700 perform_action(choice, dn, group)
701 char choice[];
702 char *dn;
703 int group;
704 {
705         int selection;
706         register int i, j = 1;
707         extern struct attribute attrlist[];
708         extern void mod_addrDN(), change_field(), set_boolean();
709
710         selection = atoi(choice);
711         if (selection < 1) {
712                 printf("\n  Choices are:\n");
713                 printf("  -----------------------\n");
714                 print_mod_list(group);
715                 return(1);
716                 /* NOTREACHED */
717         }
718
719         if (group == TRUE) {
720             for (i = 0; attrlist[i].quipu_name != NULL; i++) {
721                 if (attrlist[i].flags & ATTR_FLAG_GROUP_MOD) {
722                         if (j == selection)
723                                 break;
724                         j++;
725                 }
726             }
727         } else {
728             for (i = 0; attrlist[i].quipu_name != NULL; i++) {
729                 if (attrlist[i].flags & ATTR_FLAG_PERSON_MOD) {
730                         if (j == selection)
731                                 break;
732                         j++;
733                 }
734             }
735         }
736
737         if (attrlist[i].quipu_name == NULL) {
738                 printf("\n  Choices are:\n");
739                 printf("  -----------------------\n");
740                 print_mod_list(group);
741                 return(1);
742                 /* NOTREACHED */
743         }
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)]);
750         else
751                 (*attrlist[i].mod_func)(dn);
752         return(0);
753 }
754
755 static char * get_URL()
756 {
757         char *rvalue, label[MED_BUF_SIZE], url[MED_BUF_SIZE];
758         static int check_URL();
759         extern void * Malloc();
760
761         if (verbose) {
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);
764         }
765         for (;;) {
766                 printf("  URL: ");
767                 fflush(stdout);
768                 (void) fetch_buffer(url, sizeof(url), stdin);
769                 if (*url == '\0')
770                         continue;
771                 if (check_URL(url) == 0)
772                         break;
773                 printf("  A URL may not have any spaces or tabs in it.  Please re-enter your URL.\n\n");
774         }
775         if (verbose)
776                 printf("\n  Now please enter a descriptive label for this URL\n");
777         do {
778                 printf("  Label: ");
779                 fflush(stdout);
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);
785 }
786
787 static check_URL(url)
788 char *url;
789 {
790         register char *cp;
791
792         for (cp = url; *cp != '\n' && *cp != '\0'; cp++) {
793                 if (isspace(*cp))
794                         return(-1);
795                         /*NOTREACHED*/
796         }
797         *cp = '\0';
798         return(0);
799 }
800
801
802 mod_perror( LDAP *ld )
803 {
804         if ( ld == NULL || ( ld->ld_errno != LDAP_UNAVAILABLE &&
805             ld->ld_errno != LDAP_UNWILLING_TO_PERFORM )) {
806                 ldap_perror( ld, "modify" );
807                 return;
808         }
809
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" );
814         }
815         fprintf( stderr, ".\n  Please try again later.\n" );
816 }