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