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 /* check for username/listname clash */
312 if (createflag || (setinfo && newname && strcmp(newname, listname)))
314 status = mr_query("get_user_account_by_login", 1,
315 createflag ? &listname : &newname,
317 if (status != MR_NO_MATCH)
318 fprintf(stderr, "WARNING: A user by that name already exists.\n");
321 /* create if needed */
327 argv[1] = (active == 0) ? "0" : "1";
328 argv[2] = (public == 1) ? "1" : "0";
329 argv[3] = (hidden == 1) ? "1" : "0";
330 argv[4] = (maillist == 0) ? "0" : "1";
331 argv[5] = (grouplist == 1) ? "1" : "0";
332 argv[6] = UNIQUE_GID;
333 argv[9] = desc ? desc : "none";
337 argv[8] = owner->name;
343 status = mr_query("add_list", 10, argv, NULL, NULL);
344 if (owner->type != M_ANY || status != MR_USER)
349 status = mr_query("add_list", 10, argv, NULL, NULL);
353 argv[7] = "KERBEROS";
354 status = mr_query("add_list", 10, argv, NULL, NULL);
361 argv[8] = getenv("USER");
363 status = mr_query("add_list", 10, argv, NULL, NULL);
368 com_err(whoami, status, "while creating list.");
376 status = mr_query("get_list_info", 1, &listname,
377 save_list_info, argv);
380 com_err(whoami, status, "while getting list information");
388 argv[2] = active ? "1" : "0";
390 argv[3] = public ? "1" : "0";
392 argv[4] = hidden ? "1" : "0";
394 argv[5] = maillist ? "1" : "0";
396 argv[6] = grouplist ? "1" : "0";
402 argv[9] = owner->name;
408 status = mr_query("update_list", 11, argv, NULL, NULL);
409 if (owner->type != M_ANY || status != MR_USER)
414 status = mr_query("update_list", 11, argv, NULL, NULL);
418 argv[8] = "KERBEROS";
419 status = mr_query("update_list", 11, argv, NULL, NULL);
424 status = mr_query("update_list", 11, argv, NULL, NULL);
427 com_err(whoami, status, "while updating list.");
432 /* display list info if requested to */
435 status = mr_query("get_list_info", 1, &listname, show_list_info, NULL);
437 com_err(whoami, status, "while getting list information");
438 if (verbose && !memberflg)
440 status = mr_query("count_members_of_list", 1, &listname,
441 show_list_count, NULL);
443 com_err(whoami, status, "while getting list count");
447 /* if we're synchronizing to a file, we need to:
448 * get the current members of the list
449 * for each member of the sync file
450 * if they are on the list, remove them from the in-memory copy
451 * if they're not on the list, add them to add-list
452 * if anyone is left on the in-memory copy, put them on the delete-list
453 * lastly, reset memberlist so we can use it again later
457 status = mr_query("get_members_of_list", 1, &listname,
458 get_list_members, memberlist);
461 com_err(whoami, status, "getting members of list %s", listname);
464 while (sq_get_data(synclist, &memberstruct))
466 struct save_queue *q;
469 for (q = memberlist->q_next; q != memberlist; q = q->q_next)
471 if (membercmp(q->q_data, memberstruct) == 0)
473 q->q_prev->q_next = q->q_next;
474 q->q_next->q_prev = q->q_prev;
480 sq_save_data(addlist, memberstruct);
482 while (sq_get_data(memberlist, &memberstruct))
483 sq_save_data(dellist, memberstruct);
484 sq_destroy(memberlist);
485 memberlist = sq_create();
488 /* Process the add list */
489 while (sq_get_data(addlist, &memberstruct))
491 /* canonicalize string if necessary */
492 if (memberstruct->type == M_STRING &&
493 (p = strchr(memberstruct->name, '@')))
495 char *host = canonicalize_hostname(strdup(++p));
496 static char **mailhubs = NULL;
505 mailhubs = malloc(sizeof(char *));
507 status = mr_query("get_alias", 3, argv, collect,
509 if (status != MR_SUCCESS && status != MR_NO_MATCH)
511 com_err(whoami, status,
512 " while reading list of MAILHUB servers");
516 for (i = 0; (p = mailhubs[i]); i++)
518 if (!strcasecmp(p, host))
520 host = strdup(memberstruct->name);
521 *(strchr(memberstruct->name, '@')) = 0;
522 memberstruct->type = M_ANY;
523 fprintf(stderr, "Warning: \"STRING:%s\" converted to "
524 "\"%s\" because it is a local name.\n",
525 host, memberstruct->name);
531 /* now continue adding member */
532 membervec[0] = listname;
533 membervec[2] = memberstruct->name;
536 printf("Adding member ");
537 show_list_member(memberstruct);
539 switch (memberstruct->type)
543 membervec[1] = "USER";
544 status = mr_query("add_member_to_list", 3, membervec, NULL, NULL);
545 if (status == MR_SUCCESS)
547 else if (status != MR_USER || memberstruct->type != M_ANY)
549 com_err(whoami, status, "while adding member %s to %s",
550 memberstruct->name, listname);
555 membervec[1] = "LIST";
556 status = mr_query("add_member_to_list", 3, membervec,
558 if (status == MR_SUCCESS)
560 if (!strcmp(membervec[0], getenv("USER")))
562 fprintf(stderr, "\nWARNING: \"LIST:%s\" was just added "
563 "to list \"%s\".\n", membervec[2], membervec[0]);
564 fprintf(stderr, "If you meant to add yourself to the list "
565 "\"%s\", type:\n", membervec[2]);
566 fprintf(stderr, "\tblanche %s -d %s\t(to undo this)\n",
567 membervec[0], membervec[2]);
568 fprintf(stderr, "\tblanche %s -a %s\t(to add yourself to "
569 "that list)\n", membervec[2], membervec[0]);
573 else if (status != MR_LIST || memberstruct->type != M_ANY)
575 com_err(whoami, status, "while adding member %s to %s",
576 memberstruct->name, listname);
581 if (memberstruct->type == M_ANY &&
582 !strchr(memberstruct->name, '@') &&
583 !strchr(memberstruct->name, '!') &&
584 !strchr(memberstruct->name, '%'))
586 /* if user is trying to add something which isn't a
587 remote string, or a list, or a user, and didn't
588 explicitly specify `STRING:', it's probably a typo */
589 com_err(whoami, MR_NO_MATCH, "while adding member %s to %s",
590 memberstruct->name, listname);
595 membervec[1] = "STRING";
596 status = mr_query("add_member_to_list", 3, membervec,
598 if (status != MR_SUCCESS)
600 com_err(whoami, status, "while adding member %s to %s",
601 memberstruct->name, listname);
606 membervec[1] = "KERBEROS";
607 status = mr_query("add_member_to_list", 3, membervec,
609 if (status != MR_SUCCESS)
611 com_err(whoami, status, "while adding member %s to %s",
612 memberstruct->name, listname);
618 /* Process the delete list */
619 while (sq_get_data(dellist, &memberstruct))
621 membervec[0] = listname;
622 membervec[2] = memberstruct->name;
625 printf("Deleting member ");
626 show_list_member(memberstruct);
628 switch (memberstruct->type)
632 membervec[1] = "USER";
633 status = mr_query("delete_member_from_list", 3, membervec,
635 if (status == MR_SUCCESS)
637 else if ((status != MR_USER && status != MR_NO_MATCH) ||
638 memberstruct->type != M_ANY)
640 com_err(whoami, status, "while deleting member %s from %s",
641 memberstruct->name, listname);
646 membervec[1] = "LIST";
647 status = mr_query("delete_member_from_list", 3, membervec,
649 if (status == MR_SUCCESS)
651 else if ((status != MR_LIST && status != MR_NO_MATCH) ||
652 memberstruct->type != M_ANY)
654 if (status == MR_PERM && memberstruct->type == M_ANY &&
655 !strcmp(membervec[2], getenv("USER")))
657 /* M_ANY means we've fallen through from the user
658 * case. The user is trying to remove himself from
659 * a list, but we got MR_USER or MR_NO_MATCH above,
660 * meaning he's not really on it, and we got MR_PERM
661 * when trying to remove LIST:$USER because he's not
662 * on the acl. That error is useless, so return
663 * MR_NO_MATCH instead. However, this will generate the
664 * wrong error if the user was trying to remove the list
665 * with his username from a list he doesn't administrate
666 * without explicitly specifying "list:".
668 status = MR_NO_MATCH;
670 com_err(whoami, status, "while deleting member %s from %s",
671 memberstruct->name, listname);
676 membervec[1] = "STRING";
677 status = mr_query("delete_member_from_list", 3, membervec,
679 if (status == MR_STRING && memberstruct->type == M_ANY)
681 com_err(whoami, 0, " Unable to find member %s to delete from %s",
682 memberstruct->name, listname);
684 if (!strcmp(membervec[0], getenv("USER")))
686 fprintf(stderr, "(If you were trying to remove yourself "
687 "from the list \"%s\",\n", membervec[2]);
688 fprintf(stderr, "the correct command is \"blanche %s -d "
689 "%s\".)\n", membervec[2], membervec[0]);
692 else if (status != MR_SUCCESS)
694 com_err(whoami, status, "while deleting member %s from %s",
695 memberstruct->name, listname);
700 membervec[1] = "KERBEROS";
701 status = mr_query("delete_member_from_list", 3, membervec,
703 if (status != MR_SUCCESS)
705 com_err(whoami, status, "while deleting member %s from %s",
706 memberstruct->name, listname);
712 /* Display the members of the list now, if requested */
716 recursive_display_list_members();
719 status = mr_query("get_members_of_list", 1, &listname,
720 get_list_members, memberlist);
722 com_err(whoami, status, "while getting members of list %s",
724 while (sq_get_data(memberlist, &memberstruct))
725 show_list_member(memberstruct);
731 exit(success ? 0 : 1);
734 void usage(char **argv)
736 fprintf(stderr, "Usage: %s listname [options]\n", argv[0]);
737 fprintf(stderr, "Options are\n");
738 fprintf(stderr, " %-39s%-39s\n", "-v | -verbose",
740 fprintf(stderr, " %-39s%-39s\n", "-m | -members",
741 "-R | -rename newname");
742 fprintf(stderr, " %-39s%-39s\n", "-u | -users",
744 fprintf(stderr, " %-39s%-39s\n", "-l | -lists",
746 fprintf(stderr, " %-39s%-39s\n", "-s | -strings",
748 fprintf(stderr, " %-39s%-39s\n", "-k | -kerberos",
750 fprintf(stderr, " %-39s%-39s\n", "-i | -info",
752 fprintf(stderr, " %-39s%-39s\n", "-r | -recursive",
754 fprintf(stderr, " %-39s%-39s\n", "-a | -add member",
756 fprintf(stderr, " %-39s%-39s\n", "-d | -delete member",
758 fprintf(stderr, " %-39s%-39s\n", "-al | -addlist filename",
760 fprintf(stderr, " %-39s%-39s\n", "-dl | -deletelist filename",
762 fprintf(stderr, " %-39s%-39s\n", "-f | -file filename",
763 "-D | -desc description");
764 fprintf(stderr, " %-39s%-39s\n", "-n | -noauth",
765 "-O | -owner owner");
766 fprintf(stderr, " %-39s%-39s\n", "-db | -database host[:port]",
772 /* Display the members stored in the queue */
774 void show_list_member(struct member *memberstruct)
778 switch (memberstruct->type)
801 printf("%s\n", memberstruct->name);
806 printf("%s:%s\n", s, memberstruct->name);
809 if (memberstruct->type == M_LIST)
810 printf("LIST:%s\n", memberstruct->name);
811 else if (memberstruct->type == M_KERBEROS)
812 printf("KERBEROS:%s\n", memberstruct->name);
813 else if (memberstruct->type == M_STRING &&
814 !strchr(memberstruct->name, '@'))
815 printf("STRING:%s\n", memberstruct->name);
817 printf("%s\n", memberstruct->name);
822 /* Show the retrieved information about a list */
824 int show_list_info(int argc, char **argv, void *hint)
826 printf("List: %s\n", argv[0]);
827 printf("Description: %s\n", argv[9]);
828 printf("Flags: %s, %s, and %s\n",
829 atoi(argv[1]) ? "active" : "inactive",
830 atoi(argv[2]) ? "public" : "private",
831 atoi(argv[3]) ? "hidden" : "visible");
832 printf("%s is %sa maillist and is %sa group", argv[0],
833 atoi(argv[4]) ? "" : "not ",
834 atoi(argv[5]) ? "" : "not ");
836 printf(" with GID %d\n", atoi(argv[6]));
839 printf("Owner: %s %s\n", argv[7], argv[8]);
840 printf("Last modified by %s with %s on %s\n", argv[11], argv[12], argv[10]);
845 /* Copy retrieved information about a list into a new argv */
847 int save_list_info(int argc, char **argv, void *hint)
851 for (argc = 0; argc < 10; argc++)
852 nargv[argc + 1] = strdup(argv[argc]);
856 /* Show the retrieve list member count */
858 int show_list_count(int argc, char **argv, void *hint)
860 printf("Members: %s\n", argv[0]);
865 /* Recursively find all of the members of listname, and then display them */
867 void recursive_display_list_members(void)
869 int status, count, savecount;
870 struct save_queue *lists, *members;
871 struct member *m, *m1, *data;
874 members = sq_create();
875 m = malloc(sizeof(struct member));
878 sq_save_data(lists, m);
880 while (sq_get_data(lists, &m))
882 sq_destroy(memberlist);
883 memberlist = sq_create();
884 status = mr_query("get_members_of_list", 1, &(m->name),
885 get_list_members, memberlist);
887 com_err(whoami, status, "while getting members of list %s", m->name);
888 while (sq_get_data(memberlist, &m1))
890 if (m1->type == M_LIST)
891 unique_add_member(lists, m1);
893 unique_add_member(members, m1);
896 savecount = count = sq_count_elts(members);
897 data = malloc(count * sizeof(struct member));
899 while (sq_get_data(members, &m))
900 memcpy(&data[count++], m, sizeof(struct member));
901 qsort(data, count, sizeof(struct member), membercmp);
902 for (count = 0; count < savecount; count++)
903 show_list_member(&data[count]);
907 /* add a struct member to a queue if that member isn't already there. */
909 void unique_add_member(struct save_queue *q, struct member *m)
911 struct save_queue *qp;
913 for (qp = q->q_next; qp != q; qp = qp->q_next)
915 if (!membercmp(qp->q_data, m))
922 /* Collect the retrieved members of the list */
924 int get_list_members(int argc, char **argv, void *sq)
926 struct save_queue *q = sq;
929 m = malloc(sizeof(struct member));
942 m->type = M_KERBEROS;
945 m->name = strdup(argv[1]);
951 /* Open file, parse members from file, and put them on the specified queue */
952 void get_members_from_file(char *filename, struct save_queue *queue)
956 struct member *memberstruct;
958 if (!strcmp(filename, "-"))
962 in = fopen(filename, "r");
965 com_err(whoami, errno, "while opening %s for input", filename);
970 while (fgets(buf, BUFSIZ, in))
972 if ((memberstruct = parse_member(buf)))
973 sq_save_data(queue, memberstruct);
977 com_err(whoami, errno, "while reading from %s", filename);
983 /* Collect the possible expansions of the alias MAILHUB */
985 int collect(int argc, char **argv, void *l)
990 for (i = 0; (*list)[i]; i++)
992 *list = realloc(*list, (i + 2) * sizeof(char *));
993 (*list)[i] = strdup(argv[2]);
994 (*list)[i + 1] = NULL;
999 /* Parse a line of input, fetching a member. NULL is returned if a member
1000 * is not found. ';' is a comment character.
1003 struct member *parse_member(char *s)
1008 while (*s && isspace(*s))
1011 while (*p && *p != '\n' && *p != ';')
1013 if (isprint(*p) && !isspace(*p))
1020 if (p == s || strlen(s) == 0)
1023 if (!(m = malloc(sizeof(struct member))))
1026 if ((p = strchr(s, ':')))
1030 if (!strcasecmp("user", s))
1032 else if (!strcasecmp("list", s))
1034 else if (!strcasecmp("string", s))
1036 else if (!strcasecmp("kerberos", s))
1037 m->type = M_KERBEROS;
1044 m->name = strdup(m->name);
1048 m->name = strdup(s);
1056 * This routine two compares members by the following rules:
1057 * 1. A USER is less than a LIST
1058 * 2. A LIST is less than a STRING
1059 * 3. If two members are of the same type, the one alphabetically first
1060 * is less than the other
1061 * It returs < 0 if the first member is less, 0 if they are identical, and
1062 * > 0 if the second member is less (the first member is greater).
1065 int membercmp(const void *mem1, const void *mem2)
1067 const struct member *m1 = mem1, *m2 = mem2;
1069 if (m1->type == M_ANY || m2->type == M_ANY || (m1->type == m2->type))
1070 return strcmp(m1->name, m2->name);
1072 return m1->type - m2->type;
1076 int sq_count_elts(struct save_queue *q)
1082 while (sq_get_data(q, &foo))