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>
30 /* It is important to membercmp that M_USER < M_LIST < M_STRING */
37 /* argument parsing macro */
38 #define argis(a, b) (!strcmp(*arg + 1, a) || !strcmp(*arg + 1, b))
40 /* flags from command line */
41 int infoflg, verbose, syncflg, memberflg, recursflg, noauth;
42 int showusers, showstrings, showkerberos, showlists;
43 int createflag, setinfo, active, public, hidden, maillist, grouplist;
47 /* various member lists */
48 struct save_queue *addlist, *dellist, *memberlist, *synclist;
50 char *listname, *whoami;
52 void usage(char **argv);
53 void show_list_member(struct member *memberstruct);
54 int show_list_info(int argc, char **argv, void *hint);
55 int save_list_info(int argc, char **argv, void *hint);
56 int show_list_count(int argc, char **argv, void *hint);
57 void recursive_display_list_members(void);
58 void unique_add_member(struct save_queue *q, struct member *m);
59 int get_list_members(int argc, char **argv, void *sq);
60 void get_members_from_file(char *filename, struct save_queue *queue);
61 int collect(int argc, char **argv, void *l);
62 struct member *parse_member(char *s);
63 int membercmp(const void *mem1, const void *mem2);
64 int sq_count_elts(struct save_queue *q);
66 int main(int argc, char **argv)
71 struct member *memberstruct;
72 char *server = NULL, *p;
74 /* clear all flags & lists */
75 infoflg = verbose = syncflg = memberflg = recursflg = 0;
76 noauth = showusers = showstrings = showkerberos = showlists = 0;
77 createflag = setinfo = 0;
78 active = public = hidden = maillist = grouplist = -1;
79 listname = newname = desc = NULL;
81 addlist = sq_create();
82 dellist = sq_create();
83 memberlist = sq_create();
84 synclist = sq_create();
89 /* parse args, building addlist, dellist, & synclist */
90 while (++arg - argv < argc)
94 if (argis("m", "members"))
96 else if (argis("u", "users"))
98 else if (argis("s", "strings"))
100 else if (argis("l", "lists"))
102 else if (argis("k", "kerberos"))
104 else if (argis("i", "info"))
106 else if (argis("n", "noauth"))
108 else if (argis("v", "verbose"))
110 else if (argis("r", "recursive"))
112 else if (argis("S", "server") || argis("db", "database"))
114 if (arg - argv < argc - 1)
122 else if (argis("a", "add"))
124 if (arg - argv < argc - 1)
127 if ((memberstruct = parse_member(*arg)))
128 sq_save_data(addlist, memberstruct);
133 else if (argis("al", "addlist"))
135 if (arg - argv < argc - 1)
138 get_members_from_file(*arg, addlist);
143 else if (argis("d", "delete"))
145 if (arg - argv < argc - 1)
148 if ((memberstruct = parse_member(*arg)))
149 sq_save_data(dellist, memberstruct);
154 else if (argis("dl", "deletelist"))
156 if (arg - argv < argc - 1)
159 get_members_from_file(*arg, dellist);
164 else if (argis("f", "file"))
166 if (arg - argv < argc - 1)
170 get_members_from_file(*arg, synclist);
175 else if (argis("C", "create"))
177 else if (argis("P", "public"))
182 else if (argis("NP", "private"))
187 else if (argis("A", "active"))
192 else if (argis("I", "inactive"))
197 else if (argis("V", "visible"))
202 else if (argis("H", "hidden"))
207 else if (argis("M", "mail"))
212 else if (argis("NM", "notmail"))
217 else if (argis("G", "group"))
222 else if (argis("NG", "notgroup"))
227 else if (argis("D", "desc"))
229 if (arg - argv < argc - 1)
238 else if (argis("O", "owner"))
240 if (arg - argv < argc - 1)
244 owner = parse_member(*arg);
249 else if (argis("R", "rename"))
251 if (arg - argv < argc - 1)
263 else if (listname == NULL)
268 if (listname == NULL)
271 /* if no other options specified, turn on list members flag */
272 if (!(infoflg || syncflg || createflag || setinfo ||
273 addlist->q_next != addlist || dellist->q_next != dellist))
276 /* If none of {users,strings,lists,kerberos} specified, turn them all on */
277 if (!(showusers || showstrings || showlists || showkerberos))
278 showusers = showstrings = showlists = showkerberos = 1;
281 status = mrcl_connect(server, "blanche", !noauth);
282 if (status == MRCL_AUTH_ERROR)
284 com_err(whoami, 0, "Try the -noauth flag if you don't "
285 "need authentication.");
290 /* check for username/listname clash */
291 if (createflag || (setinfo && newname && strcmp(newname, listname)))
293 status = mr_query("get_user_account_by_login", 1,
294 createflag ? &listname : &newname,
296 if (status != MR_NO_MATCH)
297 fprintf(stderr, "WARNING: A user by that name already exists.\n");
300 /* create if needed */
306 argv[1] = (active == 0) ? "0" : "1";
307 argv[2] = (public == 1) ? "1" : "0";
308 argv[3] = (hidden == 1) ? "1" : "0";
309 argv[4] = (maillist == 0) ? "0" : "1";
310 argv[5] = (grouplist == 1) ? "1" : "0";
311 argv[6] = UNIQUE_GID;
312 argv[9] = desc ? desc : "none";
316 argv[8] = owner->name;
322 status = mr_query("add_list", 10, argv, NULL, NULL);
323 if (owner->type != M_ANY || status != MR_USER)
328 status = mr_query("add_list", 10, argv, NULL, NULL);
332 argv[7] = "KERBEROS";
333 status = mr_query("add_list", 10, argv, NULL, NULL);
340 argv[8] = getenv("USER");
342 status = mr_query("add_list", 10, argv, NULL, NULL);
347 com_err(whoami, status, "while creating list.");
355 status = mr_query("get_list_info", 1, &listname,
356 save_list_info, argv);
359 com_err(whoami, status, "while getting list information");
367 argv[2] = active ? "1" : "0";
369 argv[3] = public ? "1" : "0";
371 argv[4] = hidden ? "1" : "0";
373 argv[5] = maillist ? "1" : "0";
375 argv[6] = grouplist ? "1" : "0";
381 argv[9] = owner->name;
387 status = mr_query("update_list", 11, argv, NULL, NULL);
388 if (owner->type != M_ANY || status != MR_USER)
393 status = mr_query("update_list", 11, argv, NULL, NULL);
397 argv[8] = "KERBEROS";
398 status = mr_query("update_list", 11, argv, NULL, NULL);
403 status = mr_query("update_list", 11, argv, NULL, NULL);
407 com_err(whoami, status, "while updating list.");
414 /* display list info if requested to */
417 status = mr_query("get_list_info", 1, &listname, show_list_info, NULL);
420 com_err(whoami, status, "while getting list information");
423 if (verbose && !memberflg)
425 status = mr_query("count_members_of_list", 1, &listname,
426 show_list_count, NULL);
429 com_err(whoami, status, "while getting list count");
435 /* if we're synchronizing to a file, we need to:
436 * get the current members of the list
437 * for each member of the sync file
438 * if they are on the list, remove them from the in-memory copy
439 * if they're not on the list, add them to add-list
440 * if anyone is left on the in-memory copy, put them on the delete-list
441 * lastly, reset memberlist so we can use it again later
445 status = mr_query("get_members_of_list", 1, &listname,
446 get_list_members, memberlist);
449 com_err(whoami, status, "getting members of list %s", listname);
452 while (sq_get_data(synclist, &memberstruct))
454 struct save_queue *q;
457 for (q = memberlist->q_next; q != memberlist; q = q->q_next)
459 if (membercmp(q->q_data, memberstruct) == 0)
461 q->q_prev->q_next = q->q_next;
462 q->q_next->q_prev = q->q_prev;
468 sq_save_data(addlist, memberstruct);
470 while (sq_get_data(memberlist, &memberstruct))
471 sq_save_data(dellist, memberstruct);
472 sq_destroy(memberlist);
473 memberlist = sq_create();
476 /* Process the add list */
477 while (sq_get_data(addlist, &memberstruct))
479 /* canonicalize string if necessary */
480 if (memberstruct->type != M_KERBEROS &&
481 (p = strchr(memberstruct->name, '@')))
483 char *host = canonicalize_hostname(strdup(++p));
484 static char **mailhubs = NULL;
493 mailhubs = malloc(sizeof(char *));
495 status = mr_query("get_alias", 3, argv, collect,
497 if (status != MR_SUCCESS && status != MR_NO_MATCH)
499 com_err(whoami, status,
500 " while reading list of MAILHUB servers");
504 for (i = 0; (p = mailhubs[i]); i++)
506 if (!strcasecmp(p, host))
508 host = strdup(memberstruct->name);
509 *(strchr(memberstruct->name, '@')) = 0;
510 if (memberstruct->type == M_STRING)
511 memberstruct->type = M_ANY;
512 fprintf(stderr, "Warning: \"%s\" converted to "
513 "\"%s\" because it is a local name.\n",
514 host, memberstruct->name);
520 /* now continue adding member */
521 membervec[0] = listname;
522 membervec[2] = memberstruct->name;
525 printf("Adding member ");
526 show_list_member(memberstruct);
528 switch (memberstruct->type)
532 membervec[1] = "USER";
533 status = mr_query("add_member_to_list", 3, membervec, NULL, NULL);
534 if (status == MR_SUCCESS)
536 else if (status != MR_USER || memberstruct->type != M_ANY)
538 com_err(whoami, status, "while adding member %s to %s",
539 memberstruct->name, listname);
544 membervec[1] = "LIST";
545 status = mr_query("add_member_to_list", 3, membervec,
547 if (status == MR_SUCCESS)
549 if (!strcmp(membervec[0], getenv("USER")))
551 fprintf(stderr, "\nWARNING: \"LIST:%s\" was just added "
552 "to list \"%s\".\n", membervec[2], membervec[0]);
553 fprintf(stderr, "If you meant to add yourself to the list "
554 "\"%s\", type:\n", membervec[2]);
555 fprintf(stderr, "\tblanche %s -d %s\t(to undo this)\n",
556 membervec[0], membervec[2]);
557 fprintf(stderr, "\tblanche %s -a %s\t(to add yourself to "
558 "that list)\n", membervec[2], membervec[0]);
562 else if (status != MR_LIST || memberstruct->type != M_ANY)
564 com_err(whoami, status, "while adding member %s to %s",
565 memberstruct->name, listname);
570 if (memberstruct->type == M_ANY &&
571 !strchr(memberstruct->name, '@') &&
572 !strchr(memberstruct->name, '!') &&
573 !strchr(memberstruct->name, '%'))
575 /* if user is trying to add something which isn't a
576 remote string, or a list, or a user, and didn't
577 explicitly specify `STRING:', it's probably a typo */
578 com_err(whoami, MR_NO_MATCH, "while adding member %s to %s",
579 memberstruct->name, listname);
584 membervec[1] = "STRING";
585 status = mr_query("add_member_to_list", 3, membervec,
587 if (status != MR_SUCCESS)
589 com_err(whoami, status, "while adding member %s to %s",
590 memberstruct->name, listname);
595 membervec[1] = "KERBEROS";
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);
607 /* Process the delete list */
608 while (sq_get_data(dellist, &memberstruct))
610 membervec[0] = listname;
611 membervec[2] = memberstruct->name;
614 printf("Deleting member ");
615 show_list_member(memberstruct);
617 switch (memberstruct->type)
621 membervec[1] = "USER";
622 status = mr_query("delete_member_from_list", 3, membervec,
624 if (status == MR_SUCCESS)
626 else if ((status != MR_USER && status != MR_NO_MATCH) ||
627 memberstruct->type != M_ANY)
629 com_err(whoami, status, "while deleting member %s from %s",
630 memberstruct->name, listname);
635 membervec[1] = "LIST";
636 status = mr_query("delete_member_from_list", 3, membervec,
638 if (status == MR_SUCCESS)
640 else if ((status != MR_LIST && status != MR_NO_MATCH) ||
641 memberstruct->type != M_ANY)
643 if (status == MR_PERM && memberstruct->type == M_ANY &&
644 !strcmp(membervec[2], getenv("USER")))
646 /* M_ANY means we've fallen through from the user
647 * case. The user is trying to remove himself from
648 * a list, but we got MR_USER or MR_NO_MATCH above,
649 * meaning he's not really on it, and we got MR_PERM
650 * when trying to remove LIST:$USER because he's not
651 * on the acl. That error is useless, so return
652 * MR_NO_MATCH instead. However, this will generate the
653 * wrong error if the user was trying to remove the list
654 * with his username from a list he doesn't administrate
655 * without explicitly specifying "list:".
657 status = MR_NO_MATCH;
659 com_err(whoami, status, "while deleting member %s from %s",
660 memberstruct->name, listname);
665 membervec[1] = "STRING";
666 status = mr_query("delete_member_from_list", 3, membervec,
668 if (status == MR_STRING && memberstruct->type == M_ANY)
670 com_err(whoami, 0, " Unable to find member %s to delete from %s",
671 memberstruct->name, listname);
673 if (!strcmp(membervec[0], getenv("USER")))
675 fprintf(stderr, "(If you were trying to remove yourself "
676 "from the list \"%s\",\n", membervec[2]);
677 fprintf(stderr, "the correct command is \"blanche %s -d "
678 "%s\".)\n", membervec[2], membervec[0]);
681 else if (status != MR_SUCCESS)
683 com_err(whoami, status, "while deleting member %s from %s",
684 memberstruct->name, listname);
689 membervec[1] = "KERBEROS";
690 status = mr_query("delete_member_from_list", 3, membervec,
692 if (status != MR_SUCCESS)
694 com_err(whoami, status, "while deleting member %s from %s",
695 memberstruct->name, listname);
701 /* Display the members of the list now, if requested */
705 recursive_display_list_members();
708 status = mr_query("get_members_of_list", 1, &listname,
709 get_list_members, memberlist);
711 com_err(whoami, status, "while getting members of list %s",
713 while (sq_get_data(memberlist, &memberstruct))
714 show_list_member(memberstruct);
720 exit(success ? 0 : 1);
723 void usage(char **argv)
725 fprintf(stderr, "Usage: %s listname [options]\n", argv[0]);
726 fprintf(stderr, "Options are\n");
727 fprintf(stderr, " %-39s%-39s\n", "-v | -verbose",
729 fprintf(stderr, " %-39s%-39s\n", "-m | -members",
730 "-R | -rename newname");
731 fprintf(stderr, " %-39s%-39s\n", "-u | -users",
733 fprintf(stderr, " %-39s%-39s\n", "-l | -lists",
735 fprintf(stderr, " %-39s%-39s\n", "-s | -strings",
737 fprintf(stderr, " %-39s%-39s\n", "-k | -kerberos",
739 fprintf(stderr, " %-39s%-39s\n", "-i | -info",
741 fprintf(stderr, " %-39s%-39s\n", "-r | -recursive",
743 fprintf(stderr, " %-39s%-39s\n", "-a | -add member",
745 fprintf(stderr, " %-39s%-39s\n", "-d | -delete member",
747 fprintf(stderr, " %-39s%-39s\n", "-al | -addlist filename",
749 fprintf(stderr, " %-39s%-39s\n", "-dl | -deletelist filename",
751 fprintf(stderr, " %-39s%-39s\n", "-f | -file filename",
752 "-D | -desc description");
753 fprintf(stderr, " %-39s%-39s\n", "-n | -noauth",
754 "-O | -owner owner");
755 fprintf(stderr, " %-39s%-39s\n", "-db | -database host[:port]",
761 /* Display the members stored in the queue */
763 void show_list_member(struct member *memberstruct)
767 switch (memberstruct->type)
790 printf("%s\n", memberstruct->name);
795 printf("%s:%s\n", s, memberstruct->name);
798 if (memberstruct->type == M_LIST)
799 printf("LIST:%s\n", memberstruct->name);
800 else if (memberstruct->type == M_KERBEROS)
801 printf("KERBEROS:%s\n", memberstruct->name);
802 else if (memberstruct->type == M_STRING &&
803 !strchr(memberstruct->name, '@'))
804 printf("STRING:%s\n", memberstruct->name);
806 printf("%s\n", memberstruct->name);
811 /* Show the retrieved information about a list */
813 int show_list_info(int argc, char **argv, void *hint)
815 printf("List: %s\n", argv[0]);
816 printf("Description: %s\n", argv[9]);
817 printf("Flags: %s, %s, and %s\n",
818 atoi(argv[1]) ? "active" : "inactive",
819 atoi(argv[2]) ? "public" : "private",
820 atoi(argv[3]) ? "hidden" : "visible");
821 printf("%s is %sa maillist and is %sa group", argv[0],
822 atoi(argv[4]) ? "" : "not ",
823 atoi(argv[5]) ? "" : "not ");
825 printf(" with GID %d\n", atoi(argv[6]));
828 printf("Owner: %s %s\n", argv[7], argv[8]);
829 printf("Last modified by %s with %s on %s\n", argv[11], argv[12], argv[10]);
834 /* Copy retrieved information about a list into a new argv */
836 int save_list_info(int argc, char **argv, void *hint)
840 for (argc = 0; argc < 10; argc++)
841 nargv[argc + 1] = strdup(argv[argc]);
845 /* Show the retrieve list member count */
847 int show_list_count(int argc, char **argv, void *hint)
849 printf("Members: %s\n", argv[0]);
854 /* Recursively find all of the members of listname, and then display them */
856 void recursive_display_list_members(void)
858 int status, count, savecount;
859 struct save_queue *lists, *members;
860 struct member *m, *m1, *data;
863 members = sq_create();
864 m = malloc(sizeof(struct member));
867 sq_save_data(lists, m);
869 while (sq_get_data(lists, &m))
871 sq_destroy(memberlist);
872 memberlist = sq_create();
873 status = mr_query("get_members_of_list", 1, &(m->name),
874 get_list_members, memberlist);
876 com_err(whoami, status, "while getting members of list %s", m->name);
877 while (sq_get_data(memberlist, &m1))
879 if (m1->type == M_LIST)
880 unique_add_member(lists, m1);
882 unique_add_member(members, m1);
885 savecount = count = sq_count_elts(members);
886 data = malloc(count * sizeof(struct member));
888 while (sq_get_data(members, &m))
889 memcpy(&data[count++], m, sizeof(struct member));
890 qsort(data, count, sizeof(struct member), membercmp);
891 for (count = 0; count < savecount; count++)
892 show_list_member(&data[count]);
896 /* add a struct member to a queue if that member isn't already there. */
898 void unique_add_member(struct save_queue *q, struct member *m)
900 struct save_queue *qp;
902 for (qp = q->q_next; qp != q; qp = qp->q_next)
904 if (!membercmp(qp->q_data, m))
911 /* Collect the retrieved members of the list */
913 int get_list_members(int argc, char **argv, void *sq)
915 struct save_queue *q = sq;
918 m = malloc(sizeof(struct member));
931 m->type = M_KERBEROS;
934 m->name = strdup(argv[1]);
940 /* Open file, parse members from file, and put them on the specified queue */
941 void get_members_from_file(char *filename, struct save_queue *queue)
945 struct member *memberstruct;
947 if (!strcmp(filename, "-"))
951 in = fopen(filename, "r");
954 com_err(whoami, errno, "while opening %s for input", filename);
959 while (fgets(buf, BUFSIZ, in))
961 if ((memberstruct = parse_member(buf)))
962 sq_save_data(queue, memberstruct);
966 com_err(whoami, errno, "while reading from %s", filename);
972 /* Collect the possible expansions of the alias MAILHUB */
974 int collect(int argc, char **argv, void *l)
979 for (i = 0; (*list)[i]; i++)
981 *list = realloc(*list, (i + 2) * sizeof(char *));
982 (*list)[i] = strdup(argv[2]);
983 (*list)[i + 1] = NULL;
988 /* Parse a line of input, fetching a member. NULL is returned if a member
989 * is not found. ';' is a comment character.
992 struct member *parse_member(char *s)
997 while (*s && isspace(*s))
1000 while (*p && *p != '\n' && *p != ';')
1002 if (isprint(*p) && !isspace(*p))
1009 if (p == s || strlen(s) == 0)
1012 if (!(m = malloc(sizeof(struct member))))
1015 if ((p = strchr(s, ':')))
1019 if (!strcasecmp("user", s))
1021 else if (!strcasecmp("list", s))
1023 else if (!strcasecmp("string", s))
1025 else if (!strcasecmp("kerberos", s))
1026 m->type = M_KERBEROS;
1033 m->name = strdup(m->name);
1037 m->name = strdup(s);
1045 * This routine two compares members by the following rules:
1046 * 1. A USER is less than a LIST
1047 * 2. A LIST is less than a STRING
1048 * 3. If two members are of the same type, the one alphabetically first
1049 * is less than the other
1050 * It returs < 0 if the first member is less, 0 if they are identical, and
1051 * > 0 if the second member is less (the first member is greater).
1054 int membercmp(const void *mem1, const void *mem2)
1056 const struct member *m1 = mem1, *m2 = mem2;
1058 if (m1->type == M_ANY || m2->type == M_ANY || (m1->type == m2->type))
1059 return strcmp(m1->name, m2->name);
1061 return m1->type - m2->type;
1065 int sq_count_elts(struct save_queue *q)
1071 while (sq_get_data(q, &foo))