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