]> git.sur5r.net Git - openldap/blob - clients/ud/mod.c
Found the really, really stupid bug. The SAFEMEMCPY macro
[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
17 #include <ac/ctype.h>
18 #include <ac/string.h>
19 #include <ac/time.h>
20
21 #include <lber.h>
22 #include <ldap.h>
23 #include "ud.h"
24
25 extern struct entry Entry; 
26 extern int verbose;
27 extern LDAP *ld;
28
29 extern LDAPMessage *find();
30 extern void * Malloc();
31
32 static char * get_URL();
33 static int check_URL();
34
35 #ifdef DEBUG
36 extern int debug;
37 #endif
38
39 modify(who)
40 char *who;
41 {
42 #ifdef UOFM
43         void set_updates();     /* routine to modify noBatchUpdates */
44 #endif
45         LDAPMessage *mp;        /* returned from find() */
46         char *dn;               /* distinguished name */
47         char **rdns;            /* for fiddling with the DN */
48         char name[MED_BUF_SIZE];        /* entry to modify */
49         int displayed_choices = 0;
50         static char ans[SMALL_BUF_SIZE];        /* for holding user input */
51 #ifdef UOFM
52         static char printed_warning = 0;        /* for use with the */
53         struct attribute no_batch_update_attr;
54         extern char * fetch_boolean_value();
55 #endif
56         int is_a_group;         /* TRUE if it is; FALSE otherwise */
57         extern void Free();
58         extern int bind_status;
59
60 #ifdef DEBUG
61         if (debug & D_TRACE)
62                 printf("->modify(%s)\n", who);
63 #endif
64         /*
65          *  Require them to bind first if the are modifying a group.
66          */
67         if (bind_status == UD_NOT_BOUND) {
68                 if (auth((char *) NULL, 1) < 0)
69                         return;
70         }
71
72         /*
73          *  First, decide what entry we are going to modify.  If the
74          *  user has not included a name on the modify command line,
75          *  we will use the person who was last looked up with a find
76          *  command.  If there is no value there either, we don't know
77          *  who to modify.
78          *
79          *  Once we know who to modify, be sure that they exist, and
80          *  parse out their DN.
81          */
82         if (who == NULL) {
83                 if (verbose) {
84                         printf("  Enter the name of the person or\n");
85                         printf("  group whose entry you want to modify: ");
86                 }
87                 else
88                         printf("  Modify whose entry? ");
89                 fflush(stdout);
90                 fetch_buffer(name, sizeof(name), stdin);
91                 if (name[0] != '\0')
92                         who = name;
93                 else
94                         return;
95         }
96         if ((mp = find(who, TRUE)) == NULL) {
97                 (void) ldap_msgfree(mp);
98                 printf("  Could not locate \"%s\" in the Directory.\n", who);
99                 return;
100         }
101         dn = ldap_get_dn(ld, ldap_first_entry(ld, mp));
102         rdns = ldap_explode_dn(dn, TRUE);
103         if (verbose)
104                 printf("\n  Modifying Directory entry of \"%s\"\n", *rdns);
105
106 #ifdef UOFM
107         /*
108          *  If verbose mode is turned on and the user has not set a value
109          *  for noBatchUpdates, warn them that what they are about to do
110          *  may be overwritten automatically by that Stinkbug.
111          */
112         no_batch_update_attr.quipu_name = "noBatchUpdates";
113         (void) fetch_boolean_value(dn, no_batch_update_attr);
114         if (verbose && !printed_warning && (ld->ld_errno == LDAP_NO_SUCH_ATTRIBUTE)) {
115                 printed_warning = 1;
116                 printf("\n  WARNING!\n");
117                 printf("  You are about to make a modification to an LDAP entry\n");
118                 printf("  that has its \"automatic updates\" field set to ON.\n");
119                 printf("  This means that the entry will be automatically updated\n");
120                 printf("  each month from official University sources like the\n");
121                 printf("  Personnel Office.  With \"automatic updates\" set to ON,\n");
122                 printf("  the following fields will be overwritten each month:\n");
123                 printf("         Title, home address and phone,\n");
124                 printf("         business address and phone\n");
125                 printf("  If you modify any of these fields, you may want to change\n");
126                 printf("  the \"automatic updates\" field to OFF so that your\n");
127                 printf("  changes will not be overwritten.  You may change this\n");
128                 printf("  setting by choosing \"u\" at the \"Modify what?\" prompt\n");
129         }
130 #endif
131
132         /*
133          *  Current values for user 'who' are being held in 'mp'. We
134          *  should parse up that buffer and fill in the Entry structure.
135          *  Once we're done with that, we can find out which fields the
136          *  user would like to modify.
137          */
138         parse_answer(mp);
139         is_a_group = isgroup();
140         (void) ldap_msgfree(mp);
141         printf("  You now need to specify what field you'd like to modify.\n");
142         for (;;) {
143                 if ( verbose || !displayed_choices ) {
144                         printf("\n  Choices are:\n");
145                         printf("  -----------------------\n");
146                         print_mod_list(is_a_group);
147                         printf("\n  Pressing Return will cancel the process.\n");
148                         displayed_choices = 1;
149                 }
150                 printf("\n  Modify what? ");
151                 fflush(stdout);
152                 fetch_buffer(ans, sizeof(ans), stdin);
153                 if (ans[0] == '\0')
154                         break;
155                 perform_action(ans, dn, is_a_group);
156                 if ((mp = find(*rdns, TRUE)) == NULL)
157                         break;
158                 parse_answer(mp);
159                 (void) ldap_msgfree(mp);
160         }
161         (void) Free(dn);
162         ldap_value_free(rdns);
163         return;
164 }
165
166 /* generic routine for changing any field */
167 void change_field(who, attr)
168 char *who;                      /* DN of entry we are changing */
169 struct attribute attr;          /* attribute to change */
170 {
171
172 #define IS_MOD(x)       (!strncasecmp(resp, (x), strlen(resp)))
173                                 
174         char *get_value();              /* routine to extract values */
175         static char buf[MED_BUF_SIZE];  /* for printing things */
176         static char resp[SMALL_BUF_SIZE];       /* for user input */
177         char *prompt, *prompt2, *more;
178         register int i;                         /* for looping thru values */
179         static LDAPMod mod;
180         static LDAPMod *mods[2] = { &mod };     /* passed to ldap_modify */
181         static char *values[MAX_VALUES];        /* passed to ldap_modify */
182         extern void Free();
183
184 #ifdef DEBUG
185         if (debug & D_TRACE)
186                 printf("->change_field(%x, %s)\n", attr, who);
187 #endif
188         /*
189          *  If there is no current value associated with the attribute,
190          *  then this is the easy case.  Collect one (or more) attributes
191          *  from the user, and then call ldap_modify_s() to write the changes
192          *  to the LDAP server.
193          */
194         for (i = 0; i < MAX_VALUES; i++)
195                 values[i] = NULL;
196         if (attr.values == (char **) NULL) {
197                 printf("\n  No current \"%s\"\n", attr.output_string);
198                 values[0] = get_value(attr.quipu_name, "Enter a value");
199                 if ( values[0] == NULL )
200                         return;
201                 mod.mod_op = LDAP_MOD_REPLACE;
202                 mod.mod_type = attr.quipu_name;
203                 mod.mod_values = values;
204                 for (i = 1; i < MAX_VALUES; i++) {
205                         printf("  Do you wish to add an additional value? ");
206                         fflush(stdout);
207                         fetch_buffer(resp, sizeof(resp), stdin);
208                         if ((resp[0] == 'y') || (resp[0] == 'Y'))
209                                 values[i] = get_value(attr.quipu_name, "Enter an additional value");
210                         else
211                                 break;
212                 }
213 #ifdef DEBUG
214                 if (debug & D_MODIFY) {
215                         printf("  ld = 0x%x\n", ld);
216                         printf("  who = [%s]\n", who);
217                         printf("  mods[0]->mod_op = %1d\n", mods[0]->mod_op);
218                         printf("  mods[0]->mod_type = %s\n", mods[0]->mod_type);
219                         for (i = 0; mods[0]->mod_values[i] != NULL; i++)
220                                 printf("  mods[0]->mod_values[%1d] = %s\n", i, mods[0]->mod_values[i]);
221                 }
222 #endif
223                 if (ldap_modify_s(ld, who, mods)) {
224                         mod_perror(ld);
225                         return;
226                 }
227                 else if (verbose)
228                         printf("  Modification of '%s' complete.\n", attr.output_string);
229                 ldap_uncache_entry( ld, who );
230                 for (i--; i > 0; i--)
231                         (void) Free(values[i]);
232         }
233         /*
234          *  There are values for this attribute already.  In this case,
235          *  we want to allow the user to delete all existing values,
236          *  add additional values to the ones there already, or just
237          *  delete some of the values already present.  DIXIE does not
238          *  handle modifications where the attribute occurs on the LHS
239          *  more than once.  So to delete entries and add entries, we
240          *  need to call ldap_modify() twice.
241          */
242         else {
243                 /*
244                  *  If the attribute holds values which are DNs, print them
245                  *  in a most pleasant way.
246                  */
247                 sprintf(buf, "%s:  ", attr.output_string);
248                 if (!strcmp(attr.quipu_name, "owner"))
249                         (void) print_DN(attr);
250                 else
251                         (void) print_values(attr);
252
253                 if (verbose) {
254                         printf("  You may now:\n");
255                         printf("    Add additional values to the existing ones, OR\n");
256                         printf("    Clear all values listed above, OR\n");
257                         printf("    Delete one of the values listed above, OR\n");
258                         printf("    Replace all of the values above with new ones.\n");
259                 }
260                 printf("\n  Add, Clear, Delete, or Replace? ");
261                 fflush(stdout);
262                 fetch_buffer(resp, sizeof(resp), stdin);
263
264                 /*
265                  *  Bail if they just hit the RETURN key.
266                  */
267                 if (resp[0] == '\0') {
268                         if (verbose)
269                                 printf("\n  No changes made.\n");
270                         return;
271                 }
272
273                 /*
274                  *  If the want to clear the values, just do it.
275                  */
276                 mod.mod_type = attr.quipu_name;
277                 mod.mod_values = values;
278                 if (IS_MOD("clear")) {
279                         mod.mod_op = LDAP_MOD_DELETE;
280                         mod.mod_values = NULL;
281                         if ( verbose && !confirm_action( "All existing values will be removed." )) {
282                                 printf("  Modification halted.\n");
283                                 return;
284                         }
285 #ifdef DEBUG
286                         if (debug & D_MODIFY) {
287                                 printf("Clearing attribute '%s'\n", attr.quipu_name);
288                                 printf("who = [%s]\n", who);
289                                 printf("mod = [%d] [%s] [%x]\n", mod.mod_op,
290                                         mod.mod_type, mod.mod_values);
291                         }
292 #endif
293                         if (ldap_modify_s(ld, who, mods)) {
294                                 mod_perror(ld);
295                                 return;
296                         }
297                         else if (verbose)
298                                 printf("  '%s' has been cleared.\n", attr.output_string);
299                         ldap_uncache_entry( ld,  who );
300                         return;
301                 }
302
303                 if (IS_MOD("add")) {
304                         prompt = "Enter the value you wish to add";
305                         more = "  Add an additional value? ";
306                         prompt2 = "Enter another value you wish to add";
307                         mod.mod_op = LDAP_MOD_ADD;
308                 }
309                 else if (IS_MOD("delete")) {
310                         prompt = "Enter the value you wish to delete";
311                         more = "  Delete an additional value? ";
312                         prompt2 = "Enter another value you wish to delete";
313                         mod.mod_op = LDAP_MOD_DELETE;
314                 }
315                 else if (IS_MOD("replace")) {
316                         prompt = "Enter the new value";
317                         more = "  Add an additional value? ";
318                         prompt2 = "Enter another value you wish to add";
319                         mod.mod_op = LDAP_MOD_REPLACE;
320                         if ( verbose && !confirm_action( "All existing values will be overwritten with the new values you are about to enter." )) {
321                                 printf("  Modification halted.\n");
322                                 return;
323                         }
324
325                 }
326                 else {
327                         printf("  No changes made.\n");
328                         return;
329                 }
330
331                 values[0] = get_value(attr.quipu_name, prompt);
332                 for (i = 1; i < MAX_VALUES; i++) {
333                         printf(more);
334                         fflush(stdout);
335                         fetch_buffer(resp, sizeof(resp), stdin);
336                         if ((resp[0] == 'y') || (resp[0] == 'Y'))
337                                 values[i] = get_value(attr.quipu_name, prompt2);
338                         else
339                                 break;
340                 }
341
342                 /* if the first value in the value-array is NULL, bail */
343                 if (values[0] == NULL) {
344                         if (verbose)
345                                 printf("  No modification made.\n");
346                         return;
347                 }
348 #ifdef DEBUG
349                 if (debug & D_MODIFY) {
350                         printf("  ld = 0x%x\n", ld);
351                         printf("  who = [%s]\n", who);
352                         printf("  mods[0]->mod_op = %1d\n", mods[0]->mod_op);
353                         printf("  mods[0]->mod_type = %s\n", mods[0]->mod_type);
354                         for (i = 0; mods[0]->mod_values[i] != NULL; i++)
355                                 printf("  mods[0]->mod_values[%1d] = %s\n", i, mods[0]->mod_values[i]);
356                 }
357 #endif
358                 if (ldap_modify_s(ld, who, mods)) {
359                         mod_perror(ld);
360                         return;
361                 }
362                 else if (verbose)
363                         printf("  Modifications to '%s' complete.\n", attr.output_string);
364                 ldap_uncache_entry( ld, who );
365                 for (i--; i > 0; i--)
366                         (void) Free(values[i]);
367         }
368         return;
369 }
370
371 /*
372  *  These are used to size the buffers we use when collecting values that
373  *  can cross more than one line.
374  */
375 #define LINE_SIZE       80
376 #define MAX_LINES        6
377 #define MAX_DESC_LINES  24
378 #define INTL_ADDR_LIMIT 30
379
380 char *get_value(id, prompt)
381 char *id, *prompt;
382 {
383         char *cp;               /* for the Malloc() */
384         int count;              /* line # of new value -- if multiline */
385         int multiline = 0;      /* 1 if this value is multiline */
386         static char line[LINE_SIZE];    /* raw line from user */
387         static char buffer[MAX_DESC_LINES * LINE_SIZE]; /* holds ALL of the 
388                                                            lines we get */
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 LDAP Directory.  The name you have typed above could not be found in the LDAP 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 LDAP database each month.  Sometimes this\n");
626                 printf("  feature is undesirable.  For example, if you maintain your\n");
627                 printf("  entry in the LDAP 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
759         if (verbose) {
760                 printf("  First, enter the URL.  (Example: http://www.us.itd.umich.edu/users/).\n");
761                 printf("  The URL may be up to %d characters long.\n", MED_BUF_SIZE);
762         }
763         for (;;) {
764                 printf("  URL: ");
765                 fflush(stdout);
766                 (void) fetch_buffer(url, sizeof(url), stdin);
767                 if (*url == '\0')
768                         continue;
769                 if (check_URL(url) == 0)
770                         break;
771                 printf("  A URL may not have any spaces or tabs in it.  Please re-enter your URL.\n\n");
772         }
773         if (verbose)
774                 printf("\n  Now please enter a descriptive label for this URL\n");
775         do {
776                 printf("  Label: ");
777                 fflush(stdout);
778                 (void) fetch_buffer(label, sizeof(label), stdin);
779         } while (label[0] == '\0');
780         rvalue = (char *) Malloc((unsigned) (strlen(url) + 2 + strlen(label)));
781         sprintf(rvalue, "%s %s", url, label);
782         return((char *) rvalue);
783 }
784
785 static check_URL(url)
786 char *url;
787 {
788         register char *cp;
789
790         for (cp = url; *cp != '\n' && *cp != '\0'; cp++) {
791                 if (isspace(*cp))
792                         return(-1);
793                         /*NOTREACHED*/
794         }
795         *cp = '\0';
796         return(0);
797 }
798
799
800 mod_perror( LDAP *ld )
801 {
802         if ( ld == NULL || ( ld->ld_errno != LDAP_UNAVAILABLE &&
803             ld->ld_errno != LDAP_UNWILLING_TO_PERFORM )) {
804                 ldap_perror( ld, "modify" );
805                 return;
806         }
807
808         fprintf( stderr, "\n  modify: failed because part of the online directory is not able\n" );
809         fprintf( stderr, "  to be modified right now" );
810         if ( ld->ld_errno == LDAP_UNAVAILABLE ) {
811                 fprintf( stderr, " or is temporarily unavailable" );
812         }
813         fprintf( stderr, ".\n  Please try again later.\n" );
814 }