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>
23 #include <moira_site.h>
26 static char blanche_rcsid[] = "$Header$";
35 /* It is important to membercmp that M_USER < M_LIST < M_STRING */
42 /* argument parsing macro */
43 #define argis(a,b) ((strcmp(*arg+1, a) == 0) || (strcmp(*arg+1, b) == 0))
45 /* flags from command line */
46 int infoflg, verbose, syncflg, memberflg, recursflg, debugflg, noauth;
47 int showusers, showstrings, showkerberos, showlists;
49 /* various member lists */
50 struct save_queue *addlist, *dellist, *memberlist, *synclist;
52 char *listname, *whoami;
58 int show_list_info(), show_list_count(), get_list_members(), scream();
59 int show_list_members(), membercmp();
60 struct member *parse_member();
70 char *membervec[3], *motd;
71 struct member *memberstruct;
72 char *server = NULL, *p;
74 /* clear all flags & lists */
75 infoflg = verbose = syncflg = memberflg = debugflg = recursflg = 0;
76 noauth = showusers = showstrings = showkerberos = showlists = 0;
78 addlist = sq_create();
79 dellist = sq_create();
80 memberlist = sq_create();
81 synclist = sq_create();
84 /* parse args, building addlist, dellist, & synclist */
85 while (++arg - argv < argc) {
88 if (argis("m", "members"))
90 else if (argis("u", "users"))
92 else if (argis("s", "strings"))
94 else if (argis("l", "lists"))
96 else if (argis("k", "kerberos"))
98 else if (argis("D", "debug"))
100 else if (argis("i","info"))
102 else if (argis("n","noauth"))
104 else if (argis("v","verbose"))
106 else if (argis("r","recursive"))
108 else if (argis("S","server"))
109 if (arg - argv < argc - 1) {
114 else if (argis("a","add"))
115 if (arg - argv < argc - 1) {
117 if (memberstruct = parse_member(*arg))
118 sq_save_data(addlist, memberstruct);
121 else if (argis("al","addlist"))
122 if (arg - argv < argc - 1) {
124 get_members_from_file(*arg, addlist);
127 else if (argis("d","delete"))
128 if (arg - argv < argc - 1) {
130 if (memberstruct = parse_member(*arg))
131 sq_save_data(dellist, memberstruct);
134 else if (argis("dl","deletelist"))
135 if (arg - argv < argc - 1) {
137 get_members_from_file(*arg, dellist);
140 else if (argis("f","file"))
141 if (arg - argv < argc - 1) {
144 get_members_from_file(*arg, synclist);
150 else if (listname == NULL)
155 if (listname == NULL)
158 /* if no other options specified, turn on list members flag */
159 if (!(infoflg || syncflg ||
160 addlist->q_next != addlist || dellist->q_next != dellist))
163 /* If none of {users,strings,lists,kerberos} specified, turn them all on */
164 if (!(showusers || showstrings || showlists || showkerberos))
165 showusers = showstrings = showlists = showkerberos = 1;
168 if (status = mr_connect(server)) {
169 com_err(whoami, status, "unable to connect to the Moira server");
172 if ( status = mr_motd(&motd) ) {
173 com_err(whoami, status, "unable to check server status");
177 fprintf(stderr, "The Moira server is currently unavailable:\n%s\n", motd);
182 if (!noauth && (status = mr_auth("blanche"))) {
183 if (status == MR_USER_AUTH)
184 com_err(whoami, status, "");
186 com_err(whoami, status, "unable to authenticate to Moira");
188 " Try the -noauth flag if you don't need authentication");
193 /* display list info if requested to */
195 status = mr_query("get_list_info", 1, &listname, show_list_info,NULL);
197 com_err(whoami, status, "while getting list information");
198 if (verbose && !memberflg) {
199 status = mr_query("count_members_of_list", 1, &listname,
200 show_list_count, NULL);
202 com_err(whoami, status, "while getting list count");
206 /* if we're synchronizing to a file, we need to:
207 * get the current members of the list
208 * for each member of the sync file
209 * if they are on the list, remove them from the in-memory copy
210 * if they're not on the list, add them to add-list
211 * if anyone is left on the in-memory copy, put them on the delete-list
212 * lastly, reset memberlist so we can use it again later
215 status = mr_query("get_members_of_list", 1, &listname,
216 get_list_members, (char *)memberlist);
218 com_err(whoami, status, "getting members of list %s", listname);
221 while (sq_get_data(synclist, &memberstruct)) {
222 struct save_queue *q;
225 for (q = memberlist->q_next; q != memberlist; q = q->q_next) {
226 if (membercmp(q->q_data, memberstruct) == 0) {
227 q->q_prev->q_next = q->q_next;
228 q->q_next->q_prev = q->q_prev;
234 sq_save_data(addlist, memberstruct);
236 while (sq_get_data(memberlist, &memberstruct))
237 sq_save_data(dellist, memberstruct);
238 sq_destroy(memberlist);
239 memberlist = sq_create();
242 /* Process the add list */
243 while (sq_get_data(addlist, &memberstruct)) {
244 if ((memberstruct->type == M_STRING ||
245 memberstruct->type == M_ANY) &&
246 strchr(memberstruct->name, '\'')) {
247 fprintf(stderr, "Illegal character \"'\" in \"STRING:%s\", aborting blanche.\n",
249 fprintf(stderr, "No changes were made.\n");
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)
311 else if (status != MR_LIST || memberstruct->type != M_ANY) {
312 com_err(whoami, status, "while adding member %s to %s",
313 memberstruct->name, listname);
317 membervec[1] = "STRING";
318 status = mr_query("add_member_to_list", 3, membervec,
320 if (status != MR_SUCCESS)
321 com_err(whoami, status, "while adding member %s to %s",
322 memberstruct->name, listname);
323 else if (!strchr(memberstruct->name, '@') &&
324 !strchr(memberstruct->name, '!') &&
325 !strchr(memberstruct->name, '%')) {
326 fprintf(stderr, "\nWARNING: \"STRING:%s\" was just added to list \"%s\".\n",
327 memberstruct->name, listname);
328 fprintf(stderr, "\tIf %s is a mailing list, this may cause it to stop working.\n", listname);
329 fprintf(stderr, "\tYou should consider removing \"STRING:%s\" from the list.\n", memberstruct->name);
333 membervec[1] = "KERBEROS";
334 status = mr_query("add_member_to_list", 3, membervec,
336 if (status != MR_SUCCESS)
337 com_err(whoami, status, "while adding member %s to %s",
338 memberstruct->name, listname);
342 /* Process the delete list */
343 while (sq_get_data(dellist, &memberstruct)) {
344 membervec[0] = listname;
345 membervec[2] = memberstruct->name;
347 printf("Deleting member ");
348 show_list_member(memberstruct);
350 switch (memberstruct->type) {
353 membervec[1] = "USER";
354 status = mr_query("delete_member_from_list", 3, membervec,
356 if (status == MR_SUCCESS)
358 else if ((status != MR_USER && status != MR_NO_MATCH) ||
359 memberstruct->type != M_ANY) {
360 com_err(whoami, status, "while deleting member %s from %s",
361 memberstruct->name, listname);
365 membervec[1] = "LIST";
366 status = mr_query("delete_member_from_list", 3, membervec,
368 if (status == MR_SUCCESS)
370 else if ((status != MR_LIST && status != MR_NO_MATCH) ||
371 memberstruct->type != M_ANY) {
372 if (status == MR_PERM && memberstruct->type == M_ANY) {
373 /* M_ANY means we've fallen through from the user case
374 * The fact that we didn't get MR_PERM there indicates
375 * that we had permission to remove the specified member
376 * from the list if it is a user, but not a list. This is
377 * if we are the member in question. Since we exist as a user
378 * we must have gotten the MR_NO_MATCH error, so we will
379 * return that, since it will be less confusing. However,
380 * This will generate the wrongerror if the user was trying
381 * to remove the list with his/her username from a list they
382 * don't administrate, without explicitly specifying "list:".
384 status = MR_NO_MATCH;
386 com_err(whoami, status, "while deleting member %s from %s",
387 memberstruct->name, listname);
391 membervec[1] = "STRING";
392 status = mr_query("delete_member_from_list", 3, membervec,
394 if (status == MR_STRING && memberstruct->type == M_ANY)
395 com_err(whoami, 0, " Unable to find member %s to delete from %s",
396 memberstruct->name, listname);
397 else if (status != MR_SUCCESS)
398 com_err(whoami, status, "while deleting member %s from %s",
399 memberstruct->name, listname);
402 membervec[1] = "KERBEROS";
403 status = mr_query("delete_member_from_list", 3, membervec,
405 if (status != MR_SUCCESS)
406 com_err(whoami, status, "while deleting member %s from %s",
407 memberstruct->name, listname);
411 /* Display the members of the list now, if requested */
414 recursive_display_list_members();
416 status = mr_query("get_members_of_list", 1, &listname,
417 get_list_members, (char *)memberlist);
419 com_err(whoami, status, "while getting members of list %s",
421 while (sq_get_data(memberlist, &memberstruct))
422 show_list_member(memberstruct);
434 fprintf(stderr, "Usage: %s listname [options]\n",argv[0]);
435 fprintf(stderr, "Options are\n");
436 fprintf(stderr, " -v | -verbose\n");
437 fprintf(stderr, " -m | -members\n");
438 fprintf(stderr, " -u | -users\n");
439 fprintf(stderr, " -l | -lists\n");
440 fprintf(stderr, " -s | -strings\n");
441 fprintf(stderr, " -k | -kerberos\n");
442 fprintf(stderr, " -i | -info\n");
443 fprintf(stderr, " -r | -recursive\n");
444 fprintf(stderr, " -a | -add member\n");
445 fprintf(stderr, " -d | -delete member\n");
446 fprintf(stderr, " -al | -addlist filename\n");
447 fprintf(stderr, " -dl | -deletelist filename\n");
448 fprintf(stderr, " -f | -file filename\n");
449 fprintf(stderr, " -n | -noauth\n");
450 fprintf(stderr, " -S | -server host[:port]\n");
451 fprintf(stderr, " -D | -debug\n");
456 /* Display the members stored in the queue */
458 show_list_member(memberstruct)
459 struct member *memberstruct;
463 switch (memberstruct->type) {
485 printf("%s\n", memberstruct->name);
490 printf("%s:%s\n", s, memberstruct->name);
492 if (memberstruct->type == M_LIST)
493 printf("LIST:%s\n", memberstruct->name);
494 else if (memberstruct->type == M_KERBEROS)
495 printf("KERBEROS:%s\n", memberstruct->name);
496 else if (memberstruct->type == M_STRING &&
497 !strchr(memberstruct->name, '@'))
498 printf("STRING:%s\n", memberstruct->name);
500 printf("%s\n", memberstruct->name);
505 /* Show the retrieved information about a list */
507 show_list_info(argc, argv, hint)
512 printf("List: %s\n", argv[0]);
513 printf("Description: %s\n", argv[9]);
514 printf("Flags: %s, %s, and %s\n",
515 atoi(argv[1]) ? "active" : "inactive",
516 atoi(argv[2]) ? "public" : "private",
517 atoi(argv[3]) ? "hidden" : "visible");
518 printf("%s is %sa maillist and is %sa group", argv[0],
519 atoi(argv[4]) ? "" : "not ",
520 atoi(argv[5]) ? "" : "not ");
522 printf(" with GID %d\n", atoi(argv[6]));
525 printf("Owner: %s %s\n", argv[7], argv[8]);
526 printf("Last modified by %s with %s on %s\n", argv[11], argv[12], argv[10]);
531 /* Show the retrieve list member count */
533 show_list_count(argc, argv, hint)
538 printf("Members: %s\n", argv[0]);
542 /* Recursively find all of the members of listname, and then display them */
544 recursive_display_list_members()
546 int status, count, savecount;
547 struct save_queue *lists, *members;
548 struct member *m, *m1, *data;
551 members = sq_create();
552 m = (struct member *) malloc(sizeof(struct member));
555 sq_save_data(lists, m);
557 while (sq_get_data(lists, &m)) {
558 sq_destroy(memberlist);
559 memberlist = sq_create();
561 fprintf(stderr, "Fetching members of %s\n", m->name);
562 status = mr_query("get_members_of_list", 1, &(m->name),
563 get_list_members, (char *)memberlist);
565 com_err(whoami, status, "while getting members of list %s", m->name);
566 while (sq_get_data(memberlist, &m1)) {
567 if (m1->type == M_LIST)
568 unique_add_member(lists, m1);
570 unique_add_member(members, m1);
573 savecount = count = sq_count_elts(members);
574 data = (struct member *) malloc(count * sizeof(struct member));
576 while (sq_get_data(members, &m))
577 memcpy(&data[count++], m, sizeof(struct member));
578 qsort(data, count, sizeof(struct member), membercmp);
579 for (count = 0; count < savecount; count++) {
580 show_list_member(&data[count]);
585 /* add a struct member to a queue if that member isn't already there. */
587 unique_add_member(q, m)
588 struct save_queue *q;
591 struct save_queue *qp;
593 for (qp = q->q_next; qp != q; qp = qp->q_next) {
594 if (!membercmp(qp->q_data, m))
601 /* Collect the retrieved members of the list */
603 get_list_members(argc, argv, q)
606 struct save_queue *q;
610 m = (struct member *) malloc(sizeof(struct member));
611 switch (argv[0][0]) {
622 m->type = M_KERBEROS;
625 m->name = strsave(argv[1]);
631 /* Called only if a query returns a value that we weren't expecting */
635 fprintf(stderr, "Programmer botch\n");
640 /* Open file, parse members from file, and put them on the specified queue */
641 get_members_from_file(filename, queue)
643 struct save_queue *queue;
647 struct member *memberstruct;
649 if (!strcmp(filename, "-"))
652 in = fopen(filename, "r");
654 com_err(whoami, errno, "while opening %s for input", filename);
659 while (fgets(buf, BUFSIZ, in))
660 if (memberstruct = parse_member(buf))
661 sq_save_data(queue, memberstruct);
663 com_err(whoami, errno, "while reading from %s", filename);
667 /* Collect the possible expansions of the alias MAILHUB */
669 int collect(argc, argv, list)
676 for (i = 0; (*list)[i]; i++);
677 *list = (char **)realloc(*list, (i + 2) * sizeof(char *));
678 (*list)[i] = strsave(argv[2]);
684 /* Parse a line of input, fetching a member. NULL is returned if a member
685 * is not found. ';' is a comment character.
688 struct member *parse_member(s)
691 register struct member *m;
694 while (*s && isspace(*s))
697 while (*p && *p != '\n' && *p != ';')
698 if (isprint(*p) && !isspace(*p))
704 if (p == s || strlen(s) == 0)
707 if ((m = (struct member *) malloc(sizeof(struct member))) == NULL)
710 if (p = strchr(s, ':')) {
713 if (!strcasecmp("user", s))
715 else if (!strcasecmp("list", s))
717 else if (!strcasecmp("string", s))
719 else if (!strcasecmp("kerberos", s))
720 m->type = M_KERBEROS;
726 m->name = strsave(m->name);
728 m->name = strsave(s);
729 if (strchr(s, '@') || strchr(s, '!') || strchr(s, '%') || strchr(s, ' '))
739 * This routine two compares members by the following rules:
740 * 1. A USER is less than a LIST
741 * 2. A LIST is less than a STRING
742 * 3. If two members are of the same type, the one alphabetically first
743 * is less than the other
744 * It returs < 0 if the first member is less, 0 if they are identical, and
745 * > 0 if the second member is less (the first member is greater).
748 int membercmp(m1, m2)
749 struct member *m1, *m2;
751 if (m1->type == M_ANY || m2->type == M_ANY || (m1->type == m2->type))
752 return(strcmp(m1->name, m2->name));
754 return(m1->type - m2->type);
759 struct save_queue *q;
765 while (sq_get_data(q, &foo))