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