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