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