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