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