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, showtags;
43 int createflag, setinfo, active, public, hidden, maillist, grouplist;
47 /* various member lists */
48 struct save_queue *addlist, *dellist, *memberlist, *synclist, *taglist;
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();
85 taglist = sq_create();
90 /* parse args, building addlist, dellist, & synclist */
91 while (++arg - argv < argc)
95 if (argis("m", "members"))
97 else if (argis("u", "users"))
99 else if (argis("s", "strings"))
101 else if (argis("l", "lists"))
103 else if (argis("k", "kerberos"))
105 else if (argis("t", "tags"))
107 else if (argis("i", "info"))
109 else if (argis("n", "noauth"))
111 else if (argis("v", "verbose"))
113 else if (argis("r", "recursive"))
115 else if (argis("S", "server") || argis("db", "database"))
117 if (arg - argv < argc - 1)
125 else if (argis("a", "add"))
127 if (arg - argv < argc - 1)
130 if ((memberstruct = parse_member(*arg)))
131 sq_save_data(addlist, memberstruct);
136 else if (argis("at", "addtagged"))
138 if (arg - argv < argc - 2)
141 if ((memberstruct = parse_member(*arg)))
142 sq_save_data(addlist, memberstruct);
143 memberstruct->tag = *++arg;
148 else if (argis("al", "addlist"))
150 if (arg - argv < argc - 1)
153 get_members_from_file(*arg, addlist);
158 else if (argis("d", "delete"))
160 if (arg - argv < argc - 1)
163 if ((memberstruct = parse_member(*arg)))
164 sq_save_data(dellist, memberstruct);
169 else if (argis("dl", "deletelist"))
171 if (arg - argv < argc - 1)
174 get_members_from_file(*arg, dellist);
179 else if (argis("f", "file"))
181 if (arg - argv < argc - 1)
185 get_members_from_file(*arg, synclist);
190 else if (argis("ct", "changetag"))
192 if (arg - argv < argc - 2)
195 if ((memberstruct = parse_member(*arg)))
196 sq_save_data(taglist, memberstruct);
197 memberstruct->tag = *++arg;
202 else if (argis("C", "create"))
204 else if (argis("P", "public"))
209 else if (argis("NP", "private"))
214 else if (argis("A", "active"))
219 else if (argis("I", "inactive"))
224 else if (argis("V", "visible"))
229 else if (argis("H", "hidden"))
234 else if (argis("M", "mail"))
239 else if (argis("NM", "notmail"))
244 else if (argis("G", "group"))
249 else if (argis("NG", "notgroup"))
254 else if (argis("D", "desc"))
256 if (arg - argv < argc - 1)
265 else if (argis("O", "owner"))
267 if (arg - argv < argc - 1)
271 owner = parse_member(*arg);
276 else if (argis("R", "rename"))
278 if (arg - argv < argc - 1)
290 else if (listname == NULL)
295 if (listname == NULL)
298 /* if no other options specified, turn on list members flag */
299 if (!(infoflg || syncflg || createflag || setinfo ||
300 addlist->q_next != addlist || dellist->q_next != dellist ||
301 taglist->q_next != taglist))
304 /* If none of {users,strings,lists,kerberos} specified, turn them all on */
305 if (!(showusers || showstrings || showlists || showkerberos))
306 showusers = showstrings = showlists = showkerberos = 1;
309 status = mrcl_connect(server, "blanche", 2, !noauth);
310 if (status == MRCL_AUTH_ERROR)
312 com_err(whoami, 0, "Try the -noauth flag if you don't "
313 "need authentication.");
318 /* check for username/listname clash */
319 if (createflag || (setinfo && newname && strcmp(newname, listname)))
321 status = mr_query("get_user_account_by_login", 1,
322 createflag ? &listname : &newname,
324 if (status != MR_NO_MATCH)
325 fprintf(stderr, "WARNING: A user by that name already exists.\n");
328 /* create if needed */
334 argv[1] = (active == 0) ? "0" : "1";
335 argv[2] = (public == 1) ? "1" : "0";
336 argv[3] = (hidden == 1) ? "1" : "0";
337 argv[4] = (maillist == 0) ? "0" : "1";
338 argv[5] = (grouplist == 1) ? "1" : "0";
339 argv[6] = UNIQUE_GID;
340 argv[9] = desc ? desc : "none";
344 argv[8] = owner->name;
350 status = mr_query("add_list", 10, argv, NULL, NULL);
351 if (owner->type != M_ANY || status != MR_USER)
356 status = mr_query("add_list", 10, argv, NULL, NULL);
360 argv[7] = "KERBEROS";
361 status = mr_query("add_list", 10, argv, NULL, NULL);
368 argv[8] = getenv("USER");
370 status = mr_query("add_list", 10, argv, NULL, NULL);
375 com_err(whoami, status, "while creating list.");
383 status = mr_query("get_list_info", 1, &listname,
384 save_list_info, argv);
387 com_err(whoami, status, "while getting list information");
395 argv[2] = active ? "1" : "0";
397 argv[3] = public ? "1" : "0";
399 argv[4] = hidden ? "1" : "0";
401 argv[5] = maillist ? "1" : "0";
403 argv[6] = grouplist ? "1" : "0";
409 argv[9] = owner->name;
415 status = mr_query("update_list", 11, argv, NULL, NULL);
416 if (owner->type != M_ANY || status != MR_USER)
421 status = mr_query("update_list", 11, argv, NULL, NULL);
425 argv[8] = "KERBEROS";
426 status = mr_query("update_list", 11, argv, NULL, NULL);
431 status = mr_query("update_list", 11, argv, NULL, NULL);
435 com_err(whoami, status, "while updating list.");
442 /* display list info if requested to */
445 status = mr_query("get_list_info", 1, &listname, show_list_info, NULL);
448 com_err(whoami, status, "while getting list information");
451 if (verbose && !memberflg)
453 status = mr_query("count_members_of_list", 1, &listname,
454 show_list_count, NULL);
457 com_err(whoami, status, "while getting list count");
463 /* if we're synchronizing to a file, we need to:
464 * get the current members of the list
465 * for each member of the sync file
466 * if they are on the list, remove them from the in-memory copy
467 * if they're not on the list, add them to add-list
468 * if anyone is left on the in-memory copy, put them on the delete-list
469 * lastly, reset memberlist so we can use it again later
473 status = mr_query("get_members_of_list", 1, &listname,
474 get_list_members, memberlist);
477 com_err(whoami, status, "getting members of list %s", listname);
480 while (sq_get_data(synclist, &memberstruct))
482 struct save_queue *q;
485 for (q = memberlist->q_next; q != memberlist; q = q->q_next)
487 if (membercmp(q->q_data, memberstruct) == 0)
489 q->q_prev->q_next = q->q_next;
490 q->q_next->q_prev = q->q_prev;
496 sq_save_data(addlist, memberstruct);
498 while (sq_get_data(memberlist, &memberstruct))
499 sq_save_data(dellist, memberstruct);
500 sq_destroy(memberlist);
501 memberlist = sq_create();
504 /* Process the add list */
505 while (sq_get_data(addlist, &memberstruct))
507 /* canonicalize string if necessary */
508 if (memberstruct->type != M_KERBEROS &&
509 (p = strchr(memberstruct->name, '@')))
511 char *host = canonicalize_hostname(strdup(++p));
512 static char **mailhubs = NULL;
521 mailhubs = malloc(sizeof(char *));
523 status = mr_query("get_alias", 3, argv, collect,
525 if (status != MR_SUCCESS && status != MR_NO_MATCH)
527 com_err(whoami, status,
528 " while reading list of MAILHUB servers");
532 for (i = 0; (p = mailhubs[i]); i++)
534 if (!strcasecmp(p, host))
536 host = strdup(memberstruct->name);
537 *(strchr(memberstruct->name, '@')) = 0;
538 if (memberstruct->type == M_STRING)
539 memberstruct->type = M_ANY;
540 fprintf(stderr, "Warning: \"%s\" converted to "
541 "\"%s\" because it is a local name.\n",
542 host, memberstruct->name);
548 /* now continue adding member */
549 membervec[0] = listname;
550 membervec[2] = memberstruct->name;
551 membervec[3] = memberstruct->tag;
554 printf("Adding member ");
555 show_list_member(memberstruct);
557 switch (memberstruct->type)
561 membervec[1] = "USER";
562 status = mr_query("add_tagged_member_to_list", 4, membervec,
564 if (status == MR_SUCCESS)
566 else if (status != MR_USER || memberstruct->type != M_ANY)
568 com_err(whoami, status, "while adding member %s to %s",
569 memberstruct->name, listname);
574 membervec[1] = "LIST";
575 status = mr_query("add_tagged_member_to_list", 4, membervec,
577 if (status == MR_SUCCESS)
579 if (!strcmp(membervec[0], getenv("USER")))
581 fprintf(stderr, "\nWARNING: \"LIST:%s\" was just added "
582 "to list \"%s\".\n", membervec[2], membervec[0]);
583 fprintf(stderr, "If you meant to add yourself to the list "
584 "\"%s\", type:\n", membervec[2]);
585 fprintf(stderr, "\tblanche %s -d %s\t(to undo this)\n",
586 membervec[0], membervec[2]);
587 fprintf(stderr, "\tblanche %s -a %s\t(to add yourself to "
588 "that list)\n", membervec[2], membervec[0]);
592 else if (status != MR_LIST || memberstruct->type != M_ANY)
594 com_err(whoami, status, "while adding member %s to %s",
595 memberstruct->name, listname);
600 if (memberstruct->type == M_ANY &&
601 !strchr(memberstruct->name, '@') &&
602 !strchr(memberstruct->name, '!') &&
603 !strchr(memberstruct->name, '%'))
605 /* if user is trying to add something which isn't a
606 remote string, or a list, or a user, and didn't
607 explicitly specify `STRING:', it's probably a typo */
608 com_err(whoami, MR_NO_MATCH, "while adding member %s to %s",
609 memberstruct->name, listname);
614 membervec[1] = "STRING";
615 status = mr_query("add_tagged_member_to_list", 4, membervec,
617 if (status != MR_SUCCESS)
619 com_err(whoami, status, "while adding member %s to %s",
620 memberstruct->name, listname);
625 membervec[1] = "KERBEROS";
626 status = mr_query("add_tagged_member_to_list", 4, membervec,
628 if (status != MR_SUCCESS)
630 com_err(whoami, status, "while adding member %s to %s",
631 memberstruct->name, listname);
637 /* Process the delete list */
638 while (sq_get_data(dellist, &memberstruct))
640 membervec[0] = listname;
641 membervec[2] = memberstruct->name;
644 printf("Deleting member ");
645 show_list_member(memberstruct);
647 switch (memberstruct->type)
651 membervec[1] = "USER";
652 status = mr_query("delete_member_from_list", 3, membervec,
654 if (status == MR_SUCCESS)
656 else if ((status != MR_USER && status != MR_NO_MATCH) ||
657 memberstruct->type != M_ANY)
659 com_err(whoami, status, "while deleting member %s from %s",
660 memberstruct->name, listname);
665 membervec[1] = "LIST";
666 status = mr_query("delete_member_from_list", 3, membervec,
668 if (status == MR_SUCCESS)
670 else if ((status != MR_LIST && status != MR_NO_MATCH) ||
671 memberstruct->type != M_ANY)
673 if (status == MR_PERM && memberstruct->type == M_ANY &&
674 !strcmp(membervec[2], getenv("USER")))
676 /* M_ANY means we've fallen through from the user
677 * case. The user is trying to remove himself from
678 * a list, but we got MR_USER or MR_NO_MATCH above,
679 * meaning he's not really on it, and we got MR_PERM
680 * when trying to remove LIST:$USER because he's not
681 * on the acl. That error is useless, so return
682 * MR_NO_MATCH instead. However, this will generate the
683 * wrong error if the user was trying to remove the list
684 * with his username from a list he doesn't administrate
685 * without explicitly specifying "list:".
687 status = MR_NO_MATCH;
689 com_err(whoami, status, "while deleting member %s from %s",
690 memberstruct->name, listname);
695 membervec[1] = "STRING";
696 status = mr_query("delete_member_from_list", 3, membervec,
698 if (status == MR_STRING && memberstruct->type == M_ANY)
700 com_err(whoami, 0, " Unable to find member %s to delete from %s",
701 memberstruct->name, listname);
703 if (!strcmp(membervec[0], getenv("USER")))
705 fprintf(stderr, "(If you were trying to remove yourself "
706 "from the list \"%s\",\n", membervec[2]);
707 fprintf(stderr, "the correct command is \"blanche %s -d "
708 "%s\".)\n", membervec[2], membervec[0]);
711 else if (status != MR_SUCCESS)
713 com_err(whoami, status, "while deleting member %s from %s",
714 memberstruct->name, listname);
719 membervec[1] = "KERBEROS";
720 status = mr_query("delete_member_from_list", 3, membervec,
722 if (status != MR_SUCCESS)
724 com_err(whoami, status, "while deleting member %s from %s",
725 memberstruct->name, listname);
731 /* Process the tag list */
732 while (sq_get_data(taglist, &memberstruct))
734 membervec[0] = listname;
735 membervec[2] = memberstruct->name;
736 membervec[3] = memberstruct->tag;
739 printf("Tagging member ");
740 show_list_member(memberstruct);
742 switch (memberstruct->type)
746 membervec[1] = "USER";
747 status = mr_query("tag_member_of_list", 4, membervec,
749 if (status == MR_SUCCESS)
751 else if ((status != MR_USER && status != MR_NO_MATCH) ||
752 memberstruct->type != M_ANY)
754 com_err(whoami, status, "while changing tag on member %s of %s",
755 memberstruct->name, listname);
760 membervec[1] = "LIST";
761 status = mr_query("tag_member_of_list", 4, membervec,
763 if (status == MR_SUCCESS)
765 else if ((status != MR_LIST && status != MR_NO_MATCH) ||
766 memberstruct->type != M_ANY)
768 com_err(whoami, status, "while changing tag on member %s of %s",
769 memberstruct->name, listname);
774 membervec[1] = "STRING";
775 status = mr_query("tag_member_of_list", 4, membervec,
777 if (status == MR_STRING && memberstruct->type == M_ANY)
779 com_err(whoami, 0, " Unable to find member %s on list %s",
780 memberstruct->name, listname);
783 else if (status != MR_SUCCESS)
785 com_err(whoami, status, "while retagging member %s on %s",
786 memberstruct->name, listname);
791 membervec[1] = "KERBEROS";
792 status = mr_query("tag_member_of_list", 4, membervec,
794 if (status != MR_SUCCESS)
796 com_err(whoami, status, "while changing tag on member %s of %s",
797 memberstruct->name, listname);
804 /* Display the members of the list now, if requested */
808 recursive_display_list_members();
811 status = mr_query(showtags ? "get_tagged_members_of_list" :
812 "get_members_of_list", 1, &listname,
813 get_list_members, memberlist);
815 com_err(whoami, status, "while getting members of list %s",
817 while (sq_get_data(memberlist, &memberstruct))
818 show_list_member(memberstruct);
824 exit(success ? 0 : 1);
827 void usage(char **argv)
829 #define USAGE_OPTIONS_FORMAT " %-39s%s\n"
830 fprintf(stderr, "Usage: %s listname [options]\n", argv[0]);
831 fprintf(stderr, "Options are\n");
832 fprintf(stderr, USAGE_OPTIONS_FORMAT, "-v | -verbose",
834 fprintf(stderr, USAGE_OPTIONS_FORMAT, "-m | -members",
835 "-R | -rename newname");
836 fprintf(stderr, USAGE_OPTIONS_FORMAT, "-u | -users",
838 fprintf(stderr, USAGE_OPTIONS_FORMAT, "-l | -lists",
840 fprintf(stderr, USAGE_OPTIONS_FORMAT, "-s | -strings",
842 fprintf(stderr, USAGE_OPTIONS_FORMAT, "-k | -kerberos",
844 fprintf(stderr, USAGE_OPTIONS_FORMAT, "-i | -info",
846 fprintf(stderr, USAGE_OPTIONS_FORMAT, "-r | -recursive",
848 fprintf(stderr, USAGE_OPTIONS_FORMAT, "-a | -add member",
850 fprintf(stderr, USAGE_OPTIONS_FORMAT, "-d | -delete member",
852 fprintf(stderr, USAGE_OPTIONS_FORMAT, "-al | -addlist filename",
854 fprintf(stderr, USAGE_OPTIONS_FORMAT, "-dl | -deletelist filename",
856 fprintf(stderr, USAGE_OPTIONS_FORMAT, "-f | -file filename",
857 "-D | -desc description");
858 fprintf(stderr, USAGE_OPTIONS_FORMAT, "-at | -addtagged member tag",
859 "-O | -owner owner");
860 fprintf(stderr, USAGE_OPTIONS_FORMAT, "-ct | -changetag member tag",
862 fprintf(stderr, USAGE_OPTIONS_FORMAT, "-n | -noauth",
863 "-db | -database host[:port]");
868 /* Display the members stored in the queue */
870 void show_list_member(struct member *memberstruct)
874 switch (memberstruct->type)
897 printf("%s\n", memberstruct->name);
902 printf("%s:%s", s, memberstruct->name);
905 if (memberstruct->type == M_LIST)
906 printf("LIST:%s", memberstruct->name);
907 else if (memberstruct->type == M_KERBEROS)
908 printf("KERBEROS:%s", memberstruct->name);
909 else if (memberstruct->type == M_STRING &&
910 !strchr(memberstruct->name, '@'))
911 printf("STRING:%s", memberstruct->name);
913 printf("%s", memberstruct->name);
915 if (showtags && *(memberstruct->tag))
916 printf(" (%s)\n", memberstruct->tag);
922 /* Show the retrieved information about a list */
924 int show_list_info(int argc, char **argv, void *hint)
926 printf("List: %s\n", argv[0]);
927 printf("Description: %s\n", argv[9]);
928 printf("Flags: %s, %s, and %s\n",
929 atoi(argv[1]) ? "active" : "inactive",
930 atoi(argv[2]) ? "public" : "private",
931 atoi(argv[3]) ? "hidden" : "visible");
932 printf("%s is %sa maillist and is %sa group", argv[0],
933 atoi(argv[4]) ? "" : "not ",
934 atoi(argv[5]) ? "" : "not ");
936 printf(" with GID %d\n", atoi(argv[6]));
939 printf("Owner: %s %s\n", argv[7], argv[8]);
940 printf("Last modified by %s with %s on %s\n", argv[11], argv[12], argv[10]);
945 /* Copy retrieved information about a list into a new argv */
947 int save_list_info(int argc, char **argv, void *hint)
951 for (argc = 0; argc < 10; argc++)
952 nargv[argc + 1] = strdup(argv[argc]);
956 /* Show the retrieve list member count */
958 int show_list_count(int argc, char **argv, void *hint)
960 printf("Members: %s\n", argv[0]);
965 /* Recursively find all of the members of listname, and then display them */
967 void recursive_display_list_members(void)
969 int status, count, savecount;
970 struct save_queue *lists, *members;
971 struct member *m, *m1, *data;
974 members = sq_create();
975 m = malloc(sizeof(struct member));
978 sq_save_data(lists, m);
980 while (sq_get_data(lists, &m))
982 sq_destroy(memberlist);
983 memberlist = sq_create();
984 status = mr_query("get_members_of_list", 1, &(m->name),
985 get_list_members, memberlist);
987 com_err(whoami, status, "while getting members of list %s", m->name);
988 while (sq_get_data(memberlist, &m1))
990 if (m1->type == M_LIST)
991 unique_add_member(lists, m1);
993 unique_add_member(members, m1);
996 savecount = count = sq_count_elts(members);
997 data = malloc(count * sizeof(struct member));
999 while (sq_get_data(members, &m))
1000 memcpy(&data[count++], m, sizeof(struct member));
1001 qsort(data, count, sizeof(struct member), membercmp);
1002 for (count = 0; count < savecount; count++)
1003 show_list_member(&data[count]);
1007 /* add a struct member to a queue if that member isn't already there. */
1009 void unique_add_member(struct save_queue *q, struct member *m)
1011 struct save_queue *qp;
1013 for (qp = q->q_next; qp != q; qp = qp->q_next)
1015 if (!membercmp(qp->q_data, m))
1022 /* Collect the retrieved members of the list */
1024 int get_list_members(int argc, char **argv, void *sq)
1026 struct save_queue *q = sq;
1029 m = malloc(sizeof(struct member));
1042 m->type = M_KERBEROS;
1045 m->name = strdup(argv[1]);
1047 m->tag = strdup(argv[2]);
1049 m->tag = strdup("");
1055 /* Open file, parse members from file, and put them on the specified queue */
1056 void get_members_from_file(char *filename, struct save_queue *queue)
1060 struct member *memberstruct;
1062 if (!strcmp(filename, "-"))
1066 in = fopen(filename, "r");
1069 com_err(whoami, errno, "while opening %s for input", filename);
1074 while (fgets(buf, BUFSIZ, in))
1076 if ((memberstruct = parse_member(buf)))
1077 sq_save_data(queue, memberstruct);
1081 com_err(whoami, errno, "while reading from %s", filename);
1087 /* Collect the possible expansions of the alias MAILHUB */
1089 int collect(int argc, char **argv, void *l)
1094 for (i = 0; (*list)[i]; i++)
1096 *list = realloc(*list, (i + 2) * sizeof(char *));
1097 (*list)[i] = strdup(argv[2]);
1098 (*list)[i + 1] = NULL;
1103 /* Parse a line of input, fetching a member. NULL is returned if a member
1104 * is not found. ';' is a comment character.
1107 struct member *parse_member(char *s)
1112 while (*s && isspace(*s))
1115 while (*p && *p != '\n' && *p != ';')
1117 if (isprint(*p) && !isspace(*p))
1124 if (p == s || strlen(s) == 0)
1127 if (!(m = malloc(sizeof(struct member))))
1129 m->tag = strdup("");
1131 if ((p = strchr(s, ':')))
1135 if (!strcasecmp("user", s))
1137 else if (!strcasecmp("list", s))
1139 else if (!strcasecmp("string", s))
1141 else if (!strcasecmp("kerberos", s))
1142 m->type = M_KERBEROS;
1149 m->name = strdup(m->name);
1153 m->name = strdup(s);
1161 * This routine two compares members by the following rules:
1162 * 1. A USER is less than a LIST
1163 * 2. A LIST is less than a STRING
1164 * 3. If two members are of the same type, the one alphabetically first
1165 * is less than the other
1166 * It returs < 0 if the first member is less, 0 if they are identical, and
1167 * > 0 if the second member is less (the first member is greater).
1170 int membercmp(const void *mem1, const void *mem2)
1172 const struct member *m1 = mem1, *m2 = mem2;
1174 if (m1->type == M_ANY || m2->type == M_ANY || (m1->type == m2->type))
1175 return strcmp(m1->name, m2->name);
1177 return m1->type - m2->type;
1181 int sq_count_elts(struct save_queue *q)
1187 while (sq_get_data(q, &foo))