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