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