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