3 * Command line oriented Moira List tool.
5 * by Mark Rosenstein, September 1988.
7 * Copyright 1989 by the Massachusetts Institute of Technology.
9 * (c) Copyright 1988 by the Massachusetts Institute of Technology.
10 * For copying and distribution information, please see the file
14 /* ### Aren't there a lot of sq abstraction barrier violations here?
15 Do we need to improve the support for queue operations? */
17 #include <mit-copyright.h>
24 #include <moira_site.h>
27 static char blanche_rcsid[] = "$Header$";
36 /* It is important to membercmp that M_USER < M_LIST < M_STRING */
43 /* argument parsing macro */
44 #define argis(a,b) ((strcmp(*arg+1, a) == 0) || (strcmp(*arg+1, b) == 0))
46 /* flags from command line */
47 int infoflg, verbose, syncflg, memberflg, recursflg, debugflg, noauth;
48 int showusers, showstrings, showkerberos, showlists;
50 /* various member lists */
51 struct save_queue *addlist, *dellist, *memberlist, *synclist;
53 char *listname, *whoami;
59 int show_list_info(), show_list_count(), get_list_members(), scream();
60 int show_list_members(), membercmp();
61 struct member *parse_member();
71 char *membervec[3], *motd;
72 struct member *memberstruct;
73 char *server = NULL, *p;
75 /* clear all flags & lists */
76 infoflg = verbose = syncflg = memberflg = debugflg = recursflg = 0;
77 noauth = showusers = showstrings = showkerberos = showlists = 0;
79 addlist = sq_create();
80 dellist = sq_create();
81 memberlist = sq_create();
82 synclist = sq_create();
87 /* parse args, building addlist, dellist, & synclist */
88 while (++arg - argv < argc) {
91 if (argis("m", "members"))
93 else if (argis("u", "users"))
95 else if (argis("s", "strings"))
97 else if (argis("l", "lists"))
99 else if (argis("k", "kerberos"))
101 else if (argis("D", "debug"))
103 else if (argis("i","info"))
105 else if (argis("n","noauth"))
107 else if (argis("v","verbose"))
109 else if (argis("r","recursive"))
111 else if (argis("S","server") || argis("db","database"))
112 if (arg - argv < argc - 1) {
117 else if (argis("a","add"))
118 if (arg - argv < argc - 1) {
120 if (memberstruct = parse_member(*arg))
121 sq_save_data(addlist, memberstruct);
124 else if (argis("al","addlist"))
125 if (arg - argv < argc - 1) {
127 get_members_from_file(*arg, addlist);
130 else if (argis("d","delete"))
131 if (arg - argv < argc - 1) {
133 if (memberstruct = parse_member(*arg))
134 sq_save_data(dellist, memberstruct);
137 else if (argis("dl","deletelist"))
138 if (arg - argv < argc - 1) {
140 get_members_from_file(*arg, dellist);
143 else if (argis("f","file"))
144 if (arg - argv < argc - 1) {
147 get_members_from_file(*arg, synclist);
153 else if (listname == NULL)
158 if (listname == NULL)
161 /* if no other options specified, turn on list members flag */
162 if (!(infoflg || syncflg ||
163 addlist->q_next != addlist || dellist->q_next != dellist))
166 /* If none of {users,strings,lists,kerberos} specified, turn them all on */
167 if (!(showusers || showstrings || showlists || showkerberos))
168 showusers = showstrings = showlists = showkerberos = 1;
171 if (status = mr_connect(server)) {
172 com_err(whoami, status, "unable to connect to the Moira server");
175 if ( status = mr_motd(&motd) ) {
176 com_err(whoami, status, "unable to check server status");
180 fprintf(stderr, "The Moira server is currently unavailable:\n%s\n", motd);
185 if (!noauth && (status = mr_auth("blanche"))) {
186 if (status == MR_USER_AUTH)
187 com_err(whoami, status, "");
189 com_err(whoami, status, "unable to authenticate to Moira");
191 " Try the -noauth flag if you don't need authentication");
196 /* display list info if requested to */
198 status = mr_query("get_list_info", 1, &listname, show_list_info,NULL);
200 com_err(whoami, status, "while getting list information");
201 if (verbose && !memberflg) {
202 status = mr_query("count_members_of_list", 1, &listname,
203 show_list_count, NULL);
205 com_err(whoami, status, "while getting list count");
209 /* if we're synchronizing to a file, we need to:
210 * get the current members of the list
211 * for each member of the sync file
212 * if they are on the list, remove them from the in-memory copy
213 * if they're not on the list, add them to add-list
214 * if anyone is left on the in-memory copy, put them on the delete-list
215 * lastly, reset memberlist so we can use it again later
218 status = mr_query("get_members_of_list", 1, &listname,
219 get_list_members, (char *)memberlist);
221 com_err(whoami, status, "getting members of list %s", listname);
224 while (sq_get_data(synclist, &memberstruct)) {
225 struct save_queue *q;
228 for (q = memberlist->q_next; q != memberlist; q = q->q_next) {
229 if (membercmp(q->q_data, memberstruct) == 0) {
230 q->q_prev->q_next = q->q_next;
231 q->q_next->q_prev = q->q_prev;
237 sq_save_data(addlist, memberstruct);
239 while (sq_get_data(memberlist, &memberstruct))
240 sq_save_data(dellist, memberstruct);
241 sq_destroy(memberlist);
242 memberlist = sq_create();
245 /* Process the add list */
246 while (sq_get_data(addlist, &memberstruct)) {
247 /* canonicalize string if necessary */
248 if (memberstruct->type == M_STRING &&
249 (p = strchr(memberstruct->name, '@'))) {
250 char *host = canonicalize_hostname(strsave(++p));
251 static char **mailhubs = NULL;
259 mailhubs = (char **)malloc(sizeof(char *));
261 status = mr_query("get_alias", 3, argv, collect,
263 if (status != MR_SUCCESS && status != MR_NO_MATCH) {
264 com_err(whoami, status,
265 " while reading list of MAILHUB servers");
269 for (i = 0; p = mailhubs[i]; i++) {
270 if (!strcasecmp(p, host)) {
271 host = strsave(memberstruct->name);
272 *(strchr(memberstruct->name, '@')) = 0;
273 memberstruct->type = M_ANY;
274 fprintf(stderr, "Warning: \"STRING:%s\" converted to \"%s\" because it is a local name.\n",
275 host, memberstruct->name);
281 /* now continue adding member */
282 membervec[0] = listname;
283 membervec[2] = memberstruct->name;
285 printf("Adding member ");
286 show_list_member(memberstruct);
288 switch (memberstruct->type) {
291 membervec[1] = "USER";
292 status = mr_query("add_member_to_list", 3, membervec, scream, NULL);
293 if (status == MR_SUCCESS)
295 else if (status != MR_USER || memberstruct->type != M_ANY) {
296 com_err(whoami, status, "while adding member %s to %s",
297 memberstruct->name, listname);
302 membervec[1] = "LIST";
303 status = mr_query("add_member_to_list", 3, membervec,
305 if (status == MR_SUCCESS) {
306 if (!strcmp(membervec[0], getenv("USER"))) {
307 fprintf(stderr, "\nWARNING: \"LIST:%s\" was just added to list \"%s\".\n",
308 membervec[2], membervec[0]);
309 fprintf(stderr, "If you meant to add yourself to the list \"%s\", type:\n", membervec[2]);
310 fprintf(stderr, "\tblanche %s -d %s\t(to undo this)\n",
311 membervec[0], membervec[2]);
312 fprintf(stderr, "\tblanche %s -a %s\t(to add yourself to that list)\n",
313 membervec[2], membervec[0]);
316 } else if (status != MR_LIST || memberstruct->type != M_ANY) {
317 com_err(whoami, status, "while adding member %s to %s",
318 memberstruct->name, listname);
323 membervec[1] = "STRING";
324 status = mr_query("add_member_to_list", 3, membervec,
326 if (status != MR_SUCCESS) {
327 com_err(whoami, status, "while adding member %s to %s",
328 memberstruct->name, listname);
331 else if (!strchr(memberstruct->name, '@') &&
332 !strchr(memberstruct->name, '!') &&
333 !strchr(memberstruct->name, '%')) {
334 fprintf(stderr, "\nWARNING: \"STRING:%s\" was just added to list \"%s\".\n",
335 memberstruct->name, listname);
336 fprintf(stderr, "\tIf %s is a mailing list, this may cause it to stop working.\n", listname);
337 fprintf(stderr, "\tYou should consider removing \"STRING:%s\" from the list.\n", memberstruct->name);
341 membervec[1] = "KERBEROS";
342 status = mr_query("add_member_to_list", 3, membervec,
344 if (status != MR_SUCCESS) {
345 com_err(whoami, status, "while adding member %s to %s",
346 memberstruct->name, listname);
352 /* Process the delete list */
353 while (sq_get_data(dellist, &memberstruct)) {
354 membervec[0] = listname;
355 membervec[2] = memberstruct->name;
357 printf("Deleting member ");
358 show_list_member(memberstruct);
360 switch (memberstruct->type) {
363 membervec[1] = "USER";
364 status = mr_query("delete_member_from_list", 3, membervec,
366 if (status == MR_SUCCESS)
368 else if ((status != MR_USER && status != MR_NO_MATCH) ||
369 memberstruct->type != M_ANY) {
370 com_err(whoami, status, "while deleting member %s from %s",
371 memberstruct->name, listname);
376 membervec[1] = "LIST";
377 status = mr_query("delete_member_from_list", 3, membervec,
379 if (status == MR_SUCCESS)
381 else if ((status != MR_LIST && status != MR_NO_MATCH) ||
382 memberstruct->type != M_ANY) {
383 if (status == MR_PERM && memberstruct->type == M_ANY &&
384 !strcmp(membervec[2], getenv("USER"))) {
385 /* M_ANY means we've fallen through from the user
386 * case. The user is trying to remove himself from
387 * a list, but we got MR_USER or MR_NO_MATCH above,
388 * meaning he's not really on it, and we got MR_PERM
389 * when trying to remove LIST:$USER because he's not
390 * on the acl. That error is useless, so return
391 * MR_NO_MATCH instead. However, this will generate the
392 * wrong error if the user was trying to remove the list
393 * with his username from a list he doesn't administrate
394 * without explicitly specifying "list:".
396 status = MR_NO_MATCH;
398 com_err(whoami, status, "while deleting member %s from %s",
399 memberstruct->name, listname);
404 membervec[1] = "STRING";
405 status = mr_query("delete_member_from_list", 3, membervec,
407 if (status == MR_STRING && memberstruct->type == M_ANY) {
408 com_err(whoami, 0, " Unable to find member %s to delete from %s",
409 memberstruct->name, listname);
411 if (!strcmp(membervec[0], getenv("USER"))) {
412 fprintf(stderr, "(If you were trying to remove yourself from the list \"%s\",\n", membervec[2]);
413 fprintf(stderr, "the correct command is \"blanche %s -d %s\".)\n",
414 membervec[2], membervec[0]);
416 } else if (status != MR_SUCCESS) {
417 com_err(whoami, status, "while deleting member %s from %s",
418 memberstruct->name, listname);
423 membervec[1] = "KERBEROS";
424 status = mr_query("delete_member_from_list", 3, membervec,
426 if (status != MR_SUCCESS) {
427 com_err(whoami, status, "while deleting member %s from %s",
428 memberstruct->name, listname);
434 /* Display the members of the list now, if requested */
437 recursive_display_list_members();
439 status = mr_query("get_members_of_list", 1, &listname,
440 get_list_members, (char *)memberlist);
442 com_err(whoami, status, "while getting members of list %s",
444 while (sq_get_data(memberlist, &memberstruct))
445 show_list_member(memberstruct);
457 fprintf(stderr, "Usage: %s listname [options]\n",argv[0]);
458 fprintf(stderr, "Options are\n");
459 fprintf(stderr, " -v | -verbose\n");
460 fprintf(stderr, " -m | -members\n");
461 fprintf(stderr, " -u | -users\n");
462 fprintf(stderr, " -l | -lists\n");
463 fprintf(stderr, " -s | -strings\n");
464 fprintf(stderr, " -k | -kerberos\n");
465 fprintf(stderr, " -i | -info\n");
466 fprintf(stderr, " -r | -recursive\n");
467 fprintf(stderr, " -a | -add member\n");
468 fprintf(stderr, " -d | -delete member\n");
469 fprintf(stderr, " -al | -addlist filename\n");
470 fprintf(stderr, " -dl | -deletelist filename\n");
471 fprintf(stderr, " -f | -file filename\n");
472 fprintf(stderr, " -n | -noauth\n");
473 fprintf(stderr, " -db | -database host[:port]\n");
474 fprintf(stderr, " -D | -debug\n");
479 /* Display the members stored in the queue */
481 show_list_member(memberstruct)
482 struct member *memberstruct;
486 switch (memberstruct->type) {
508 printf("%s\n", memberstruct->name);
513 printf("%s:%s\n", s, memberstruct->name);
515 if (memberstruct->type == M_LIST)
516 printf("LIST:%s\n", memberstruct->name);
517 else if (memberstruct->type == M_KERBEROS)
518 printf("KERBEROS:%s\n", memberstruct->name);
519 else if (memberstruct->type == M_STRING &&
520 !strchr(memberstruct->name, '@'))
521 printf("STRING:%s\n", memberstruct->name);
523 printf("%s\n", memberstruct->name);
528 /* Show the retrieved information about a list */
530 show_list_info(argc, argv, hint)
535 printf("List: %s\n", argv[0]);
536 printf("Description: %s\n", argv[9]);
537 printf("Flags: %s, %s, and %s\n",
538 atoi(argv[1]) ? "active" : "inactive",
539 atoi(argv[2]) ? "public" : "private",
540 atoi(argv[3]) ? "hidden" : "visible");
541 printf("%s is %sa maillist and is %sa group", argv[0],
542 atoi(argv[4]) ? "" : "not ",
543 atoi(argv[5]) ? "" : "not ");
545 printf(" with GID %d\n", atoi(argv[6]));
548 printf("Owner: %s %s\n", argv[7], argv[8]);
549 printf("Last modified by %s with %s on %s\n", argv[11], argv[12], argv[10]);
554 /* Show the retrieve list member count */
556 show_list_count(argc, argv, hint)
561 printf("Members: %s\n", argv[0]);
565 /* Recursively find all of the members of listname, and then display them */
567 recursive_display_list_members()
569 int status, count, savecount;
570 struct save_queue *lists, *members;
571 struct member *m, *m1, *data;
574 members = sq_create();
575 m = (struct member *) malloc(sizeof(struct member));
578 sq_save_data(lists, m);
580 while (sq_get_data(lists, &m)) {
581 sq_destroy(memberlist);
582 memberlist = sq_create();
584 fprintf(stderr, "Fetching members of %s\n", m->name);
585 status = mr_query("get_members_of_list", 1, &(m->name),
586 get_list_members, (char *)memberlist);
588 com_err(whoami, status, "while getting members of list %s", m->name);
589 while (sq_get_data(memberlist, &m1)) {
590 if (m1->type == M_LIST)
591 unique_add_member(lists, m1);
593 unique_add_member(members, m1);
596 savecount = count = sq_count_elts(members);
597 data = (struct member *) malloc(count * sizeof(struct member));
599 while (sq_get_data(members, &m))
600 memcpy(&data[count++], m, sizeof(struct member));
601 qsort(data, count, sizeof(struct member), membercmp);
602 for (count = 0; count < savecount; count++) {
603 show_list_member(&data[count]);
608 /* add a struct member to a queue if that member isn't already there. */
610 unique_add_member(q, m)
611 struct save_queue *q;
614 struct save_queue *qp;
616 for (qp = q->q_next; qp != q; qp = qp->q_next) {
617 if (!membercmp(qp->q_data, m))
624 /* Collect the retrieved members of the list */
626 get_list_members(argc, argv, q)
629 struct save_queue *q;
633 m = (struct member *) malloc(sizeof(struct member));
634 switch (argv[0][0]) {
645 m->type = M_KERBEROS;
648 m->name = strsave(argv[1]);
654 /* Called only if a query returns a value that we weren't expecting */
658 fprintf(stderr, "Programmer botch\n");
663 /* Open file, parse members from file, and put them on the specified queue */
664 get_members_from_file(filename, queue)
666 struct save_queue *queue;
670 struct member *memberstruct;
672 if (!strcmp(filename, "-"))
675 in = fopen(filename, "r");
677 com_err(whoami, errno, "while opening %s for input", filename);
682 while (fgets(buf, BUFSIZ, in))
683 if (memberstruct = parse_member(buf))
684 sq_save_data(queue, memberstruct);
686 com_err(whoami, errno, "while reading from %s", filename);
692 /* Collect the possible expansions of the alias MAILHUB */
694 int collect(argc, argv, list)
701 for (i = 0; (*list)[i]; i++);
702 *list = (char **)realloc(*list, (i + 2) * sizeof(char *));
703 (*list)[i] = strsave(argv[2]);
709 /* Parse a line of input, fetching a member. NULL is returned if a member
710 * is not found. ';' is a comment character.
713 struct member *parse_member(s)
716 register struct member *m;
719 while (*s && isspace(*s))
722 while (*p && *p != '\n' && *p != ';')
723 if (isprint(*p) && !isspace(*p))
729 if (p == s || strlen(s) == 0)
732 if ((m = (struct member *) malloc(sizeof(struct member))) == NULL)
735 if (p = strchr(s, ':')) {
738 if (!strcasecmp("user", s))
740 else if (!strcasecmp("list", s))
742 else if (!strcasecmp("string", s))
744 else if (!strcasecmp("kerberos", s))
745 m->type = M_KERBEROS;
751 m->name = strsave(m->name);
753 m->name = strsave(s);
754 if (strchr(s, '@') || strchr(s, '!') || strchr(s, '%') || strchr(s, ' '))
764 * This routine two compares members by the following rules:
765 * 1. A USER is less than a LIST
766 * 2. A LIST is less than a STRING
767 * 3. If two members are of the same type, the one alphabetically first
768 * is less than the other
769 * It returs < 0 if the first member is less, 0 if they are identical, and
770 * > 0 if the second member is less (the first member is greater).
773 int membercmp(m1, m2)
774 struct member *m1, *m2;
776 if (m1->type == M_ANY || m2->type == M_ANY || (m1->type == m2->type))
777 return(strcmp(m1->name, m2->name));
779 return(m1->type - m2->type);
784 struct save_queue *q;
790 while (sq_get_data(q, &foo))