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);
219 while (sq_get_data(synclist, &memberstruct)) {
220 struct save_queue *q;
223 for (q = memberlist->q_next; q != memberlist; q = q->q_next) {
224 if (membercmp(q->q_data, memberstruct) == 0) {
225 q->q_prev->q_next = q->q_next;
226 q->q_next->q_prev = q->q_prev;
232 sq_save_data(addlist, memberstruct);
234 while (sq_get_data(memberlist, &memberstruct))
235 sq_save_data(dellist, memberstruct);
236 sq_destroy(memberlist);
237 memberlist = sq_create();
240 /* Process the add list */
241 while (sq_get_data(addlist, &memberstruct)) {
242 if ((memberstruct->type == M_STRING ||
243 memberstruct->type == M_ANY) &&
244 strchr(memberstruct->name, '\'')) {
245 fprintf(stderr, "Illegal character \"'\" in \"STRING:%s\", aborting blanche.\n",
247 fprintf(stderr, "No changes were made.\n");
250 /* canonicalize string if necessary */
251 if (memberstruct->type == M_STRING &&
252 (p = strchr(memberstruct->name, '@'))) {
253 char *host = canonicalize_hostname(strsave(++p));
254 static char **mailhubs = NULL;
262 mailhubs = (char **)malloc(sizeof(char *));
264 status = mr_query("get_alias", 3, argv, collect,
266 if (status != MR_SUCCESS && status != MR_NO_MATCH) {
267 com_err(whoami, status,
268 " while reading list of MAILHUB servers");
272 for (i = 0; p = mailhubs[i]; i++) {
273 if (!strcasecmp(p, host)) {
274 host = strsave(memberstruct->name);
275 *(strchr(memberstruct->name, '@')) = 0;
276 memberstruct->type = M_ANY;
277 fprintf(stderr, "Warning: \"STRING:%s\" converted to \"%s\" because it is a local name.\n",
278 host, memberstruct->name);
284 /* now continue adding member */
285 membervec[0] = listname;
286 membervec[2] = memberstruct->name;
288 printf("Adding member ");
289 show_list_member(memberstruct);
291 switch (memberstruct->type) {
294 membervec[1] = "USER";
295 status = mr_query("add_member_to_list", 3, membervec, scream, NULL);
296 if (status == MR_SUCCESS)
298 else if (status != MR_USER || memberstruct->type != M_ANY) {
299 com_err(whoami, status, "while adding member %s to %s",
300 memberstruct->name, listname);
304 membervec[1] = "LIST";
305 status = mr_query("add_member_to_list", 3, membervec,
307 if (status == MR_SUCCESS)
309 else if (status != MR_LIST || memberstruct->type != M_ANY) {
310 com_err(whoami, status, "while adding member %s to %s",
311 memberstruct->name, listname);
315 membervec[1] = "STRING";
316 status = mr_query("add_member_to_list", 3, membervec,
318 if (status != MR_SUCCESS)
319 com_err(whoami, status, "while adding member %s to %s",
320 memberstruct->name, listname);
321 else if (!strchr(memberstruct->name, '@') &&
322 !strchr(memberstruct->name, '!') &&
323 !strchr(memberstruct->name, '%')) {
324 fprintf(stderr, "\nWARNING: \"STRING:%s\" was just added to list \"%s\".\n",
325 memberstruct->name, listname);
326 fprintf(stderr, "\tIf %s is a mailing list, this may cause it to stop working.\n", listname);
327 fprintf(stderr, "\tYou should consider removing \"STRING:%s\" from the list.\n", memberstruct->name);
331 membervec[1] = "KERBEROS";
332 status = mr_query("add_member_to_list", 3, membervec,
334 if (status != MR_SUCCESS)
335 com_err(whoami, status, "while adding member %s to %s",
336 memberstruct->name, listname);
340 /* Process the delete list */
341 while (sq_get_data(dellist, &memberstruct)) {
342 membervec[0] = listname;
343 membervec[2] = memberstruct->name;
345 printf("Deleting member ");
346 show_list_member(memberstruct);
348 switch (memberstruct->type) {
351 membervec[1] = "USER";
352 status = mr_query("delete_member_from_list", 3, membervec,
354 if (status == MR_SUCCESS)
356 else if ((status != MR_USER && status != MR_NO_MATCH) ||
357 memberstruct->type != M_ANY) {
358 com_err(whoami, status, "while deleting member %s from %s",
359 memberstruct->name, listname);
363 membervec[1] = "LIST";
364 status = mr_query("delete_member_from_list", 3, membervec,
366 if (status == MR_SUCCESS)
368 else if ((status != MR_LIST && status != MR_NO_MATCH) ||
369 memberstruct->type != M_ANY) {
370 if (status == MR_PERM && memberstruct->type == M_ANY) {
371 /* M_ANY means we've fallen through from the user case
372 * The fact that we didn't get MR_PERM there indicates
373 * that we had permission to remove the specified member
374 * from the list if it is a user, but not a list. This is
375 * if we are the member in question. Since we exist as a user
376 * we must have gotten the MR_NO_MATCH error, so we will
377 * return that, since it will be less confusing. However,
378 * This will generate the wrongerror if the user was trying
379 * to remove the list with his/her username from a list they
380 * don't administrate, without explicitly specifying "list:".
382 status = MR_NO_MATCH;
384 com_err(whoami, status, "while deleting member %s from %s",
385 memberstruct->name, listname);
389 membervec[1] = "STRING";
390 status = mr_query("delete_member_from_list", 3, membervec,
392 if (status == MR_STRING && memberstruct->type == M_ANY)
393 com_err(whoami, 0, " Unable to find member %s to delete from %s",
394 memberstruct->name, listname);
395 else if (status != MR_SUCCESS)
396 com_err(whoami, status, "while deleting member %s from %s",
397 memberstruct->name, listname);
400 membervec[1] = "KERBEROS";
401 status = mr_query("delete_member_from_list", 3, membervec,
403 if (status != MR_SUCCESS)
404 com_err(whoami, status, "while deleting member %s from %s",
405 memberstruct->name, listname);
409 /* Display the members of the list now, if requested */
412 recursive_display_list_members();
414 status = mr_query("get_members_of_list", 1, &listname,
415 get_list_members, (char *)memberlist);
417 com_err(whoami, status, "while getting members of list %s",
419 while (sq_get_data(memberlist, &memberstruct))
420 show_list_member(memberstruct);
432 fprintf(stderr, "Usage: %s listname [options]\n",argv[0]);
433 fprintf(stderr, "Options are\n");
434 fprintf(stderr, " -v | -verbose\n");
435 fprintf(stderr, " -m | -members\n");
436 fprintf(stderr, " -u | -users\n");
437 fprintf(stderr, " -l | -lists\n");
438 fprintf(stderr, " -s | -strings\n");
439 fprintf(stderr, " -k | -kerberos\n");
440 fprintf(stderr, " -i | -info\n");
441 fprintf(stderr, " -r | -recursive\n");
442 fprintf(stderr, " -a | -add member\n");
443 fprintf(stderr, " -d | -delete member\n");
444 fprintf(stderr, " -al | -addlist filename\n");
445 fprintf(stderr, " -dl | -deletelist filename\n");
446 fprintf(stderr, " -f | -file filename\n");
447 fprintf(stderr, " -n | -noauth\n");
448 fprintf(stderr, " -S | -server host[:port]\n");
449 fprintf(stderr, " -D | -debug\n");
454 /* Display the members stored in the queue */
456 show_list_member(memberstruct)
457 struct member *memberstruct;
461 switch (memberstruct->type) {
483 printf("%s\n", memberstruct->name);
488 printf("%s:%s\n", s, memberstruct->name);
490 if (memberstruct->type == M_LIST)
491 printf("LIST:%s\n", memberstruct->name);
492 else if (memberstruct->type == M_KERBEROS)
493 printf("KERBEROS:%s\n", memberstruct->name);
494 else if (memberstruct->type == M_STRING &&
495 !strchr(memberstruct->name, '@'))
496 printf("STRING:%s\n", memberstruct->name);
498 printf("%s\n", memberstruct->name);
503 /* Show the retrieved information about a list */
505 show_list_info(argc, argv, hint)
510 printf("List: %s\n", argv[0]);
511 printf("Description: %s\n", argv[9]);
512 printf("Flags: %s, %s, and %s\n",
513 atoi(argv[1]) ? "active" : "inactive",
514 atoi(argv[2]) ? "public" : "private",
515 atoi(argv[3]) ? "hidden" : "visible");
516 printf("%s is %sa maillist and is %sa group", argv[0],
517 atoi(argv[4]) ? "" : "not ",
518 atoi(argv[5]) ? "" : "not ");
520 printf(" with GID %d\n", atoi(argv[6]));
523 printf("Owner: %s %s\n", argv[7], argv[8]);
524 printf("Last modified by %s with %s on %s\n", argv[11], argv[12], argv[10]);
529 /* Show the retrieve list member count */
531 show_list_count(argc, argv, hint)
536 printf("Members: %s\n", argv[0]);
540 /* Recursively find all of the members of listname, and then display them */
542 recursive_display_list_members()
544 int status, count, savecount;
545 struct save_queue *lists, *members;
546 struct member *m, *m1, *data;
549 members = sq_create();
550 m = (struct member *) malloc(sizeof(struct member));
553 sq_save_data(lists, m);
555 while (sq_get_data(lists, &m)) {
556 sq_destroy(memberlist);
557 memberlist = sq_create();
559 fprintf(stderr, "Fetching members of %s\n", m->name);
560 status = mr_query("get_members_of_list", 1, &(m->name),
561 get_list_members, (char *)memberlist);
563 com_err(whoami, status, "while getting members of list %s", m->name);
564 while (sq_get_data(memberlist, &m1)) {
565 if (m1->type == M_LIST)
566 unique_add_member(lists, m1);
568 unique_add_member(members, m1);
571 savecount = count = sq_count_elts(members);
572 data = (struct member *) malloc(count * sizeof(struct member));
574 while (sq_get_data(members, &m))
575 memcpy(&data[count++], m, sizeof(struct member));
576 qsort(data, count, sizeof(struct member), membercmp);
577 for (count = 0; count < savecount; count++) {
578 show_list_member(&data[count]);
583 /* add a struct member to a queue if that member isn't already there. */
585 unique_add_member(q, m)
586 struct save_queue *q;
589 struct save_queue *qp;
591 for (qp = q->q_next; qp != q; qp = qp->q_next) {
592 if (!membercmp(qp->q_data, m))
599 /* Collect the retrieved members of the list */
601 get_list_members(argc, argv, q)
604 struct save_queue *q;
608 m = (struct member *) malloc(sizeof(struct member));
609 switch (argv[0][0]) {
620 m->type = M_KERBEROS;
623 m->name = strsave(argv[1]);
629 /* Called only if a query returns a value that we weren't expecting */
633 fprintf(stderr, "Programmer botch\n");
638 /* Open file, parse members from file, and put them on the specified queue */
639 get_members_from_file(filename, queue)
641 struct save_queue *queue;
645 struct member *memberstruct;
647 if (!strcmp(filename, "-"))
650 in = fopen(filename, "r");
652 com_err(whoami, errno, "while opening %s for input", filename);
657 while (fgets(buf, BUFSIZ, in))
658 if (memberstruct = parse_member(buf))
659 sq_save_data(queue, memberstruct);
661 com_err(whoami, errno, "while reading from %s", filename);
665 /* Collect the possible expansions of the alias MAILHUB */
667 int collect(argc, argv, list)
674 for (i = 0; (*list)[i]; i++);
675 *list = (char **)realloc(*list, (i + 2) * sizeof(char *));
676 (*list)[i] = strsave(argv[2]);
682 /* Parse a line of input, fetching a member. NULL is returned if a member
683 * is not found. ';' is a comment character.
686 struct member *parse_member(s)
689 register struct member *m;
692 while (*s && isspace(*s))
695 while (*p && *p != '\n' && *p != ';')
696 if (isprint(*p) && !isspace(*p))
702 if (p == s || strlen(s) == 0)
705 if ((m = (struct member *) malloc(sizeof(struct member))) == NULL)
708 if (p = strchr(s, ':')) {
711 if (!strcasecmp("user", s))
713 else if (!strcasecmp("list", s))
715 else if (!strcasecmp("string", s))
717 else if (!strcasecmp("kerberos", s))
718 m->type = M_KERBEROS;
724 m->name = strsave(m->name);
726 m->name = strsave(s);
727 if (strchr(s, '@') || strchr(s, '!') || strchr(s, '%') || strchr(s, ' '))
737 * This routine two compares members by the following rules:
738 * 1. A USER is less than a LIST
739 * 2. A LIST is less than a STRING
740 * 3. If two members are of the same type, the one alphabetically first
741 * is less than the other
742 * It returs < 0 if the first member is less, 0 if they are identical, and
743 * > 0 if the second member is less (the first member is greater).
746 int membercmp(m1, m2)
747 struct member *m1, *m2;
749 if (m1->type == M_ANY || m2->type == M_ANY || (m1->type == m2->type))
750 return(strcmp(m1->name, m2->name));
752 return(m1->type - m2->type);
757 struct save_queue *q;
763 while (sq_get_data(q, &foo))