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