3 * Command line oriented Moira List tool.
5 * by Mark Rosenstein, September 1988.
7 * Copyright 1989 by the Massachusetts Institute of Technology.
9 * (c) Copyright 1988 by the Massachusetts Institute of Technology.
10 * For copying and distribution information, please see the file
14 /* ### Aren't there a lot of sq abstraction barrier violations here?
15 Do we need to improve the support for queue operations? */
17 #include <mit-copyright.h>
24 #include <moira_site.h>
27 static char blanche_rcsid[] = "$Header$";
36 /* It is important to membercmp that M_USER < M_LIST < M_STRING */
43 /* argument parsing macro */
44 #define argis(a,b) ((strcmp(*arg+1, a) == 0) || (strcmp(*arg+1, b) == 0))
46 /* flags from command line */
47 int infoflg, verbose, syncflg, memberflg, recursflg, debugflg, noauth;
48 int showusers, showstrings, showkerberos, showlists;
50 /* various member lists */
51 struct save_queue *addlist, *dellist, *memberlist, *synclist;
53 char *listname, *whoami;
59 int show_list_info(), show_list_count(), get_list_members(), scream();
60 int show_list_members(), membercmp();
61 struct member *parse_member();
71 char *membervec[3], *motd;
72 struct member *memberstruct;
73 char *server = NULL, *p;
75 /* clear all flags & lists */
76 infoflg = verbose = syncflg = memberflg = debugflg = recursflg = 0;
77 noauth = showusers = showstrings = showkerberos = showlists = 0;
79 addlist = sq_create();
80 dellist = sq_create();
81 memberlist = sq_create();
82 synclist = sq_create();
87 /* parse args, building addlist, dellist, & synclist */
88 while (++arg - argv < argc) {
91 if (argis("m", "members"))
93 else if (argis("u", "users"))
95 else if (argis("s", "strings"))
97 else if (argis("l", "lists"))
99 else if (argis("k", "kerberos"))
101 else if (argis("D", "debug"))
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"))
112 if (arg - argv < argc - 1) {
117 else if (argis("a","add"))
118 if (arg - argv < argc - 1) {
120 if (memberstruct = parse_member(*arg))
121 sq_save_data(addlist, memberstruct);
124 else if (argis("al","addlist"))
125 if (arg - argv < argc - 1) {
127 get_members_from_file(*arg, addlist);
130 else if (argis("d","delete"))
131 if (arg - argv < argc - 1) {
133 if (memberstruct = parse_member(*arg))
134 sq_save_data(dellist, memberstruct);
137 else if (argis("dl","deletelist"))
138 if (arg - argv < argc - 1) {
140 get_members_from_file(*arg, dellist);
143 else if (argis("f","file"))
144 if (arg - argv < argc - 1) {
147 get_members_from_file(*arg, synclist);
153 else if (listname == NULL)
158 if (listname == NULL)
161 /* if no other options specified, turn on list members flag */
162 if (!(infoflg || syncflg ||
163 addlist->q_next != addlist || dellist->q_next != dellist))
166 /* If none of {users,strings,lists,kerberos} specified, turn them all on */
167 if (!(showusers || showstrings || showlists || showkerberos))
168 showusers = showstrings = showlists = showkerberos = 1;
171 if (status = mr_connect(server)) {
172 com_err(whoami, status, "unable to connect to the Moira server");
175 if ( status = mr_motd(&motd) ) {
176 com_err(whoami, status, "unable to check server status");
180 fprintf(stderr, "The Moira server is currently unavailable:\n%s\n", motd);
185 if (!noauth && (status = mr_auth("blanche"))) {
186 if (status == MR_USER_AUTH)
187 com_err(whoami, status, "");
189 com_err(whoami, status, "unable to authenticate to Moira");
191 " Try the -noauth flag if you don't need authentication");
196 /* display list info if requested to */
198 status = mr_query("get_list_info", 1, &listname, show_list_info,NULL);
200 com_err(whoami, status, "while getting list information");
201 if (verbose && !memberflg) {
202 status = mr_query("count_members_of_list", 1, &listname,
203 show_list_count, NULL);
205 com_err(whoami, status, "while getting list count");
209 /* if we're synchronizing to a file, we need to:
210 * get the current members of the list
211 * for each member of the sync file
212 * if they are on the list, remove them from the in-memory copy
213 * if they're not on the list, add them to add-list
214 * if anyone is left on the in-memory copy, put them on the delete-list
215 * lastly, reset memberlist so we can use it again later
218 status = mr_query("get_members_of_list", 1, &listname,
219 get_list_members, (char *)memberlist);
221 com_err(whoami, status, "getting members of list %s", listname);
224 while (sq_get_data(synclist, &memberstruct)) {
225 struct save_queue *q;
228 for (q = memberlist->q_next; q != memberlist; q = q->q_next) {
229 if (membercmp(q->q_data, memberstruct) == 0) {
230 q->q_prev->q_next = q->q_next;
231 q->q_next->q_prev = q->q_prev;
237 sq_save_data(addlist, memberstruct);
239 while (sq_get_data(memberlist, &memberstruct))
240 sq_save_data(dellist, memberstruct);
241 sq_destroy(memberlist);
242 memberlist = sq_create();
245 /* Process the add list */
246 while (sq_get_data(addlist, &memberstruct)) {
247 /* canonicalize string if necessary */
248 if (memberstruct->type == M_STRING &&
249 (p = strchr(memberstruct->name, '@'))) {
250 char *host = canonicalize_hostname(strsave(++p));
251 static char **mailhubs = NULL;
259 mailhubs = (char **)malloc(sizeof(char *));
261 status = mr_query("get_alias", 3, argv, collect,
263 if (status != MR_SUCCESS && status != MR_NO_MATCH) {
264 com_err(whoami, status,
265 " while reading list of MAILHUB servers");
269 for (i = 0; p = mailhubs[i]; i++) {
270 if (!strcasecmp(p, host)) {
271 host = strsave(memberstruct->name);
272 *(strchr(memberstruct->name, '@')) = 0;
273 memberstruct->type = M_ANY;
274 fprintf(stderr, "Warning: \"STRING:%s\" converted to \"%s\" because it is a local name.\n",
275 host, memberstruct->name);
281 /* now continue adding member */
282 membervec[0] = listname;
283 membervec[2] = memberstruct->name;
285 printf("Adding member ");
286 show_list_member(memberstruct);
288 switch (memberstruct->type) {
291 membervec[1] = "USER";
292 status = mr_query("add_member_to_list", 3, membervec, scream, NULL);
293 if (status == MR_SUCCESS)
295 else if (status != MR_USER || memberstruct->type != M_ANY) {
296 com_err(whoami, status, "while adding member %s to %s",
297 memberstruct->name, listname);
302 membervec[1] = "LIST";
303 status = mr_query("add_member_to_list", 3, membervec,
305 if (status == MR_SUCCESS) {
306 if (!strcmp(membervec[0], getenv("USER"))) {
307 fprintf(stderr, "\nWARNING: \"LIST:%s\" was just added to list \"%s\".\n",
308 membervec[2], membervec[0]);
309 fprintf(stderr, "If you meant to add yourself to the list \"%s\", type:\n", membervec[2]);
310 fprintf(stderr, "\tblanche %s -d %s\t(to undo this)\n",
311 membervec[0], membervec[2]);
312 fprintf(stderr, "\tblanche %s -a %s\t(to add yourself to that list)\n",
313 membervec[2], membervec[0]);
316 } else if (status != MR_LIST || memberstruct->type != M_ANY) {
317 com_err(whoami, status, "while adding member %s to %s",
318 memberstruct->name, listname);
323 membervec[1] = "STRING";
324 status = mr_query("add_member_to_list", 3, membervec,
326 if (status != MR_SUCCESS) {
327 com_err(whoami, status, "while adding member %s to %s",
328 memberstruct->name, listname);
331 else if (!strchr(memberstruct->name, '@') &&
332 !strchr(memberstruct->name, '!') &&
333 !strchr(memberstruct->name, '%')) {
334 fprintf(stderr, "\nWARNING: \"STRING:%s\" was just added to list \"%s\".\n",
335 memberstruct->name, listname);
336 fprintf(stderr, "\tIf %s is a mailing list, this may cause it to stop working.\n", listname);
337 fprintf(stderr, "\tYou should consider removing \"STRING:%s\" from the list.\n", memberstruct->name);
341 membervec[1] = "KERBEROS";
342 status = mr_query("add_member_to_list", 3, membervec,
344 if (status != MR_SUCCESS) {
345 com_err(whoami, status, "while adding member %s to %s",
346 memberstruct->name, listname);
352 /* Process the delete list */
353 while (sq_get_data(dellist, &memberstruct)) {
354 membervec[0] = listname;
355 membervec[2] = memberstruct->name;
357 printf("Deleting member ");
358 show_list_member(memberstruct);
360 switch (memberstruct->type) {
363 membervec[1] = "USER";
364 status = mr_query("delete_member_from_list", 3, membervec,
366 if (status == MR_SUCCESS)
368 else if ((status != MR_USER && status != MR_NO_MATCH) ||
369 memberstruct->type != M_ANY) {
370 com_err(whoami, status, "while deleting member %s from %s",
371 memberstruct->name, listname);
376 membervec[1] = "LIST";
377 status = mr_query("delete_member_from_list", 3, membervec,
379 if (status == MR_SUCCESS)
381 else if ((status != MR_LIST && status != MR_NO_MATCH) ||
382 memberstruct->type != M_ANY) {
383 if (status == MR_PERM && memberstruct->type == M_ANY) {
384 /* M_ANY means we've fallen through from the user case
385 * The fact that we didn't get MR_PERM there indicates
386 * that we had permission to remove the specified member
387 * from the list if it is a user, but not a list. This is
388 * if we are the member in question. Since we exist as a user
389 * we must have gotten the MR_NO_MATCH error, so we will
390 * return that, since it will be less confusing. However,
391 * This will generate the wrongerror if the user was trying
392 * to remove the list with his/her username from a list they
393 * don't administrate, without explicitly specifying "list:".
395 status = MR_NO_MATCH;
397 com_err(whoami, status, "while deleting member %s from %s",
398 memberstruct->name, listname);
403 membervec[1] = "STRING";
404 status = mr_query("delete_member_from_list", 3, membervec,
406 if (status == MR_STRING && memberstruct->type == M_ANY) {
407 com_err(whoami, 0, " Unable to find member %s to delete from %s",
408 memberstruct->name, listname);
410 if (!strcmp(membervec[0], getenv("USER"))) {
411 fprintf(stderr, "(If you were trying to remove yourself from the list \"%s\",\n", membervec[2]);
412 fprintf(stderr, "the correct command is \"blanche %s -d %s\".)\n",
413 membervec[2], membervec[0]);
415 } else if (status != MR_SUCCESS) {
416 com_err(whoami, status, "while deleting member %s from %s",
417 memberstruct->name, listname);
422 membervec[1] = "KERBEROS";
423 status = mr_query("delete_member_from_list", 3, membervec,
425 if (status != MR_SUCCESS) {
426 com_err(whoami, status, "while deleting member %s from %s",
427 memberstruct->name, listname);
433 /* Display the members of the list now, if requested */
436 recursive_display_list_members();
438 status = mr_query("get_members_of_list", 1, &listname,
439 get_list_members, (char *)memberlist);
441 com_err(whoami, status, "while getting members of list %s",
443 while (sq_get_data(memberlist, &memberstruct))
444 show_list_member(memberstruct);
456 fprintf(stderr, "Usage: %s listname [options]\n",argv[0]);
457 fprintf(stderr, "Options are\n");
458 fprintf(stderr, " -v | -verbose\n");
459 fprintf(stderr, " -m | -members\n");
460 fprintf(stderr, " -u | -users\n");
461 fprintf(stderr, " -l | -lists\n");
462 fprintf(stderr, " -s | -strings\n");
463 fprintf(stderr, " -k | -kerberos\n");
464 fprintf(stderr, " -i | -info\n");
465 fprintf(stderr, " -r | -recursive\n");
466 fprintf(stderr, " -a | -add member\n");
467 fprintf(stderr, " -d | -delete member\n");
468 fprintf(stderr, " -al | -addlist filename\n");
469 fprintf(stderr, " -dl | -deletelist filename\n");
470 fprintf(stderr, " -f | -file filename\n");
471 fprintf(stderr, " -n | -noauth\n");
472 fprintf(stderr, " -db | -database host[:port]\n");
473 fprintf(stderr, " -D | -debug\n");
478 /* Display the members stored in the queue */
480 show_list_member(memberstruct)
481 struct member *memberstruct;
485 switch (memberstruct->type) {
507 printf("%s\n", memberstruct->name);
512 printf("%s:%s\n", s, memberstruct->name);
514 if (memberstruct->type == M_LIST)
515 printf("LIST:%s\n", memberstruct->name);
516 else if (memberstruct->type == M_KERBEROS)
517 printf("KERBEROS:%s\n", memberstruct->name);
518 else if (memberstruct->type == M_STRING &&
519 !strchr(memberstruct->name, '@'))
520 printf("STRING:%s\n", memberstruct->name);
522 printf("%s\n", memberstruct->name);
527 /* Show the retrieved information about a list */
529 show_list_info(argc, argv, hint)
534 printf("List: %s\n", argv[0]);
535 printf("Description: %s\n", argv[9]);
536 printf("Flags: %s, %s, and %s\n",
537 atoi(argv[1]) ? "active" : "inactive",
538 atoi(argv[2]) ? "public" : "private",
539 atoi(argv[3]) ? "hidden" : "visible");
540 printf("%s is %sa maillist and is %sa group", argv[0],
541 atoi(argv[4]) ? "" : "not ",
542 atoi(argv[5]) ? "" : "not ");
544 printf(" with GID %d\n", atoi(argv[6]));
547 printf("Owner: %s %s\n", argv[7], argv[8]);
548 printf("Last modified by %s with %s on %s\n", argv[11], argv[12], argv[10]);
553 /* Show the retrieve list member count */
555 show_list_count(argc, argv, hint)
560 printf("Members: %s\n", argv[0]);
564 /* Recursively find all of the members of listname, and then display them */
566 recursive_display_list_members()
568 int status, count, savecount;
569 struct save_queue *lists, *members;
570 struct member *m, *m1, *data;
573 members = sq_create();
574 m = (struct member *) malloc(sizeof(struct member));
577 sq_save_data(lists, m);
579 while (sq_get_data(lists, &m)) {
580 sq_destroy(memberlist);
581 memberlist = sq_create();
583 fprintf(stderr, "Fetching members of %s\n", m->name);
584 status = mr_query("get_members_of_list", 1, &(m->name),
585 get_list_members, (char *)memberlist);
587 com_err(whoami, status, "while getting members of list %s", m->name);
588 while (sq_get_data(memberlist, &m1)) {
589 if (m1->type == M_LIST)
590 unique_add_member(lists, m1);
592 unique_add_member(members, m1);
595 savecount = count = sq_count_elts(members);
596 data = (struct member *) malloc(count * sizeof(struct member));
598 while (sq_get_data(members, &m))
599 memcpy(&data[count++], m, sizeof(struct member));
600 qsort(data, count, sizeof(struct member), membercmp);
601 for (count = 0; count < savecount; count++) {
602 show_list_member(&data[count]);
607 /* add a struct member to a queue if that member isn't already there. */
609 unique_add_member(q, m)
610 struct save_queue *q;
613 struct save_queue *qp;
615 for (qp = q->q_next; qp != q; qp = qp->q_next) {
616 if (!membercmp(qp->q_data, m))
623 /* Collect the retrieved members of the list */
625 get_list_members(argc, argv, q)
628 struct save_queue *q;
632 m = (struct member *) malloc(sizeof(struct member));
633 switch (argv[0][0]) {
644 m->type = M_KERBEROS;
647 m->name = strsave(argv[1]);
653 /* Called only if a query returns a value that we weren't expecting */
657 fprintf(stderr, "Programmer botch\n");
662 /* Open file, parse members from file, and put them on the specified queue */
663 get_members_from_file(filename, queue)
665 struct save_queue *queue;
669 struct member *memberstruct;
671 if (!strcmp(filename, "-"))
674 in = fopen(filename, "r");
676 com_err(whoami, errno, "while opening %s for input", filename);
681 while (fgets(buf, BUFSIZ, in))
682 if (memberstruct = parse_member(buf))
683 sq_save_data(queue, memberstruct);
685 com_err(whoami, errno, "while reading from %s", filename);
691 /* Collect the possible expansions of the alias MAILHUB */
693 int collect(argc, argv, list)
700 for (i = 0; (*list)[i]; i++);
701 *list = (char **)realloc(*list, (i + 2) * sizeof(char *));
702 (*list)[i] = strsave(argv[2]);
708 /* Parse a line of input, fetching a member. NULL is returned if a member
709 * is not found. ';' is a comment character.
712 struct member *parse_member(s)
715 register struct member *m;
718 while (*s && isspace(*s))
721 while (*p && *p != '\n' && *p != ';')
722 if (isprint(*p) && !isspace(*p))
728 if (p == s || strlen(s) == 0)
731 if ((m = (struct member *) malloc(sizeof(struct member))) == NULL)
734 if (p = strchr(s, ':')) {
737 if (!strcasecmp("user", s))
739 else if (!strcasecmp("list", s))
741 else if (!strcasecmp("string", s))
743 else if (!strcasecmp("kerberos", s))
744 m->type = M_KERBEROS;
750 m->name = strsave(m->name);
752 m->name = strsave(s);
753 if (strchr(s, '@') || strchr(s, '!') || strchr(s, '%') || strchr(s, ' '))
763 * This routine two compares members by the following rules:
764 * 1. A USER is less than a LIST
765 * 2. A LIST is less than a STRING
766 * 3. If two members are of the same type, the one alphabetically first
767 * is less than the other
768 * It returs < 0 if the first member is less, 0 if they are identical, and
769 * > 0 if the second member is less (the first member is greater).
772 int membercmp(m1, m2)
773 struct member *m1, *m2;
775 if (m1->type == M_ANY || m2->type == M_ANY || (m1->type == m2->type))
776 return(strcmp(m1->name, m2->name));
778 return(m1->type - m2->type);
783 struct save_queue *q;
789 while (sq_get_data(q, &foo))