]> git.sur5r.net Git - openldap/blob - clients/ud/group.c
Cast `char' arguments to ctype.h functions to `unsigned char'.
[openldap] / clients / ud / group.c
1 /*
2  * Copyright (c) 1993, 1994  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
14 #include "portable.h"
15
16 #include <stdio.h>
17
18 #include <ac/string.h>
19 #include <ac/ctype.h>
20 #include <ac/time.h>
21 #include <ac/unistd.h>
22
23 #ifdef HAVE_IO_H
24 #include <io.h>
25 #endif
26
27 #include <lber.h>
28 #include <ldap.h>
29 #include <ldapconfig.h>
30 #include "ud.h"
31
32 static char * bind_and_fetch(char *name);
33
34
35 void
36 add_group( char *name )
37 {
38         int idx = 0, prompt = 0;
39         char tmp[BUFSIZ], dn[BUFSIZ];
40         static LDAPMod *attrs[9];
41         LDAPMod init_rdn,    init_owner,   init_domain,
42                 init_errors, init_request, init_joinable;
43         char *init_rdn_value[2], *init_owner_value[2], *init_domain_value[2],
44                 *init_errors_value[MAX_VALUES], *init_joinable_value[2],
45                 *init_request_value[MAX_VALUES];
46
47 #ifdef DEBUG
48         if (debug & D_TRACE) {
49                 if (name == NULL)
50                         printf("->add_group(NULL)\n");
51                 else
52                         printf("->add_group(%s)\n", name);
53         }
54 #endif
55
56         if (bind_status == UD_NOT_BOUND) {
57                 if (auth((char *) NULL, 1) < 0) {
58                         return;
59                 }
60         }
61
62         /*
63          *  If the user did not supply us with a name, prompt them for
64          *  a name.
65          */
66         if ((name == NULL) || (*name == '\0') || !strcasecmp(name, "group")) {
67                 ++prompt;
68                 printf("  Group to create? ");
69                 fflush(stdout);
70                 fetch_buffer(tmp, sizeof(tmp), stdin);
71                 if (tmp[0] == '\0')
72                         return;
73                 name = strdup(tmp);
74         }
75
76         /* remove quotes, dots, and underscores. */
77         name = strip_ignore_chars(name);
78
79 #ifdef UOFM
80         if (isauniqname(name)) {
81                 printf(" '%s' could be confused with a U-M uniqname.\n", name);
82                 printf(" You can create the group, but you need to make sure that\n");
83                 printf(" you reserve the uniqname for use as your groupname\n\n");
84                 printf(" Are you sure that you want to do this? ");
85                 fflush(stdout);
86                 fetch_buffer(tmp, sizeof(tmp), stdin);
87                 if (!(tmp[0] == 'y' || tmp[0] == 'Y'))
88                         return;
89                 printf("\n Be sure to contact your uniqname administrator to reserve\n");
90                 printf(" the uniqname '%s' for use as your group name.\n", name);
91         }
92 #endif
93         sprintf(dn, "cn=%s, %s", name, group_base);
94
95         /*
96          *  Make sure that this group does not already exist.
97          */
98         if (vrfy(dn) == TRUE) {
99                 printf("  The group \"%s\" already exists.\n", name);
100                 return;
101         }
102
103         /*
104          *  Take the easy way out:  Fill in some reasonable values for
105          *  the most important fields, and make the user use the modify
106          *  command to change them, or to give values to other fields.
107          */
108         init_rdn_value[0] = name;
109         init_rdn_value[1] = NULL;
110         init_rdn.mod_op = LDAP_MOD_ADD;
111         init_rdn.mod_type = "cn";
112         init_rdn.mod_values = init_rdn_value;
113         attrs[idx++] = &init_rdn;
114
115         init_owner_value[0] = bound_dn;
116         init_owner_value[1] = NULL;
117         init_owner.mod_op = LDAP_MOD_ADD;
118         init_owner.mod_type = "owner";
119         init_owner.mod_values = init_owner_value;
120         attrs[idx++] = &init_owner;
121
122 #ifdef UOFM
123         init_domain_value[0] = "umich.edu";
124 #else
125         init_domain_value[0] = ".";
126 #endif
127         init_domain_value[1] = NULL;
128         init_domain.mod_op = LDAP_MOD_ADD;
129         init_domain.mod_type = "associatedDomain";
130         init_domain.mod_values = init_domain_value;
131         attrs[idx++] = &init_domain;
132
133         init_errors_value[0] = bound_dn;
134         init_errors_value[1] = NULL;
135         init_errors.mod_op = LDAP_MOD_ADD;
136         init_errors.mod_type = "ErrorsTo";
137         init_errors.mod_values = init_errors_value;
138         attrs[idx++] = &init_errors;
139
140         init_request_value[0] = bound_dn;
141         init_request_value[1] = NULL;
142         init_request.mod_op = LDAP_MOD_ADD;
143         init_request.mod_type = "RequestsTo";
144         init_request.mod_values = init_request_value;
145         attrs[idx++] = &init_request;
146
147         init_joinable_value[0] = "FALSE";
148         init_joinable_value[1] = NULL;
149         init_joinable.mod_op = LDAP_MOD_ADD;
150         init_joinable.mod_type = "joinable";
151         init_joinable.mod_values = init_joinable_value;
152         attrs[idx++] = &init_joinable;
153
154         /* end it with a NULL */
155         attrs[idx] = NULL;
156
157 #ifdef DEBUG
158         if (debug & D_GROUPS) {
159                 LDAPMod **lpp;
160                 char **cpp;
161                 int i, j;
162                 printf("  About to call ldap_add()\n");
163                 printf("  ld = 0x%x\n", ld);
164                 printf("  dn = [%s]\n", dn);
165                 for (lpp = attrs, i = 0; *lpp != NULL; lpp++, i++) {
166                         printf("  attrs[%1d]  code = %s  type = %s\n", i, 
167                                 code_to_str((*lpp)->mod_op), (*lpp)->mod_type);
168                         for (cpp = (*lpp)->mod_values, j = 0; *cpp != NULL; cpp++, j++)
169                                 printf("    value #%1d = %s\n", j, *cpp);
170                         printf("    value #%1d = NULL\n", j);
171                 }
172         }
173 #endif
174
175         /*
176          *  Now add this to the LDAP Directory.
177          */
178         if (ldap_add_s(ld, dn, attrs) != 0) {
179                 ldap_perror(ld, "  ldap_add_s");
180                 printf("  Group not added.\n");
181                 if (prompt) Free(name);
182                 return;
183         }
184         if (verbose)
185                 printf("  Group \"%s\" has been added to the Directory\n",
186                        name);
187
188         /*
189          *  We need to blow away the cache here.
190          *
191          *  Since we first looked up the name before trying to create it,
192          *  and that look-up failed, the cache will falsely claim that this
193          *  entry does not exist.
194          */
195         (void) ldap_flush_cache(ld);
196         if (prompt) Free(name);
197         return;
198 }
199
200 void
201 remove_group( char *name )
202 {
203         char *dn, tmp[BUFSIZ];
204
205 #ifdef DEBUG
206         if (debug & D_TRACE) {
207                 if (name == NULL)
208                         printf("->remove_group(NULL)\n");
209                 else
210                         printf("->remove_group(%s)\n", name);
211         }
212 #endif
213         if ((dn = bind_and_fetch(name)) == NULL)
214                 return;
215
216         printf("\n  The group '%s' will be permanently removed from\n",
217                 name);
218         printf("  the Directory.  Are you absolutely sure that you want to\n" );        printf("  remove this entire group? ");
219         fflush(stdout);
220         fetch_buffer(tmp, sizeof(tmp), stdin);
221         if (!(tmp[0] == 'y' || tmp[0] == 'Y'))
222                 return;
223
224         /*
225          *  Now remove this from the LDAP Directory.
226          */
227         if (ldap_delete_s(ld, dn) != 0) {
228                 int ld_errno = 0;
229                 ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ld_errno);
230                 if (ld_errno == LDAP_INSUFFICIENT_ACCESS)
231                         printf("  You do not own the group \"%s\".\n", name);
232                 else
233                         ldap_perror(ld, "  ldap_delete_s");
234                 printf("  Group not removed.\n");
235                 Free(dn);
236                 return;
237         }
238         ldap_uncache_entry(ld, dn);
239         if (verbose)
240             if (name == NULL)
241                 printf("  The group has been removed.\n");
242             else
243                 printf("  The group \"%s\" has been removed.\n", name);
244         Free(dn);
245         return;
246 }
247
248 void
249 x_group( int action, char *name )
250 {
251         char **vp;
252         char *values[2], *group_name;
253         LDAPMod mod, *mods[2];
254         static char *actions[] = { "join", "resign from", NULL };
255
256 #ifdef DEBUG
257         if (debug & D_TRACE) {
258                 if (name == NULL)
259                         printf("->x_group(%d, NULL)\n", action);
260                 else
261                         printf("->x_group(%d, %s)\n", action, name);
262         }
263 #endif
264
265         /* the action desired sets the opcode to use */
266         switch (action) {
267         case G_JOIN:
268                 mod.mod_op = LDAP_MOD_ADD;
269                 break;
270         case G_RESIGN:
271                 mod.mod_op = LDAP_MOD_DELETE;
272                 break;
273         default:
274                 printf("x_group:  %d is not a known action\n", action);
275         }
276
277         if ((group_name = bind_and_fetch(name)) == NULL)
278                 return;
279         vp = Entry.attrs[attr_to_index("joinable")].values;
280         if (action == G_JOIN) {
281                 if (vp == NULL) {
282                         printf("  No one is permitted to join \"%s\"\n", group_name);
283                         Free(group_name);
284                         return;
285                 }
286                 if (!strcasecmp(*vp, "FALSE")) {
287                         printf("  No one is permitted to join \"%s\"\n", group_name);
288                         Free(group_name);
289                         return;
290                 }
291         }
292
293         /*  fill in the rest of the modification structure */
294         mods[0] = &mod;
295         mods[1] = (LDAPMod *) NULL;
296         values[0] = Entry.DN;
297         values[1] = NULL;
298         mod.mod_type = "memberOfGroup";
299         mod.mod_values = values;
300
301 #ifdef DEBUG
302         if (debug & D_GROUPS) {
303                 register LDAPMod **lpp;
304                 register char **cp;
305                 register int i, j;
306                 printf("  About to call ldap_modify_s()\n");
307                 printf("  ld = 0x%x\n", ld);
308                 printf("  dn = [%s]\n", bound_dn);
309                 for (lpp = mods, i = 1; *lpp != NULL; lpp++, i++) {
310                         printf("  mods[%1d] code = %1d\n", i, (*lpp)->mod_op);
311                         printf("  mods[%1d] type = %s\n", i, (*lpp)->mod_type);
312                         for (cp = (*lpp)->mod_values, j = 1; *cp != NULL; cp++, j++)
313                                 printf("  mods[%1d] v[%1d] = %s\n", i, j, *cp);
314                                 
315                 }
316         }
317 #endif
318
319         if (ldap_modify_s(ld, bound_dn, mods)) {
320                 int ld_errno = 0;
321                 ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ld_errno);
322                 if ((action == G_JOIN) && (ld_errno == LDAP_TYPE_OR_VALUE_EXISTS))
323                         printf("  You are already subscribed to \"%s\"\n", group_name);
324                 else if ((action == G_RESIGN) && (ld_errno == LDAP_NO_SUCH_ATTRIBUTE))
325                         printf("  You are not subscribed to \"%s\"\n", group_name);
326                 else
327                         mod_perror(ld);
328                 Free(group_name);
329                 return;
330         }
331         ldap_uncache_entry(ld, bound_dn);
332         if (verbose) {
333                 switch (action) {
334                 case G_JOIN:
335                         printf("  You are now subscribed to \"%s\"\n", group_name);
336                         break;
337                 case G_RESIGN:
338                         printf("  You are no longer subscribed to \"%s\"\n", group_name);
339                         break;
340                 }
341         }
342         Free(group_name);
343         return;
344 }
345
346 void
347 bulk_load( char *group )
348 {
349         register int idx_mail, idx_x500;
350         register int count_mail, count_x500;
351         char *values_mail[MAX_VALUES + 1], *values_x500[MAX_VALUES + 1];
352         int added_mail_entries = FALSE, added_x500_entries = FALSE;
353         char s[MED_BUF_SIZE];
354         LDAPMod mod, *mods[2];
355         LDAPMessage *lm;
356         FILE *fp;
357         int len;
358
359 #ifdef DEBUG
360         if (debug & D_TRACE)
361                 printf("->bulk_load(%s)\n", group);
362 #endif
363
364         /* you lose if going through MichNet */
365         if ( !isatty( 1 )) {
366 #ifdef UOFM
367                 printf("  Not allowed via UM-X500 connections.\n");
368 #endif
369                 return;
370         }
371
372         /* fetch entries from the file containing the e-mail addresses */
373         printf("\n  File to load? ");
374         fflush(stdout);
375         fetch_buffer(s, sizeof(s), stdin);
376         if (s[0] == '\0') {
377                 return;
378                 /*NOTREACHED*/
379         }
380         if ((fp = fopen(s, "r")) == NULL) {
381                 perror("bulk_load: fopen");
382                 return;
383         }
384         if (verbose)
385                 printf("  Loading group members from %s\n", s);
386
387         /* load them in MAX_VALUES at a time */
388         for (;;) {
389                 for (idx_mail = 0, idx_x500 = 0; 
390                      idx_mail < MAX_VALUES && idx_x500 < MAX_VALUES; ) {
391                         (void) fgets(s, sizeof(s), fp);
392                         if (feof(fp))
393                                 break;
394                         len = strlen(s) - 1;
395                         if (len == 0)
396                                 continue;
397                         s[len] = '\0';
398                         if (strchr(s, '@'))
399                                 values_mail[idx_mail++] = strdup(s);
400                         else {
401                                 if ((lm = find(s, !verbose)) == (LDAPMessage *) NULL) {
402                                         printf("  Could not locate \"%s\" -- skipping.\n", s);
403                                 }
404                                 else {
405                                     parse_answer(lm);
406                                     values_x500[idx_x500++] = strdup(Entry.DN);
407                                 }
408                         }
409                 }
410                 values_mail[idx_mail] = NULL;
411                 values_x500[idx_x500] = NULL;
412                 count_mail = idx_mail;
413                 count_x500 = idx_x500;
414
415                 /*
416                  *  Add the e-mail addresses.
417                  */
418                 if (count_mail > 0) {
419                         mods[0] = &mod;
420                         mods[1] = (LDAPMod *) NULL;
421                         mod.mod_type = "mail";
422                         mod.mod_values = values_mail;
423                         if (added_mail_entries)
424                                 mod.mod_op = LDAP_MOD_ADD;
425                         else
426                                 mod.mod_op = LDAP_MOD_REPLACE;
427
428 #ifdef DEBUG
429                         if (debug & D_GROUPS) {
430                         register LDAPMod **lpp;
431                         register char **cp;
432                         register int i, j;
433                         printf("  About to call ldap_modify_s()\n");
434                         printf("  ld = 0x%x\n", ld);
435                         printf("  dn = [%s]\n", group);
436                         for (lpp = mods, i = 1; *lpp != NULL; lpp++, i++) {
437                                 printf("  mods[%1d] code = %1d\n", i, (*lpp)->mod_op);
438                                 printf("  mods[%1d] type = %s\n", i, (*lpp)->mod_type);
439                                 for (cp = (*lpp)->mod_values, j = 1; *cp != NULL; cp++, j++)
440                                         printf("  mods[%1d] v[%1d] = %s\n", i, j, *cp);
441                                 }
442                         }
443 #endif
444                         if (ldap_modify_s(ld, group, mods))
445                                 mod_perror(ld);
446                         for (idx_mail--; idx_mail >= 0; idx_mail--)
447                                 Free(values_mail[idx_mail]);
448                         ldap_uncache_entry(ld, group);
449                         added_mail_entries = TRUE;
450
451                 }
452
453                 /*
454                  *  Add the LDAP style names.
455                  */
456                 if (count_x500 > 0) {
457                         mods[0] = &mod;
458                         mods[1] = (LDAPMod *) NULL;
459                         mod.mod_type = "member";
460                         mod.mod_values = values_x500;
461                         if (added_x500_entries)
462                                 mod.mod_op = LDAP_MOD_ADD;
463                         else
464                                 mod.mod_op = LDAP_MOD_REPLACE;
465
466 #ifdef DEBUG
467                         if (debug & D_GROUPS) {
468                         register LDAPMod **lpp;
469                         register char **cp;
470                         register int i, j;
471                         printf("  About to call ldap_modify_s()\n");
472                         printf("  ld = 0x%x\n", ld);
473                         printf("  dn = [%s]\n", group);
474                         for (lpp = mods, i = 1; *lpp != NULL; lpp++, i++) {
475                                 printf("  mods[%1d] code = %1d\n", i, (*lpp)->mod_op);
476                                 printf("  mods[%1d] type = %s\n", i, (*lpp)->mod_type);
477                                 for (cp = (*lpp)->mod_values, j = 1; *cp != NULL; cp++, j++)
478                                         printf("  mods[%1d] v[%1d] = %s\n", i, j, *cp);
479                                 }
480                         }
481 #endif
482                         if (ldap_modify_s(ld, group, mods))
483                                 mod_perror(ld);
484                         for (idx_x500--; idx_x500 >= 0; idx_x500--)
485                                 Free(values_x500[idx_x500]);
486                         ldap_uncache_entry(ld, group);
487                         added_x500_entries = TRUE;
488
489                 }
490
491                 /*
492                  *  If both counts were less than the maximum number we
493                  *  can handle at a time, then we are done.
494                  */
495                 if ((count_mail < MAX_VALUES) && (count_x500 < MAX_VALUES))
496                         break;
497         }
498         fclose(fp);
499         return;
500 }
501
502 void
503 purge_group( char *group )
504 {
505         int isclean = TRUE;
506         LDAPMessage *lm;
507         LDAPMod mod, *mods[2];
508         char dn[BUFSIZ], tmp[BUFSIZ], *values[2], **vp, **rdns;
509
510 #ifdef DEBUG
511         if (debug & D_TRACE) {
512                 if (group == NULL)
513                         printf("->purge_group(NULL)\n");
514                 else
515                         printf("->purge_group(%s)\n", group);
516         }
517 #endif
518         if (bind_status == UD_NOT_BOUND) {
519                 if (auth((char *) NULL, 1) < 0)
520                         return;
521         }
522         /*
523          *  If the user did not supply us with a name, prompt them for
524          *  a name.
525          */
526         if ((group == NULL) || (*group == '\0')) {
527                 printf("Group to purge? ");
528                 fflush(stdout);
529                 fetch_buffer(tmp, sizeof(tmp), stdin);
530                 if (tmp[0] == '\0')
531                         return;
532                 group = tmp;
533         }
534         sprintf(dn, "cn=%s, %s", group, group_base);
535
536         /* make sure the group in question exists */
537         if ((lm = find(group, FALSE)) == (LDAPMessage *) NULL) {
538                 printf("  Could not locate group \"%s\"\n", group);
539                 return;
540         }
541         parse_answer(lm);
542         ldap_msgfree(lm);
543
544         /* none of this stuff changes */
545         mods[0] = &mod;
546         mods[1] = (LDAPMod *) NULL;
547
548         values[1] = NULL;
549
550         mod.mod_values = values;
551         mod.mod_type = "member";
552         mod.mod_op = LDAP_MOD_DELETE;
553
554         /*
555          *  Now cycle through all of the names in the "members" part of the
556          *  group (but not the e-mail address part).  Lookup each one, and
557          *  if it isn't found, let the user know so s/he can delete it.
558          */
559         vp = Entry.attrs[attr_to_index("member")].values;
560         if (vp == NULL) {
561                 if (verbose)
562                         printf("  \"%s\" has no LDAP members.  There is nothing to purge.\n", group);
563                 return;
564         }
565         for (; *vp != NULL; vp++) {
566                 char ans[BUFSIZ], *ufn, *label = "Did not find:  ";
567                 int len = strlen(label);
568
569                 if (vrfy(*vp))
570                         continue;
571                 isclean = FALSE;
572                 ufn = my_ldap_dn2ufn(*vp);
573                 format2(ufn, label, (char *) NULL, 2, 2 + len, col_size);
574 ask:
575                 printf("  Purge, Keep, Replace, Abort [Keep]? ");
576                 fflush(stdout);
577                 fetch_buffer(ans, sizeof(ans), stdin);
578                 if ((ans[0] == '\0') || !strncasecmp(ans, "Keep", strlen(ans)))
579                         continue;
580                 if (!strncasecmp(ans, "Abort", strlen(ans))) {
581                         ldap_uncache_entry(ld, dn);
582                         return;
583                 }
584                 if (!strncasecmp(ans, "Purge", strlen(ans)) || !strncasecmp(ans, "Replace", strlen(ans))) {
585                         values[0] = *vp;
586 #ifdef DEBUG
587                         if (debug & D_GROUPS) {
588                                 register LDAPMod **lpp;
589                                 register char **cp;
590                                 register int i, j;
591                                 printf("  About to call ldap_modify_s()\n");
592                                 printf("  ld = 0x%x\n", ld);
593                                 printf("  dn = [%s]\n", Entry.DN);
594                                 for (lpp = mods, i = 1; *lpp != NULL; lpp++, i++) {
595                                         printf("  mods[%1d] code = %1d\n", i, (*lpp)->mod_op);
596                                         printf("  mods[%1d] type = %s\n", i, (*lpp)->mod_type);
597                                         for (cp = (*lpp)->mod_values, j = 1; *cp != NULL; cp++, j++)
598                                                 printf("  mods[%1d] v[%1d] = %s\n", i, j, *cp);
599                                                 
600                                 }
601                         }
602 #endif
603                         if (ldap_modify_s(ld, Entry.DN, mods))
604                                 mod_perror(ld);
605
606                         /* now add the replacement if requested */
607                         if (!strncasecmp(ans, "Purge", strlen(ans)))
608                                 continue;
609                         rdns = ldap_explode_dn(*vp, TRUE);
610                         if ((lm = find(*rdns, FALSE)) == NULL) {
611                                 printf("  Could not find a replacement for %s; purged only.\n", *rdns);
612                                 ldap_msgfree(lm);
613                                 ldap_value_free(rdns);
614                                 break;
615                         }
616                         values[0] = ldap_get_dn(ld, ldap_first_entry(ld, lm));
617                         mod.mod_op = LDAP_MOD_ADD;
618 #ifdef DEBUG
619                         if (debug & D_GROUPS) {
620                                 register LDAPMod **lpp;
621                                 register char **cp;
622                                 register int i, j;
623                                 printf("  About to call ldap_modify_s()\n");
624                                 printf("  ld = 0x%x\n", ld);
625                                 printf("  dn = [%s]\n", Entry.DN);
626                                 for (lpp = mods, i = 1; *lpp != NULL; lpp++, i++) {
627                                         printf("  mods[%1d] code = %1d\n", i, (*lpp)->mod_op);
628                                         printf("  mods[%1d] type = %s\n", i, (*lpp)->mod_type);
629                                         for (cp = (*lpp)->mod_values, j = 1; *cp != NULL; cp++, j++)
630                                                 printf("  mods[%1d] v[%1d] = %s\n", i, j, *cp);
631                                                 
632                                 }
633                         }
634 #endif
635                         if (ldap_modify_s(ld, Entry.DN, mods))
636                                 mod_perror(ld);
637                         ldap_msgfree(lm);
638                         ldap_value_free(rdns);
639         
640                         /* set this back to DELETE for other purges */
641                         mod.mod_op = LDAP_MOD_DELETE;
642                 }
643                 else {
644                         printf("  Did not recognize that answer.\n\n");
645                         goto ask;
646                 }
647         }
648         ldap_uncache_entry(ld, Entry.DN);
649         if (isclean)
650                 printf("  No entries were purged.\n");
651         return;
652 }
653
654 void
655 tidy_up( void )
656 {
657         register int i = 0;
658         int found_one = 0;
659         register char **vp;
660         LDAPMessage *lm;
661         static LDAPMod mod;
662         static LDAPMod *mods[2] = { &mod, NULL };
663         static char *values[MAX_VALUES];
664
665 #ifdef DEBUG
666         if (debug & D_TRACE)
667                 printf("->tidy()\n");
668 #endif
669
670         if (bind_status == UD_NOT_BOUND) {
671                 if (auth((char *) NULL, 1) < 0) {
672                         return;
673                 }
674         }
675
676         /* lookup the user, and see to which groups he has subscribed */
677         vp = ldap_explode_dn(bound_dn, TRUE);
678         if ((lm = find(*vp, TRUE)) == (LDAPMessage *) NULL) {
679                 printf("  Could not locate \"%s\"\n", *vp);
680                 ldap_value_free(vp);
681                 return;
682         }
683         ldap_value_free(vp);
684         parse_answer(lm);
685         ldap_msgfree(lm);
686         vp = Entry.attrs[attr_to_index("memberOfGroup")].values;
687         if (vp == NULL) {
688                 printf("  You have not subscribed to any groups.\n");
689                 return;
690         }
691
692         /* now, loop through these groups, deleting the bogus */
693         for ( ; *vp != NULL; vp++) {
694                 if (vrfy(*vp))
695                         continue;
696                 found_one++;
697                 printf("  \"%s\" is not a valid group name.\n", *vp);
698                 values[i++] = strdup(*vp);
699                 if ( i >= MAX_VALUES ) {
700                         printf( "  At most %d invalid groups can be removed at one time; skipping the rest.\n", MAX_VALUES );
701                         break;
702                 }
703         }
704         if (found_one == 0) {
705                 if (verbose)
706                         printf("  You are not a member of any invalid groups.  There is nothing to tidy.\n");
707                 return;
708         }
709
710         /* delete the most heinous entries */
711         values[i] = NULL;
712         mod.mod_values = values;
713         mod.mod_op = LDAP_MOD_DELETE;
714         mod.mod_type = "memberOfGroup";
715         if (ldap_modify_s(ld, bound_dn, mods))
716                 mod_perror(ld);
717         ldap_uncache_entry(ld, bound_dn);
718
719         /* tidy up before we finish tidy_up */
720         for ( ; i >= 1; i--)
721                 Free(values[i - 1]);
722         return;
723 }
724
725 /*
726  *  This routine is used to modify lists that can contain either Distinguished
727  *  Names or e-mail addresses.  This includes things like group members,
728  *  the errors-to field in groups, and so on.
729  */
730 void
731 mod_addrDN( char *group, int offset )
732 {
733         char s[BUFSIZ], *new_value /* was member */, *values[2];
734         char attrtype[ 64 ];
735         LDAPMod mod, *mods[2];
736         LDAPMessage *mp;
737
738 #ifdef DEBUG
739         if (debug & D_TRACE)
740                 printf("->mod_addrDN(%s)\n", group);
741 #endif
742         /*
743          *  At this point the user can indicate that he wishes to add values
744          *  to the attribute, delete values from the attribute, or replace the 
745          *  current list of values with a new list.  The first two cases
746          *  are very straight-forward, but the last case requires a little
747          *  extra care and work.
748          */
749         if (verbose) {
750                 printf("\n");
751                 if ( !isatty( 1 ))
752                         format("There are three options available at this point.  You may:  Add additional values; Delete values; or Replace the entire list of values with a new list entered interactively.\n", 75, 2);
753                 else
754                         format("There are four options available at this point.  You may:  Add one or more additional values; Delete one or more existing values; Replace the entire list of values with a new list entered interactively; or Bulk load a new list of values from a file, overwriting the existing list.\n", 75, 2);
755         }
756
757         /* initialize the modififier type */
758         mod.mod_type = NULL;
759
760         for (;;) {
761                 if ( !isatty( 1 ))
762                         printf("  Do you want to Add, Delete, or Replace? ");
763                 else
764                         printf("  Do you want to Add, Delete, Replace, or Bulk load? ");
765                 fflush(stdout);
766                 fetch_buffer(s, sizeof(s), stdin);
767                 if (s[0] == '\0') {
768                         return;
769                         /*NOTREACHED*/
770                 }
771                 if (!strncasecmp(s, "add", strlen(s))) {
772                         mod.mod_op = LDAP_MOD_ADD;
773                         break;
774                 }
775                 else if (!strncasecmp(s, "delete", strlen(s))) {
776                         mod.mod_op = LDAP_MOD_DELETE;
777                         break;
778                 }
779                 else if (!strncasecmp(s, "replace", strlen(s))) {
780                         mod.mod_op = LDAP_MOD_REPLACE;
781                         break;
782                 }
783                 else if(!strncasecmp(s, "bulk", strlen(s))) {
784                         bulk_load(group);
785                         return;
786                 }
787                 else if (verbose) {
788                         printf("\n");
789                         if ( !isatty( 1 ))
790                                 format("Did not recognize that response.  Please use 'A' to add, 'D' to delete, or 'R' to replace the entire list with a new list\n", 75, 2);
791                         else
792                                 format("Did not recognize that response.  Please use 'A' to add, 'D' to delete, 'R' to replace the entire list with a new list, or 'B' to bulk load a new list from a file\n", 75, 2);
793                 }
794         }
795         if (mod.mod_op == LDAP_MOD_REPLACE) {
796                 if ( verbose && !confirm_action( "The entire existing list will be overwritten with the new values you are about to enter." )) {
797                         printf("\n  Modification halted.\n");
798                         return;
799                 }
800         }
801         if (verbose) {
802                 printf("\n");
803                 format("Values may be specified as a name (which is then looked up in the LDAP Directory) or as a domain-style (i.e., user@domain) e-mail address.  Simply hit the RETURN key at the prompt when finished.\n", 75, 2);
804                 printf("\n");
805         }
806
807         for (;;) {
808                 printf("%s? ", attrlist[offset].output_string);
809                 fflush(stdout);
810                 fetch_buffer(s, sizeof(s), stdin);
811                 if (s[0] == '\0')
812                         return;
813
814                 /*
815                  *  If the string the user has just typed has an @-sign in it,
816                  *  then we assume it is an e-mail address.  In this case, we
817                  *  just store away whatever it is they have typed.
818                  *
819                  *  If the string had no @-sign, then we look in the Directory,
820                  *  make sure it exists, and if it does, we add that.
821                  *
822                  *  If the string begins with a comma, then strip off the
823                  *  comma, and pass it along to the LDAP server.  This is
824                  *  the way one can force ud to accept a name.  Handy for
825                  *  debugging purposes.
826                  */
827                 if (*s == ',') {
828                         new_value = s + 1;
829                         mod.mod_type = attrlist[offset].quipu_name;
830                 }
831                 else if (strchr(s, '@') == NULL) {
832                         if ((mp = find(s, FALSE)) == (LDAPMessage *) NULL) {
833                                 printf("  Could not find \"%s\"\n", s);
834                                 if (verbose && (mod.mod_op == LDAP_MOD_DELETE)){
835                                         printf("\n");
836                                         format("I could not find anything that matched what you typed.  You might try the \"purge\" command instead.  It is used to purge corrupted or unlocatable entries from a group.", 75, 2);
837                                         printf("\n");
838                                 }
839                                 continue;
840                         }
841                         parse_answer(mp);
842                         new_value = Entry.DN;
843                         mod.mod_type = attrlist[offset].quipu_name;
844                 }
845                 else if (mod.mod_op != LDAP_MOD_DELETE) {
846                         /*
847                          * Don't screw around with what the user has typed
848                          * if they are simply trying to delete a rfc822mailbox
849                          * value.
850                          *
851                          * spaces on the left hand side of the e-mail
852                          * address are bad news - we know that there
853                          * must be a @-sign in the string, or else we
854                          * would not be here
855                          *
856                          * note that this means a value like:
857                          *
858                          *      first m. last@host.domain
859                          *
860                          * will be turned into:
861                          *
862                          *      first.m..last@host.domain
863                          *
864                          * and the mailer will need to do the right thing
865                          * with this; alternatively we could add code that
866                          * collapsed multiple dots into a single dot
867                          *
868                          * Don't screw up things like:
869                          *
870                          *      "Bryan Beecher" <bryan@umich.edu>
871                          *       Bryan Beecher  <bryan@umich.edu>
872                          */
873                         char *cp;
874                         if (strchr(s, '<') == NULL) {
875                                 for (cp = s; *cp != '@'; cp++)
876                                         if (isspace((unsigned char)*cp))
877                                                 *cp = '.';
878                         }
879                         new_value = s;
880                         strcpy(attrtype, "rfc822");
881                         strcat(attrtype, attrlist[offset].quipu_name);
882                         mod.mod_type = attrtype;
883                 }
884                 else {
885                         new_value = s;
886                         strcpy(attrtype, "rfc822");
887                         strcat(attrtype, attrlist[offset].quipu_name);
888                         mod.mod_type = attrtype;
889                 }
890
891                 /*  fill in the rest of the ldap_mod() structure */
892                 mods[0] = &mod;
893                 mods[1] = (LDAPMod *) NULL;
894
895                 values[0] = new_value;
896                 values[1] = NULL;
897                 mod.mod_values = values;
898
899 #ifdef DEBUG
900                 if (debug & D_GROUPS) {
901                         LDAPMod **lpp;
902                         char **cp;
903                         int i, j;
904                         printf("  About to call ldap_modify_s()\n");
905                         printf("  ld = 0x%x\n", ld);
906                         printf("  dn = [%s]\n", group);
907                         for (lpp = mods, i = 1; *lpp != NULL; lpp++, i++) {
908                                 printf("  mods[%1d] code = %1d\n", 
909                                                         i, (*lpp)->mod_op);
910                                 printf("  mods[%1d] type = %s\n", 
911                                                         i, (*lpp)->mod_type);
912                                 for (cp = (*lpp)->mod_values, j = 1; *cp != NULL; cp++, j++)
913                                         printf("  mods[%1d] v[%1d] = %s\n", 
914                                                                 i, j, *cp);
915                         }
916                 }
917 #endif
918
919                 if (my_ldap_modify_s(ld, group, mods)) {
920                         int ld_errno = 0;
921                         ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ld_errno);
922                         if (ld_errno == LDAP_NO_SUCH_ATTRIBUTE) {
923                                 printf("  Could not locate value \"%s\"\n", 
924                                                                 new_value);
925                                 continue;
926                         }
927                         else {
928                                 mod_perror(ld);
929                                 return;
930                         }
931                 }
932                 ldap_uncache_entry(ld, group);
933
934                 /*
935                  *  If the operation was REPLACE, we now need to "zero out" the
936                  *  other "half" of the list (e.g., user specified an e-mail
937                  *  address; now we need to clear the DN part of the list).
938                  *
939                  *  NOTE:  WE HAVE ALREADY DONE HALF OF THE REPLACE ABOVE.
940                  *
941                  *  Also, change the opcode to LDAP_MOD_ADD and give the user an
942                  *  opportunity to add additional members to the group.  We
943                  *  only take this branch the very first time during a REPLACE
944                  *  operation.
945                  */
946                 if (mod.mod_op == LDAP_MOD_REPLACE) {
947                         if (!strncmp(mod.mod_type, "rfc822", 6))
948                                 mod.mod_type = mod.mod_type + 6;
949                         else {
950                                 strcpy(attrtype, "rfc822");
951                                 strcat(attrtype, mod.mod_type);
952                                 mod.mod_type = attrtype;
953                         }
954                         mods[0] = &mod;
955                         values[0] = NULL;
956                         mod.mod_values = values;
957                         mod.mod_op = LDAP_MOD_DELETE;
958 #ifdef DEBUG
959                         if (debug & D_GROUPS) {
960                                 LDAPMod **lpp;
961                                 char **cp;
962                                 int i, j;
963                                 printf("  About to call ldap_modify_s()\n");
964                                 printf("  ld = 0x%x\n", ld);
965                                 printf("  dn = [%s]\n", group);
966                                 for (lpp = mods, i = 1; *lpp != NULL; lpp++, i++) {
967                                         printf("  mods[%1d] code = %1d\n", 
968                                                         i, (*lpp)->mod_op);
969                                         printf("  mods[%1d] type = %s\n", 
970                                                         i, (*lpp)->mod_type);
971                                         for (cp = (*lpp)->mod_values, j = 1; *cp != NULL; cp++, j++)
972                                                 printf("  mods[%1d] v[%1d] = %s\n", i, j, *cp);
973                                 }
974                         }
975 #endif
976                         if (my_ldap_modify_s(ld, group, mods)) {
977                                 /*
978                                 *  A "No such attribute" error is no big deal.
979                                 *  We only wanted to clear the attribute anyhow.
980                                 */
981                                 int ld_errno = 0;
982                                 ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ld_errno);
983                                 if (ld_errno != LDAP_NO_SUCH_ATTRIBUTE) {
984                                         mod_perror(ld);
985                                         return;
986                                 }
987                         }
988                         ldap_uncache_entry(ld, group);
989                         if (verbose)
990                                 printf("  \"%s\" has been added\n", new_value);
991                         mod.mod_op = LDAP_MOD_ADD;
992                 }
993                 else if (verbose && (mod.mod_op == LDAP_MOD_ADD))
994                         printf("  \"%s\" has been added\n", new_value);
995                 else if (verbose && (mod.mod_op == LDAP_MOD_DELETE))
996                         printf("  \"%s\" has been removed\n", new_value);
997         }
998 }
999
1000 int
1001 my_ldap_modify_s( LDAP *ldap, char *group, LDAPMod **mods )
1002 {
1003         int     was_rfc822member, rc;
1004
1005         was_rfc822member = 0;
1006
1007         if (!strcasecmp(mods[0]->mod_type, "rfc822member")) {
1008                 mods[0]->mod_type = "mail";
1009                 was_rfc822member = 1;
1010         }
1011
1012         rc = ldap_modify_s(ldap, group, mods);
1013
1014         if (was_rfc822member)
1015             mods[0]->mod_type = "rfc822member";
1016
1017         return(rc);
1018 }
1019
1020 void
1021 list_groups( char *who )
1022 {
1023         LDAPMessage *mp;
1024         char name[BUFSIZ], filter[BUFSIZ], *search_attrs[2];
1025         char *work_area[MAX_NUM_NAMES];
1026         char *dn, **rdns;
1027         int i, rc;
1028         
1029
1030 #ifdef DEBUG
1031         if (debug & D_TRACE) {
1032                 if (who == NULL)
1033                         printf("->list_groups(NULL)\n");
1034                 else
1035                         printf("->list_groups(%s)\n", who);
1036         }
1037 #endif
1038         /*
1039          *  First, decide what entry we are going to list.  If the
1040          *  user has not included a name on the list command line,
1041          *  we will use the person who was last looked up with a find
1042          *  command.
1043          *
1044          *  Once we know who to modify, be sure that they exist, and
1045          *  parse out their DN.
1046          */
1047         if (who == NULL) {
1048                 printf("  List groups belonging to whose entry? ");
1049                 fflush(stdout);
1050                 fetch_buffer(name, sizeof(name), stdin);
1051                 if (name[0] != '\0')
1052                         who = name;
1053                 else
1054                         return;
1055         }
1056         if ((mp = find(who, TRUE)) == NULL) {
1057                 (void) ldap_msgfree(mp);
1058                 printf("  Could not locate \"%s\" in the Directory.\n", who);
1059                 return;
1060         }
1061         dn = ldap_get_dn(ld, ldap_first_entry(ld, mp));
1062         ldap_msgfree(mp);
1063         rdns = ldap_explode_dn(dn, TRUE);
1064         if (verbose)
1065                 printf("\n  Listing groups belonging to \"%s\"\n", *rdns);
1066
1067         /* lookup the groups belonging to this person */
1068         sprintf(filter, "owner=%s", dn);
1069         Free(dn);
1070         search_attrs[0] = "cn";
1071         search_attrs[1] = NULL;
1072         if ((rc = ldap_search_s(ld, UD_WHERE_ALL_GROUPS_LIVE, LDAP_SCOPE_SUBTREE, 
1073                 filter, search_attrs, FALSE, &mp)) != LDAP_SUCCESS &&
1074             rc != LDAP_SIZELIMIT_EXCEEDED && rc != LDAP_TIMELIMIT_EXCEEDED) {
1075                 ldap_perror(ld, "ldap_search_s");
1076                 ldap_value_free(rdns);
1077                 return;
1078         }
1079         if ((rc = ldap_count_entries(ld, mp)) < 0) {
1080                 ldap_perror(ld, "ldap_count_entries");
1081                 ldap_value_free(rdns);
1082                 return;
1083         }
1084         if (rc == 0) {
1085                 printf("  %s owns no groups in this portion of the Directory.\n", *rdns);
1086                 ldap_value_free(rdns);
1087                 return;
1088         }
1089         if (verbose)
1090                 printf("  %s owns %d groups.\n\n", *rdns, rc);
1091         print_list(mp, work_area, &rc);
1092         for (i = 1; work_area[i] != NULL; i++)
1093                 Free(work_area[i]);
1094         ldap_msgfree(mp);
1095         ldap_value_free(rdns);
1096         return;
1097 }
1098
1099 static char *
1100 bind_and_fetch( char *name )
1101 {
1102         LDAPMessage *lm;
1103         char tmp[MED_BUF_SIZE];
1104
1105 #ifdef DEBUG
1106         if (debug & D_TRACE) {
1107                 if (name == NULL)
1108                         printf("->bind_and_fetch(NULL)\n");
1109                 else
1110                         printf("->bind_and_fetch(%s)\n", name);
1111         }
1112 #endif
1113         if (bind_status == UD_NOT_BOUND) {
1114                 if (auth((char *) NULL, 1) < 0)
1115                         return(NULL);
1116         }
1117
1118         /*
1119          *  If the user did not supply us with a name, prompt them for
1120          *  a name.
1121          */
1122         if ((name == NULL) || (*name == '\0')) {
1123                 printf("  Group? ");
1124                 fflush(stdout);
1125                 fetch_buffer(tmp, sizeof(tmp), stdin);
1126                 if (tmp[0] == '\0')
1127                         return(NULL);
1128                 name = tmp;
1129         }
1130         /* remove quotes, dots, and underscores. */
1131         name = strip_ignore_chars(name);
1132
1133 #ifdef DEBUG
1134         if (debug & D_GROUPS)
1135                 printf("Group name = (%s)\n", name);
1136 #endif
1137
1138         /* make sure the group in question exists and is joinable */
1139         if ((lm = find(name, TRUE)) == (LDAPMessage *) NULL) {
1140                 printf("  Could not locate group \"%s\"\n", name);
1141                 return(NULL);
1142         }
1143         parse_answer(lm);
1144         ldap_msgfree(lm);
1145
1146 #ifdef DEBUG
1147         if (debug & D_GROUPS)
1148                 printf("Group DN = (%s)\n", Entry.DN);
1149 #endif
1150         return(strdup(Entry.DN));
1151 }
1152
1153 void
1154 list_memberships( char *who )
1155 {
1156         LDAPMessage *mp;
1157         char name[BUFSIZ], filter[BUFSIZ], *search_attrs[2];
1158         char *work_area[MAX_NUM_NAMES];
1159         char *dn, **rdns;
1160         int i, rc;
1161         
1162
1163 #ifdef DEBUG
1164         if (debug & D_TRACE) {
1165                 if (who == NULL)
1166                         printf("->list_memberships(NULL)\n");
1167                 else
1168                         printf("->list_memberships(%s)\n", who);
1169         }
1170 #endif
1171         /*
1172          *  First, decide what entry we are going to list.  If the
1173          *  user has not included a name on the list command line,
1174          *  we will use the person who was last looked up with a find
1175          *  command.
1176          *
1177          *  Once we know who to modify, be sure that they exist, and
1178          *  parse out their DN.
1179          */
1180         if (who == NULL) {
1181                 printf("  List memberships containing whose entry? ");
1182                 fflush(stdout);
1183                 fetch_buffer(name, sizeof(name), stdin);
1184                 if (name[0] != '\0')
1185                         who = name;
1186                 else
1187                         return;
1188         }
1189         if ((mp = find(who, TRUE)) == NULL) {
1190                 (void) ldap_msgfree(mp);
1191                 printf("  Could not locate \"%s\" in the Directory.\n", who);
1192                 ldap_msgfree(mp);
1193                 return;
1194         }
1195         dn = ldap_get_dn(ld, ldap_first_entry(ld, mp));
1196         rdns = ldap_explode_dn(dn, TRUE);
1197         if (verbose)
1198                 printf("\n  Listing memberships of \"%s\"\n", *rdns);
1199
1200         /* lookup the groups belonging to this person */
1201         sprintf(filter, "member=%s", dn);
1202         Free(dn);
1203         search_attrs[0] = "cn";
1204         search_attrs[1] = NULL;
1205         ldap_msgfree(mp);
1206         if ((rc = ldap_search_s(ld, UD_WHERE_ALL_GROUPS_LIVE, LDAP_SCOPE_SUBTREE, 
1207                 filter, search_attrs, FALSE, &mp)) != LDAP_SUCCESS &&
1208             rc != LDAP_SIZELIMIT_EXCEEDED && rc != LDAP_TIMELIMIT_EXCEEDED) {
1209                 ldap_perror(ld, "ldap_search_s");
1210                 ldap_msgfree(mp);
1211                 ldap_value_free(rdns);
1212                 return;
1213         }
1214         if ((rc = ldap_count_entries(ld, mp)) < 0) {
1215                 ldap_perror(ld, "ldap_count_entries");
1216                 ldap_msgfree(mp);
1217                 ldap_value_free(rdns);
1218                 return;
1219         }
1220         if (rc == 0) {
1221                 printf("  %s is not a member of any groups in this portion of the Directory.\n", *rdns);
1222                 ldap_msgfree(mp);
1223                 ldap_value_free(rdns);
1224                 return;
1225         }
1226         if (verbose)
1227                 printf("  %s is a member of %d groups.\n\n", *rdns, rc);
1228
1229         /*
1230          *  print_list fills in the char * array starting at 1, not 0
1231          */
1232         print_list(mp, work_area, &rc);
1233         for (i = 1; work_area[i] != NULL; i++)
1234                 Free(work_area[i]);
1235         ldap_msgfree(mp);
1236         ldap_value_free(rdns);
1237         return;
1238 }