]> andersk Git - moira.git/blob - clients/blanche/blanche.c
02107188a5039a5f6e7dad4f7f1fa0e5e535a3d6
[moira.git] / clients / blanche / blanche.c
1 /* $Header$
2  *
3  * Command line oriented Moira List tool.
4  *
5  * by Mark Rosenstein, September 1988.
6  *
7  * Copyright 1989 by the Massachusetts Institute of Technology.
8  *
9  * (c) Copyright 1988 by the Massachusetts Institute of Technology.
10  * For copying and distribution information, please see the file
11  * <mit-copyright.h>.
12  */
13
14 /* ### Aren't there a lot of sq abstraction barrier violations here?
15    Do we need to improve the support for queue operations? */
16
17 #include <mit-copyright.h>
18 #include <errno.h>
19 #include <stdio.h>
20 #include <ctype.h>
21 #include <string.h>
22 #include <moira.h>
23 #include <moira_site.h>
24
25 #ifndef LINT
26 static char blanche_rcsid[] = "$Header$";
27 #endif
28
29
30 struct member {
31     int type;
32     char *name;
33 };
34
35 /* It is important to membercmp that M_USER < M_LIST < M_STRING */
36 #define M_ANY           0
37 #define M_USER          1
38 #define M_LIST          2
39 #define M_STRING        3
40 #define M_KERBEROS      4
41
42 /* argument parsing macro */
43 #define argis(a,b) ((strcmp(*arg+1, a) == 0) || (strcmp(*arg+1, b) == 0))
44
45 /* flags from command line */
46 int infoflg, verbose, syncflg, memberflg, recursflg, debugflg, noauth;
47 int showusers, showstrings, showkerberos, showlists;
48
49 /* various member lists */
50 struct save_queue *addlist, *dellist, *memberlist, *synclist;
51
52 char *listname, *whoami;
53
54 #ifndef POSIX
55 extern int errno;
56 #endif
57
58 int show_list_info(), show_list_count(), get_list_members(), scream();
59 int show_list_members(), membercmp();
60 struct member *parse_member();
61
62
63
64 main(argc, argv)
65 int argc;
66 char **argv;
67 {
68     int status;
69     char **arg = argv;
70     char *membervec[3], *motd;
71     struct member *memberstruct;
72     char *server = NULL, *p;
73
74     /* clear all flags & lists */
75     infoflg = verbose = syncflg = memberflg = debugflg = recursflg = 0;
76     noauth = showusers = showstrings = showkerberos = showlists = 0;
77     listname = NULL;
78     addlist = sq_create();
79     dellist = sq_create();
80     memberlist = sq_create();
81     synclist = sq_create();
82     whoami = argv[0];
83
84     /* parse args, building addlist, dellist, & synclist */
85     while (++arg - argv < argc) {
86         if  (**arg == '-')
87         {
88             if (argis("m", "members"))
89                 memberflg++;
90             else if (argis("u", "users"))
91                 showusers++;
92             else if (argis("s", "strings"))
93                 showstrings++;
94             else if (argis("l", "lists"))
95                 showlists++;
96             else if (argis("k", "kerberos"))
97                 showkerberos++;
98             else if (argis("D", "debug"))
99                 debugflg++;
100             else if (argis("i","info"))
101               infoflg++;
102             else if (argis("n","noauth"))
103               noauth++;
104             else if (argis("v","verbose"))
105               verbose++;
106             else if (argis("r","recursive"))
107               recursflg++;
108             else if (argis("S","server"))
109                 if (arg - argv < argc - 1) {
110                     ++arg;
111                     server = *arg;
112                 } else
113                     usage(argv);
114             else if (argis("a","add"))
115                 if (arg - argv < argc - 1) {
116                     ++arg;
117                     if (memberstruct = parse_member(*arg))
118                         sq_save_data(addlist, memberstruct);
119                 } else
120                     usage(argv);
121             else if (argis("al","addlist"))
122                 if (arg - argv < argc - 1) {
123                     ++arg;
124                     get_members_from_file(*arg, addlist);
125                 } else
126                   usage(argv);
127             else if (argis("d","delete"))
128                 if (arg - argv < argc - 1) {
129                     ++arg;
130                     if (memberstruct = parse_member(*arg))
131                         sq_save_data(dellist, memberstruct);
132                 } else
133                     usage(argv);
134             else if (argis("dl","deletelist"))
135                 if (arg - argv < argc - 1) {
136                     ++arg;
137                     get_members_from_file(*arg, dellist);
138                 } else
139                   usage(argv);
140             else if (argis("f","file"))
141                 if (arg - argv < argc - 1) {
142                     syncflg++;
143                     ++arg;
144                     get_members_from_file(*arg, synclist);
145                 } else
146                   usage(argv);
147             else
148                 usage(argv);
149         }
150         else if (listname == NULL)
151           listname = *arg;
152         else
153           usage(argv);
154     }
155     if (listname == NULL)
156       usage(argv);
157
158     /* if no other options specified, turn on list members flag */
159     if (!(infoflg || syncflg ||
160           addlist->q_next != addlist || dellist->q_next != dellist))
161       memberflg++;
162
163     /* If none of {users,strings,lists,kerberos} specified, turn them all on */
164     if (!(showusers || showstrings || showlists || showkerberos))
165       showusers = showstrings = showlists = showkerberos = 1;
166
167     /* fire up Moira */
168     if (status = mr_connect(server)) {
169         com_err(whoami, status, "unable to connect to the Moira server");
170         exit(2);
171     }
172     if ( status = mr_motd(&motd) ) {
173         com_err(whoami, status, "unable to check server status");
174         exit(2);
175     }
176     if (motd) {
177         fprintf(stderr, "The Moira server is currently unavailable:\n%s\n", motd);
178         mr_disconnect();
179         exit(2);
180     }
181
182     if (!noauth && (status = mr_auth("blanche"))) {
183         if (status == MR_USER_AUTH)
184           com_err(whoami, status, "");
185         else {
186             com_err(whoami, status, "unable to authenticate to Moira");
187             com_err(whoami, 0,
188                     " Try the -noauth flag if you don't need authentication");
189             exit(2);
190         }
191     }
192
193     /* display list info if requested to */
194     if (infoflg) {
195         status = mr_query("get_list_info", 1, &listname, show_list_info,NULL);
196         if (status)
197           com_err(whoami, status, "while getting list information");
198         if (verbose && !memberflg) {
199             status = mr_query("count_members_of_list", 1, &listname,
200                                show_list_count, NULL);
201             if (status)
202               com_err(whoami, status, "while getting list count");
203         }
204     }
205
206     /* if we're synchronizing to a file, we need to:
207      *  get the current members of the list
208      *  for each member of the sync file
209      *     if they are on the list, remove them from the in-memory copy
210      *     if they're not on the list, add them to add-list
211      *  if anyone is left on the in-memory copy, put them on the delete-list
212      * lastly, reset memberlist so we can use it again later
213      */
214     if (syncflg) {
215         status = mr_query("get_members_of_list", 1, &listname,
216                            get_list_members, (char *)memberlist);
217         if (status)
218           com_err(whoami, status, "getting members of list %s", listname);
219         while (sq_get_data(synclist, &memberstruct)) {
220             struct save_queue *q;
221             int removed = 0;
222
223             for (q = memberlist->q_next; q != memberlist; q = q->q_next) {
224                 if (membercmp(q->q_data, memberstruct) == 0) {
225                     q->q_prev->q_next = q->q_next;
226                     q->q_next->q_prev = q->q_prev;
227                     removed++;
228                     break;
229                 }
230             }
231             if (!removed)
232               sq_save_data(addlist, memberstruct);
233         }
234         while (sq_get_data(memberlist, &memberstruct))
235           sq_save_data(dellist, memberstruct);
236         sq_destroy(memberlist);
237         memberlist = sq_create();
238     }
239
240     /* Process the add list */
241     while (sq_get_data(addlist, &memberstruct)) {
242         if ((memberstruct->type == M_STRING ||
243              memberstruct->type == M_ANY) &&
244             strchr(memberstruct->name, '\'')) {
245                 fprintf(stderr, "Illegal character \"'\" in \"STRING:%s\", aborting blanche.\n",
246                         memberstruct->name);
247                 fprintf(stderr, "No changes were made.\n");
248                 exit(1);
249         }
250         /* canonicalize string if necessary */
251         if (memberstruct->type == M_STRING &&
252             (p = strchr(memberstruct->name, '@'))) {
253             char *host = canonicalize_hostname(strsave(++p));
254             static char **mailhubs = NULL;
255             char *argv[4];
256             int i, collect();
257
258             if (!mailhubs) {
259                 argv[0] = "mailhub";
260                 argv[1] = "TYPE";
261                 argv[2] = "*";
262                 mailhubs = (char **)malloc(sizeof(char *));
263                 mailhubs[0] = NULL;
264                 status = mr_query("get_alias", 3, argv, collect,
265                                    (char *)&mailhubs);
266                 if (status != MR_SUCCESS && status != MR_NO_MATCH) {
267                     com_err(whoami, status,
268                             " while reading list of MAILHUB servers");
269                     mailhubs[0] = NULL;
270                 }
271             }
272             for (i = 0; p = mailhubs[i]; i++) {
273                 if (!strcasecmp(p, host)) {
274                     host = strsave(memberstruct->name);
275                     *(strchr(memberstruct->name, '@')) = 0;
276                     memberstruct->type = M_ANY;
277                     fprintf(stderr, "Warning: \"STRING:%s\" converted to \"%s\" because it is a local name.\n",
278                             host, memberstruct->name);
279                     break;
280                 }
281             }
282             free(host);
283         }
284         /* now continue adding member */
285         membervec[0] = listname;
286         membervec[2] = memberstruct->name;
287         if (verbose) {
288             printf("Adding member ");
289             show_list_member(memberstruct);
290         }
291         switch (memberstruct->type) {
292         case M_ANY:
293         case M_USER:
294             membervec[1] = "USER";
295             status = mr_query("add_member_to_list", 3, membervec, scream, NULL);
296             if (status == MR_SUCCESS)
297               break;
298             else if (status != MR_USER || memberstruct->type != M_ANY) {
299                 com_err(whoami, status, "while adding member %s to %s",
300                         memberstruct->name, listname);
301                 break;
302             }
303         case M_LIST:
304             membervec[1] = "LIST";
305             status = mr_query("add_member_to_list", 3, membervec,
306                                scream, NULL);
307             if (status == MR_SUCCESS)
308               break;
309             else if (status != MR_LIST || memberstruct->type != M_ANY) {
310                 com_err(whoami, status, "while adding member %s to %s",
311                         memberstruct->name, listname);
312                 break;
313             }
314         case M_STRING:
315             membervec[1] = "STRING";
316             status = mr_query("add_member_to_list", 3, membervec,
317                                scream, NULL);
318             if (status != MR_SUCCESS)
319               com_err(whoami, status, "while adding member %s to %s",
320                       memberstruct->name, listname);
321             else if (!strchr(memberstruct->name, '@') &&
322                      !strchr(memberstruct->name, '!') &&
323                      !strchr(memberstruct->name, '%')) {
324                 fprintf(stderr, "\nWARNING: \"STRING:%s\" was just added to list \"%s\".\n",
325                         memberstruct->name, listname);
326                 fprintf(stderr, "\tIf %s is a mailing list, this may cause it to stop working.\n", listname);
327                 fprintf(stderr, "\tYou should consider removing \"STRING:%s\" from the list.\n", memberstruct->name);
328             }
329             break;
330         case M_KERBEROS:
331             membervec[1] = "KERBEROS";
332             status = mr_query("add_member_to_list", 3, membervec,
333                                scream, NULL);
334             if (status != MR_SUCCESS)
335               com_err(whoami, status, "while adding member %s to %s",
336                       memberstruct->name, listname);
337         }
338     }
339
340     /* Process the delete list */
341     while (sq_get_data(dellist, &memberstruct)) {
342         membervec[0] = listname;
343         membervec[2] = memberstruct->name;
344         if (verbose) {
345             printf("Deleting member ");
346             show_list_member(memberstruct);
347         }
348         switch (memberstruct->type) {
349         case M_ANY:
350         case M_USER:
351             membervec[1] = "USER";
352             status = mr_query("delete_member_from_list", 3, membervec,
353                                scream, NULL);
354             if (status == MR_SUCCESS)
355               break;
356             else if ((status != MR_USER && status != MR_NO_MATCH) ||
357                      memberstruct->type != M_ANY) {
358                 com_err(whoami, status, "while deleting member %s from %s",
359                         memberstruct->name, listname);
360                 break;
361             }
362         case M_LIST:
363             membervec[1] = "LIST";
364             status = mr_query("delete_member_from_list", 3, membervec,
365                                scream, NULL);
366             if (status == MR_SUCCESS)
367               break;
368             else if ((status != MR_LIST && status != MR_NO_MATCH) ||
369                      memberstruct->type != M_ANY) {
370                 if (status == MR_PERM && memberstruct->type == M_ANY)  {
371                   /* M_ANY means we've fallen through from the user case
372                    * The fact that we didn't get MR_PERM there indicates
373                    * that we had permission to remove the specified member 
374                    * from the list if it is a user, but not a list.  This is 
375                    * if we are the member in question.  Since we exist as a user
376                    * we must have gotten the MR_NO_MATCH error, so we will
377                    * return that, since it will be less confusing.  However,
378                    * This will generate the wrongerror if the user was trying
379                    * to remove the list with his/her username from a list they
380                    * don't administrate, without explicitly specifying "list:".
381                    */
382                   status = MR_NO_MATCH;
383                 }
384                 com_err(whoami, status, "while deleting member %s from %s",
385                         memberstruct->name, listname);
386                 break;
387             }
388         case M_STRING:
389             membervec[1] = "STRING";
390             status = mr_query("delete_member_from_list", 3, membervec,
391                                scream, NULL);
392             if (status == MR_STRING && memberstruct->type == M_ANY)
393               com_err(whoami, 0, " Unable to find member %s to delete from %s",
394                       memberstruct->name, listname);
395             else if (status != MR_SUCCESS)
396               com_err(whoami, status, "while deleting member %s from %s",
397                       memberstruct->name, listname);
398             break;
399         case M_KERBEROS:
400             membervec[1] = "KERBEROS";
401             status = mr_query("delete_member_from_list", 3, membervec,
402                                scream, NULL);
403             if (status != MR_SUCCESS)
404               com_err(whoami, status, "while deleting member %s from %s",
405                       memberstruct->name, listname);
406         }
407     }
408
409     /* Display the members of the list now, if requested */
410     if (memberflg) {
411         if (recursflg)
412           recursive_display_list_members();
413         else {
414             status = mr_query("get_members_of_list", 1, &listname,
415                                get_list_members, (char *)memberlist);
416             if (status)
417               com_err(whoami, status, "while getting members of list %s",
418                       listname);
419             while (sq_get_data(memberlist, &memberstruct))
420               show_list_member(memberstruct);
421         }
422     }
423
424     /* We're done! */
425     mr_disconnect();
426     exit(0);
427 }
428
429 usage(argv)
430 char **argv;
431 {
432     fprintf(stderr, "Usage: %s listname [options]\n",argv[0]);
433     fprintf(stderr, "Options are\n");
434     fprintf(stderr, "   -v | -verbose\n");
435     fprintf(stderr, "   -m | -members\n");
436     fprintf(stderr, "   -u | -users\n");
437     fprintf(stderr, "   -l | -lists\n");
438     fprintf(stderr, "   -s | -strings\n");
439     fprintf(stderr, "   -k | -kerberos\n");
440     fprintf(stderr, "   -i | -info\n");
441     fprintf(stderr, "   -r | -recursive\n");
442     fprintf(stderr, "   -a | -add member\n");
443     fprintf(stderr, "   -d | -delete member\n");
444     fprintf(stderr, "   -al | -addlist filename\n");
445     fprintf(stderr, "   -dl | -deletelist filename\n");
446     fprintf(stderr, "   -f | -file filename\n");
447     fprintf(stderr, "   -n | -noauth\n");
448     fprintf(stderr, "   -S | -server host[:port]\n");
449     fprintf(stderr, "   -D | -debug\n");
450     exit(1);
451 }
452
453
454 /* Display the members stored in the queue */
455
456 show_list_member(memberstruct)
457 struct member *memberstruct;
458 {
459     char *s = "";
460
461     switch (memberstruct->type) {
462     case M_USER:
463         if (!showusers)
464           return;
465         s = "USER";
466         break;
467     case M_LIST:
468         if (!showlists)
469           return;
470         s = "LIST";
471         break;
472     case M_STRING:
473         if (!showstrings)
474           return;
475         s = "STRING";
476         break;
477     case M_KERBEROS:
478         if (!showkerberos)
479           return;
480         s = "KERBEROS";
481         break;
482     case M_ANY:
483         printf("%s\n", memberstruct->name);
484         return;
485     }
486
487     if (verbose)
488       printf("%s:%s\n", s, memberstruct->name);
489     else {
490         if (memberstruct->type == M_LIST)
491           printf("LIST:%s\n", memberstruct->name);
492         else if (memberstruct->type == M_KERBEROS)
493           printf("KERBEROS:%s\n", memberstruct->name);
494         else if (memberstruct->type == M_STRING &&
495                  !strchr(memberstruct->name, '@'))
496           printf("STRING:%s\n", memberstruct->name);
497         else
498           printf("%s\n", memberstruct->name);
499     }
500 }
501
502
503 /* Show the retrieved information about a list */
504
505 show_list_info(argc, argv, hint)
506 int argc;
507 char **argv;
508 int hint;
509 {
510     printf("List: %s\n", argv[0]);
511     printf("Description: %s\n", argv[9]);
512     printf("Flags: %s, %s, and %s\n",
513            atoi(argv[1]) ? "active" : "inactive",
514            atoi(argv[2]) ? "public" : "private",
515            atoi(argv[3]) ? "hidden" : "visible");
516     printf("%s is %sa maillist and is %sa group", argv[0],
517            atoi(argv[4]) ? "" : "not ",
518            atoi(argv[5]) ? "" : "not ");
519     if (atoi(argv[5]))
520       printf(" with GID %d\n", atoi(argv[6]));
521     else
522       printf("\n");
523     printf("Owner: %s %s\n", argv[7], argv[8]);
524     printf("Last modified by %s with %s on %s\n", argv[11], argv[12], argv[10]);
525     return(MR_CONT);
526 }
527
528
529 /* Show the retrieve list member count */
530
531 show_list_count(argc, argv, hint)
532 int argc;
533 char **argv;
534 int hint;
535 {
536     printf("Members: %s\n", argv[0]);
537 }
538
539
540 /* Recursively find all of the members of listname, and then display them */
541
542 recursive_display_list_members()
543 {
544     int status, count, savecount;
545     struct save_queue *lists, *members;
546     struct member *m, *m1, *data;
547
548     lists = sq_create();
549     members = sq_create();
550     m = (struct member *) malloc(sizeof(struct member));
551     m->type = M_LIST;
552     m->name = listname;
553     sq_save_data(lists, m);
554
555     while (sq_get_data(lists, &m)) {
556         sq_destroy(memberlist);
557         memberlist = sq_create();
558         if (debugflg)
559           fprintf(stderr, "Fetching members of %s\n", m->name);
560         status = mr_query("get_members_of_list", 1, &(m->name),
561                            get_list_members, (char *)memberlist);
562         if (status)
563           com_err(whoami, status, "while getting members of list %s", m->name);
564         while (sq_get_data(memberlist, &m1)) {
565             if (m1->type == M_LIST)
566               unique_add_member(lists, m1);
567             else
568               unique_add_member(members, m1);
569         }
570     }
571     savecount = count = sq_count_elts(members);
572     data = (struct member *) malloc(count * sizeof(struct member));
573     count = 0;
574     while (sq_get_data(members, &m))
575       memcpy(&data[count++], m, sizeof(struct member));
576     qsort(data, count, sizeof(struct member), membercmp);
577     for (count = 0; count < savecount; count++) {
578         show_list_member(&data[count]);
579     }
580 }
581
582
583 /* add a struct member to a queue if that member isn't already there. */
584
585 unique_add_member(q, m)
586 struct save_queue  *q;
587 struct member *m;
588 {
589     struct save_queue *qp;
590
591     for (qp = q->q_next; qp != q; qp = qp->q_next) {
592         if (!membercmp(qp->q_data, m))
593           return;
594     }
595     sq_save_data(q, m);
596 }
597
598
599 /* Collect the retrieved members of the list */
600
601 get_list_members(argc, argv, q)
602 int argc;
603 char **argv;
604 struct save_queue *q;
605 {
606     struct member *m;
607
608     m = (struct member *) malloc(sizeof(struct member));
609     switch (argv[0][0]) {
610     case 'U':
611         m->type = M_USER;
612         break;
613     case 'L':
614         m->type = M_LIST;
615         break;
616     case 'S':
617         m->type = M_STRING;
618         break;
619     case 'K':
620         m->type = M_KERBEROS;
621         break;
622     }
623     m->name = strsave(argv[1]);
624     sq_save_data(q, m);
625     return(MR_CONT);
626 }
627
628
629 /* Called only if a query returns a value that we weren't expecting */
630
631 scream()
632 {
633     fprintf(stderr, "Programmer botch\n");
634     exit(3);
635 }
636
637
638 /* Open file, parse members from file, and put them on the specified queue */
639 get_members_from_file(filename, queue)
640 char *filename;
641 struct save_queue *queue;
642 {
643     FILE *in;
644     char buf[BUFSIZ];
645     struct member *memberstruct;
646
647     if (!strcmp(filename, "-"))
648       in = stdin;
649     else {
650         in = fopen(filename, "r");
651         if (!in) {
652             com_err(whoami, errno, "while opening %s for input", filename);
653             exit(2);
654         }
655     }
656
657     while (fgets(buf, BUFSIZ, in))
658       if (memberstruct = parse_member(buf))
659         sq_save_data(queue, memberstruct);
660     if (!feof(in))
661       com_err(whoami, errno, "while reading from %s", filename);
662 }
663
664
665 /* Collect the possible expansions of the alias MAILHUB */
666
667 int collect(argc, argv, list)
668 int argc;
669 char **argv;
670 char ***list;
671 {
672     int i;
673
674     for (i = 0; (*list)[i]; i++);
675     *list = (char **)realloc(*list, (i + 2) * sizeof(char *));
676     (*list)[i] = strsave(argv[2]);
677     (*list)[i+1] = NULL;
678     return(MR_CONT);
679 }
680
681
682 /* Parse a line of input, fetching a member.  NULL is returned if a member
683  * is not found.  ';' is a comment character.
684  */
685
686 struct member *parse_member(s)
687 register char *s;
688 {
689     register struct member *m;
690     char *p, *lastchar;
691
692     while (*s && isspace(*s))
693       s++;
694     lastchar = p = s;
695     while (*p && *p != '\n' && *p != ';')
696       if (isprint(*p) && !isspace(*p))
697         lastchar = p++;
698       else
699         p++;
700     lastchar++;
701     *lastchar = 0;
702     if (p == s || strlen(s) == 0)
703       return(NULL);
704
705     if ((m = (struct member *) malloc(sizeof(struct member))) == NULL)
706       return(NULL);
707
708     if (p = strchr(s, ':')) {
709         *p = 0;
710         m->name = ++p;
711         if (!strcasecmp("user", s))
712           m->type = M_USER;
713         else if (!strcasecmp("list", s))
714           m->type = M_LIST;
715         else if (!strcasecmp("string", s))
716           m->type = M_STRING;
717         else if (!strcasecmp("kerberos", s))
718           m->type = M_KERBEROS;
719         else {
720             m->type = M_STRING;
721             *(--p) = ':';
722             m->name = s;
723         }
724         m->name = strsave(m->name);
725     } else {
726         m->name = strsave(s);
727         if (strchr(s, '@') || strchr(s, '!') || strchr(s, '%') || strchr(s, ' '))
728           m->type = M_STRING;
729         else
730           m->type = M_ANY;
731     }
732     return(m);
733 }
734
735
736   /* 
737    * This routine two compares members by the following rules:
738    * 1.  A USER is less than a LIST
739    * 2.  A LIST is less than a STRING
740    * 3.  If two members are of the same type, the one alphabetically first
741    *     is less than the other
742    * It returs < 0 if the first member is less, 0 if they are identical, and
743    * > 0 if the second member is less (the first member is greater).
744    */
745
746 int membercmp(m1, m2)
747   struct member *m1, *m2;
748 {
749     if (m1->type == M_ANY || m2->type  == M_ANY || (m1->type == m2->type))
750         return(strcmp(m1->name, m2->name));
751     else
752         return(m1->type - m2->type);
753 }
754
755
756 sq_count_elts(q)
757 struct save_queue *q;
758 {
759     char  *foo;
760     int count;
761
762     count = 0;
763     while (sq_get_data(q, &foo))
764       count++;
765     return(count);
766 }
This page took 0.10134 seconds and 3 git commands to generate.