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