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