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);
406 com_err(whoami, status, "while updating list.");
411 /* display list info if requested to */
414 status = mr_query("get_list_info", 1, &listname, show_list_info, NULL);
416 com_err(whoami, status, "while getting list information");
417 if (verbose && !memberflg)
419 status = mr_query("count_members_of_list", 1, &listname,
420 show_list_count, NULL);
422 com_err(whoami, status, "while getting list count");
426 /* if we're synchronizing to a file, we need to:
427 * get the current members of the list
428 * for each member of the sync file
429 * if they are on the list, remove them from the in-memory copy
430 * if they're not on the list, add them to add-list
431 * if anyone is left on the in-memory copy, put them on the delete-list
432 * lastly, reset memberlist so we can use it again later
436 status = mr_query("get_members_of_list", 1, &listname,
437 get_list_members, memberlist);
440 com_err(whoami, status, "getting members of list %s", listname);
443 while (sq_get_data(synclist, &memberstruct))
445 struct save_queue *q;
448 for (q = memberlist->q_next; q != memberlist; q = q->q_next)
450 if (membercmp(q->q_data, memberstruct) == 0)
452 q->q_prev->q_next = q->q_next;
453 q->q_next->q_prev = q->q_prev;
459 sq_save_data(addlist, memberstruct);
461 while (sq_get_data(memberlist, &memberstruct))
462 sq_save_data(dellist, memberstruct);
463 sq_destroy(memberlist);
464 memberlist = sq_create();
467 /* Process the add list */
468 while (sq_get_data(addlist, &memberstruct))
470 /* canonicalize string if necessary */
471 if (memberstruct->type == M_STRING &&
472 (p = strchr(memberstruct->name, '@')))
474 char *host = canonicalize_hostname(strdup(++p));
475 static char **mailhubs = NULL;
484 mailhubs = malloc(sizeof(char *));
486 status = mr_query("get_alias", 3, argv, collect,
488 if (status != MR_SUCCESS && status != MR_NO_MATCH)
490 com_err(whoami, status,
491 " while reading list of MAILHUB servers");
495 for (i = 0; (p = mailhubs[i]); i++)
497 if (!strcasecmp(p, host))
499 host = strdup(memberstruct->name);
500 *(strchr(memberstruct->name, '@')) = 0;
501 memberstruct->type = M_ANY;
502 fprintf(stderr, "Warning: \"STRING:%s\" converted to "
503 "\"%s\" because it is a local name.\n",
504 host, memberstruct->name);
510 /* now continue adding member */
511 membervec[0] = listname;
512 membervec[2] = memberstruct->name;
515 printf("Adding member ");
516 show_list_member(memberstruct);
518 switch (memberstruct->type)
522 membervec[1] = "USER";
523 status = mr_query("add_member_to_list", 3, membervec, NULL, NULL);
524 if (status == MR_SUCCESS)
526 else if (status != MR_USER || memberstruct->type != M_ANY)
528 com_err(whoami, status, "while adding member %s to %s",
529 memberstruct->name, listname);
534 membervec[1] = "LIST";
535 status = mr_query("add_member_to_list", 3, membervec,
537 if (status == MR_SUCCESS)
539 if (!strcmp(membervec[0], getenv("USER")))
541 fprintf(stderr, "\nWARNING: \"LIST:%s\" was just added "
542 "to list \"%s\".\n", membervec[2], membervec[0]);
543 fprintf(stderr, "If you meant to add yourself to the list "
544 "\"%s\", type:\n", membervec[2]);
545 fprintf(stderr, "\tblanche %s -d %s\t(to undo this)\n",
546 membervec[0], membervec[2]);
547 fprintf(stderr, "\tblanche %s -a %s\t(to add yourself to "
548 "that list)\n", membervec[2], membervec[0]);
552 else if (status != MR_LIST || memberstruct->type != M_ANY)
554 com_err(whoami, status, "while adding member %s to %s",
555 memberstruct->name, listname);
560 if (memberstruct->type == M_ANY &&
561 !strchr(memberstruct->name, '@') &&
562 !strchr(memberstruct->name, '!') &&
563 !strchr(memberstruct->name, '%'))
565 /* if user is trying to add something which isn't a
566 remote string, or a list, or a user, and didn't
567 explicitly specify `STRING:', it's probably a typo */
568 com_err(whoami, MR_NO_MATCH, "while adding member %s to %s",
569 memberstruct->name, listname);
574 membervec[1] = "STRING";
575 status = mr_query("add_member_to_list", 3, membervec,
577 if (status != MR_SUCCESS)
579 com_err(whoami, status, "while adding member %s to %s",
580 memberstruct->name, listname);
585 membervec[1] = "KERBEROS";
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);
597 /* Process the delete list */
598 while (sq_get_data(dellist, &memberstruct))
600 membervec[0] = listname;
601 membervec[2] = memberstruct->name;
604 printf("Deleting member ");
605 show_list_member(memberstruct);
607 switch (memberstruct->type)
611 membervec[1] = "USER";
612 status = mr_query("delete_member_from_list", 3, membervec,
614 if (status == MR_SUCCESS)
616 else if ((status != MR_USER && status != MR_NO_MATCH) ||
617 memberstruct->type != M_ANY)
619 com_err(whoami, status, "while deleting member %s from %s",
620 memberstruct->name, listname);
625 membervec[1] = "LIST";
626 status = mr_query("delete_member_from_list", 3, membervec,
628 if (status == MR_SUCCESS)
630 else if ((status != MR_LIST && status != MR_NO_MATCH) ||
631 memberstruct->type != M_ANY)
633 if (status == MR_PERM && memberstruct->type == M_ANY &&
634 !strcmp(membervec[2], getenv("USER")))
636 /* M_ANY means we've fallen through from the user
637 * case. The user is trying to remove himself from
638 * a list, but we got MR_USER or MR_NO_MATCH above,
639 * meaning he's not really on it, and we got MR_PERM
640 * when trying to remove LIST:$USER because he's not
641 * on the acl. That error is useless, so return
642 * MR_NO_MATCH instead. However, this will generate the
643 * wrong error if the user was trying to remove the list
644 * with his username from a list he doesn't administrate
645 * without explicitly specifying "list:".
647 status = MR_NO_MATCH;
649 com_err(whoami, status, "while deleting member %s from %s",
650 memberstruct->name, listname);
655 membervec[1] = "STRING";
656 status = mr_query("delete_member_from_list", 3, membervec,
658 if (status == MR_STRING && memberstruct->type == M_ANY)
660 com_err(whoami, 0, " Unable to find member %s to delete from %s",
661 memberstruct->name, listname);
663 if (!strcmp(membervec[0], getenv("USER")))
665 fprintf(stderr, "(If you were trying to remove yourself "
666 "from the list \"%s\",\n", membervec[2]);
667 fprintf(stderr, "the correct command is \"blanche %s -d "
668 "%s\".)\n", membervec[2], membervec[0]);
671 else if (status != MR_SUCCESS)
673 com_err(whoami, status, "while deleting member %s from %s",
674 memberstruct->name, listname);
679 membervec[1] = "KERBEROS";
680 status = mr_query("delete_member_from_list", 3, membervec,
682 if (status != MR_SUCCESS)
684 com_err(whoami, status, "while deleting member %s from %s",
685 memberstruct->name, listname);
691 /* Display the members of the list now, if requested */
695 recursive_display_list_members();
698 status = mr_query("get_members_of_list", 1, &listname,
699 get_list_members, memberlist);
701 com_err(whoami, status, "while getting members of list %s",
703 while (sq_get_data(memberlist, &memberstruct))
704 show_list_member(memberstruct);
710 exit(success ? 0 : 1);
713 void usage(char **argv)
715 fprintf(stderr, "Usage: %s listname [options]\n", argv[0]);
716 fprintf(stderr, "Options are\n");
717 fprintf(stderr, " %-39s%-39s\n", "-v | -verbose",
719 fprintf(stderr, " %-39s%-39s\n", "-m | -members",
720 "-R | -rename newname");
721 fprintf(stderr, " %-39s%-39s\n", "-u | -users",
723 fprintf(stderr, " %-39s%-39s\n", "-l | -lists",
725 fprintf(stderr, " %-39s%-39s\n", "-s | -strings",
727 fprintf(stderr, " %-39s%-39s\n", "-k | -kerberos",
729 fprintf(stderr, " %-39s%-39s\n", "-i | -info",
731 fprintf(stderr, " %-39s%-39s\n", "-r | -recursive",
733 fprintf(stderr, " %-39s%-39s\n", "-a | -add member",
735 fprintf(stderr, " %-39s%-39s\n", "-d | -delete member",
737 fprintf(stderr, " %-39s%-39s\n", "-al | -addlist filename",
739 fprintf(stderr, " %-39s%-39s\n", "-dl | -deletelist filename",
741 fprintf(stderr, " %-39s%-39s\n", "-f | -file filename",
742 "-D | -desc description");
743 fprintf(stderr, " %-39s%-39s\n", "-n | -noauth",
744 "-O | -owner owner");
745 fprintf(stderr, " %-39s%-39s\n", "-db | -database host[:port]",
751 /* Display the members stored in the queue */
753 void show_list_member(struct member *memberstruct)
757 switch (memberstruct->type)
780 printf("%s\n", memberstruct->name);
785 printf("%s:%s\n", s, memberstruct->name);
788 if (memberstruct->type == M_LIST)
789 printf("LIST:%s\n", memberstruct->name);
790 else if (memberstruct->type == M_KERBEROS)
791 printf("KERBEROS:%s\n", memberstruct->name);
792 else if (memberstruct->type == M_STRING &&
793 !strchr(memberstruct->name, '@'))
794 printf("STRING:%s\n", memberstruct->name);
796 printf("%s\n", memberstruct->name);
801 /* Show the retrieved information about a list */
803 int show_list_info(int argc, char **argv, void *hint)
805 printf("List: %s\n", argv[0]);
806 printf("Description: %s\n", argv[9]);
807 printf("Flags: %s, %s, and %s\n",
808 atoi(argv[1]) ? "active" : "inactive",
809 atoi(argv[2]) ? "public" : "private",
810 atoi(argv[3]) ? "hidden" : "visible");
811 printf("%s is %sa maillist and is %sa group", argv[0],
812 atoi(argv[4]) ? "" : "not ",
813 atoi(argv[5]) ? "" : "not ");
815 printf(" with GID %d\n", atoi(argv[6]));
818 printf("Owner: %s %s\n", argv[7], argv[8]);
819 printf("Last modified by %s with %s on %s\n", argv[11], argv[12], argv[10]);
824 /* Copy retrieved information about a list into a new argv */
826 int save_list_info(int argc, char **argv, void *hint)
830 for (argc = 0; argc < 10; argc++)
831 nargv[argc + 1] = strdup(argv[argc]);
835 /* Show the retrieve list member count */
837 int show_list_count(int argc, char **argv, void *hint)
839 printf("Members: %s\n", argv[0]);
844 /* Recursively find all of the members of listname, and then display them */
846 void recursive_display_list_members(void)
848 int status, count, savecount;
849 struct save_queue *lists, *members;
850 struct member *m, *m1, *data;
853 members = sq_create();
854 m = malloc(sizeof(struct member));
857 sq_save_data(lists, m);
859 while (sq_get_data(lists, &m))
861 sq_destroy(memberlist);
862 memberlist = sq_create();
863 status = mr_query("get_members_of_list", 1, &(m->name),
864 get_list_members, memberlist);
866 com_err(whoami, status, "while getting members of list %s", m->name);
867 while (sq_get_data(memberlist, &m1))
869 if (m1->type == M_LIST)
870 unique_add_member(lists, m1);
872 unique_add_member(members, m1);
875 savecount = count = sq_count_elts(members);
876 data = malloc(count * sizeof(struct member));
878 while (sq_get_data(members, &m))
879 memcpy(&data[count++], m, sizeof(struct member));
880 qsort(data, count, sizeof(struct member), membercmp);
881 for (count = 0; count < savecount; count++)
882 show_list_member(&data[count]);
886 /* add a struct member to a queue if that member isn't already there. */
888 void unique_add_member(struct save_queue *q, struct member *m)
890 struct save_queue *qp;
892 for (qp = q->q_next; qp != q; qp = qp->q_next)
894 if (!membercmp(qp->q_data, m))
901 /* Collect the retrieved members of the list */
903 int get_list_members(int argc, char **argv, void *sq)
905 struct save_queue *q = sq;
908 m = malloc(sizeof(struct member));
921 m->type = M_KERBEROS;
924 m->name = strdup(argv[1]);
930 /* Open file, parse members from file, and put them on the specified queue */
931 void get_members_from_file(char *filename, struct save_queue *queue)
935 struct member *memberstruct;
937 if (!strcmp(filename, "-"))
941 in = fopen(filename, "r");
944 com_err(whoami, errno, "while opening %s for input", filename);
949 while (fgets(buf, BUFSIZ, in))
951 if ((memberstruct = parse_member(buf)))
952 sq_save_data(queue, memberstruct);
956 com_err(whoami, errno, "while reading from %s", filename);
962 /* Collect the possible expansions of the alias MAILHUB */
964 int collect(int argc, char **argv, void *l)
969 for (i = 0; (*list)[i]; i++)
971 *list = realloc(*list, (i + 2) * sizeof(char *));
972 (*list)[i] = strdup(argv[2]);
973 (*list)[i + 1] = NULL;
978 /* Parse a line of input, fetching a member. NULL is returned if a member
979 * is not found. ';' is a comment character.
982 struct member *parse_member(char *s)
987 while (*s && isspace(*s))
990 while (*p && *p != '\n' && *p != ';')
992 if (isprint(*p) && !isspace(*p))
999 if (p == s || strlen(s) == 0)
1002 if (!(m = malloc(sizeof(struct member))))
1005 if ((p = strchr(s, ':')))
1009 if (!strcasecmp("user", s))
1011 else if (!strcasecmp("list", s))
1013 else if (!strcasecmp("string", s))
1015 else if (!strcasecmp("kerberos", s))
1016 m->type = M_KERBEROS;
1023 m->name = strdup(m->name);
1027 m->name = strdup(s);
1035 * This routine two compares members by the following rules:
1036 * 1. A USER is less than a LIST
1037 * 2. A LIST is less than a STRING
1038 * 3. If two members are of the same type, the one alphabetically first
1039 * is less than the other
1040 * It returs < 0 if the first member is less, 0 if they are identical, and
1041 * > 0 if the second member is less (the first member is greater).
1044 int membercmp(const void *mem1, const void *mem2)
1046 const struct member *m1 = mem1, *m2 = mem2;
1048 if (m1->type == M_ANY || m2->type == M_ANY || (m1->type == m2->type))
1049 return strcmp(m1->name, m2->name);
1051 return m1->type - m2->type;
1055 int sq_count_elts(struct save_queue *q)
1061 while (sq_get_data(q, &foo))