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