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 /* canonicalize string if necessary */
246 if (memberstruct->type == M_STRING &&
247 (p = strchr(memberstruct->name, '@'))) {
248 char *host = canonicalize_hostname(strsave(++p));
249 static char **mailhubs = NULL;
257 mailhubs = (char **)malloc(sizeof(char *));
259 status = mr_query("get_alias", 3, argv, collect,
261 if (status != MR_SUCCESS && status != MR_NO_MATCH) {
262 com_err(whoami, status,
263 " while reading list of MAILHUB servers");
267 for (i = 0; p = mailhubs[i]; i++) {
268 if (!strcasecmp(p, host)) {
269 host = strsave(memberstruct->name);
270 *(strchr(memberstruct->name, '@')) = 0;
271 memberstruct->type = M_ANY;
272 fprintf(stderr, "Warning: \"STRING:%s\" converted to \"%s\" because it is a local name.\n",
273 host, memberstruct->name);
279 /* now continue adding member */
280 membervec[0] = listname;
281 membervec[2] = memberstruct->name;
283 printf("Adding member ");
284 show_list_member(memberstruct);
286 switch (memberstruct->type) {
289 membervec[1] = "USER";
290 status = mr_query("add_member_to_list", 3, membervec, scream, NULL);
291 if (status == MR_SUCCESS)
293 else if (status != MR_USER || memberstruct->type != M_ANY) {
294 com_err(whoami, status, "while adding member %s to %s",
295 memberstruct->name, listname);
299 membervec[1] = "LIST";
300 status = mr_query("add_member_to_list", 3, membervec,
302 if (status == MR_SUCCESS) {
303 if (!strcmp(membervec[0], getenv("USER"))) {
304 fprintf(stderr, "\nWARNING: \"LIST:%s\" was just added to list \"%s\".\n",
305 membervec[2], membervec[0]);
306 fprintf(stderr, "If you meant to add yourself to the list \"%s\", type:\n", membervec[2]);
307 fprintf(stderr, "\tblanche %s -d %s\t(to undo this)\n",
308 membervec[0], membervec[2]);
309 fprintf(stderr, "\tblanche %s -a %s\t(to add yourself to that list)\n",
310 membervec[2], membervec[0]);
313 } else if (status != MR_LIST || memberstruct->type != M_ANY) {
314 com_err(whoami, status, "while adding member %s to %s",
315 memberstruct->name, listname);
319 membervec[1] = "STRING";
320 status = mr_query("add_member_to_list", 3, membervec,
322 if (status != MR_SUCCESS)
323 com_err(whoami, status, "while adding member %s to %s",
324 memberstruct->name, listname);
325 else if (!strchr(memberstruct->name, '@') &&
326 !strchr(memberstruct->name, '!') &&
327 !strchr(memberstruct->name, '%')) {
328 fprintf(stderr, "\nWARNING: \"STRING:%s\" was just added to list \"%s\".\n",
329 memberstruct->name, listname);
330 fprintf(stderr, "\tIf %s is a mailing list, this may cause it to stop working.\n", listname);
331 fprintf(stderr, "\tYou should consider removing \"STRING:%s\" from the list.\n", memberstruct->name);
335 membervec[1] = "KERBEROS";
336 status = mr_query("add_member_to_list", 3, membervec,
338 if (status != MR_SUCCESS)
339 com_err(whoami, status, "while adding member %s to %s",
340 memberstruct->name, listname);
344 /* Process the delete list */
345 while (sq_get_data(dellist, &memberstruct)) {
346 membervec[0] = listname;
347 membervec[2] = memberstruct->name;
349 printf("Deleting member ");
350 show_list_member(memberstruct);
352 switch (memberstruct->type) {
355 membervec[1] = "USER";
356 status = mr_query("delete_member_from_list", 3, membervec,
358 if (status == MR_SUCCESS)
360 else if ((status != MR_USER && status != MR_NO_MATCH) ||
361 memberstruct->type != M_ANY) {
362 com_err(whoami, status, "while deleting member %s from %s",
363 memberstruct->name, listname);
367 membervec[1] = "LIST";
368 status = mr_query("delete_member_from_list", 3, membervec,
370 if (status == MR_SUCCESS)
372 else if ((status != MR_LIST && status != MR_NO_MATCH) ||
373 memberstruct->type != M_ANY) {
374 if (status == MR_PERM && memberstruct->type == M_ANY) {
375 /* M_ANY means we've fallen through from the user case
376 * The fact that we didn't get MR_PERM there indicates
377 * that we had permission to remove the specified member
378 * from the list if it is a user, but not a list. This is
379 * if we are the member in question. Since we exist as a user
380 * we must have gotten the MR_NO_MATCH error, so we will
381 * return that, since it will be less confusing. However,
382 * This will generate the wrongerror if the user was trying
383 * to remove the list with his/her username from a list they
384 * don't administrate, without explicitly specifying "list:".
386 status = MR_NO_MATCH;
388 com_err(whoami, status, "while deleting member %s from %s",
389 memberstruct->name, listname);
393 membervec[1] = "STRING";
394 status = mr_query("delete_member_from_list", 3, membervec,
396 if (status == MR_STRING && memberstruct->type == M_ANY) {
397 com_err(whoami, 0, " Unable to find member %s to delete from %s",
398 memberstruct->name, listname);
399 if (!strcmp(membervec[0], getenv("USER"))) {
400 fprintf(stderr, "(If you were trying to remove yourself from the list \"%s\",\n", membervec[2]);
401 fprintf(stderr, "the correct command is \"blanche %s -d %s\".)\n",
402 membervec[2], membervec[0]);
404 } else if (status != MR_SUCCESS)
405 com_err(whoami, status, "while deleting member %s from %s",
406 memberstruct->name, listname);
409 membervec[1] = "KERBEROS";
410 status = mr_query("delete_member_from_list", 3, membervec,
412 if (status != MR_SUCCESS)
413 com_err(whoami, status, "while deleting member %s from %s",
414 memberstruct->name, listname);
418 /* Display the members of the list now, if requested */
421 recursive_display_list_members();
423 status = mr_query("get_members_of_list", 1, &listname,
424 get_list_members, (char *)memberlist);
426 com_err(whoami, status, "while getting members of list %s",
428 while (sq_get_data(memberlist, &memberstruct))
429 show_list_member(memberstruct);
441 fprintf(stderr, "Usage: %s listname [options]\n",argv[0]);
442 fprintf(stderr, "Options are\n");
443 fprintf(stderr, " -v | -verbose\n");
444 fprintf(stderr, " -m | -members\n");
445 fprintf(stderr, " -u | -users\n");
446 fprintf(stderr, " -l | -lists\n");
447 fprintf(stderr, " -s | -strings\n");
448 fprintf(stderr, " -k | -kerberos\n");
449 fprintf(stderr, " -i | -info\n");
450 fprintf(stderr, " -r | -recursive\n");
451 fprintf(stderr, " -a | -add member\n");
452 fprintf(stderr, " -d | -delete member\n");
453 fprintf(stderr, " -al | -addlist filename\n");
454 fprintf(stderr, " -dl | -deletelist filename\n");
455 fprintf(stderr, " -f | -file filename\n");
456 fprintf(stderr, " -n | -noauth\n");
457 fprintf(stderr, " -db | -database host[:port]\n");
458 fprintf(stderr, " -D | -debug\n");
463 /* Display the members stored in the queue */
465 show_list_member(memberstruct)
466 struct member *memberstruct;
470 switch (memberstruct->type) {
492 printf("%s\n", memberstruct->name);
497 printf("%s:%s\n", s, memberstruct->name);
499 if (memberstruct->type == M_LIST)
500 printf("LIST:%s\n", memberstruct->name);
501 else if (memberstruct->type == M_KERBEROS)
502 printf("KERBEROS:%s\n", memberstruct->name);
503 else if (memberstruct->type == M_STRING &&
504 !strchr(memberstruct->name, '@'))
505 printf("STRING:%s\n", memberstruct->name);
507 printf("%s\n", memberstruct->name);
512 /* Show the retrieved information about a list */
514 show_list_info(argc, argv, hint)
519 printf("List: %s\n", argv[0]);
520 printf("Description: %s\n", argv[9]);
521 printf("Flags: %s, %s, and %s\n",
522 atoi(argv[1]) ? "active" : "inactive",
523 atoi(argv[2]) ? "public" : "private",
524 atoi(argv[3]) ? "hidden" : "visible");
525 printf("%s is %sa maillist and is %sa group", argv[0],
526 atoi(argv[4]) ? "" : "not ",
527 atoi(argv[5]) ? "" : "not ");
529 printf(" with GID %d\n", atoi(argv[6]));
532 printf("Owner: %s %s\n", argv[7], argv[8]);
533 printf("Last modified by %s with %s on %s\n", argv[11], argv[12], argv[10]);
538 /* Show the retrieve list member count */
540 show_list_count(argc, argv, hint)
545 printf("Members: %s\n", argv[0]);
549 /* Recursively find all of the members of listname, and then display them */
551 recursive_display_list_members()
553 int status, count, savecount;
554 struct save_queue *lists, *members;
555 struct member *m, *m1, *data;
558 members = sq_create();
559 m = (struct member *) malloc(sizeof(struct member));
562 sq_save_data(lists, m);
564 while (sq_get_data(lists, &m)) {
565 sq_destroy(memberlist);
566 memberlist = sq_create();
568 fprintf(stderr, "Fetching members of %s\n", m->name);
569 status = mr_query("get_members_of_list", 1, &(m->name),
570 get_list_members, (char *)memberlist);
572 com_err(whoami, status, "while getting members of list %s", m->name);
573 while (sq_get_data(memberlist, &m1)) {
574 if (m1->type == M_LIST)
575 unique_add_member(lists, m1);
577 unique_add_member(members, m1);
580 savecount = count = sq_count_elts(members);
581 data = (struct member *) malloc(count * sizeof(struct member));
583 while (sq_get_data(members, &m))
584 memcpy(&data[count++], m, sizeof(struct member));
585 qsort(data, count, sizeof(struct member), membercmp);
586 for (count = 0; count < savecount; count++) {
587 show_list_member(&data[count]);
592 /* add a struct member to a queue if that member isn't already there. */
594 unique_add_member(q, m)
595 struct save_queue *q;
598 struct save_queue *qp;
600 for (qp = q->q_next; qp != q; qp = qp->q_next) {
601 if (!membercmp(qp->q_data, m))
608 /* Collect the retrieved members of the list */
610 get_list_members(argc, argv, q)
613 struct save_queue *q;
617 m = (struct member *) malloc(sizeof(struct member));
618 switch (argv[0][0]) {
629 m->type = M_KERBEROS;
632 m->name = strsave(argv[1]);
638 /* Called only if a query returns a value that we weren't expecting */
642 fprintf(stderr, "Programmer botch\n");
647 /* Open file, parse members from file, and put them on the specified queue */
648 get_members_from_file(filename, queue)
650 struct save_queue *queue;
654 struct member *memberstruct;
656 if (!strcmp(filename, "-"))
659 in = fopen(filename, "r");
661 com_err(whoami, errno, "while opening %s for input", filename);
666 while (fgets(buf, BUFSIZ, in))
667 if (memberstruct = parse_member(buf))
668 sq_save_data(queue, memberstruct);
670 com_err(whoami, errno, "while reading from %s", filename);
674 /* Collect the possible expansions of the alias MAILHUB */
676 int collect(argc, argv, list)
683 for (i = 0; (*list)[i]; i++);
684 *list = (char **)realloc(*list, (i + 2) * sizeof(char *));
685 (*list)[i] = strsave(argv[2]);
691 /* Parse a line of input, fetching a member. NULL is returned if a member
692 * is not found. ';' is a comment character.
695 struct member *parse_member(s)
698 register struct member *m;
701 while (*s && isspace(*s))
704 while (*p && *p != '\n' && *p != ';')
705 if (isprint(*p) && !isspace(*p))
711 if (p == s || strlen(s) == 0)
714 if ((m = (struct member *) malloc(sizeof(struct member))) == NULL)
717 if (p = strchr(s, ':')) {
720 if (!strcasecmp("user", s))
722 else if (!strcasecmp("list", s))
724 else if (!strcasecmp("string", s))
726 else if (!strcasecmp("kerberos", s))
727 m->type = M_KERBEROS;
733 m->name = strsave(m->name);
735 m->name = strsave(s);
736 if (strchr(s, '@') || strchr(s, '!') || strchr(s, '%') || strchr(s, ' '))
746 * This routine two compares members by the following rules:
747 * 1. A USER is less than a LIST
748 * 2. A LIST is less than a STRING
749 * 3. If two members are of the same type, the one alphabetically first
750 * is less than the other
751 * It returs < 0 if the first member is less, 0 if they are identical, and
752 * > 0 if the second member is less (the first member is greater).
755 int membercmp(m1, m2)
756 struct member *m1, *m2;
758 if (m1->type == M_ANY || m2->type == M_ANY || (m1->type == m2->type))
759 return(strcmp(m1->name, m2->name));
761 return(m1->type - m2->type);
766 struct save_queue *q;
772 while (sq_get_data(q, &foo))