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, "Illegal character \"'\" in \"STRING:%s\", aborting blanche.\n",
250 fprintf(stderr, "No changes were made.\n");
253 /* canonicalize string if necessary */
254 if (memberstruct->type == M_STRING &&
255 (p = strchr(memberstruct->name, '@'))) {
256 char *host = canonicalize_hostname(strsave(++p));
257 static char **mailhubs = NULL;
265 mailhubs = (char **)malloc(sizeof(char *));
267 status = mr_query("get_alias", 3, argv, collect,
269 if (status != MR_SUCCESS && status != MR_NO_MATCH) {
270 com_err(whoami, status,
271 " while reading list of MAILHUB servers");
275 for (i = 0; p = mailhubs[i]; i++) {
276 if (!strcasecmp(p, host)) {
277 host = strsave(memberstruct->name);
278 *(strchr(memberstruct->name, '@')) = 0;
279 memberstruct->type = M_ANY;
280 fprintf(stderr, "Warning: \"STRING:%s\" converted to \"%s\" because it is a local name.\n",
281 host, memberstruct->name);
287 /* now continue adding member */
288 membervec[0] = listname;
289 membervec[2] = memberstruct->name;
291 printf("Adding member ");
292 show_list_member(memberstruct);
294 switch (memberstruct->type) {
297 membervec[1] = "USER";
298 status = mr_query("add_member_to_list", 3, membervec, scream, NULL);
299 if (status == MR_SUCCESS)
301 else if (status != MR_USER || memberstruct->type != M_ANY) {
302 com_err(whoami, status, "while adding member %s to %s",
303 memberstruct->name, listname);
307 membervec[1] = "LIST";
308 status = mr_query("add_member_to_list", 3, membervec,
310 if (status == MR_SUCCESS) {
311 if (!strcmp(membervec[0], getenv("USER"))) {
312 fprintf(stderr, "\nWARNING: \"LIST:%s\" was just added to list \"%s\".\n",
313 membervec[2], membervec[0]);
314 fprintf(stderr, "If you meant to add yourself to the list \"%s\", type:\n", membervec[2]);
315 fprintf(stderr, "\tblanche %s -d %s\t(to undo this)\n",
316 membervec[0], membervec[2]);
317 fprintf(stderr, "\tblanche %s -a %s\t(to add yourself to that list)\n",
318 membervec[2], membervec[0]);
321 } else if (status != MR_LIST || memberstruct->type != M_ANY) {
322 com_err(whoami, status, "while adding member %s to %s",
323 memberstruct->name, listname);
327 membervec[1] = "STRING";
328 status = mr_query("add_member_to_list", 3, membervec,
330 if (status != MR_SUCCESS)
331 com_err(whoami, status, "while adding member %s to %s",
332 memberstruct->name, listname);
333 else if (!strchr(memberstruct->name, '@') &&
334 !strchr(memberstruct->name, '!') &&
335 !strchr(memberstruct->name, '%')) {
336 fprintf(stderr, "\nWARNING: \"STRING:%s\" was just added to list \"%s\".\n",
337 memberstruct->name, listname);
338 fprintf(stderr, "\tIf %s is a mailing list, this may cause it to stop working.\n", listname);
339 fprintf(stderr, "\tYou should consider removing \"STRING:%s\" from the list.\n", memberstruct->name);
343 membervec[1] = "KERBEROS";
344 status = mr_query("add_member_to_list", 3, membervec,
346 if (status != MR_SUCCESS)
347 com_err(whoami, status, "while adding member %s to %s",
348 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);
375 membervec[1] = "LIST";
376 status = mr_query("delete_member_from_list", 3, membervec,
378 if (status == MR_SUCCESS)
380 else if ((status != MR_LIST && status != MR_NO_MATCH) ||
381 memberstruct->type != M_ANY) {
382 if (status == MR_PERM && memberstruct->type == M_ANY) {
383 /* M_ANY means we've fallen through from the user case
384 * The fact that we didn't get MR_PERM there indicates
385 * that we had permission to remove the specified member
386 * from the list if it is a user, but not a list. This is
387 * if we are the member in question. Since we exist as a user
388 * we must have gotten the MR_NO_MATCH error, so we will
389 * return that, since it will be less confusing. However,
390 * This will generate the wrongerror if the user was trying
391 * to remove the list with his/her username from a list they
392 * don't administrate, without explicitly specifying "list:".
394 status = MR_NO_MATCH;
396 com_err(whoami, status, "while deleting member %s from %s",
397 memberstruct->name, listname);
401 membervec[1] = "STRING";
402 status = mr_query("delete_member_from_list", 3, membervec,
404 if (status == MR_STRING && memberstruct->type == M_ANY) {
405 com_err(whoami, 0, " Unable to find member %s to delete from %s",
406 memberstruct->name, listname);
407 if (!strcmp(membervec[0], getenv("USER"))) {
408 fprintf(stderr, "(If you were trying to remove yourself from the list \"%s\",\n", membervec[2]);
409 fprintf(stderr, "the correct command is \"blanche %s -d %s\".)\n",
410 membervec[2], membervec[0]);
412 } else if (status != MR_SUCCESS)
413 com_err(whoami, status, "while deleting member %s from %s",
414 memberstruct->name, listname);
417 membervec[1] = "KERBEROS";
418 status = mr_query("delete_member_from_list", 3, membervec,
420 if (status != MR_SUCCESS)
421 com_err(whoami, status, "while deleting member %s from %s",
422 memberstruct->name, listname);
426 /* Display the members of the list now, if requested */
429 recursive_display_list_members();
431 status = mr_query("get_members_of_list", 1, &listname,
432 get_list_members, (char *)memberlist);
434 com_err(whoami, status, "while getting members of list %s",
436 while (sq_get_data(memberlist, &memberstruct))
437 show_list_member(memberstruct);
449 fprintf(stderr, "Usage: %s listname [options]\n",argv[0]);
450 fprintf(stderr, "Options are\n");
451 fprintf(stderr, " -v | -verbose\n");
452 fprintf(stderr, " -m | -members\n");
453 fprintf(stderr, " -u | -users\n");
454 fprintf(stderr, " -l | -lists\n");
455 fprintf(stderr, " -s | -strings\n");
456 fprintf(stderr, " -k | -kerberos\n");
457 fprintf(stderr, " -i | -info\n");
458 fprintf(stderr, " -r | -recursive\n");
459 fprintf(stderr, " -a | -add member\n");
460 fprintf(stderr, " -d | -delete member\n");
461 fprintf(stderr, " -al | -addlist filename\n");
462 fprintf(stderr, " -dl | -deletelist filename\n");
463 fprintf(stderr, " -f | -file filename\n");
464 fprintf(stderr, " -n | -noauth\n");
465 fprintf(stderr, " -db | -server host[:port]\n");
466 fprintf(stderr, " -D | -debug\n");
471 /* Display the members stored in the queue */
473 show_list_member(memberstruct)
474 struct member *memberstruct;
478 switch (memberstruct->type) {
500 printf("%s\n", memberstruct->name);
505 printf("%s:%s\n", s, memberstruct->name);
507 if (memberstruct->type == M_LIST)
508 printf("LIST:%s\n", memberstruct->name);
509 else if (memberstruct->type == M_KERBEROS)
510 printf("KERBEROS:%s\n", memberstruct->name);
511 else if (memberstruct->type == M_STRING &&
512 !strchr(memberstruct->name, '@'))
513 printf("STRING:%s\n", memberstruct->name);
515 printf("%s\n", memberstruct->name);
520 /* Show the retrieved information about a list */
522 show_list_info(argc, argv, hint)
527 printf("List: %s\n", argv[0]);
528 printf("Description: %s\n", argv[9]);
529 printf("Flags: %s, %s, and %s\n",
530 atoi(argv[1]) ? "active" : "inactive",
531 atoi(argv[2]) ? "public" : "private",
532 atoi(argv[3]) ? "hidden" : "visible");
533 printf("%s is %sa maillist and is %sa group", argv[0],
534 atoi(argv[4]) ? "" : "not ",
535 atoi(argv[5]) ? "" : "not ");
537 printf(" with GID %d\n", atoi(argv[6]));
540 printf("Owner: %s %s\n", argv[7], argv[8]);
541 printf("Last modified by %s with %s on %s\n", argv[11], argv[12], argv[10]);
546 /* Show the retrieve list member count */
548 show_list_count(argc, argv, hint)
553 printf("Members: %s\n", argv[0]);
557 /* Recursively find all of the members of listname, and then display them */
559 recursive_display_list_members()
561 int status, count, savecount;
562 struct save_queue *lists, *members;
563 struct member *m, *m1, *data;
566 members = sq_create();
567 m = (struct member *) malloc(sizeof(struct member));
570 sq_save_data(lists, m);
572 while (sq_get_data(lists, &m)) {
573 sq_destroy(memberlist);
574 memberlist = sq_create();
576 fprintf(stderr, "Fetching members of %s\n", m->name);
577 status = mr_query("get_members_of_list", 1, &(m->name),
578 get_list_members, (char *)memberlist);
580 com_err(whoami, status, "while getting members of list %s", m->name);
581 while (sq_get_data(memberlist, &m1)) {
582 if (m1->type == M_LIST)
583 unique_add_member(lists, m1);
585 unique_add_member(members, m1);
588 savecount = count = sq_count_elts(members);
589 data = (struct member *) malloc(count * sizeof(struct member));
591 while (sq_get_data(members, &m))
592 memcpy(&data[count++], m, sizeof(struct member));
593 qsort(data, count, sizeof(struct member), membercmp);
594 for (count = 0; count < savecount; count++) {
595 show_list_member(&data[count]);
600 /* add a struct member to a queue if that member isn't already there. */
602 unique_add_member(q, m)
603 struct save_queue *q;
606 struct save_queue *qp;
608 for (qp = q->q_next; qp != q; qp = qp->q_next) {
609 if (!membercmp(qp->q_data, m))
616 /* Collect the retrieved members of the list */
618 get_list_members(argc, argv, q)
621 struct save_queue *q;
625 m = (struct member *) malloc(sizeof(struct member));
626 switch (argv[0][0]) {
637 m->type = M_KERBEROS;
640 m->name = strsave(argv[1]);
646 /* Called only if a query returns a value that we weren't expecting */
650 fprintf(stderr, "Programmer botch\n");
655 /* Open file, parse members from file, and put them on the specified queue */
656 get_members_from_file(filename, queue)
658 struct save_queue *queue;
662 struct member *memberstruct;
664 if (!strcmp(filename, "-"))
667 in = fopen(filename, "r");
669 com_err(whoami, errno, "while opening %s for input", filename);
674 while (fgets(buf, BUFSIZ, in))
675 if (memberstruct = parse_member(buf))
676 sq_save_data(queue, memberstruct);
678 com_err(whoami, errno, "while reading from %s", filename);
682 /* Collect the possible expansions of the alias MAILHUB */
684 int collect(argc, argv, list)
691 for (i = 0; (*list)[i]; i++);
692 *list = (char **)realloc(*list, (i + 2) * sizeof(char *));
693 (*list)[i] = strsave(argv[2]);
699 /* Parse a line of input, fetching a member. NULL is returned if a member
700 * is not found. ';' is a comment character.
703 struct member *parse_member(s)
706 register struct member *m;
709 while (*s && isspace(*s))
712 while (*p && *p != '\n' && *p != ';')
713 if (isprint(*p) && !isspace(*p))
719 if (p == s || strlen(s) == 0)
722 if ((m = (struct member *) malloc(sizeof(struct member))) == NULL)
725 if (p = strchr(s, ':')) {
728 if (!strcasecmp("user", s))
730 else if (!strcasecmp("list", s))
732 else if (!strcasecmp("string", s))
734 else if (!strcasecmp("kerberos", s))
735 m->type = M_KERBEROS;
741 m->name = strsave(m->name);
743 m->name = strsave(s);
744 if (strchr(s, '@') || strchr(s, '!') || strchr(s, '%') || strchr(s, ' '))
754 * This routine two compares members by the following rules:
755 * 1. A USER is less than a LIST
756 * 2. A LIST is less than a STRING
757 * 3. If two members are of the same type, the one alphabetically first
758 * is less than the other
759 * It returs < 0 if the first member is less, 0 if they are identical, and
760 * > 0 if the second member is less (the first member is greater).
763 int membercmp(m1, m2)
764 struct member *m1, *m2;
766 if (m1->type == M_ANY || m2->type == M_ANY || (m1->type == m2->type))
767 return(strcmp(m1->name, m2->name));
769 return(m1->type - m2->type);
774 struct save_queue *q;
780 while (sq_get_data(q, &foo))