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