3 * Command line oriented SMS 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 static char smslist_rcsid[] = "$Header$";
33 /* It is important to membercmp that M_USER < M_LIST < M_STRING */
39 /* argument parsing macro */
40 #define argis(a,b) ((strcmp(*arg+1, a) == 0) || (strcmp(*arg+1, b) == 0))
42 /* flags from command line */
43 int infoflg, verbose, syncflg, memberflg, recursflg, debugflg, noauth;
44 int showusers, showstrings, showlists;
46 /* various member lists */
47 struct save_queue *addlist, *dellist, *memberlist, *synclist;
49 char *listname, *whoami;
54 int show_list_info(), show_list_count(), get_list_members(), scream();
55 int show_list_members();
56 struct member *parse_member();
66 char *membervec[3], *motd;
67 struct member *memberstruct;
69 /* clear all flags & lists */
70 infoflg = verbose = syncflg = memberflg = debugflg = recursflg = 0;
71 noauth = showusers = showstrings = showlists = 0;
73 addlist = sq_create();
74 dellist = sq_create();
75 memberlist = sq_create();
76 synclist = sq_create();
79 /* parse args, building addlist, dellist, & synclist */
80 while (++arg - argv < argc) {
83 if (argis("m", "members"))
85 else if (argis("u", "users"))
87 else if (argis("s", "strings"))
89 else if (argis("l", "lists"))
91 else if (argis("D", "debug"))
93 else if (argis("i","information"))
95 else if (argis("n","noauth"))
97 else if (argis("v","verbose"))
99 else if (argis("r","recursive"))
101 else if (argis("a","add"))
102 if (arg - argv < argc - 1) {
104 if (memberstruct = parse_member(*arg))
105 sq_save_data(addlist, memberstruct);
108 else if (argis("d","delete"))
109 if (arg - argv < argc - 1) {
111 if (memberstruct = parse_member(*arg))
112 sq_save_data(dellist, memberstruct);
115 else if (argis("f","file"))
116 if (arg - argv < argc - 1) {
122 if (!strcmp(*arg, "-"))
125 in = fopen(*arg, "r");
127 com_err(whoami, errno,
128 " while opening %s for input", *arg);
132 while (fgets(buf, BUFSIZ, in))
133 if (memberstruct = parse_member(buf))
134 sq_save_data(synclist, memberstruct);
136 com_err(whoami, errno, " while reading from %s", *arg);
142 else if (listname == NULL)
147 if (listname == NULL)
150 /* if no other options specified, turn on list members flag */
151 if (!(infoflg || syncflg ||
152 addlist->q_next != addlist || dellist->q_next != dellist))
155 /* If none of {users,strings,lists} specified, turn them all on */
156 if (!(showusers || showstrings || showlists))
157 showusers = showstrings = showlists = 1;
160 if (status = sms_connect(SMS_SERVER)) {
161 com_err(whoami, status, " unable to connect to SMS");
164 if ( status = sms_motd(&motd) ) {
165 com_err(whoami, status, " unable to check server status");
169 fprintf(stderr, "The SMS server is currently unavailable:\n%s\n", motd);
174 if (!noauth && (status = sms_auth("blanche"))) {
175 com_err(whoami, status, " unable to authenticate to SMS");
177 "Try the -noauth flag if you don't need authentication");
181 /* display list info if requested to */
183 status = sms_query("get_list_info", 1, &listname, show_list_info,NULL);
185 com_err(whoami, status, " while getting list information");
186 if (verbose && !memberflg) {
187 status = sms_query("count_members_of_list", 1, &listname,
188 show_list_count, NULL);
190 com_err(whoami, status, " while getting list count");
194 /* if we're synchronizing to a file, we need to:
195 * get the current members of the list
196 * for each member of the sync file
197 * if they are on the list, remove them from the in-memory copy
198 * if they're not on the list, add them to add-list
199 * if anyone is left on the in-memory copy, put them on the delete-list
200 * lastly, reset memberlist so we can use it again later
203 status = sms_query("get_members_of_list", 1, &listname,
204 get_list_members, (char *)memberlist);
206 com_err(whoami, status, " getting members of list %s", listname);
207 while (sq_get_data(synclist, &memberstruct)) {
208 struct save_queue *q;
211 for (q = memberlist->q_next; q != memberlist; q = q->q_next) {
212 if (membercmp(q->q_data, memberstruct) == 0) {
213 q->q_prev->q_next = q->q_next;
214 q->q_next->q_prev = q->q_prev;
220 sq_save_data(addlist, memberstruct);
222 while (sq_get_data(memberlist, &memberstruct))
223 sq_save_data(dellist, memberstruct);
224 sq_destroy(memberlist);
225 memberlist = sq_create();
228 /* Process the add list */
229 while (sq_get_data(addlist, &memberstruct)) {
230 membervec[0] = listname;
231 membervec[2] = memberstruct->name;
232 if (verbose && syncflg) {
233 printf("Adding member ");
234 show_list_member(memberstruct);
236 switch (memberstruct->type) {
239 membervec[1] = "USER";
240 status = sms_query("add_member_to_list", 3, membervec, scream, NULL);
241 if (status == SMS_SUCCESS)
243 else if (status != SMS_USER || memberstruct->type != M_ANY) {
244 com_err(whoami, status, " while adding member %s to %s",
245 memberstruct->name, listname);
249 membervec[1] = "LIST";
250 status = sms_query("add_member_to_list", 3, membervec,
252 if (status == SMS_SUCCESS)
254 else if (status != SMS_LIST || memberstruct->type != M_ANY) {
255 com_err(whoami, status, " while adding member %s to %s",
256 memberstruct->name, listname);
260 membervec[1] = "STRING";
261 status = sms_query("add_member_to_list", 3, membervec,
263 if (status != SMS_SUCCESS)
264 com_err(whoami, status, " while adding member %s to %s",
265 memberstruct->name, listname);
269 /* Process the delete list */
270 while (sq_get_data(dellist, &memberstruct)) {
271 membervec[0] = listname;
272 membervec[2] = memberstruct->name;
273 if (verbose && syncflg) {
274 printf("Deleting member ");
275 show_list_member(memberstruct);
277 switch (memberstruct->type) {
280 membervec[1] = "USER";
281 status = sms_query("delete_member_from_list", 3, membervec,
283 if (status == SMS_SUCCESS)
285 else if ((status != SMS_USER && status != SMS_NO_MATCH) ||
286 memberstruct->type != M_ANY) {
287 com_err(whoami, status, " while deleteing member %s from %s",
288 memberstruct->name, listname);
292 membervec[1] = "LIST";
293 status = sms_query("delete_member_from_list", 3, membervec,
295 if (status == SMS_SUCCESS)
297 else if ((status != SMS_LIST && status != SMS_NO_MATCH) ||
298 memberstruct->type != M_ANY) {
299 com_err(whoami, status, " while deleteing member %s from %s",
300 memberstruct->name, listname);
304 membervec[1] = "STRING";
305 status = sms_query("delete_member_from_list", 3, membervec,
307 if (status == SMS_STRING && memberstruct->type == M_ANY)
308 com_err(whoami, 0, "Unable to find member %s to delete from %s",
309 memberstruct->name, listname);
310 else if (status != SMS_SUCCESS)
311 com_err(whoami, status, " while deleteing member %s from %s",
312 memberstruct->name, listname);
316 /* Display the members of the list now, if requested */
319 recursive_display_list_members();
321 status = sms_query("get_members_of_list", 1, &listname,
322 get_list_members, (char *)memberlist);
324 com_err(whoami, status, " while getting members of list %s",
326 while (sq_get_data(memberlist, &memberstruct))
327 show_list_member(memberstruct);
339 fprintf(stderr, "Usage: %s [options] listname [options]\n",argv[0]);
340 fprintf(stderr, "Options are\n");
341 fprintf(stderr, " -v | -verbose\n");
342 fprintf(stderr, " -m | -members\n");
343 fprintf(stderr, " -u | -users\n");
344 fprintf(stderr, " -l | -lists\n");
345 fprintf(stderr, " -s | -strings\n");
346 fprintf(stderr, " -i | -info\n");
347 fprintf(stderr, " -r | -recursive\n");
348 fprintf(stderr, " -a | -add member\n");
349 fprintf(stderr, " -d | -delete member\n");
350 fprintf(stderr, " -f | -file filename\n");
351 fprintf(stderr, " -n | -noauth\n");
352 fprintf(stderr, " -D | -debug\n");
357 /* Display the members stored in the queue */
359 show_list_member(memberstruct)
360 struct member *memberstruct;
364 switch (memberstruct->type) {
381 printf("%s\n", memberstruct->name);
386 printf("%s:%s\n", s, memberstruct->name);
388 if (memberstruct->type == M_LIST)
389 printf("LIST:%s\n", memberstruct->name);
390 else if (memberstruct->type == M_STRING &&
391 !index(memberstruct->name, '@'))
392 printf("STRING:%s\n", memberstruct->name);
394 printf("%s\n", memberstruct->name);
399 /* Show the retrieved information about a list */
401 show_list_info(argc, argv, hint)
406 printf("List: %s\n", argv[0]);
407 printf("Description: %s\n", argv[9]);
408 printf("Flags: %s, %s, and %s\n",
409 atoi(argv[1]) ? "active" : "inactive",
410 atoi(argv[2]) ? "public" : "private",
411 atoi(argv[3]) ? "hidden" : "visible");
412 printf("%s is %sa maillist and is %sa group", argv[0],
413 atoi(argv[4]) ? "" : "not ",
414 atoi(argv[5]) ? "" : "not ");
416 printf(" with GID %d\n", atoi(argv[6]));
419 printf("Owner: %s %s\n", argv[7], argv[8]);
420 printf("Last modified by %s with %s on %s\n", argv[11], argv[12], argv[10]);
425 /* Show the retrieve list member count */
427 show_list_count(argc, argv, hint)
432 printf("Members: %s\n", argv[0]);
436 /* Recursively find all of the members of listname, and then display them */
438 recursive_display_list_members()
440 int status, count, savecount;
441 struct save_queue *lists, *members;
442 struct member *m, *m1, *data;
445 members = sq_create();
446 m = (struct member *) malloc(sizeof(struct member));
449 sq_save_data(lists, m);
451 while (sq_get_data(lists, &m)) {
452 sq_destroy(memberlist);
453 memberlist = sq_create();
455 fprintf(stderr, "Fetching members of %s\n", m->name);
456 status = sms_query("get_members_of_list", 1, &(m->name),
457 get_list_members, (char *)memberlist);
459 com_err(whoami, status, " while getting members of list %s", m->name);
460 while (sq_get_data(memberlist, &m1)) {
461 if (m1->type == M_LIST)
462 unique_add_member(lists, m1);
464 unique_add_member(members, m1);
467 savecount = count = sq_count_elts(members);
468 data = (struct member *) malloc(count * sizeof(struct member));
470 while (sq_get_data(members, &m))
471 bcopy(m, &data[count++], sizeof(struct member));
472 qsort(data, count, sizeof(struct member), membercmp);
473 for (count = 0; count < savecount; count++) {
474 show_list_member(&data[count]);
479 /* add a struct member to a queue if that member isn't already there. */
481 unique_add_member(q, m)
482 struct save_queue *q;
485 struct save_queue *qp;
487 for (qp = q->q_next; qp != q; qp = qp->q_next) {
488 if (!membercmp(qp->q_data, m))
495 /* Collect the retrieved members of the list */
497 get_list_members(argc, argv, q)
500 struct save_queue *q;
504 m = (struct member *) malloc(sizeof(struct member));
505 switch (argv[0][0]) {
516 m->name = strsave(argv[1]);
522 /* Called only if a query returns a value that we weren't expecting */
526 fprintf(stderr, "Programmer botch\n");
531 /* Parse a line of input, fetching a member. NULL is returned if a member
532 * is not found. Only the first token on the line is parsed. ';' is a
536 struct member *parse_member(s)
539 register struct member *m;
542 while (*s && isspace(*s))
545 while (*p && isprint(*p) && !isspace(*p) && *p != ';')
548 if (p == s || strlen(s) == 0)
551 if ((m = (struct member *) malloc(sizeof(struct member))) == NULL)
554 if (p = index(s, ':')) {
557 if (!strcasecmp("user", s))
559 else if (!strcasecmp("list", s))
561 else if (!strcasecmp("string", s))
568 m->name = strsave(m->name);
571 m->name = strsave(s);
572 if (index(s, '@') || index(s, '!') || index(s, '%'))
581 * This routine two compares members by the following rules:
582 * 1. A USER is less than a LIST
583 * 2. A LIST is less than a STRING
584 * 3. If two members are of the same type, the one alphabetically first
585 * is less than the other
586 * It returs < 0 if the first member is less, 0 if they are identical, and
587 * > 0 if the second member is less (the first member is greater).
590 int membercmp(m1, m2)
591 struct member *m1, *m2;
593 if (m1->type == M_ANY || m2->type == M_ANY || (m1->type == m2->type))
594 return(strcmp(m1->name, m2->name));
596 return(m1->type - m2->type);
601 struct save_queue *q;
607 while (sq_get_data(q, &foo))