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