3 * Command line oriented Moira List tool.
5 * by Mark Rosenstein, September 1988.
7 * Copyright (C) 1988-1998 by the Massachusetts Institute of Technology.
8 * For copying and distribution information, please see the file
12 #include <mit-copyright.h>
14 #include <moira_site.h>
29 /* It is important to membercmp that M_USER < M_LIST < M_STRING */
36 /* argument parsing macro */
37 #define argis(a, b) (!strcmp(*arg + 1, a) || !strcmp(*arg + 1, b))
39 /* flags from command line */
40 int infoflg, verbose, syncflg, memberflg, recursflg, noauth;
41 int showusers, showstrings, showkerberos, showlists;
42 int createflag, setinfo, active, public, hidden, maillist, grouplist;
46 /* various member lists */
47 struct save_queue *addlist, *dellist, *memberlist, *synclist;
49 char *listname, *whoami;
51 void usage(char **argv);
52 void show_list_member(struct member *memberstruct);
53 int show_list_info(int argc, char **argv, void *hint);
54 int save_list_info(int argc, char **argv, void *hint);
55 int show_list_count(int argc, char **argv, void *hint);
56 void recursive_display_list_members(void);
57 void unique_add_member(struct save_queue *q, struct member *m);
58 int get_list_members(int argc, char **argv, void *sq);
59 void get_members_from_file(char *filename, struct save_queue *queue);
60 int collect(int argc, char **argv, void *l);
61 struct member *parse_member(char *s);
62 int membercmp(const void *mem1, const void *mem2);
63 int sq_count_elts(struct save_queue *q);
65 int main(int argc, char **argv)
69 char *membervec[3], *motd;
70 struct member *memberstruct;
71 char *server = NULL, *p;
73 /* clear all flags & lists */
74 infoflg = verbose = syncflg = memberflg = recursflg = 0;
75 noauth = showusers = showstrings = showkerberos = showlists = 0;
76 createflag = setinfo = 0;
77 active = public = hidden = maillist = grouplist = -1;
78 listname = newname = desc = NULL;
80 addlist = sq_create();
81 dellist = sq_create();
82 memberlist = sq_create();
83 synclist = sq_create();
88 /* parse args, building addlist, dellist, & synclist */
89 while (++arg - argv < argc)
93 if (argis("m", "members"))
95 else if (argis("u", "users"))
97 else if (argis("s", "strings"))
99 else if (argis("l", "lists"))
101 else if (argis("k", "kerberos"))
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"))
113 if (arg - argv < argc - 1)
121 else if (argis("a", "add"))
123 if (arg - argv < argc - 1)
126 if ((memberstruct = parse_member(*arg)))
127 sq_save_data(addlist, memberstruct);
132 else if (argis("al", "addlist"))
134 if (arg - argv < argc - 1)
137 get_members_from_file(*arg, addlist);
142 else if (argis("d", "delete"))
144 if (arg - argv < argc - 1)
147 if ((memberstruct = parse_member(*arg)))
148 sq_save_data(dellist, memberstruct);
153 else if (argis("dl", "deletelist"))
155 if (arg - argv < argc - 1)
158 get_members_from_file(*arg, dellist);
163 else if (argis("f", "file"))
165 if (arg - argv < argc - 1)
169 get_members_from_file(*arg, synclist);
174 else if (argis("C", "create"))
176 else if (argis("P", "public"))
181 else if (argis("NP", "private"))
186 else if (argis("A", "active"))
191 else if (argis("I", "inactive"))
196 else if (argis("V", "visible"))
201 else if (argis("H", "hidden"))
206 else if (argis("M", "mail"))
211 else if (argis("NM", "notmail"))
216 else if (argis("G", "group"))
221 else if (argis("NG", "notgroup"))
226 else if (argis("D", "desc"))
228 if (arg - argv < argc - 1)
237 else if (argis("O", "owner"))
239 if (arg - argv < argc - 1)
243 owner = parse_member(*arg);
248 else if (argis("R", "rename"))
250 if (arg - argv < argc - 1)
262 else if (listname == NULL)
267 if (listname == NULL)
270 /* if no other options specified, turn on list members flag */
271 if (!(infoflg || syncflg || createflag || setinfo ||
272 addlist->q_next != addlist || dellist->q_next != dellist))
275 /* If none of {users,strings,lists,kerberos} specified, turn them all on */
276 if (!(showusers || showstrings || showlists || showkerberos))
277 showusers = showstrings = showlists = showkerberos = 1;
280 if ((status = mr_connect(server)))
282 com_err(whoami, status, "unable to connect to the Moira server");
285 if ((status = mr_motd(&motd)))
287 com_err(whoami, status, "unable to check server status");
292 fprintf(stderr, "The Moira server is currently unavailable:\n%s\n",
298 if (!noauth && (status = mr_auth("blanche")))
300 if (status == MR_USER_AUTH)
301 com_err(whoami, status, "");
304 com_err(whoami, status, "unable to authenticate to Moira");
306 " Try the -noauth flag if you don't need authentication");
311 /* create if needed */
317 argv[1] = (active == 0) ? "0" : "1";
318 argv[2] = (public == 1) ? "1" : "0";
319 argv[3] = (hidden == 1) ? "1" : "0";
320 argv[4] = (maillist == 0) ? "0" : "1";
321 argv[5] = (grouplist == 1) ? "1" : "0";
322 argv[6] = UNIQUE_GID;
323 argv[9] = desc ? desc : "none";
327 argv[8] = owner->name;
333 status = mr_query("add_list", 10, argv, NULL, NULL);
334 if (owner->type != M_ANY || status != MR_USER)
339 status = mr_query("add_list", 10, argv, NULL, NULL);
343 argv[7] = "KERBEROS";
344 status = mr_query("add_list", 10, argv, NULL, NULL);
351 argv[8] = getenv("USER");
353 status = mr_query("add_list", 10, argv, NULL, NULL);
358 com_err(whoami, status, "while creating list.");
366 status = mr_query("get_list_info", 1, &listname,
367 save_list_info, argv);
370 com_err(whoami, status, "while getting list information");
378 argv[2] = active ? "1" : "0";
380 argv[3] = public ? "1" : "0";
382 argv[4] = hidden ? "1" : "0";
384 argv[5] = maillist ? "1" : "0";
386 argv[6] = grouplist ? "1" : "0";
392 argv[9] = owner->name;
398 status = mr_query("update_list", 11, argv, NULL, NULL);
399 if (owner->type != M_ANY || status != MR_USER)
404 status = mr_query("update_list", 11, argv, NULL, NULL);
408 argv[8] = "KERBEROS";
409 status = mr_query("update_list", 11, argv, NULL, NULL);
414 status = mr_query("update_list", 11, argv, NULL, NULL);
417 com_err(whoami, status, "while updating list.");
422 /* display list info if requested to */
425 status = mr_query("get_list_info", 1, &listname, show_list_info, NULL);
427 com_err(whoami, status, "while getting list information");
428 if (verbose && !memberflg)
430 status = mr_query("count_members_of_list", 1, &listname,
431 show_list_count, NULL);
433 com_err(whoami, status, "while getting list count");
437 /* if we're synchronizing to a file, we need to:
438 * get the current members of the list
439 * for each member of the sync file
440 * if they are on the list, remove them from the in-memory copy
441 * if they're not on the list, add them to add-list
442 * if anyone is left on the in-memory copy, put them on the delete-list
443 * lastly, reset memberlist so we can use it again later
447 status = mr_query("get_members_of_list", 1, &listname,
448 get_list_members, memberlist);
451 com_err(whoami, status, "getting members of list %s", listname);
454 while (sq_get_data(synclist, &memberstruct))
456 struct save_queue *q;
459 for (q = memberlist->q_next; q != memberlist; q = q->q_next)
461 if (membercmp(q->q_data, memberstruct) == 0)
463 q->q_prev->q_next = q->q_next;
464 q->q_next->q_prev = q->q_prev;
470 sq_save_data(addlist, memberstruct);
472 while (sq_get_data(memberlist, &memberstruct))
473 sq_save_data(dellist, memberstruct);
474 sq_destroy(memberlist);
475 memberlist = sq_create();
478 /* Process the add list */
479 while (sq_get_data(addlist, &memberstruct))
481 /* canonicalize string if necessary */
482 if (memberstruct->type == M_STRING &&
483 (p = strchr(memberstruct->name, '@')))
485 char *host = canonicalize_hostname(strdup(++p));
486 static char **mailhubs = NULL;
495 mailhubs = malloc(sizeof(char *));
497 status = mr_query("get_alias", 3, argv, collect,
499 if (status != MR_SUCCESS && status != MR_NO_MATCH)
501 com_err(whoami, status,
502 " while reading list of MAILHUB servers");
506 for (i = 0; (p = mailhubs[i]); i++)
508 if (!strcasecmp(p, host))
510 host = strdup(memberstruct->name);
511 *(strchr(memberstruct->name, '@')) = 0;
512 memberstruct->type = M_ANY;
513 fprintf(stderr, "Warning: \"STRING:%s\" converted to "
514 "\"%s\" because it is a local name.\n",
515 host, memberstruct->name);
521 /* now continue adding member */
522 membervec[0] = listname;
523 membervec[2] = memberstruct->name;
526 printf("Adding member ");
527 show_list_member(memberstruct);
529 switch (memberstruct->type)
533 membervec[1] = "USER";
534 status = mr_query("add_member_to_list", 3, membervec, NULL, NULL);
535 if (status == MR_SUCCESS)
537 else if (status != MR_USER || memberstruct->type != M_ANY)
539 com_err(whoami, status, "while adding member %s to %s",
540 memberstruct->name, listname);
545 membervec[1] = "LIST";
546 status = mr_query("add_member_to_list", 3, membervec,
548 if (status == MR_SUCCESS)
550 if (!strcmp(membervec[0], getenv("USER")))
552 fprintf(stderr, "\nWARNING: \"LIST:%s\" was just added "
553 "to list \"%s\".\n", membervec[2], membervec[0]);
554 fprintf(stderr, "If you meant to add yourself to the list "
555 "\"%s\", type:\n", membervec[2]);
556 fprintf(stderr, "\tblanche %s -d %s\t(to undo this)\n",
557 membervec[0], membervec[2]);
558 fprintf(stderr, "\tblanche %s -a %s\t(to add yourself to "
559 "that list)\n", membervec[2], membervec[0]);
563 else if (status != MR_LIST || memberstruct->type != M_ANY)
565 com_err(whoami, status, "while adding member %s to %s",
566 memberstruct->name, listname);
571 if (memberstruct->type == M_ANY &&
572 !strchr(memberstruct->name, '@') &&
573 !strchr(memberstruct->name, '!') &&
574 !strchr(memberstruct->name, '%'))
576 /* if user is trying to add something which isn't a
577 remote string, or a list, or a user, and didn't
578 explicitly specify `STRING:', it's probably a typo */
579 com_err(whoami, MR_NO_MATCH, "while adding member %s to %s",
580 memberstruct->name, listname);
585 membervec[1] = "STRING";
586 status = mr_query("add_member_to_list", 3, membervec,
588 if (status != MR_SUCCESS)
590 com_err(whoami, status, "while adding member %s to %s",
591 memberstruct->name, listname);
596 membervec[1] = "KERBEROS";
597 status = mr_query("add_member_to_list", 3, membervec,
599 if (status != MR_SUCCESS)
601 com_err(whoami, status, "while adding member %s to %s",
602 memberstruct->name, listname);
608 /* Process the delete list */
609 while (sq_get_data(dellist, &memberstruct))
611 membervec[0] = listname;
612 membervec[2] = memberstruct->name;
615 printf("Deleting member ");
616 show_list_member(memberstruct);
618 switch (memberstruct->type)
622 membervec[1] = "USER";
623 status = mr_query("delete_member_from_list", 3, membervec,
625 if (status == MR_SUCCESS)
627 else if ((status != MR_USER && status != MR_NO_MATCH) ||
628 memberstruct->type != M_ANY)
630 com_err(whoami, status, "while deleting member %s from %s",
631 memberstruct->name, listname);
636 membervec[1] = "LIST";
637 status = mr_query("delete_member_from_list", 3, membervec,
639 if (status == MR_SUCCESS)
641 else if ((status != MR_LIST && status != MR_NO_MATCH) ||
642 memberstruct->type != M_ANY)
644 if (status == MR_PERM && memberstruct->type == M_ANY &&
645 !strcmp(membervec[2], getenv("USER")))
647 /* M_ANY means we've fallen through from the user
648 * case. The user is trying to remove himself from
649 * a list, but we got MR_USER or MR_NO_MATCH above,
650 * meaning he's not really on it, and we got MR_PERM
651 * when trying to remove LIST:$USER because he's not
652 * on the acl. That error is useless, so return
653 * MR_NO_MATCH instead. However, this will generate the
654 * wrong error if the user was trying to remove the list
655 * with his username from a list he doesn't administrate
656 * without explicitly specifying "list:".
658 status = MR_NO_MATCH;
660 com_err(whoami, status, "while deleting member %s from %s",
661 memberstruct->name, listname);
666 membervec[1] = "STRING";
667 status = mr_query("delete_member_from_list", 3, membervec,
669 if (status == MR_STRING && memberstruct->type == M_ANY)
671 com_err(whoami, 0, " Unable to find member %s to delete from %s",
672 memberstruct->name, listname);
674 if (!strcmp(membervec[0], getenv("USER")))
676 fprintf(stderr, "(If you were trying to remove yourself "
677 "from the list \"%s\",\n", membervec[2]);
678 fprintf(stderr, "the correct command is \"blanche %s -d "
679 "%s\".)\n", membervec[2], membervec[0]);
682 else if (status != MR_SUCCESS)
684 com_err(whoami, status, "while deleting member %s from %s",
685 memberstruct->name, listname);
690 membervec[1] = "KERBEROS";
691 status = mr_query("delete_member_from_list", 3, membervec,
693 if (status != MR_SUCCESS)
695 com_err(whoami, status, "while deleting member %s from %s",
696 memberstruct->name, listname);
702 /* Display the members of the list now, if requested */
706 recursive_display_list_members();
709 status = mr_query("get_members_of_list", 1, &listname,
710 get_list_members, memberlist);
712 com_err(whoami, status, "while getting members of list %s",
714 while (sq_get_data(memberlist, &memberstruct))
715 show_list_member(memberstruct);
721 exit(success ? 0 : 1);
724 void usage(char **argv)
726 fprintf(stderr, "Usage: %s listname [options]\n", argv[0]);
727 fprintf(stderr, "Options are\n");
728 fprintf(stderr, " %-39s%-39s\n", "-v | -verbose",
730 fprintf(stderr, " %-39s%-39s\n", "-m | -members",
731 "-R | -rename newname");
732 fprintf(stderr, " %-39s%-39s\n", "-u | -users",
734 fprintf(stderr, " %-39s%-39s\n", "-l | -lists",
736 fprintf(stderr, " %-39s%-39s\n", "-s | -strings",
738 fprintf(stderr, " %-39s%-39s\n", "-k | -kerberos",
740 fprintf(stderr, " %-39s%-39s\n", "-i | -info",
742 fprintf(stderr, " %-39s%-39s\n", "-r | -recursive",
744 fprintf(stderr, " %-39s%-39s\n", "-a | -add member",
746 fprintf(stderr, " %-39s%-39s\n", "-d | -delete member",
748 fprintf(stderr, " %-39s%-39s\n", "-al | -addlist filename",
750 fprintf(stderr, " %-39s%-39s\n", "-dl | -deletelist filename",
752 fprintf(stderr, " %-39s%-39s\n", "-f | -file filename",
753 "-D | -desc description");
754 fprintf(stderr, " %-39s%-39s\n", "-n | -noauth",
755 "-O | -owner owner");
756 fprintf(stderr, " %-39s%-39s\n", "-db | -database host[:port]",
762 /* Display the members stored in the queue */
764 void show_list_member(struct member *memberstruct)
768 switch (memberstruct->type)
791 printf("%s\n", memberstruct->name);
796 printf("%s:%s\n", s, memberstruct->name);
799 if (memberstruct->type == M_LIST)
800 printf("LIST:%s\n", memberstruct->name);
801 else if (memberstruct->type == M_KERBEROS)
802 printf("KERBEROS:%s\n", memberstruct->name);
803 else if (memberstruct->type == M_STRING &&
804 !strchr(memberstruct->name, '@'))
805 printf("STRING:%s\n", memberstruct->name);
807 printf("%s\n", memberstruct->name);
812 /* Show the retrieved information about a list */
814 int show_list_info(int argc, char **argv, void *hint)
816 printf("List: %s\n", argv[0]);
817 printf("Description: %s\n", argv[9]);
818 printf("Flags: %s, %s, and %s\n",
819 atoi(argv[1]) ? "active" : "inactive",
820 atoi(argv[2]) ? "public" : "private",
821 atoi(argv[3]) ? "hidden" : "visible");
822 printf("%s is %sa maillist and is %sa group", argv[0],
823 atoi(argv[4]) ? "" : "not ",
824 atoi(argv[5]) ? "" : "not ");
826 printf(" with GID %d\n", atoi(argv[6]));
829 printf("Owner: %s %s\n", argv[7], argv[8]);
830 printf("Last modified by %s with %s on %s\n", argv[11], argv[12], argv[10]);
835 /* Copy retrieved information about a list into a new argv */
837 int save_list_info(int argc, char **argv, void *hint)
841 for (argc = 0; argc < 10; argc++)
842 nargv[argc + 1] = strdup(argv[argc]);
846 /* Show the retrieve list member count */
848 int show_list_count(int argc, char **argv, void *hint)
850 printf("Members: %s\n", argv[0]);
855 /* Recursively find all of the members of listname, and then display them */
857 void recursive_display_list_members(void)
859 int status, count, savecount;
860 struct save_queue *lists, *members;
861 struct member *m, *m1, *data;
864 members = sq_create();
865 m = malloc(sizeof(struct member));
868 sq_save_data(lists, m);
870 while (sq_get_data(lists, &m))
872 sq_destroy(memberlist);
873 memberlist = sq_create();
874 status = mr_query("get_members_of_list", 1, &(m->name),
875 get_list_members, memberlist);
877 com_err(whoami, status, "while getting members of list %s", m->name);
878 while (sq_get_data(memberlist, &m1))
880 if (m1->type == M_LIST)
881 unique_add_member(lists, m1);
883 unique_add_member(members, m1);
886 savecount = count = sq_count_elts(members);
887 data = malloc(count * sizeof(struct member));
889 while (sq_get_data(members, &m))
890 memcpy(&data[count++], m, sizeof(struct member));
891 qsort(data, count, sizeof(struct member), membercmp);
892 for (count = 0; count < savecount; count++)
893 show_list_member(&data[count]);
897 /* add a struct member to a queue if that member isn't already there. */
899 void unique_add_member(struct save_queue *q, struct member *m)
901 struct save_queue *qp;
903 for (qp = q->q_next; qp != q; qp = qp->q_next)
905 if (!membercmp(qp->q_data, m))
912 /* Collect the retrieved members of the list */
914 int get_list_members(int argc, char **argv, void *sq)
916 struct save_queue *q = sq;
919 m = malloc(sizeof(struct member));
932 m->type = M_KERBEROS;
935 m->name = strdup(argv[1]);
941 /* Open file, parse members from file, and put them on the specified queue */
942 void get_members_from_file(char *filename, struct save_queue *queue)
946 struct member *memberstruct;
948 if (!strcmp(filename, "-"))
952 in = fopen(filename, "r");
955 com_err(whoami, errno, "while opening %s for input", filename);
960 while (fgets(buf, BUFSIZ, in))
962 if ((memberstruct = parse_member(buf)))
963 sq_save_data(queue, memberstruct);
967 com_err(whoami, errno, "while reading from %s", filename);
973 /* Collect the possible expansions of the alias MAILHUB */
975 int collect(int argc, char **argv, void *l)
980 for (i = 0; (*list)[i]; i++)
982 *list = realloc(*list, (i + 2) * sizeof(char *));
983 (*list)[i] = strdup(argv[2]);
984 (*list)[i + 1] = NULL;
989 /* Parse a line of input, fetching a member. NULL is returned if a member
990 * is not found. ';' is a comment character.
993 struct member *parse_member(char *s)
998 while (*s && isspace(*s))
1001 while (*p && *p != '\n' && *p != ';')
1003 if (isprint(*p) && !isspace(*p))
1010 if (p == s || strlen(s) == 0)
1013 if (!(m = malloc(sizeof(struct member))))
1016 if ((p = strchr(s, ':')))
1020 if (!strcasecmp("user", s))
1022 else if (!strcasecmp("list", s))
1024 else if (!strcasecmp("string", s))
1026 else if (!strcasecmp("kerberos", s))
1027 m->type = M_KERBEROS;
1034 m->name = strdup(m->name);
1038 m->name = strdup(s);
1046 * This routine two compares members by the following rules:
1047 * 1. A USER is less than a LIST
1048 * 2. A LIST is less than a STRING
1049 * 3. If two members are of the same type, the one alphabetically first
1050 * is less than the other
1051 * It returs < 0 if the first member is less, 0 if they are identical, and
1052 * > 0 if the second member is less (the first member is greater).
1055 int membercmp(const void *mem1, const void *mem2)
1057 const struct member *m1 = mem1, *m2 = mem2;
1059 if (m1->type == M_ANY || m2->type == M_ANY || (m1->type == m2->type))
1060 return strcmp(m1->name, m2->name);
1062 return m1->type - m2->type;
1066 int sq_count_elts(struct save_queue *q)
1072 while (sq_get_data(q, &foo))