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();
85 /* parse args, building addlist, dellist, & synclist */
86 while (++arg - argv < argc) {
89 if (argis("m", "members"))
91 else if (argis("u", "users"))
93 else if (argis("s", "strings"))
95 else if (argis("l", "lists"))
97 else if (argis("k", "kerberos"))
99 else if (argis("D", "debug"))
101 else if (argis("i","info"))
103 else if (argis("n","noauth"))
105 else if (argis("v","verbose"))
107 else if (argis("r","recursive"))
109 else if (argis("S","server") || argis("db","database"))
110 if (arg - argv < argc - 1) {
115 else if (argis("a","add"))
116 if (arg - argv < argc - 1) {
118 if (memberstruct = parse_member(*arg))
119 sq_save_data(addlist, memberstruct);
122 else if (argis("al","addlist"))
123 if (arg - argv < argc - 1) {
125 get_members_from_file(*arg, addlist);
128 else if (argis("d","delete"))
129 if (arg - argv < argc - 1) {
131 if (memberstruct = parse_member(*arg))
132 sq_save_data(dellist, memberstruct);
135 else if (argis("dl","deletelist"))
136 if (arg - argv < argc - 1) {
138 get_members_from_file(*arg, dellist);
141 else if (argis("f","file"))
142 if (arg - argv < argc - 1) {
145 get_members_from_file(*arg, synclist);
151 else if (listname == NULL)
156 if (listname == NULL)
159 /* if no other options specified, turn on list members flag */
160 if (!(infoflg || syncflg ||
161 addlist->q_next != addlist || dellist->q_next != dellist))
164 /* If none of {users,strings,lists,kerberos} specified, turn them all on */
165 if (!(showusers || showstrings || showlists || showkerberos))
166 showusers = showstrings = showlists = showkerberos = 1;
169 if (status = mr_connect(server)) {
170 com_err(whoami, status, "unable to connect to the Moira server");
173 if ( status = mr_motd(&motd) ) {
174 com_err(whoami, status, "unable to check server status");
178 fprintf(stderr, "The Moira server is currently unavailable:\n%s\n", motd);
183 if (!noauth && (status = mr_auth("blanche"))) {
184 if (status == MR_USER_AUTH)
185 com_err(whoami, status, "");
187 com_err(whoami, status, "unable to authenticate to Moira");
189 " Try the -noauth flag if you don't need authentication");
194 /* display list info if requested to */
196 status = mr_query("get_list_info", 1, &listname, show_list_info,NULL);
198 com_err(whoami, status, "while getting list information");
199 if (verbose && !memberflg) {
200 status = mr_query("count_members_of_list", 1, &listname,
201 show_list_count, NULL);
203 com_err(whoami, status, "while getting list count");
207 /* if we're synchronizing to a file, we need to:
208 * get the current members of the list
209 * for each member of the sync file
210 * if they are on the list, remove them from the in-memory copy
211 * if they're not on the list, add them to add-list
212 * if anyone is left on the in-memory copy, put them on the delete-list
213 * lastly, reset memberlist so we can use it again later
216 status = mr_query("get_members_of_list", 1, &listname,
217 get_list_members, (char *)memberlist);
219 com_err(whoami, status, "getting members of list %s", listname);
222 while (sq_get_data(synclist, &memberstruct)) {
223 struct save_queue *q;
226 for (q = memberlist->q_next; q != memberlist; q = q->q_next) {
227 if (membercmp(q->q_data, memberstruct) == 0) {
228 q->q_prev->q_next = q->q_next;
229 q->q_next->q_prev = q->q_prev;
235 sq_save_data(addlist, memberstruct);
237 while (sq_get_data(memberlist, &memberstruct))
238 sq_save_data(dellist, memberstruct);
239 sq_destroy(memberlist);
240 memberlist = sq_create();
243 /* Process the add list */
244 while (sq_get_data(addlist, &memberstruct)) {
245 if ((memberstruct->type == M_STRING ||
246 memberstruct->type == M_ANY) &&
247 strchr(memberstruct->name, '\'')) {
248 fprintf(stderr, "%s: Illegal character \"'\" in argument while adding \"STRING:%s\" to %s.\n",
249 whoami, memberstruct->name, listname);
252 /* canonicalize string if necessary */
253 if (memberstruct->type == M_STRING &&
254 (p = strchr(memberstruct->name, '@'))) {
255 char *host = canonicalize_hostname(strsave(++p));
256 static char **mailhubs = NULL;
264 mailhubs = (char **)malloc(sizeof(char *));
266 status = mr_query("get_alias", 3, argv, collect,
268 if (status != MR_SUCCESS && status != MR_NO_MATCH) {
269 com_err(whoami, status,
270 " while reading list of MAILHUB servers");
274 for (i = 0; p = mailhubs[i]; i++) {
275 if (!strcasecmp(p, host)) {
276 host = strsave(memberstruct->name);
277 *(strchr(memberstruct->name, '@')) = 0;
278 memberstruct->type = M_ANY;
279 fprintf(stderr, "Warning: \"STRING:%s\" converted to \"%s\" because it is a local name.\n",
280 host, memberstruct->name);
286 /* now continue adding member */
287 membervec[0] = listname;
288 membervec[2] = memberstruct->name;
290 printf("Adding member ");
291 show_list_member(memberstruct);
293 switch (memberstruct->type) {
296 membervec[1] = "USER";
297 status = mr_query("add_member_to_list", 3, membervec, scream, NULL);
298 if (status == MR_SUCCESS)
300 else if (status != MR_USER || memberstruct->type != M_ANY) {
301 com_err(whoami, status, "while adding member %s to %s",
302 memberstruct->name, listname);
306 membervec[1] = "LIST";
307 status = mr_query("add_member_to_list", 3, membervec,
309 if (status == MR_SUCCESS) {
310 if (!strcmp(membervec[0], getenv("USER"))) {
311 fprintf(stderr, "\nWARNING: \"LIST:%s\" was just added to list \"%s\".\n",
312 membervec[2], membervec[0]);
313 fprintf(stderr, "If you meant to add yourself to the list \"%s\", type:\n", membervec[2]);
314 fprintf(stderr, "\tblanche %s -d %s\t(to undo this)\n",
315 membervec[0], membervec[2]);
316 fprintf(stderr, "\tblanche %s -a %s\t(to add yourself to that list)\n",
317 membervec[2], membervec[0]);
320 } else if (status != MR_LIST || memberstruct->type != M_ANY) {
321 com_err(whoami, status, "while adding member %s to %s",
322 memberstruct->name, listname);
326 membervec[1] = "STRING";
327 status = mr_query("add_member_to_list", 3, membervec,
329 if (status != MR_SUCCESS)
330 com_err(whoami, status, "while adding member %s to %s",
331 memberstruct->name, listname);
332 else if (!strchr(memberstruct->name, '@') &&
333 !strchr(memberstruct->name, '!') &&
334 !strchr(memberstruct->name, '%')) {
335 fprintf(stderr, "\nWARNING: \"STRING:%s\" was just added to list \"%s\".\n",
336 memberstruct->name, listname);
337 fprintf(stderr, "\tIf %s is a mailing list, this may cause it to stop working.\n", listname);
338 fprintf(stderr, "\tYou should consider removing \"STRING:%s\" from the list.\n", memberstruct->name);
342 membervec[1] = "KERBEROS";
343 status = mr_query("add_member_to_list", 3, membervec,
345 if (status != MR_SUCCESS)
346 com_err(whoami, status, "while adding member %s to %s",
347 memberstruct->name, listname);
351 /* Process the delete list */
352 while (sq_get_data(dellist, &memberstruct)) {
353 membervec[0] = listname;
354 membervec[2] = memberstruct->name;
356 printf("Deleting member ");
357 show_list_member(memberstruct);
359 switch (memberstruct->type) {
362 membervec[1] = "USER";
363 status = mr_query("delete_member_from_list", 3, membervec,
365 if (status == MR_SUCCESS)
367 else if ((status != MR_USER && status != MR_NO_MATCH) ||
368 memberstruct->type != M_ANY) {
369 com_err(whoami, status, "while deleting member %s from %s",
370 memberstruct->name, listname);
374 membervec[1] = "LIST";
375 status = mr_query("delete_member_from_list", 3, membervec,
377 if (status == MR_SUCCESS)
379 else if ((status != MR_LIST && status != MR_NO_MATCH) ||
380 memberstruct->type != M_ANY) {
381 if (status == MR_PERM && memberstruct->type == M_ANY) {
382 /* M_ANY means we've fallen through from the user case
383 * The fact that we didn't get MR_PERM there indicates
384 * that we had permission to remove the specified member
385 * from the list if it is a user, but not a list. This is
386 * if we are the member in question. Since we exist as a user
387 * we must have gotten the MR_NO_MATCH error, so we will
388 * return that, since it will be less confusing. However,
389 * This will generate the wrongerror if the user was trying
390 * to remove the list with his/her username from a list they
391 * don't administrate, without explicitly specifying "list:".
393 status = MR_NO_MATCH;
395 com_err(whoami, status, "while deleting member %s from %s",
396 memberstruct->name, listname);
400 membervec[1] = "STRING";
401 status = mr_query("delete_member_from_list", 3, membervec,
403 if (status == MR_STRING && memberstruct->type == M_ANY) {
404 com_err(whoami, 0, " Unable to find member %s to delete from %s",
405 memberstruct->name, listname);
406 if (!strcmp(membervec[0], getenv("USER"))) {
407 fprintf(stderr, "(If you were trying to remove yourself from the list \"%s\",\n", membervec[2]);
408 fprintf(stderr, "the correct command is \"blanche %s -d %s\".)\n",
409 membervec[2], membervec[0]);
411 } else if (status != MR_SUCCESS)
412 com_err(whoami, status, "while deleting member %s from %s",
413 memberstruct->name, listname);
416 membervec[1] = "KERBEROS";
417 status = mr_query("delete_member_from_list", 3, membervec,
419 if (status != MR_SUCCESS)
420 com_err(whoami, status, "while deleting member %s from %s",
421 memberstruct->name, listname);
425 /* Display the members of the list now, if requested */
428 recursive_display_list_members();
430 status = mr_query("get_members_of_list", 1, &listname,
431 get_list_members, (char *)memberlist);
433 com_err(whoami, status, "while getting members of list %s",
435 while (sq_get_data(memberlist, &memberstruct))
436 show_list_member(memberstruct);
448 fprintf(stderr, "Usage: %s listname [options]\n",argv[0]);
449 fprintf(stderr, "Options are\n");
450 fprintf(stderr, " -v | -verbose\n");
451 fprintf(stderr, " -m | -members\n");
452 fprintf(stderr, " -u | -users\n");
453 fprintf(stderr, " -l | -lists\n");
454 fprintf(stderr, " -s | -strings\n");
455 fprintf(stderr, " -k | -kerberos\n");
456 fprintf(stderr, " -i | -info\n");
457 fprintf(stderr, " -r | -recursive\n");
458 fprintf(stderr, " -a | -add member\n");
459 fprintf(stderr, " -d | -delete member\n");
460 fprintf(stderr, " -al | -addlist filename\n");
461 fprintf(stderr, " -dl | -deletelist filename\n");
462 fprintf(stderr, " -f | -file filename\n");
463 fprintf(stderr, " -n | -noauth\n");
464 fprintf(stderr, " -db | -database host[:port]\n");
465 fprintf(stderr, " -D | -debug\n");
470 /* Display the members stored in the queue */
472 show_list_member(memberstruct)
473 struct member *memberstruct;
477 switch (memberstruct->type) {
499 printf("%s\n", memberstruct->name);
504 printf("%s:%s\n", s, memberstruct->name);
506 if (memberstruct->type == M_LIST)
507 printf("LIST:%s\n", memberstruct->name);
508 else if (memberstruct->type == M_KERBEROS)
509 printf("KERBEROS:%s\n", memberstruct->name);
510 else if (memberstruct->type == M_STRING &&
511 !strchr(memberstruct->name, '@'))
512 printf("STRING:%s\n", memberstruct->name);
514 printf("%s\n", memberstruct->name);
519 /* Show the retrieved information about a list */
521 show_list_info(argc, argv, hint)
526 printf("List: %s\n", argv[0]);
527 printf("Description: %s\n", argv[9]);
528 printf("Flags: %s, %s, and %s\n",
529 atoi(argv[1]) ? "active" : "inactive",
530 atoi(argv[2]) ? "public" : "private",
531 atoi(argv[3]) ? "hidden" : "visible");
532 printf("%s is %sa maillist and is %sa group", argv[0],
533 atoi(argv[4]) ? "" : "not ",
534 atoi(argv[5]) ? "" : "not ");
536 printf(" with GID %d\n", atoi(argv[6]));
539 printf("Owner: %s %s\n", argv[7], argv[8]);
540 printf("Last modified by %s with %s on %s\n", argv[11], argv[12], argv[10]);
545 /* Show the retrieve list member count */
547 show_list_count(argc, argv, hint)
552 printf("Members: %s\n", argv[0]);
556 /* Recursively find all of the members of listname, and then display them */
558 recursive_display_list_members()
560 int status, count, savecount;
561 struct save_queue *lists, *members;
562 struct member *m, *m1, *data;
565 members = sq_create();
566 m = (struct member *) malloc(sizeof(struct member));
569 sq_save_data(lists, m);
571 while (sq_get_data(lists, &m)) {
572 sq_destroy(memberlist);
573 memberlist = sq_create();
575 fprintf(stderr, "Fetching members of %s\n", m->name);
576 status = mr_query("get_members_of_list", 1, &(m->name),
577 get_list_members, (char *)memberlist);
579 com_err(whoami, status, "while getting members of list %s", m->name);
580 while (sq_get_data(memberlist, &m1)) {
581 if (m1->type == M_LIST)
582 unique_add_member(lists, m1);
584 unique_add_member(members, m1);
587 savecount = count = sq_count_elts(members);
588 data = (struct member *) malloc(count * sizeof(struct member));
590 while (sq_get_data(members, &m))
591 memcpy(&data[count++], m, sizeof(struct member));
592 qsort(data, count, sizeof(struct member), membercmp);
593 for (count = 0; count < savecount; count++) {
594 show_list_member(&data[count]);
599 /* add a struct member to a queue if that member isn't already there. */
601 unique_add_member(q, m)
602 struct save_queue *q;
605 struct save_queue *qp;
607 for (qp = q->q_next; qp != q; qp = qp->q_next) {
608 if (!membercmp(qp->q_data, m))
615 /* Collect the retrieved members of the list */
617 get_list_members(argc, argv, q)
620 struct save_queue *q;
624 m = (struct member *) malloc(sizeof(struct member));
625 switch (argv[0][0]) {
636 m->type = M_KERBEROS;
639 m->name = strsave(argv[1]);
645 /* Called only if a query returns a value that we weren't expecting */
649 fprintf(stderr, "Programmer botch\n");
654 /* Open file, parse members from file, and put them on the specified queue */
655 get_members_from_file(filename, queue)
657 struct save_queue *queue;
661 struct member *memberstruct;
663 if (!strcmp(filename, "-"))
666 in = fopen(filename, "r");
668 com_err(whoami, errno, "while opening %s for input", filename);
673 while (fgets(buf, BUFSIZ, in))
674 if (memberstruct = parse_member(buf))
675 sq_save_data(queue, memberstruct);
677 com_err(whoami, errno, "while reading from %s", filename);
681 /* Collect the possible expansions of the alias MAILHUB */
683 int collect(argc, argv, list)
690 for (i = 0; (*list)[i]; i++);
691 *list = (char **)realloc(*list, (i + 2) * sizeof(char *));
692 (*list)[i] = strsave(argv[2]);
698 /* Parse a line of input, fetching a member. NULL is returned if a member
699 * is not found. ';' is a comment character.
702 struct member *parse_member(s)
705 register struct member *m;
708 while (*s && isspace(*s))
711 while (*p && *p != '\n' && *p != ';')
712 if (isprint(*p) && !isspace(*p))
718 if (p == s || strlen(s) == 0)
721 if ((m = (struct member *) malloc(sizeof(struct member))) == NULL)
724 if (p = strchr(s, ':')) {
727 if (!strcasecmp("user", s))
729 else if (!strcasecmp("list", s))
731 else if (!strcasecmp("string", s))
733 else if (!strcasecmp("kerberos", s))
734 m->type = M_KERBEROS;
740 m->name = strsave(m->name);
742 m->name = strsave(s);
743 if (strchr(s, '@') || strchr(s, '!') || strchr(s, '%') || strchr(s, ' '))
753 * This routine two compares members by the following rules:
754 * 1. A USER is less than a LIST
755 * 2. A LIST is less than a STRING
756 * 3. If two members are of the same type, the one alphabetically first
757 * is less than the other
758 * It returs < 0 if the first member is less, 0 if they are identical, and
759 * > 0 if the second member is less (the first member is greater).
762 int membercmp(m1, m2)
763 struct member *m1, *m2;
765 if (m1->type == M_ANY || m2->type == M_ANY || (m1->type == m2->type))
766 return(strcmp(m1->name, m2->name));
768 return(m1->type - m2->type);
773 struct save_queue *q;
779 while (sq_get_data(q, &foo))