]> andersk Git - moira.git/blob - clients/blanche/blanche.c
blanche should exit if it is sync-ing a list to a file and
[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             exit (2);
220         }
221         while (sq_get_data(synclist, &memberstruct)) {
222             struct save_queue *q;
223             int removed = 0;
224
225             for (q = memberlist->q_next; q != memberlist; q = q->q_next) {
226                 if (membercmp(q->q_data, memberstruct) == 0) {
227                     q->q_prev->q_next = q->q_next;
228                     q->q_next->q_prev = q->q_prev;
229                     removed++;
230                     break;
231                 }
232             }
233             if (!removed)
234               sq_save_data(addlist, memberstruct);
235         }
236         while (sq_get_data(memberlist, &memberstruct))
237           sq_save_data(dellist, memberstruct);
238         sq_destroy(memberlist);
239         memberlist = sq_create();
240     }
241
242     /* Process the add list */
243     while (sq_get_data(addlist, &memberstruct)) {
244         if ((memberstruct->type == M_STRING ||
245              memberstruct->type == M_ANY) &&
246             strchr(memberstruct->name, '\'')) {
247                 fprintf(stderr, "Illegal character \"'\" in \"STRING:%s\", aborting blanche.\n",
248                         memberstruct->name);
249                 fprintf(stderr, "No changes were made.\n");
250                 exit(2);
251         }
252         /* canonicalize string if necessary */
253         if (memberstruct->type == M_STRING &&
254             (p = strchr(memberstruct->name, '@'))) {
255             char *host = canonicalize_hostname(strsave(++p));
256             static char **mailhubs = NULL;
257             char *argv[4];
258             int i, collect();
259
260             if (!mailhubs) {
261                 argv[0] = "mailhub";
262                 argv[1] = "TYPE";
263                 argv[2] = "*";
264                 mailhubs = (char **)malloc(sizeof(char *));
265                 mailhubs[0] = NULL;
266                 status = mr_query("get_alias", 3, argv, collect,
267                                    (char *)&mailhubs);
268                 if (status != MR_SUCCESS && status != MR_NO_MATCH) {
269                     com_err(whoami, status,
270                             " while reading list of MAILHUB servers");
271                     mailhubs[0] = NULL;
272                 }
273             }
274             for (i = 0; p = mailhubs[i]; i++) {
275                 if (!strcasecmp(p, host)) {
276                     host = strsave(memberstruct->name);
277                     *(strchr(memberstruct->name, '@')) = 0;
278                     memberstruct->type = M_ANY;
279                     fprintf(stderr, "Warning: \"STRING:%s\" converted to \"%s\" because it is a local name.\n",
280                             host, memberstruct->name);
281                     break;
282                 }
283             }
284             free(host);
285         }
286         /* now continue adding member */
287         membervec[0] = listname;
288         membervec[2] = memberstruct->name;
289         if (verbose) {
290             printf("Adding member ");
291             show_list_member(memberstruct);
292         }
293         switch (memberstruct->type) {
294         case M_ANY:
295         case M_USER:
296             membervec[1] = "USER";
297             status = mr_query("add_member_to_list", 3, membervec, scream, NULL);
298             if (status == MR_SUCCESS)
299               break;
300             else if (status != MR_USER || memberstruct->type != M_ANY) {
301                 com_err(whoami, status, "while adding member %s to %s",
302                         memberstruct->name, listname);
303                 break;
304             }
305         case M_LIST:
306             membervec[1] = "LIST";
307             status = mr_query("add_member_to_list", 3, membervec,
308                                scream, NULL);
309             if (status == MR_SUCCESS)
310               break;
311             else if (status != MR_LIST || memberstruct->type != M_ANY) {
312                 com_err(whoami, status, "while adding member %s to %s",
313                         memberstruct->name, listname);
314                 break;
315             }
316         case M_STRING:
317             membervec[1] = "STRING";
318             status = mr_query("add_member_to_list", 3, membervec,
319                                scream, NULL);
320             if (status != MR_SUCCESS)
321               com_err(whoami, status, "while adding member %s to %s",
322                       memberstruct->name, listname);
323             else if (!strchr(memberstruct->name, '@') &&
324                      !strchr(memberstruct->name, '!') &&
325                      !strchr(memberstruct->name, '%')) {
326                 fprintf(stderr, "\nWARNING: \"STRING:%s\" was just added to list \"%s\".\n",
327                         memberstruct->name, listname);
328                 fprintf(stderr, "\tIf %s is a mailing list, this may cause it to stop working.\n", listname);
329                 fprintf(stderr, "\tYou should consider removing \"STRING:%s\" from the list.\n", memberstruct->name);
330             }
331             break;
332         case M_KERBEROS:
333             membervec[1] = "KERBEROS";
334             status = mr_query("add_member_to_list", 3, membervec,
335                                scream, NULL);
336             if (status != MR_SUCCESS)
337               com_err(whoami, status, "while adding member %s to %s",
338                       memberstruct->name, listname);
339         }
340     }
341
342     /* Process the delete list */
343     while (sq_get_data(dellist, &memberstruct)) {
344         membervec[0] = listname;
345         membervec[2] = memberstruct->name;
346         if (verbose) {
347             printf("Deleting member ");
348             show_list_member(memberstruct);
349         }
350         switch (memberstruct->type) {
351         case M_ANY:
352         case M_USER:
353             membervec[1] = "USER";
354             status = mr_query("delete_member_from_list", 3, membervec,
355                                scream, NULL);
356             if (status == MR_SUCCESS)
357               break;
358             else if ((status != MR_USER && status != MR_NO_MATCH) ||
359                      memberstruct->type != M_ANY) {
360                 com_err(whoami, status, "while deleting member %s from %s",
361                         memberstruct->name, listname);
362                 break;
363             }
364         case M_LIST:
365             membervec[1] = "LIST";
366             status = mr_query("delete_member_from_list", 3, membervec,
367                                scream, NULL);
368             if (status == MR_SUCCESS)
369               break;
370             else if ((status != MR_LIST && status != MR_NO_MATCH) ||
371                      memberstruct->type != M_ANY) {
372                 if (status == MR_PERM && memberstruct->type == M_ANY)  {
373                   /* M_ANY means we've fallen through from the user case
374                    * The fact that we didn't get MR_PERM there indicates
375                    * that we had permission to remove the specified member 
376                    * from the list if it is a user, but not a list.  This is 
377                    * if we are the member in question.  Since we exist as a user
378                    * we must have gotten the MR_NO_MATCH error, so we will
379                    * return that, since it will be less confusing.  However,
380                    * This will generate the wrongerror if the user was trying
381                    * to remove the list with his/her username from a list they
382                    * don't administrate, without explicitly specifying "list:".
383                    */
384                   status = MR_NO_MATCH;
385                 }
386                 com_err(whoami, status, "while deleting member %s from %s",
387                         memberstruct->name, listname);
388                 break;
389             }
390         case M_STRING:
391             membervec[1] = "STRING";
392             status = mr_query("delete_member_from_list", 3, membervec,
393                                scream, NULL);
394             if (status == MR_STRING && memberstruct->type == M_ANY)
395               com_err(whoami, 0, " Unable to find member %s to delete from %s",
396                       memberstruct->name, listname);
397             else if (status != MR_SUCCESS)
398               com_err(whoami, status, "while deleting member %s from %s",
399                       memberstruct->name, listname);
400             break;
401         case M_KERBEROS:
402             membervec[1] = "KERBEROS";
403             status = mr_query("delete_member_from_list", 3, membervec,
404                                scream, NULL);
405             if (status != MR_SUCCESS)
406               com_err(whoami, status, "while deleting member %s from %s",
407                       memberstruct->name, listname);
408         }
409     }
410
411     /* Display the members of the list now, if requested */
412     if (memberflg) {
413         if (recursflg)
414           recursive_display_list_members();
415         else {
416             status = mr_query("get_members_of_list", 1, &listname,
417                                get_list_members, (char *)memberlist);
418             if (status)
419               com_err(whoami, status, "while getting members of list %s",
420                       listname);
421             while (sq_get_data(memberlist, &memberstruct))
422               show_list_member(memberstruct);
423         }
424     }
425
426     /* We're done! */
427     mr_disconnect();
428     exit(0);
429 }
430
431 usage(argv)
432 char **argv;
433 {
434     fprintf(stderr, "Usage: %s listname [options]\n",argv[0]);
435     fprintf(stderr, "Options are\n");
436     fprintf(stderr, "   -v | -verbose\n");
437     fprintf(stderr, "   -m | -members\n");
438     fprintf(stderr, "   -u | -users\n");
439     fprintf(stderr, "   -l | -lists\n");
440     fprintf(stderr, "   -s | -strings\n");
441     fprintf(stderr, "   -k | -kerberos\n");
442     fprintf(stderr, "   -i | -info\n");
443     fprintf(stderr, "   -r | -recursive\n");
444     fprintf(stderr, "   -a | -add member\n");
445     fprintf(stderr, "   -d | -delete member\n");
446     fprintf(stderr, "   -al | -addlist filename\n");
447     fprintf(stderr, "   -dl | -deletelist filename\n");
448     fprintf(stderr, "   -f | -file filename\n");
449     fprintf(stderr, "   -n | -noauth\n");
450     fprintf(stderr, "   -S | -server host[:port]\n");
451     fprintf(stderr, "   -D | -debug\n");
452     exit(1);
453 }
454
455
456 /* Display the members stored in the queue */
457
458 show_list_member(memberstruct)
459 struct member *memberstruct;
460 {
461     char *s = "";
462
463     switch (memberstruct->type) {
464     case M_USER:
465         if (!showusers)
466           return;
467         s = "USER";
468         break;
469     case M_LIST:
470         if (!showlists)
471           return;
472         s = "LIST";
473         break;
474     case M_STRING:
475         if (!showstrings)
476           return;
477         s = "STRING";
478         break;
479     case M_KERBEROS:
480         if (!showkerberos)
481           return;
482         s = "KERBEROS";
483         break;
484     case M_ANY:
485         printf("%s\n", memberstruct->name);
486         return;
487     }
488
489     if (verbose)
490       printf("%s:%s\n", s, memberstruct->name);
491     else {
492         if (memberstruct->type == M_LIST)
493           printf("LIST:%s\n", memberstruct->name);
494         else if (memberstruct->type == M_KERBEROS)
495           printf("KERBEROS:%s\n", memberstruct->name);
496         else if (memberstruct->type == M_STRING &&
497                  !strchr(memberstruct->name, '@'))
498           printf("STRING:%s\n", memberstruct->name);
499         else
500           printf("%s\n", memberstruct->name);
501     }
502 }
503
504
505 /* Show the retrieved information about a list */
506
507 show_list_info(argc, argv, hint)
508 int argc;
509 char **argv;
510 int hint;
511 {
512     printf("List: %s\n", argv[0]);
513     printf("Description: %s\n", argv[9]);
514     printf("Flags: %s, %s, and %s\n",
515            atoi(argv[1]) ? "active" : "inactive",
516            atoi(argv[2]) ? "public" : "private",
517            atoi(argv[3]) ? "hidden" : "visible");
518     printf("%s is %sa maillist and is %sa group", argv[0],
519            atoi(argv[4]) ? "" : "not ",
520            atoi(argv[5]) ? "" : "not ");
521     if (atoi(argv[5]))
522       printf(" with GID %d\n", atoi(argv[6]));
523     else
524       printf("\n");
525     printf("Owner: %s %s\n", argv[7], argv[8]);
526     printf("Last modified by %s with %s on %s\n", argv[11], argv[12], argv[10]);
527     return(MR_CONT);
528 }
529
530
531 /* Show the retrieve list member count */
532
533 show_list_count(argc, argv, hint)
534 int argc;
535 char **argv;
536 int hint;
537 {
538     printf("Members: %s\n", argv[0]);
539 }
540
541
542 /* Recursively find all of the members of listname, and then display them */
543
544 recursive_display_list_members()
545 {
546     int status, count, savecount;
547     struct save_queue *lists, *members;
548     struct member *m, *m1, *data;
549
550     lists = sq_create();
551     members = sq_create();
552     m = (struct member *) malloc(sizeof(struct member));
553     m->type = M_LIST;
554     m->name = listname;
555     sq_save_data(lists, m);
556
557     while (sq_get_data(lists, &m)) {
558         sq_destroy(memberlist);
559         memberlist = sq_create();
560         if (debugflg)
561           fprintf(stderr, "Fetching members of %s\n", m->name);
562         status = mr_query("get_members_of_list", 1, &(m->name),
563                            get_list_members, (char *)memberlist);
564         if (status)
565           com_err(whoami, status, "while getting members of list %s", m->name);
566         while (sq_get_data(memberlist, &m1)) {
567             if (m1->type == M_LIST)
568               unique_add_member(lists, m1);
569             else
570               unique_add_member(members, m1);
571         }
572     }
573     savecount = count = sq_count_elts(members);
574     data = (struct member *) malloc(count * sizeof(struct member));
575     count = 0;
576     while (sq_get_data(members, &m))
577       memcpy(&data[count++], m, sizeof(struct member));
578     qsort(data, count, sizeof(struct member), membercmp);
579     for (count = 0; count < savecount; count++) {
580         show_list_member(&data[count]);
581     }
582 }
583
584
585 /* add a struct member to a queue if that member isn't already there. */
586
587 unique_add_member(q, m)
588 struct save_queue  *q;
589 struct member *m;
590 {
591     struct save_queue *qp;
592
593     for (qp = q->q_next; qp != q; qp = qp->q_next) {
594         if (!membercmp(qp->q_data, m))
595           return;
596     }
597     sq_save_data(q, m);
598 }
599
600
601 /* Collect the retrieved members of the list */
602
603 get_list_members(argc, argv, q)
604 int argc;
605 char **argv;
606 struct save_queue *q;
607 {
608     struct member *m;
609
610     m = (struct member *) malloc(sizeof(struct member));
611     switch (argv[0][0]) {
612     case 'U':
613         m->type = M_USER;
614         break;
615     case 'L':
616         m->type = M_LIST;
617         break;
618     case 'S':
619         m->type = M_STRING;
620         break;
621     case 'K':
622         m->type = M_KERBEROS;
623         break;
624     }
625     m->name = strsave(argv[1]);
626     sq_save_data(q, m);
627     return(MR_CONT);
628 }
629
630
631 /* Called only if a query returns a value that we weren't expecting */
632
633 scream()
634 {
635     fprintf(stderr, "Programmer botch\n");
636     exit(3);
637 }
638
639
640 /* Open file, parse members from file, and put them on the specified queue */
641 get_members_from_file(filename, queue)
642 char *filename;
643 struct save_queue *queue;
644 {
645     FILE *in;
646     char buf[BUFSIZ];
647     struct member *memberstruct;
648
649     if (!strcmp(filename, "-"))
650       in = stdin;
651     else {
652         in = fopen(filename, "r");
653         if (!in) {
654             com_err(whoami, errno, "while opening %s for input", filename);
655             exit(2);
656         }
657     }
658
659     while (fgets(buf, BUFSIZ, in))
660       if (memberstruct = parse_member(buf))
661         sq_save_data(queue, memberstruct);
662     if (!feof(in))
663       com_err(whoami, errno, "while reading from %s", filename);
664 }
665
666
667 /* Collect the possible expansions of the alias MAILHUB */
668
669 int collect(argc, argv, list)
670 int argc;
671 char **argv;
672 char ***list;
673 {
674     int i;
675
676     for (i = 0; (*list)[i]; i++);
677     *list = (char **)realloc(*list, (i + 2) * sizeof(char *));
678     (*list)[i] = strsave(argv[2]);
679     (*list)[i+1] = NULL;
680     return(MR_CONT);
681 }
682
683
684 /* Parse a line of input, fetching a member.  NULL is returned if a member
685  * is not found.  ';' is a comment character.
686  */
687
688 struct member *parse_member(s)
689 register char *s;
690 {
691     register struct member *m;
692     char *p, *lastchar;
693
694     while (*s && isspace(*s))
695       s++;
696     lastchar = p = s;
697     while (*p && *p != '\n' && *p != ';')
698       if (isprint(*p) && !isspace(*p))
699         lastchar = p++;
700       else
701         p++;
702     lastchar++;
703     *lastchar = 0;
704     if (p == s || strlen(s) == 0)
705       return(NULL);
706
707     if ((m = (struct member *) malloc(sizeof(struct member))) == NULL)
708       return(NULL);
709
710     if (p = strchr(s, ':')) {
711         *p = 0;
712         m->name = ++p;
713         if (!strcasecmp("user", s))
714           m->type = M_USER;
715         else if (!strcasecmp("list", s))
716           m->type = M_LIST;
717         else if (!strcasecmp("string", s))
718           m->type = M_STRING;
719         else if (!strcasecmp("kerberos", s))
720           m->type = M_KERBEROS;
721         else {
722             m->type = M_STRING;
723             *(--p) = ':';
724             m->name = s;
725         }
726         m->name = strsave(m->name);
727     } else {
728         m->name = strsave(s);
729         if (strchr(s, '@') || strchr(s, '!') || strchr(s, '%') || strchr(s, ' '))
730           m->type = M_STRING;
731         else
732           m->type = M_ANY;
733     }
734     return(m);
735 }
736
737
738   /* 
739    * This routine two compares members by the following rules:
740    * 1.  A USER is less than a LIST
741    * 2.  A LIST is less than a STRING
742    * 3.  If two members are of the same type, the one alphabetically first
743    *     is less than the other
744    * It returs < 0 if the first member is less, 0 if they are identical, and
745    * > 0 if the second member is less (the first member is greater).
746    */
747
748 int membercmp(m1, m2)
749   struct member *m1, *m2;
750 {
751     if (m1->type == M_ANY || m2->type  == M_ANY || (m1->type == m2->type))
752         return(strcmp(m1->name, m2->name));
753     else
754         return(m1->type - m2->type);
755 }
756
757
758 sq_count_elts(q)
759 struct save_queue *q;
760 {
761     char  *foo;
762     int count;
763
764     count = 0;
765     while (sq_get_data(q, &foo))
766       count++;
767     return(count);
768 }
This page took 0.09648 seconds and 5 git commands to generate.