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