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 /* canonicalize string if necessary */
243 if (memberstruct->type == M_STRING &&
244 (p = strchr(memberstruct->name, '@'))) {
245 char *host = canonicalize_hostname(strsave(++p));
246 static char **mailhubs = NULL;
254 mailhubs = (char **)malloc(sizeof(char *));
256 status = mr_query("get_alias", 3, argv, collect,
258 if (status != MR_SUCCESS && status != MR_NO_MATCH) {
259 com_err(whoami, status,
260 " while reading list of MAILHUB servers");
264 for (i = 0; p = mailhubs[i]; i++) {
265 if (!strcasecmp(p, host)) {
266 host = strsave(memberstruct->name);
267 *(strchr(memberstruct->name, '@')) = 0;
268 memberstruct->type = M_ANY;
269 fprintf(stderr, "Warning: \"STRING:%s\" converted to \"%s\" because it is a local name.\n",
270 host, memberstruct->name);
276 /* now continue adding member */
277 membervec[0] = listname;
278 membervec[2] = memberstruct->name;
280 printf("Adding member ");
281 show_list_member(memberstruct);
283 switch (memberstruct->type) {
286 membervec[1] = "USER";
287 status = mr_query("add_member_to_list", 3, membervec, scream, NULL);
288 if (status == MR_SUCCESS)
290 else if (status != MR_USER || memberstruct->type != M_ANY) {
291 com_err(whoami, status, "while adding member %s to %s",
292 memberstruct->name, listname);
296 membervec[1] = "LIST";
297 status = mr_query("add_member_to_list", 3, membervec,
299 if (status == MR_SUCCESS)
301 else if (status != MR_LIST || memberstruct->type != M_ANY) {
302 com_err(whoami, status, "while adding member %s to %s",
303 memberstruct->name, listname);
307 membervec[1] = "STRING";
308 status = mr_query("add_member_to_list", 3, membervec,
310 if (status != MR_SUCCESS)
311 com_err(whoami, status, "while adding member %s to %s",
312 memberstruct->name, listname);
315 membervec[1] = "KERBEROS";
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);
324 /* Process the delete list */
325 while (sq_get_data(dellist, &memberstruct)) {
326 membervec[0] = listname;
327 membervec[2] = memberstruct->name;
329 printf("Deleting member ");
330 show_list_member(memberstruct);
332 switch (memberstruct->type) {
335 membervec[1] = "USER";
336 status = mr_query("delete_member_from_list", 3, membervec,
338 if (status == MR_SUCCESS)
340 else if ((status != MR_USER && status != MR_NO_MATCH) ||
341 memberstruct->type != M_ANY) {
342 com_err(whoami, status, "while deleting member %s from %s",
343 memberstruct->name, listname);
347 membervec[1] = "LIST";
348 status = mr_query("delete_member_from_list", 3, membervec,
350 if (status == MR_SUCCESS)
352 else if ((status != MR_LIST && status != MR_NO_MATCH) ||
353 memberstruct->type != M_ANY) {
354 if (status == MR_PERM && memberstruct->type == M_ANY) {
355 /* M_ANY means we've fallen through from the user case
356 * The fact that we didn't get MR_PERM there indicates
357 * that we had permission to remove the specified member
358 * from the list if it is a user, but not a list. This is
359 * if we are the member in question. Since we exist as a user
360 * we must have gotten the MR_NO_MATCH error, so we will
361 * return that, since it will be less confusing. However,
362 * This will generate the wrongerror if the user was trying
363 * to remove the list with his/her username from a list they
364 * don't administrate, without explicitly specifying "list:".
366 status = MR_NO_MATCH;
368 com_err(whoami, status, "while deleting member %s from %s",
369 memberstruct->name, listname);
373 membervec[1] = "STRING";
374 status = mr_query("delete_member_from_list", 3, membervec,
376 if (status == MR_STRING && memberstruct->type == M_ANY)
377 com_err(whoami, 0, " Unable to find member %s to delete from %s",
378 memberstruct->name, listname);
379 else if (status != MR_SUCCESS)
380 com_err(whoami, status, "while deleting member %s from %s",
381 memberstruct->name, listname);
384 membervec[1] = "KERBEROS";
385 status = mr_query("delete_member_from_list", 3, membervec,
387 if (status != MR_SUCCESS)
388 com_err(whoami, status, "while deleting member %s from %s",
389 memberstruct->name, listname);
393 /* Display the members of the list now, if requested */
396 recursive_display_list_members();
398 status = mr_query("get_members_of_list", 1, &listname,
399 get_list_members, (char *)memberlist);
401 com_err(whoami, status, "while getting members of list %s",
403 while (sq_get_data(memberlist, &memberstruct))
404 show_list_member(memberstruct);
416 fprintf(stderr, "Usage: %s listname [options]\n",argv[0]);
417 fprintf(stderr, "Options are\n");
418 fprintf(stderr, " -v | -verbose\n");
419 fprintf(stderr, " -m | -members\n");
420 fprintf(stderr, " -u | -users\n");
421 fprintf(stderr, " -l | -lists\n");
422 fprintf(stderr, " -s | -strings\n");
423 fprintf(stderr, " -k | -kerberos\n");
424 fprintf(stderr, " -i | -info\n");
425 fprintf(stderr, " -r | -recursive\n");
426 fprintf(stderr, " -a | -add member\n");
427 fprintf(stderr, " -d | -delete member\n");
428 fprintf(stderr, " -al | -addlist filename\n");
429 fprintf(stderr, " -dl | -deletelist filename\n");
430 fprintf(stderr, " -f | -file filename\n");
431 fprintf(stderr, " -n | -noauth\n");
432 fprintf(stderr, " -S | -server host[:port]\n");
433 fprintf(stderr, " -D | -debug\n");
438 /* Display the members stored in the queue */
440 show_list_member(memberstruct)
441 struct member *memberstruct;
445 switch (memberstruct->type) {
467 printf("%s\n", memberstruct->name);
472 printf("%s:%s\n", s, memberstruct->name);
474 if (memberstruct->type == M_LIST)
475 printf("LIST:%s\n", memberstruct->name);
476 else if (memberstruct->type == M_KERBEROS)
477 printf("KERBEROS:%s\n", memberstruct->name);
478 else if (memberstruct->type == M_STRING &&
479 !strchr(memberstruct->name, '@'))
480 printf("STRING:%s\n", memberstruct->name);
482 printf("%s\n", memberstruct->name);
487 /* Show the retrieved information about a list */
489 show_list_info(argc, argv, hint)
494 printf("List: %s\n", argv[0]);
495 printf("Description: %s\n", argv[9]);
496 printf("Flags: %s, %s, and %s\n",
497 atoi(argv[1]) ? "active" : "inactive",
498 atoi(argv[2]) ? "public" : "private",
499 atoi(argv[3]) ? "hidden" : "visible");
500 printf("%s is %sa maillist and is %sa group", argv[0],
501 atoi(argv[4]) ? "" : "not ",
502 atoi(argv[5]) ? "" : "not ");
504 printf(" with GID %d\n", atoi(argv[6]));
507 printf("Owner: %s %s\n", argv[7], argv[8]);
508 printf("Last modified by %s with %s on %s\n", argv[11], argv[12], argv[10]);
513 /* Show the retrieve list member count */
515 show_list_count(argc, argv, hint)
520 printf("Members: %s\n", argv[0]);
524 /* Recursively find all of the members of listname, and then display them */
526 recursive_display_list_members()
528 int status, count, savecount;
529 struct save_queue *lists, *members;
530 struct member *m, *m1, *data;
533 members = sq_create();
534 m = (struct member *) malloc(sizeof(struct member));
537 sq_save_data(lists, m);
539 while (sq_get_data(lists, &m)) {
540 sq_destroy(memberlist);
541 memberlist = sq_create();
543 fprintf(stderr, "Fetching members of %s\n", m->name);
544 status = mr_query("get_members_of_list", 1, &(m->name),
545 get_list_members, (char *)memberlist);
547 com_err(whoami, status, "while getting members of list %s", m->name);
548 while (sq_get_data(memberlist, &m1)) {
549 if (m1->type == M_LIST)
550 unique_add_member(lists, m1);
552 unique_add_member(members, m1);
555 savecount = count = sq_count_elts(members);
556 data = (struct member *) malloc(count * sizeof(struct member));
558 while (sq_get_data(members, &m))
559 memcpy(&data[count++], m, sizeof(struct member));
560 qsort(data, count, sizeof(struct member), membercmp);
561 for (count = 0; count < savecount; count++) {
562 show_list_member(&data[count]);
567 /* add a struct member to a queue if that member isn't already there. */
569 unique_add_member(q, m)
570 struct save_queue *q;
573 struct save_queue *qp;
575 for (qp = q->q_next; qp != q; qp = qp->q_next) {
576 if (!membercmp(qp->q_data, m))
583 /* Collect the retrieved members of the list */
585 get_list_members(argc, argv, q)
588 struct save_queue *q;
592 m = (struct member *) malloc(sizeof(struct member));
593 switch (argv[0][0]) {
604 m->type = M_KERBEROS;
607 m->name = strsave(argv[1]);
613 /* Called only if a query returns a value that we weren't expecting */
617 fprintf(stderr, "Programmer botch\n");
622 /* Open file, parse members from file, and put them on the specified queue */
623 get_members_from_file(filename, queue)
625 struct save_queue *queue;
629 struct member *memberstruct;
631 if (!strcmp(filename, "-"))
634 in = fopen(filename, "r");
636 com_err(whoami, errno, "while opening %s for input", filename);
641 while (fgets(buf, BUFSIZ, in))
642 if (memberstruct = parse_member(buf))
643 sq_save_data(queue, memberstruct);
645 com_err(whoami, errno, "while reading from %s", filename);
649 /* Collect the possible expansions of the alias MAILHUB */
651 int collect(argc, argv, list)
658 for (i = 0; (*list)[i]; i++);
659 *list = (char **)realloc(*list, (i + 2) * sizeof(char *));
660 (*list)[i] = strsave(argv[2]);
666 /* Parse a line of input, fetching a member. NULL is returned if a member
667 * is not found. ';' is a comment character.
670 struct member *parse_member(s)
673 register struct member *m;
676 while (*s && isspace(*s))
679 while (*p && *p != '\n' && *p != ';')
680 if (isprint(*p) && !isspace(*p))
686 if (p == s || strlen(s) == 0)
689 if ((m = (struct member *) malloc(sizeof(struct member))) == NULL)
692 if (p = strchr(s, ':')) {
695 if (!strcasecmp("user", s))
697 else if (!strcasecmp("list", s))
699 else if (!strcasecmp("string", s))
701 else if (!strcasecmp("kerberos", s))
702 m->type = M_KERBEROS;
708 m->name = strsave(m->name);
710 m->name = strsave(s);
711 if (strchr(s, '@') || strchr(s, '!') || strchr(s, '%') || strchr(s, ' '))
721 * This routine two compares members by the following rules:
722 * 1. A USER is less than a LIST
723 * 2. A LIST is less than a STRING
724 * 3. If two members are of the same type, the one alphabetically first
725 * is less than the other
726 * It returs < 0 if the first member is less, 0 if they are identical, and
727 * > 0 if the second member is less (the first member is greater).
730 int membercmp(m1, m2)
731 struct member *m1, *m2;
733 if (m1->type == M_ANY || m2->type == M_ANY || (m1->type == m2->type))
734 return(strcmp(m1->name, m2->name));
736 return(m1->type - m2->type);
741 struct save_queue *q;
747 while (sq_get_data(q, &foo))