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();
87 /* parse args, building addlist, dellist, & synclist */
88 while (++arg - argv < argc) {
91 if (argis("m", "members"))
93 else if (argis("u", "users"))
95 else if (argis("s", "strings"))
97 else if (argis("l", "lists"))
99 else if (argis("k", "kerberos"))
101 else if (argis("D", "debug"))
103 else if (argis("i","info"))
105 else if (argis("n","noauth"))
107 else if (argis("v","verbose"))
109 else if (argis("r","recursive"))
111 else if (argis("S","server") || argis("db","database"))
112 if (arg - argv < argc - 1) {
117 else if (argis("a","add"))
118 if (arg - argv < argc - 1) {
120 if (memberstruct = parse_member(*arg))
121 sq_save_data(addlist, memberstruct);
124 else if (argis("al","addlist"))
125 if (arg - argv < argc - 1) {
127 get_members_from_file(*arg, addlist);
130 else if (argis("d","delete"))
131 if (arg - argv < argc - 1) {
133 if (memberstruct = parse_member(*arg))
134 sq_save_data(dellist, memberstruct);
137 else if (argis("dl","deletelist"))
138 if (arg - argv < argc - 1) {
140 get_members_from_file(*arg, dellist);
143 else if (argis("f","file"))
144 if (arg - argv < argc - 1) {
147 get_members_from_file(*arg, synclist);
153 else if (listname == NULL)
158 if (listname == NULL)
161 /* if no other options specified, turn on list members flag */
162 if (!(infoflg || syncflg ||
163 addlist->q_next != addlist || dellist->q_next != dellist))
166 /* If none of {users,strings,lists,kerberos} specified, turn them all on */
167 if (!(showusers || showstrings || showlists || showkerberos))
168 showusers = showstrings = showlists = showkerberos = 1;
171 if (status = mr_connect(server)) {
172 com_err(whoami, status, "unable to connect to the Moira server");
175 if ( status = mr_motd(&motd) ) {
176 com_err(whoami, status, "unable to check server status");
180 fprintf(stderr, "The Moira server is currently unavailable:\n%s\n", motd);
185 if (!noauth && (status = mr_auth("blanche"))) {
186 if (status == MR_USER_AUTH)
187 com_err(whoami, status, "");
189 com_err(whoami, status, "unable to authenticate to Moira");
191 " Try the -noauth flag if you don't need authentication");
196 /* display list info if requested to */
198 status = mr_query("get_list_info", 1, &listname, show_list_info,NULL);
200 com_err(whoami, status, "while getting list information");
201 if (verbose && !memberflg) {
202 status = mr_query("count_members_of_list", 1, &listname,
203 show_list_count, NULL);
205 com_err(whoami, status, "while getting list count");
209 /* if we're synchronizing to a file, we need to:
210 * get the current members of the list
211 * for each member of the sync file
212 * if they are on the list, remove them from the in-memory copy
213 * if they're not on the list, add them to add-list
214 * if anyone is left on the in-memory copy, put them on the delete-list
215 * lastly, reset memberlist so we can use it again later
218 status = mr_query("get_members_of_list", 1, &listname,
219 get_list_members, (char *)memberlist);
221 com_err(whoami, status, "getting members of list %s", listname);
224 while (sq_get_data(synclist, &memberstruct)) {
225 struct save_queue *q;
228 for (q = memberlist->q_next; q != memberlist; q = q->q_next) {
229 if (membercmp(q->q_data, memberstruct) == 0) {
230 q->q_prev->q_next = q->q_next;
231 q->q_next->q_prev = q->q_prev;
237 sq_save_data(addlist, memberstruct);
239 while (sq_get_data(memberlist, &memberstruct))
240 sq_save_data(dellist, memberstruct);
241 sq_destroy(memberlist);
242 memberlist = sq_create();
245 /* Process the add list */
246 while (sq_get_data(addlist, &memberstruct)) {
247 /* canonicalize string if necessary */
248 if (memberstruct->type == M_STRING &&
249 (p = strchr(memberstruct->name, '@'))) {
250 char *host = canonicalize_hostname(strsave(++p));
251 static char **mailhubs = NULL;
259 mailhubs = (char **)malloc(sizeof(char *));
261 status = mr_query("get_alias", 3, argv, collect,
263 if (status != MR_SUCCESS && status != MR_NO_MATCH) {
264 com_err(whoami, status,
265 " while reading list of MAILHUB servers");
269 for (i = 0; p = mailhubs[i]; i++) {
270 if (!strcasecmp(p, host)) {
271 host = strsave(memberstruct->name);
272 *(strchr(memberstruct->name, '@')) = 0;
273 memberstruct->type = M_ANY;
274 fprintf(stderr, "Warning: \"STRING:%s\" converted to \"%s\" because it is a local name.\n",
275 host, memberstruct->name);
281 /* now continue adding member */
282 membervec[0] = listname;
283 membervec[2] = memberstruct->name;
285 printf("Adding member ");
286 show_list_member(memberstruct);
288 switch (memberstruct->type) {
291 membervec[1] = "USER";
292 status = mr_query("add_member_to_list", 3, membervec, scream, NULL);
293 if (status == MR_SUCCESS)
295 else if (status != MR_USER || memberstruct->type != M_ANY) {
296 com_err(whoami, status, "while adding member %s to %s",
297 memberstruct->name, listname);
302 membervec[1] = "LIST";
303 status = mr_query("add_member_to_list", 3, membervec,
305 if (status == MR_SUCCESS) {
306 if (!strcmp(membervec[0], getenv("USER"))) {
307 fprintf(stderr, "\nWARNING: \"LIST:%s\" was just added to list \"%s\".\n",
308 membervec[2], membervec[0]);
309 fprintf(stderr, "If you meant to add yourself to the list \"%s\", type:\n", membervec[2]);
310 fprintf(stderr, "\tblanche %s -d %s\t(to undo this)\n",
311 membervec[0], membervec[2]);
312 fprintf(stderr, "\tblanche %s -a %s\t(to add yourself to that list)\n",
313 membervec[2], membervec[0]);
316 } else if (status != MR_LIST || memberstruct->type != M_ANY) {
317 com_err(whoami, status, "while adding member %s to %s",
318 memberstruct->name, listname);
323 if (memberstruct->type == M_ANY &&
324 !strchr(memberstruct->name, '@') &&
325 !strchr(memberstruct->name, '!') &&
326 !strchr(memberstruct->name, '%')) {
327 /* if user is trying to add something which isn't a
328 remote string, or a list, or a user, and didn't
329 explicitly specify `STRING:', it's probably a typo */
330 com_err(whoami, MR_NO_MATCH, "while adding member %s to %s",
331 memberstruct->name, listname);
336 membervec[1] = "STRING";
337 status = mr_query("add_member_to_list", 3, membervec,
339 if (status != MR_SUCCESS) {
340 com_err(whoami, status, "while adding member %s to %s",
341 memberstruct->name, listname);
346 membervec[1] = "KERBEROS";
347 status = mr_query("add_member_to_list", 3, membervec,
349 if (status != MR_SUCCESS) {
350 com_err(whoami, status, "while adding member %s to %s",
351 memberstruct->name, listname);
357 /* Process the delete list */
358 while (sq_get_data(dellist, &memberstruct)) {
359 membervec[0] = listname;
360 membervec[2] = memberstruct->name;
362 printf("Deleting member ");
363 show_list_member(memberstruct);
365 switch (memberstruct->type) {
368 membervec[1] = "USER";
369 status = mr_query("delete_member_from_list", 3, membervec,
371 if (status == MR_SUCCESS)
373 else if ((status != MR_USER && status != MR_NO_MATCH) ||
374 memberstruct->type != M_ANY) {
375 com_err(whoami, status, "while deleting member %s from %s",
376 memberstruct->name, listname);
381 membervec[1] = "LIST";
382 status = mr_query("delete_member_from_list", 3, membervec,
384 if (status == MR_SUCCESS)
386 else if ((status != MR_LIST && status != MR_NO_MATCH) ||
387 memberstruct->type != M_ANY) {
388 if (status == MR_PERM && memberstruct->type == M_ANY &&
389 !strcmp(membervec[2], getenv("USER"))) {
390 /* M_ANY means we've fallen through from the user
391 * case. The user is trying to remove himself from
392 * a list, but we got MR_USER or MR_NO_MATCH above,
393 * meaning he's not really on it, and we got MR_PERM
394 * when trying to remove LIST:$USER because he's not
395 * on the acl. That error is useless, so return
396 * MR_NO_MATCH instead. However, this will generate the
397 * wrong error if the user was trying to remove the list
398 * with his username from a list he doesn't administrate
399 * without explicitly specifying "list:".
401 status = MR_NO_MATCH;
403 com_err(whoami, status, "while deleting member %s from %s",
404 memberstruct->name, listname);
409 membervec[1] = "STRING";
410 status = mr_query("delete_member_from_list", 3, membervec,
412 if (status == MR_STRING && memberstruct->type == M_ANY) {
413 com_err(whoami, 0, " Unable to find member %s to delete from %s",
414 memberstruct->name, listname);
416 if (!strcmp(membervec[0], getenv("USER"))) {
417 fprintf(stderr, "(If you were trying to remove yourself from the list \"%s\",\n", membervec[2]);
418 fprintf(stderr, "the correct command is \"blanche %s -d %s\".)\n",
419 membervec[2], membervec[0]);
421 } else if (status != MR_SUCCESS) {
422 com_err(whoami, status, "while deleting member %s from %s",
423 memberstruct->name, listname);
428 membervec[1] = "KERBEROS";
429 status = mr_query("delete_member_from_list", 3, membervec,
431 if (status != MR_SUCCESS) {
432 com_err(whoami, status, "while deleting member %s from %s",
433 memberstruct->name, listname);
439 /* Display the members of the list now, if requested */
442 recursive_display_list_members();
444 status = mr_query("get_members_of_list", 1, &listname,
445 get_list_members, (char *)memberlist);
447 com_err(whoami, status, "while getting members of list %s",
449 while (sq_get_data(memberlist, &memberstruct))
450 show_list_member(memberstruct);
462 fprintf(stderr, "Usage: %s listname [options]\n",argv[0]);
463 fprintf(stderr, "Options are\n");
464 fprintf(stderr, " -v | -verbose\n");
465 fprintf(stderr, " -m | -members\n");
466 fprintf(stderr, " -u | -users\n");
467 fprintf(stderr, " -l | -lists\n");
468 fprintf(stderr, " -s | -strings\n");
469 fprintf(stderr, " -k | -kerberos\n");
470 fprintf(stderr, " -i | -info\n");
471 fprintf(stderr, " -r | -recursive\n");
472 fprintf(stderr, " -a | -add member\n");
473 fprintf(stderr, " -d | -delete member\n");
474 fprintf(stderr, " -al | -addlist filename\n");
475 fprintf(stderr, " -dl | -deletelist filename\n");
476 fprintf(stderr, " -f | -file filename\n");
477 fprintf(stderr, " -n | -noauth\n");
478 fprintf(stderr, " -db | -database host[:port]\n");
479 fprintf(stderr, " -D | -debug\n");
484 /* Display the members stored in the queue */
486 show_list_member(memberstruct)
487 struct member *memberstruct;
491 switch (memberstruct->type) {
513 printf("%s\n", memberstruct->name);
518 printf("%s:%s\n", s, memberstruct->name);
520 if (memberstruct->type == M_LIST)
521 printf("LIST:%s\n", memberstruct->name);
522 else if (memberstruct->type == M_KERBEROS)
523 printf("KERBEROS:%s\n", memberstruct->name);
524 else if (memberstruct->type == M_STRING &&
525 !strchr(memberstruct->name, '@'))
526 printf("STRING:%s\n", memberstruct->name);
528 printf("%s\n", memberstruct->name);
533 /* Show the retrieved information about a list */
535 show_list_info(argc, argv, hint)
540 printf("List: %s\n", argv[0]);
541 printf("Description: %s\n", argv[9]);
542 printf("Flags: %s, %s, and %s\n",
543 atoi(argv[1]) ? "active" : "inactive",
544 atoi(argv[2]) ? "public" : "private",
545 atoi(argv[3]) ? "hidden" : "visible");
546 printf("%s is %sa maillist and is %sa group", argv[0],
547 atoi(argv[4]) ? "" : "not ",
548 atoi(argv[5]) ? "" : "not ");
550 printf(" with GID %d\n", atoi(argv[6]));
553 printf("Owner: %s %s\n", argv[7], argv[8]);
554 printf("Last modified by %s with %s on %s\n", argv[11], argv[12], argv[10]);
559 /* Show the retrieve list member count */
561 show_list_count(argc, argv, hint)
566 printf("Members: %s\n", argv[0]);
570 /* Recursively find all of the members of listname, and then display them */
572 recursive_display_list_members()
574 int status, count, savecount;
575 struct save_queue *lists, *members;
576 struct member *m, *m1, *data;
579 members = sq_create();
580 m = (struct member *) malloc(sizeof(struct member));
583 sq_save_data(lists, m);
585 while (sq_get_data(lists, &m)) {
586 sq_destroy(memberlist);
587 memberlist = sq_create();
589 fprintf(stderr, "Fetching members of %s\n", m->name);
590 status = mr_query("get_members_of_list", 1, &(m->name),
591 get_list_members, (char *)memberlist);
593 com_err(whoami, status, "while getting members of list %s", m->name);
594 while (sq_get_data(memberlist, &m1)) {
595 if (m1->type == M_LIST)
596 unique_add_member(lists, m1);
598 unique_add_member(members, m1);
601 savecount = count = sq_count_elts(members);
602 data = (struct member *) malloc(count * sizeof(struct member));
604 while (sq_get_data(members, &m))
605 memcpy(&data[count++], m, sizeof(struct member));
606 qsort(data, count, sizeof(struct member), membercmp);
607 for (count = 0; count < savecount; count++) {
608 show_list_member(&data[count]);
613 /* add a struct member to a queue if that member isn't already there. */
615 unique_add_member(q, m)
616 struct save_queue *q;
619 struct save_queue *qp;
621 for (qp = q->q_next; qp != q; qp = qp->q_next) {
622 if (!membercmp(qp->q_data, m))
629 /* Collect the retrieved members of the list */
631 get_list_members(argc, argv, q)
634 struct save_queue *q;
638 m = (struct member *) malloc(sizeof(struct member));
639 switch (argv[0][0]) {
650 m->type = M_KERBEROS;
653 m->name = strsave(argv[1]);
659 /* Called only if a query returns a value that we weren't expecting */
663 fprintf(stderr, "Programmer botch\n");
668 /* Open file, parse members from file, and put them on the specified queue */
669 get_members_from_file(filename, queue)
671 struct save_queue *queue;
675 struct member *memberstruct;
677 if (!strcmp(filename, "-"))
680 in = fopen(filename, "r");
682 com_err(whoami, errno, "while opening %s for input", filename);
687 while (fgets(buf, BUFSIZ, in))
688 if (memberstruct = parse_member(buf))
689 sq_save_data(queue, memberstruct);
691 com_err(whoami, errno, "while reading from %s", filename);
697 /* Collect the possible expansions of the alias MAILHUB */
699 int collect(argc, argv, list)
706 for (i = 0; (*list)[i]; i++);
707 *list = (char **)realloc(*list, (i + 2) * sizeof(char *));
708 (*list)[i] = strsave(argv[2]);
714 /* Parse a line of input, fetching a member. NULL is returned if a member
715 * is not found. ';' is a comment character.
718 struct member *parse_member(s)
721 register struct member *m;
724 while (*s && isspace(*s))
727 while (*p && *p != '\n' && *p != ';')
728 if (isprint(*p) && !isspace(*p))
734 if (p == s || strlen(s) == 0)
737 if ((m = (struct member *) malloc(sizeof(struct member))) == NULL)
740 if (p = strchr(s, ':')) {
743 if (!strcasecmp("user", s))
745 else if (!strcasecmp("list", s))
747 else if (!strcasecmp("string", s))
749 else if (!strcasecmp("kerberos", s))
750 m->type = M_KERBEROS;
756 m->name = strsave(m->name);
758 m->name = strsave(s);
766 * This routine two compares members by the following rules:
767 * 1. A USER is less than a LIST
768 * 2. A LIST is less than a STRING
769 * 3. If two members are of the same type, the one alphabetically first
770 * is less than the other
771 * It returs < 0 if the first member is less, 0 if they are identical, and
772 * > 0 if the second member is less (the first member is greater).
775 int membercmp(m1, m2)
776 struct member *m1, *m2;
778 if (m1->type == M_ANY || m2->type == M_ANY || (m1->type == m2->type))
779 return(strcmp(m1->name, m2->name));
781 return(m1->type - m2->type);
786 struct save_queue *q;
792 while (sq_get_data(q, &foo))