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