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_KERBEROS &&
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 if (memberstruct->type == M_STRING)
502 memberstruct->type = M_ANY;
503 fprintf(stderr, "Warning: \"%s\" converted to "
504 "\"%s\" because it is a local name.\n",
505 host, memberstruct->name);
511 /* now continue adding member */
512 membervec[0] = listname;
513 membervec[2] = memberstruct->name;
516 printf("Adding member ");
517 show_list_member(memberstruct);
519 switch (memberstruct->type)
523 membervec[1] = "USER";
524 status = mr_query("add_member_to_list", 3, membervec, NULL, NULL);
525 if (status == MR_SUCCESS)
527 else if (status != MR_USER || memberstruct->type != M_ANY)
529 com_err(whoami, status, "while adding member %s to %s",
530 memberstruct->name, listname);
535 membervec[1] = "LIST";
536 status = mr_query("add_member_to_list", 3, membervec,
538 if (status == MR_SUCCESS)
540 if (!strcmp(membervec[0], getenv("USER")))
542 fprintf(stderr, "\nWARNING: \"LIST:%s\" was just added "
543 "to list \"%s\".\n", membervec[2], membervec[0]);
544 fprintf(stderr, "If you meant to add yourself to the list "
545 "\"%s\", type:\n", membervec[2]);
546 fprintf(stderr, "\tblanche %s -d %s\t(to undo this)\n",
547 membervec[0], membervec[2]);
548 fprintf(stderr, "\tblanche %s -a %s\t(to add yourself to "
549 "that list)\n", membervec[2], membervec[0]);
553 else if (status != MR_LIST || memberstruct->type != M_ANY)
555 com_err(whoami, status, "while adding member %s to %s",
556 memberstruct->name, listname);
561 if (memberstruct->type == M_ANY &&
562 !strchr(memberstruct->name, '@') &&
563 !strchr(memberstruct->name, '!') &&
564 !strchr(memberstruct->name, '%'))
566 /* if user is trying to add something which isn't a
567 remote string, or a list, or a user, and didn't
568 explicitly specify `STRING:', it's probably a typo */
569 com_err(whoami, MR_NO_MATCH, "while adding member %s to %s",
570 memberstruct->name, listname);
575 membervec[1] = "STRING";
576 status = mr_query("add_member_to_list", 3, membervec,
578 if (status != MR_SUCCESS)
580 com_err(whoami, status, "while adding member %s to %s",
581 memberstruct->name, listname);
586 membervec[1] = "KERBEROS";
587 status = mr_query("add_member_to_list", 3, membervec,
589 if (status != MR_SUCCESS)
591 com_err(whoami, status, "while adding member %s to %s",
592 memberstruct->name, listname);
598 /* Process the delete list */
599 while (sq_get_data(dellist, &memberstruct))
601 membervec[0] = listname;
602 membervec[2] = memberstruct->name;
605 printf("Deleting member ");
606 show_list_member(memberstruct);
608 switch (memberstruct->type)
612 membervec[1] = "USER";
613 status = mr_query("delete_member_from_list", 3, membervec,
615 if (status == MR_SUCCESS)
617 else if ((status != MR_USER && status != MR_NO_MATCH) ||
618 memberstruct->type != M_ANY)
620 com_err(whoami, status, "while deleting member %s from %s",
621 memberstruct->name, listname);
626 membervec[1] = "LIST";
627 status = mr_query("delete_member_from_list", 3, membervec,
629 if (status == MR_SUCCESS)
631 else if ((status != MR_LIST && status != MR_NO_MATCH) ||
632 memberstruct->type != M_ANY)
634 if (status == MR_PERM && memberstruct->type == M_ANY &&
635 !strcmp(membervec[2], getenv("USER")))
637 /* M_ANY means we've fallen through from the user
638 * case. The user is trying to remove himself from
639 * a list, but we got MR_USER or MR_NO_MATCH above,
640 * meaning he's not really on it, and we got MR_PERM
641 * when trying to remove LIST:$USER because he's not
642 * on the acl. That error is useless, so return
643 * MR_NO_MATCH instead. However, this will generate the
644 * wrong error if the user was trying to remove the list
645 * with his username from a list he doesn't administrate
646 * without explicitly specifying "list:".
648 status = MR_NO_MATCH;
650 com_err(whoami, status, "while deleting member %s from %s",
651 memberstruct->name, listname);
656 membervec[1] = "STRING";
657 status = mr_query("delete_member_from_list", 3, membervec,
659 if (status == MR_STRING && memberstruct->type == M_ANY)
661 com_err(whoami, 0, " Unable to find member %s to delete from %s",
662 memberstruct->name, listname);
664 if (!strcmp(membervec[0], getenv("USER")))
666 fprintf(stderr, "(If you were trying to remove yourself "
667 "from the list \"%s\",\n", membervec[2]);
668 fprintf(stderr, "the correct command is \"blanche %s -d "
669 "%s\".)\n", membervec[2], membervec[0]);
672 else if (status != MR_SUCCESS)
674 com_err(whoami, status, "while deleting member %s from %s",
675 memberstruct->name, listname);
680 membervec[1] = "KERBEROS";
681 status = mr_query("delete_member_from_list", 3, membervec,
683 if (status != MR_SUCCESS)
685 com_err(whoami, status, "while deleting member %s from %s",
686 memberstruct->name, listname);
692 /* Display the members of the list now, if requested */
696 recursive_display_list_members();
699 status = mr_query("get_members_of_list", 1, &listname,
700 get_list_members, memberlist);
702 com_err(whoami, status, "while getting members of list %s",
704 while (sq_get_data(memberlist, &memberstruct))
705 show_list_member(memberstruct);
711 exit(success ? 0 : 1);
714 void usage(char **argv)
716 fprintf(stderr, "Usage: %s listname [options]\n", argv[0]);
717 fprintf(stderr, "Options are\n");
718 fprintf(stderr, " %-39s%-39s\n", "-v | -verbose",
720 fprintf(stderr, " %-39s%-39s\n", "-m | -members",
721 "-R | -rename newname");
722 fprintf(stderr, " %-39s%-39s\n", "-u | -users",
724 fprintf(stderr, " %-39s%-39s\n", "-l | -lists",
726 fprintf(stderr, " %-39s%-39s\n", "-s | -strings",
728 fprintf(stderr, " %-39s%-39s\n", "-k | -kerberos",
730 fprintf(stderr, " %-39s%-39s\n", "-i | -info",
732 fprintf(stderr, " %-39s%-39s\n", "-r | -recursive",
734 fprintf(stderr, " %-39s%-39s\n", "-a | -add member",
736 fprintf(stderr, " %-39s%-39s\n", "-d | -delete member",
738 fprintf(stderr, " %-39s%-39s\n", "-al | -addlist filename",
740 fprintf(stderr, " %-39s%-39s\n", "-dl | -deletelist filename",
742 fprintf(stderr, " %-39s%-39s\n", "-f | -file filename",
743 "-D | -desc description");
744 fprintf(stderr, " %-39s%-39s\n", "-n | -noauth",
745 "-O | -owner owner");
746 fprintf(stderr, " %-39s%-39s\n", "-db | -database host[:port]",
752 /* Display the members stored in the queue */
754 void show_list_member(struct member *memberstruct)
758 switch (memberstruct->type)
781 printf("%s\n", memberstruct->name);
786 printf("%s:%s\n", s, memberstruct->name);
789 if (memberstruct->type == M_LIST)
790 printf("LIST:%s\n", memberstruct->name);
791 else if (memberstruct->type == M_KERBEROS)
792 printf("KERBEROS:%s\n", memberstruct->name);
793 else if (memberstruct->type == M_STRING &&
794 !strchr(memberstruct->name, '@'))
795 printf("STRING:%s\n", memberstruct->name);
797 printf("%s\n", memberstruct->name);
802 /* Show the retrieved information about a list */
804 int show_list_info(int argc, char **argv, void *hint)
806 printf("List: %s\n", argv[0]);
807 printf("Description: %s\n", argv[9]);
808 printf("Flags: %s, %s, and %s\n",
809 atoi(argv[1]) ? "active" : "inactive",
810 atoi(argv[2]) ? "public" : "private",
811 atoi(argv[3]) ? "hidden" : "visible");
812 printf("%s is %sa maillist and is %sa group", argv[0],
813 atoi(argv[4]) ? "" : "not ",
814 atoi(argv[5]) ? "" : "not ");
816 printf(" with GID %d\n", atoi(argv[6]));
819 printf("Owner: %s %s\n", argv[7], argv[8]);
820 printf("Last modified by %s with %s on %s\n", argv[11], argv[12], argv[10]);
825 /* Copy retrieved information about a list into a new argv */
827 int save_list_info(int argc, char **argv, void *hint)
831 for (argc = 0; argc < 10; argc++)
832 nargv[argc + 1] = strdup(argv[argc]);
836 /* Show the retrieve list member count */
838 int show_list_count(int argc, char **argv, void *hint)
840 printf("Members: %s\n", argv[0]);
845 /* Recursively find all of the members of listname, and then display them */
847 void recursive_display_list_members(void)
849 int status, count, savecount;
850 struct save_queue *lists, *members;
851 struct member *m, *m1, *data;
854 members = sq_create();
855 m = malloc(sizeof(struct member));
858 sq_save_data(lists, m);
860 while (sq_get_data(lists, &m))
862 sq_destroy(memberlist);
863 memberlist = sq_create();
864 status = mr_query("get_members_of_list", 1, &(m->name),
865 get_list_members, memberlist);
867 com_err(whoami, status, "while getting members of list %s", m->name);
868 while (sq_get_data(memberlist, &m1))
870 if (m1->type == M_LIST)
871 unique_add_member(lists, m1);
873 unique_add_member(members, m1);
876 savecount = count = sq_count_elts(members);
877 data = malloc(count * sizeof(struct member));
879 while (sq_get_data(members, &m))
880 memcpy(&data[count++], m, sizeof(struct member));
881 qsort(data, count, sizeof(struct member), membercmp);
882 for (count = 0; count < savecount; count++)
883 show_list_member(&data[count]);
887 /* add a struct member to a queue if that member isn't already there. */
889 void unique_add_member(struct save_queue *q, struct member *m)
891 struct save_queue *qp;
893 for (qp = q->q_next; qp != q; qp = qp->q_next)
895 if (!membercmp(qp->q_data, m))
902 /* Collect the retrieved members of the list */
904 int get_list_members(int argc, char **argv, void *sq)
906 struct save_queue *q = sq;
909 m = malloc(sizeof(struct member));
922 m->type = M_KERBEROS;
925 m->name = strdup(argv[1]);
931 /* Open file, parse members from file, and put them on the specified queue */
932 void get_members_from_file(char *filename, struct save_queue *queue)
936 struct member *memberstruct;
938 if (!strcmp(filename, "-"))
942 in = fopen(filename, "r");
945 com_err(whoami, errno, "while opening %s for input", filename);
950 while (fgets(buf, BUFSIZ, in))
952 if ((memberstruct = parse_member(buf)))
953 sq_save_data(queue, memberstruct);
957 com_err(whoami, errno, "while reading from %s", filename);
963 /* Collect the possible expansions of the alias MAILHUB */
965 int collect(int argc, char **argv, void *l)
970 for (i = 0; (*list)[i]; i++)
972 *list = realloc(*list, (i + 2) * sizeof(char *));
973 (*list)[i] = strdup(argv[2]);
974 (*list)[i + 1] = NULL;
979 /* Parse a line of input, fetching a member. NULL is returned if a member
980 * is not found. ';' is a comment character.
983 struct member *parse_member(char *s)
988 while (*s && isspace(*s))
991 while (*p && *p != '\n' && *p != ';')
993 if (isprint(*p) && !isspace(*p))
1000 if (p == s || strlen(s) == 0)
1003 if (!(m = malloc(sizeof(struct member))))
1006 if ((p = strchr(s, ':')))
1010 if (!strcasecmp("user", s))
1012 else if (!strcasecmp("list", s))
1014 else if (!strcasecmp("string", s))
1016 else if (!strcasecmp("kerberos", s))
1017 m->type = M_KERBEROS;
1024 m->name = strdup(m->name);
1028 m->name = strdup(s);
1036 * This routine two compares members by the following rules:
1037 * 1. A USER is less than a LIST
1038 * 2. A LIST is less than a STRING
1039 * 3. If two members are of the same type, the one alphabetically first
1040 * is less than the other
1041 * It returs < 0 if the first member is less, 0 if they are identical, and
1042 * > 0 if the second member is less (the first member is greater).
1045 int membercmp(const void *mem1, const void *mem2)
1047 const struct member *m1 = mem1, *m2 = mem2;
1049 if (m1->type == M_ANY || m2->type == M_ANY || (m1->type == m2->type))
1050 return strcmp(m1->name, m2->name);
1052 return m1->type - m2->type;
1056 int sq_count_elts(struct save_queue *q)
1062 while (sq_get_data(q, &foo))