]> git.sur5r.net Git - openldap/blob - clients/ud/mod.c
clients build under FreeBSD
[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 "portable.h"
14
15 #include <stdio.h>
16 #include <string.h>
17 #include <ctype.h>
18 #include <time.h>
19 #ifndef __STDC__
20 #include <memory.h>
21 #endif
22 #include <sys/types.h>
23
24 #include <lber.h>
25 #include <ldap.h>
26 #include "ud.h"
27
28 extern struct entry Entry; 
29 extern int verbose;
30 extern LDAP *ld;
31
32 extern LDAPMessage *find();
33
34 #ifdef DEBUG
35 extern int debug;
36 #endif
37
38 modify(who)
39 char *who;
40 {
41 #ifdef UOFM
42         void set_updates();     /* routine to modify noBatchUpdates */
43 #endif
44         LDAPMessage *mp;        /* returned from find() */
45         char *dn;               /* distinguished name */
46         char **rdns;            /* for fiddling with the DN */
47         char name[MED_BUF_SIZE];        /* entry to modify */
48         int displayed_choices = 0;
49         static char ans[SMALL_BUF_SIZE];        /* for holding user input */
50 #ifdef UOFM
51         static char printed_warning = 0;        /* for use with the */
52         struct attribute no_batch_update_attr;
53         extern char * fetch_boolean_value();
54 #endif
55         int is_a_group;         /* TRUE if it is; FALSE otherwise */
56         extern void Free();
57         extern int bind_status;
58
59 #ifdef DEBUG
60         if (debug & D_TRACE)
61                 printf("->modify(%s)\n", who);
62 #endif
63         /*
64          *  Require them to bind first if the are modifying a group.
65          */
66         if (bind_status == UD_NOT_BOUND) {
67                 if (auth((char *) NULL, 1) < 0)
68                         return;
69         }
70
71         /*
72          *  First, decide what entry we are going to modify.  If the
73          *  user has not included a name on the modify command line,
74          *  we will use the person who was last looked up with a find
75          *  command.  If there is no value there either, we don't know
76          *  who to modify.
77          *
78          *  Once we know who to modify, be sure that they exist, and
79          *  parse out their DN.
80          */
81         if (who == NULL) {
82                 if (verbose) {
83                         printf("  Enter the name of the person or\n");
84                         printf("  group whose entry you want to modify: ");
85                 }
86                 else
87                         printf("  Modify whose entry? ");
88                 fflush(stdout);
89                 fetch_buffer(name, sizeof(name), stdin);
90                 if (name[0] != '\0')
91                         who = name;
92                 else
93                         return;
94         }
95         if ((mp = find(who, TRUE)) == NULL) {
96                 (void) ldap_msgfree(mp);
97                 printf("  Could not locate \"%s\" in the Directory.\n", who);
98                 return;
99         }
100         dn = ldap_get_dn(ld, ldap_first_entry(ld, mp));
101         rdns = ldap_explode_dn(dn, TRUE);
102         if (verbose)
103                 printf("\n  Modifying Directory entry of \"%s\"\n", *rdns);
104
105 #ifdef UOFM
106         /*
107          *  If verbose mode is turned on and the user has not set a value
108          *  for noBatchUpdates, warn them that what they are about to do
109          *  may be overwritten automatically by that Stinkbug.
110          */
111         no_batch_update_attr.quipu_name = "noBatchUpdates";
112         (void) fetch_boolean_value(dn, no_batch_update_attr);
113         if (verbose && !printed_warning && (ld->ld_errno == LDAP_NO_SUCH_ATTRIBUTE)) {
114                 printed_warning = 1;
115                 printf("\n  WARNING!\n");
116                 printf("  You are about to make a modification to an LDAP entry\n");
117                 printf("  that has its \"automatic updates\" field set to ON.\n");
118                 printf("  This means that the entry will be automatically updated\n");
119                 printf("  each month from official University sources like the\n");
120                 printf("  Personnel Office.  With \"automatic updates\" set to ON,\n");
121                 printf("  the following fields will be overwritten each month:\n");
122                 printf("         Title, home address and phone,\n");
123                 printf("         business address and phone\n");
124                 printf("  If you modify any of these fields, you may want to change\n");
125                 printf("  the \"automatic updates\" field to OFF so that your\n");
126                 printf("  changes will not be overwritten.  You may change this\n");
127                 printf("  setting by choosing \"u\" at the \"Modify what?\" prompt\n");
128         }
129 #endif
130
131         /*
132          *  Current values for user 'who' are being held in 'mp'. We
133          *  should parse up that buffer and fill in the Entry structure.
134          *  Once we're done with that, we can find out which fields the
135          *  user would like to modify.
136          */
137         parse_answer(mp);
138         is_a_group = isgroup();
139         (void) ldap_msgfree(mp);
140         printf("  You now need to specify what field you'd like to modify.\n");
141         for (;;) {
142                 if ( verbose || !displayed_choices ) {
143                         printf("\n  Choices are:\n");
144                         printf("  -----------------------\n");
145                         print_mod_list(is_a_group);
146                         printf("\n  Pressing Return will cancel the process.\n");
147                         displayed_choices = 1;
148                 }
149                 printf("\n  Modify what? ");
150                 fflush(stdout);
151                 fetch_buffer(ans, sizeof(ans), stdin);
152                 if (ans[0] == '\0')
153                         break;
154                 perform_action(ans, dn, is_a_group);
155                 if ((mp = find(*rdns, TRUE)) == NULL)
156                         break;
157                 parse_answer(mp);
158                 (void) ldap_msgfree(mp);
159         }
160         (void) Free(dn);
161         ldap_value_free(rdns);
162         return;
163 }
164
165 /* generic routine for changing any field */
166 void change_field(who, attr)
167 char *who;                      /* DN of entry we are changing */
168 struct attribute attr;          /* attribute to change */
169 {
170
171 #define IS_MOD(x)       (!strncasecmp(resp, (x), strlen(resp)))
172                                 
173         char *get_value();              /* routine to extract values */
174         static char buf[MED_BUF_SIZE];  /* for printing things */
175         static char resp[SMALL_BUF_SIZE];       /* for user input */
176         char *prompt, *prompt2, *more;
177         register int i;                         /* for looping thru values */
178         static LDAPMod mod;
179         static LDAPMod *mods[2] = { &mod };     /* passed to ldap_modify */
180         static char *values[MAX_VALUES];        /* passed to ldap_modify */
181         extern void Free();
182
183 #ifdef DEBUG
184         if (debug & D_TRACE)
185                 printf("->change_field(%x, %s)\n", attr, who);
186 #endif
187         /*
188          *  If there is no current value associated with the attribute,
189          *  then this is the easy case.  Collect one (or more) attributes
190          *  from the user, and then call ldap_modify_s() to write the changes
191          *  to the LDAP server.
192          */
193         for (i = 0; i < MAX_VALUES; i++)
194                 values[i] = NULL;
195         if (attr.values == (char **) NULL) {
196                 printf("\n  No current \"%s\"\n", attr.output_string);
197                 values[0] = get_value(attr.quipu_name, "Enter a value");
198                 if ( values[0] == NULL )
199                         return;
200                 mod.mod_op = LDAP_MOD_REPLACE;
201                 mod.mod_type = attr.quipu_name;
202                 mod.mod_values = values;
203                 for (i = 1; i < MAX_VALUES; i++) {
204                         printf("  Do you wish to add an additional value? ");
205                         fflush(stdout);
206                         fetch_buffer(resp, sizeof(resp), stdin);
207                         if ((resp[0] == 'y') || (resp[0] == 'Y'))
208                                 values[i] = get_value(attr.quipu_name, "Enter an additional value");
209                         else
210                                 break;
211                 }
212 #ifdef DEBUG
213                 if (debug & D_MODIFY) {
214                         printf("  ld = 0x%x\n", ld);
215                         printf("  who = [%s]\n", who);
216                         printf("  mods[0]->mod_op = %1d\n", mods[0]->mod_op);
217                         printf("  mods[0]->mod_type = %s\n", mods[0]->mod_type);
218                         for (i = 0; mods[0]->mod_values[i] != NULL; i++)
219                                 printf("  mods[0]->mod_values[%1d] = %s\n", i, mods[0]->mod_values[i]);
220                 }
221 #endif
222                 if (ldap_modify_s(ld, who, mods)) {
223                         mod_perror(ld);
224                         return;
225                 }
226                 else if (verbose)
227                         printf("  Modification of '%s' complete.\n", attr.output_string);
228                 ldap_uncache_entry( ld, who );
229                 for (i--; i > 0; i--)
230                         (void) Free(values[i]);
231         }
232         /*
233          *  There are values for this attribute already.  In this case,
234          *  we want to allow the user to delete all existing values,
235          *  add additional values to the ones there already, or just
236          *  delete some of the values already present.  DIXIE does not
237          *  handle modifications where the attribute occurs on the LHS
238          *  more than once.  So to delete entries and add entries, we
239          *  need to call ldap_modify() twice.
240          */
241         else {
242                 /*
243                  *  If the attribute holds values which are DNs, print them
244                  *  in a most pleasant way.
245                  */
246                 sprintf(buf, "%s:  ", attr.output_string);
247                 if (!strcmp(attr.quipu_name, "owner"))
248                         (void) print_DN(attr);
249                 else
250                         (void) print_values(attr);
251
252                 if (verbose) {
253                         printf("  You may now:\n");
254                         printf("    Add additional values to the existing ones, OR\n");
255                         printf("    Clear all values listed above, OR\n");
256                         printf("    Delete one of the values listed above, OR\n");
257                         printf("    Replace all of the values above with new ones.\n");
258                 }
259                 printf("\n  Add, Clear, Delete, or Replace? ");
260                 fflush(stdout);
261                 fetch_buffer(resp, sizeof(resp), stdin);
262
263                 /*
264                  *  Bail if they just hit the RETURN key.
265                  */
266                 if (resp[0] == '\0') {
267                         if (verbose)
268                                 printf("\n  No changes made.\n");
269                         return;
270                 }
271
272                 /*
273                  *  If the want to clear the values, just do it.
274                  */
275                 mod.mod_type = attr.quipu_name;
276                 mod.mod_values = values;
277                 if (IS_MOD("clear")) {
278                         mod.mod_op = LDAP_MOD_DELETE;
279                         mod.mod_values = NULL;
280                         if ( verbose && !confirm_action( "All existing values will be removed." )) {
281                                 printf("  Modification halted.\n");
282                                 return;
283                         }
284 #ifdef DEBUG
285                         if (debug & D_MODIFY) {
286                                 printf("Clearing attribute '%s'\n", attr.quipu_name);
287                                 printf("who = [%s]\n", who);
288                                 printf("mod = [%d] [%s] [%x]\n", mod.mod_op,
289                                         mod.mod_type, mod.mod_values);
290                         }
291 #endif
292                         if (ldap_modify_s(ld, who, mods)) {
293                                 mod_perror(ld);
294                                 return;
295                         }
296                         else if (verbose)
297                                 printf("  '%s' has been cleared.\n", attr.output_string);
298                         ldap_uncache_entry( ld,  who );
299                         return;
300                 }
301
302                 if (IS_MOD("add")) {
303                         prompt = "Enter the value you wish to add";
304                         more = "  Add an additional value? ";
305                         prompt2 = "Enter another value you wish to add";
306                         mod.mod_op = LDAP_MOD_ADD;
307                 }
308                 else if (IS_MOD("delete")) {
309                         prompt = "Enter the value you wish to delete";
310                         more = "  Delete an additional value? ";
311                         prompt2 = "Enter another value you wish to delete";
312                         mod.mod_op = LDAP_MOD_DELETE;
313                 }
314                 else if (IS_MOD("replace")) {
315                         prompt = "Enter the new value";
316                         more = "  Add an additional value? ";
317                         prompt2 = "Enter another value you wish to add";
318                         mod.mod_op = LDAP_MOD_REPLACE;
319                         if ( verbose && !confirm_action( "All existing values will be overwritten with the new values you are about to enter." )) {
320                                 printf("  Modification halted.\n");
321                                 return;
322                         }
323
324                 }
325                 else {
326                         printf("  No changes made.\n");
327                         return;
328                 }
329
330                 values[0] = get_value(attr.quipu_name, prompt);
331                 for (i = 1; i < MAX_VALUES; i++) {
332                         printf(more);
333                         fflush(stdout);
334                         fetch_buffer(resp, sizeof(resp), stdin);
335                         if ((resp[0] == 'y') || (resp[0] == 'Y'))
336                                 values[i] = get_value(attr.quipu_name, prompt2);
337                         else
338                                 break;
339                 }
340
341                 /* if the first value in the value-array is NULL, bail */
342                 if (values[0] == NULL) {
343                         if (verbose)
344                                 printf("  No modification made.\n");
345                         return;
346                 }
347 #ifdef DEBUG
348                 if (debug & D_MODIFY) {
349                         printf("  ld = 0x%x\n", ld);
350                         printf("  who = [%s]\n", who);
351                         printf("  mods[0]->mod_op = %1d\n", mods[0]->mod_op);
352                         printf("  mods[0]->mod_type = %s\n", mods[0]->mod_type);
353                         for (i = 0; mods[0]->mod_values[i] != NULL; i++)
354                                 printf("  mods[0]->mod_values[%1d] = %s\n", i, mods[0]->mod_values[i]);
355                 }
356 #endif
357                 if (ldap_modify_s(ld, who, mods)) {
358                         mod_perror(ld);
359                         return;
360                 }
361                 else if (verbose)
362                         printf("  Modifications to '%s' complete.\n", attr.output_string);
363                 ldap_uncache_entry( ld, who );
364                 for (i--; i > 0; i--)
365                         (void) Free(values[i]);
366         }
367         return;
368 }
369
370 /*
371  *  These are used to size the buffers we use when collecting values that
372  *  can cross more than one line.
373  */
374 #define LINE_SIZE       80
375 #define MAX_LINES        6
376 #define MAX_DESC_LINES  24
377 #define INTL_ADDR_LIMIT 30
378
379 char *get_value(id, prompt)
380 char *id, *prompt;
381 {
382         char *cp;               /* for the Malloc() */
383         int count;              /* line # of new value -- if multiline */
384         int multiline = 0;      /* 1 if this value is multiline */
385         static char line[LINE_SIZE];    /* raw line from user */
386         static char buffer[MAX_DESC_LINES * LINE_SIZE]; /* holds ALL of the 
387                                                            lines we get */
388         extern void * Malloc();
389         static char * get_URL();
390
391 #ifdef DEBUG
392         if (debug & D_TRACE)
393                 printf("->get_value(%s, %s)\n", id, prompt);
394 #endif
395         /* if this is a URL, have another routine handle this */
396         if (!strcmp(id, "labeledURL"))
397                 return(get_URL());
398
399         /*
400          *  To start with, we have one line of input from the user.
401          *
402          *  Addresses and multiline description can span multiple lines.
403          *  Other attributes may not.
404          */
405         count = 1;
406         (void) memset(buffer, 0, sizeof(buffer));
407 #ifdef UOFM
408         if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress") || !strcmp(id, "multiLineDescription") || !strcmp(id, "vacationMessage")) 
409 #else
410         if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress"))
411 #endif
412                 multiline = 1;
413         printf("\n  %s:\n", prompt);
414
415         /* fetch lines */
416         for (;;) {
417                 if (multiline)
418                         printf(" %1d: ", count);
419                 else
420                         printf("  > ");
421                 fflush(stdout);
422                 fetch_buffer(line, sizeof(line), stdin);
423
424                 if (line[0] == '\0')
425                         break;
426 #ifdef UOFM
427                 /*
428                  *  Screen out dangerous e-mail addresses of the form:
429                  *
430                  *              user@umich.edu
431                  *
432                  * and addresses that have no '@' symbol at all.
433                  */
434                 if (!strcmp(id, "mail")) {
435                         int i;
436                         char *tmp, *tmp2;
437
438                         /* if this is a group, don't worry */
439                         if (isgroup())
440                                 goto mail_is_good;
441
442                         /* if this address is not @umich.edu, don't worry */
443                         /* ...unless there is no '@' at all! */
444                         tmp = strdup(line);
445                         if ((tmp2 = strrchr(tmp, '@')) == NULL) {
446                         printf("\n");
447                         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 );
448                                 goto mail_is_bad;
449                         }
450                         
451                         *tmp2 = '\0';
452                         tmp2++;
453                         if (strcasecmp(tmp2, "umich.edu"))
454                                 goto mail_is_good;
455
456                         /* if not of the form uid@umich.edu, don't worry */
457                         if ((i = attr_to_index("uid")) < 0)
458                                 goto mail_is_good;
459                         if (strcasecmp(tmp, *(Entry.attrs[i].values)))
460                                 goto mail_is_good;
461                         printf("\n");
462                         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);
463
464 mail_is_bad:
465                         printf("\n");
466                         printf("  Please enter a legal e-mail address (or press RETURN to stop)\n");
467                         continue;
468                 }
469 mail_is_good:
470 #endif
471
472                 /*
473                  *  If the attribute which we are gathering is a "owner"
474                  *  then we should lookup the name.  The user is going to
475                  *  either have to change the search base before doing the
476                  *  modify, or the person is going to have to be within the
477                  *  scope of the current search base, or they will need to
478                  *  type in a UFN.
479                  */
480                 if (!strcmp(id, "owner")) {
481                         LDAPMessage *lmp, *elmp;
482                         char *tmp;
483                         
484                         lmp = find(line, FALSE);
485                         if (lmp == (LDAPMessage *) NULL) {
486                                 printf("  Could not find \"%s\" in the Directory\n", line);
487                                 if (verbose) 
488                                         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);
489                                 return(NULL);
490                         }
491                         elmp = ldap_first_entry(ld, lmp);
492                         if (lmp == (LDAPMessage *) NULL) {
493                                 ldap_perror(ld, "ldap_first_entry");
494                                 return(NULL);
495                         }
496                         tmp = ldap_get_dn(ld, elmp);
497                         strcpy(buffer, tmp);
498                         (void) ldap_msgfree(lmp);
499                         break;
500                 }
501
502                 if (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress")) {
503                         if (strlen(line) > INTL_ADDR_LIMIT) {
504                                 printf("  The international standard for addresses only allows for 30-character lines\n");
505                                 printf("  Please re-enter your last line.\n");
506                                 continue;
507                         }
508                 }
509
510                 /*
511                  *  Separate lines of multiline attribute values with
512                  *  dollar signs.  Copy this line into the buffer we
513                  *  use to collect up all of the user-supplied input
514                  *  lines.  If this is not a multiline attribute, we
515                  *  are done.
516                  */
517                 if (count++ > 1)
518                         (void) strcat(buffer, "$");
519                 (void) strcat(buffer, line);
520                 if (!multiline)
521                         break;
522                 if ((count > MAX_LINES) && (!strcmp(id, "postalAddress") || !strcmp(id, "homePostalAddress"))) {
523                         printf("  The international standard for addresses only allows for six lines\n");
524                         break;
525                 }
526 #ifdef UOFM
527                 if ((count > MAX_DESC_LINES) && !strcmp(id, "multiLineDescription")) {
528                         printf("  We only allow %d lines of description\n", MAX_DESC_LINES);
529                         break;
530                 }
531 #endif
532         }
533         if (buffer[0] == '\0')
534                 return(NULL);
535 #ifdef DEBUG
536         if (debug & D_MODIFY)
537                 printf("  Value is [%s]\n", buffer);
538 #endif
539         cp = (char *) Malloc((unsigned) (strlen(buffer) + 1));
540         strcpy(cp, buffer);
541         return(cp);
542 }
543
544 void set_boolean(who, attr)
545 char *who;                      /* DN of entry we are changing */
546 struct attribute attr;          /* boolean attribute to change */
547 {
548         char *cp, *s;
549         extern char * fetch_boolean_value();
550         static char response[16];
551         static char *newsetting[2] = { NULL, NULL };
552         LDAPMod mod, *mods[2];
553
554 #ifdef DEBUG
555         if (debug & D_TRACE)
556                 printf("->set_boolean(%s, %s)\n", who, attr.quipu_name);
557 #endif
558         mods[0] = &mod;
559         mods[1] = (LDAPMod *) NULL;
560         mod.mod_op = LDAP_MOD_REPLACE;
561         mod.mod_type = attr.quipu_name;
562         mod.mod_values = newsetting;
563
564         /* fetch the current setting */
565         if ((cp = fetch_boolean_value(who, attr)) == NULL)
566                 return;
567         if (!strcmp(cp, "TRUE"))
568                 newsetting[0] = "FALSE";
569         else if (!strcmp(cp, "FALSE"))
570                 newsetting[0] = "TRUE";
571         else {
572                 printf("  This field needs to be set to either TRUE or to FALSE.\n");
573                 printf("  \"%s\" is not a legal value.  Please set this field to either TRUE or to FALSE.\n", cp);
574                 newsetting[0] = "FALSE";
575         }
576
577         /* see if they want to change it */
578         printf("\n");
579         printf("  The current value of this field is %s.\n", cp);
580         printf("  Should I change the value of this field to %s?\n", 
581                                                         newsetting[0]);
582         printf("  Please enter Y for yes, N for no, or RETURN to cancel:  ");
583         fflush(stdout);
584         (void) fetch_buffer(response, sizeof(response), stdin);
585         for (s = response; isspace(*s); s++)
586                         ;
587         if ((*s == 'y') || (*s == 'Y')) {
588                 if (ldap_modify_s(ld, who, mods)) {
589                         mod_perror(ld);
590                         return;
591                 }
592                 else if (verbose)
593                         printf("  Setting has been changed\n");
594                 ldap_uncache_entry(ld, who);
595                 return;
596         }
597         if (verbose)
598                 printf("  Setting has not been changed\n");
599 }
600
601 #ifdef UOFM
602
603 void set_updates(who)
604 char *who;
605 {
606         char *cp, *s;
607         extern char * fetch_boolean_value();
608         static char response[16];
609         static char value[6];
610         static char *newsetting[2] = { value, NULL };
611         LDAPMod mod, *mods[2];
612         struct attribute attr;
613
614 #ifdef DEBUG
615         if (debug & D_TRACE)
616                 printf("->set_updates(%s)\n", who);
617 #endif
618         mods[0] = &mod;
619         mods[1] = (LDAPMod *) NULL;
620         mod.mod_op = LDAP_MOD_REPLACE;
621         mod.mod_type = "noBatchUpdates";
622         mod.mod_values = newsetting;
623         /* explain what the implications are */
624         if (verbose) {
625                 printf("\n  By default, updates that are received from the Personnel\n");
626                 printf("  Office and the Office of the Registrar are applied to all\n");
627                 printf("  entries in the LDAP database each month.  Sometimes this\n");
628                 printf("  feature is undesirable.  For example, if you maintain your\n");
629                 printf("  entry in the LDAP database manually, you may not want to\n");
630                 printf("  have these updates applied to your entry, possibly overwriting\n");
631                 printf("  correct information with out-dated information.\n\n");
632         }
633
634         /* fetch the current setting */
635         attr.quipu_name = "noBatchUpdates";
636         if ((cp = fetch_boolean_value(who, attr)) == NULL)
637                 return;
638         if (!strcmp(cp, "TRUE"))
639                 printf("  Automatic updates are currently turned OFF\n");
640         else if (!strcmp(cp, "FALSE"))
641                 printf("  Automatic updates are currently turned ON\n");
642         else {
643                 fprintf(stderr, "  Unknown update flag -> [%s]\n", cp);
644                 return;
645         }
646
647         /* see if they want to change it */
648         printf("\n  Change this setting [no]? ");
649         fflush(stdout);
650         (void) fetch_buffer(response, sizeof(response), stdin);
651         for (s = response; isspace(*s); s++)
652                         ;
653         if ((*s == 'y') || (*s == 'Y')) {
654                 if (!strcmp(cp, "TRUE"))
655                         strcpy(value, "FALSE");
656                 else
657                         strcpy(value, "TRUE");
658                 if (ldap_modify_s(ld, who, mods)) {
659                         mod_perror(ld);
660                         return;
661                 }
662                 else if (verbose)
663                         printf("  Setting has been changed\n");
664                 ldap_uncache_entry( ld, who );
665                 return;
666         }
667         if (verbose)
668                 printf("  Setting has not been changed\n");
669 }
670
671 #endif
672
673 print_mod_list(group)
674 int group;
675 {
676         register int i, j = 1;
677         extern struct attribute attrlist[];
678
679         if (group == TRUE) {
680             for (i = 0; attrlist[i].quipu_name != NULL; i++) {
681                 if (attrlist[i].flags & ATTR_FLAG_GROUP_MOD) {
682                         printf("  %2d ->  %s\n", j, attrlist[i].output_string);
683                         j++;
684                 }
685             }
686         } else {
687             for (i = 0; attrlist[i].quipu_name != NULL; i++) {
688                 if (attrlist[i].flags & ATTR_FLAG_PERSON_MOD) {
689                         printf("  %2d ->  %s\n", j, attrlist[i].output_string);
690                         j++;
691                 }
692             }
693         }
694         printf("   ? ->  Print this list\n\n");
695         printf("  Press the RETURN key without typing a number to quit.\n");
696 #ifdef UOFM
697         if (group == FALSE)
698                 printf("  To add nicknames, send mail to x500-nicknames@umich.edu\n");
699 #endif
700 }
701                         
702 perform_action(choice, dn, group)
703 char choice[];
704 char *dn;
705 int group;
706 {
707         int selection;
708         register int i, j = 1;
709         extern struct attribute attrlist[];
710         extern void mod_addrDN(), change_field(), set_boolean();
711
712         selection = atoi(choice);
713         if (selection < 1) {
714                 printf("\n  Choices are:\n");
715                 printf("  -----------------------\n");
716                 print_mod_list(group);
717                 return(1);
718                 /* NOTREACHED */
719         }
720
721         if (group == TRUE) {
722             for (i = 0; attrlist[i].quipu_name != NULL; i++) {
723                 if (attrlist[i].flags & ATTR_FLAG_GROUP_MOD) {
724                         if (j == selection)
725                                 break;
726                         j++;
727                 }
728             }
729         } else {
730             for (i = 0; attrlist[i].quipu_name != NULL; i++) {
731                 if (attrlist[i].flags & ATTR_FLAG_PERSON_MOD) {
732                         if (j == selection)
733                                 break;
734                         j++;
735                 }
736             }
737         }
738
739         if (attrlist[i].quipu_name == NULL) {
740                 printf("\n  Choices are:\n");
741                 printf("  -----------------------\n");
742                 print_mod_list(group);
743                 return(1);
744                 /* NOTREACHED */
745         }
746         if (attrlist[i].mod_func == change_field)
747                 (*attrlist[i].mod_func)(dn, Entry.attrs[attr_to_index(attrlist[i].quipu_name)]);
748         else if (attrlist[i].mod_func == mod_addrDN)
749                 (*attrlist[i].mod_func)(dn, i);
750         else if (attrlist[i].mod_func == set_boolean)
751                 (*attrlist[i].mod_func)(dn, Entry.attrs[attr_to_index(attrlist[i].quipu_name)]);
752         else
753                 (*attrlist[i].mod_func)(dn);
754         return(0);
755 }
756
757 static char * get_URL()
758 {
759         char *rvalue, label[MED_BUF_SIZE], url[MED_BUF_SIZE];
760         static int check_URL();
761         extern void * Malloc();
762
763         if (verbose) {
764                 printf("  First, enter the URL.  (Example: http://www.us.itd.umich.edu/users/).\n");
765                 printf("  The URL may be up to %d characters long.\n", MED_BUF_SIZE);
766         }
767         for (;;) {
768                 printf("  URL: ");
769                 fflush(stdout);
770                 (void) fetch_buffer(url, sizeof(url), stdin);
771                 if (*url == '\0')
772                         continue;
773                 if (check_URL(url) == 0)
774                         break;
775                 printf("  A URL may not have any spaces or tabs in it.  Please re-enter your URL.\n\n");
776         }
777         if (verbose)
778                 printf("\n  Now please enter a descriptive label for this URL\n");
779         do {
780                 printf("  Label: ");
781                 fflush(stdout);
782                 (void) fetch_buffer(label, sizeof(label), stdin);
783         } while (label[0] == '\0');
784         rvalue = (char *) Malloc((unsigned) (strlen(url) + 2 + strlen(label)));
785         sprintf(rvalue, "%s %s", url, label);
786         return((char *) rvalue);
787 }
788
789 static check_URL(url)
790 char *url;
791 {
792         register char *cp;
793
794         for (cp = url; *cp != '\n' && *cp != '\0'; cp++) {
795                 if (isspace(*cp))
796                         return(-1);
797                         /*NOTREACHED*/
798         }
799         *cp = '\0';
800         return(0);
801 }
802
803
804 mod_perror( LDAP *ld )
805 {
806         if ( ld == NULL || ( ld->ld_errno != LDAP_UNAVAILABLE &&
807             ld->ld_errno != LDAP_UNWILLING_TO_PERFORM )) {
808                 ldap_perror( ld, "modify" );
809                 return;
810         }
811
812         fprintf( stderr, "\n  modify: failed because part of the online directory is not able\n" );
813         fprintf( stderr, "  to be modified right now" );
814         if ( ld->ld_errno == LDAP_UNAVAILABLE ) {
815                 fprintf( stderr, " or is temporarily unavailable" );
816         }
817         fprintf( stderr, ".\n  Please try again later.\n" );
818 }