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();
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 (!noauth && (status = sms_auth("blanche"))) {
165 com_err(whoami, status, " unable to authenticate to SMS");
167 "Try the -noauth flag if you don't need authentication");
171 /* display list info if requested to */
173 status = sms_query("get_list_info", 1, &listname, show_list_info,NULL);
175 com_err(whoami, status, " while getting list information");
176 if (verbose && !memberflg) {
177 status = sms_query("count_members_of_list", 1, &listname,
178 show_list_count, NULL);
180 com_err(whoami, status, " while getting list count");
184 /* if we're synchronizing to a file, we need to:
185 * get the current members of the list
186 * for each member of the sync file
187 * if they are on the list, remove them from the in-memory copy
188 * if they're not on the list, add them to add-list
189 * if anyone is left on the in-memory copy, put them on the delete-list
190 * lastly, reset memberlist so we can use it again later
193 status = sms_query("get_members_of_list", 1, &listname,
194 get_list_members, (char *)memberlist);
196 com_err(whoami, status, " getting members of list %s", listname);
197 while (sq_get_data(synclist, &memberstruct)) {
198 struct save_queue *q;
201 for (q = memberlist->q_next; q != memberlist; q = q->q_next) {
202 if (membercmp(q->q_data, memberstruct) == 0) {
203 q->q_prev->q_next = q->q_next;
204 q->q_next->q_prev = q->q_prev;
210 sq_save_data(addlist, memberstruct);
212 while (sq_get_data(memberlist, &memberstruct))
213 sq_save_data(dellist, memberstruct);
214 sq_destroy(memberlist);
215 memberlist = sq_create();
218 /* Process the add list */
219 while (sq_get_data(addlist, &memberstruct)) {
220 membervec[0] = listname;
221 membervec[2] = memberstruct->name;
222 if (verbose && syncflg) {
223 printf("Adding member ");
224 show_list_member(memberstruct);
226 switch (memberstruct->type) {
229 membervec[1] = "USER";
230 status = sms_query("add_member_to_list", 3, membervec, scream, NULL);
231 if (status == SMS_SUCCESS)
233 else if (status != SMS_USER || memberstruct->type != M_ANY) {
234 com_err(whoami, status, " while adding member %s to %s",
235 memberstruct->name, listname);
239 membervec[1] = "LIST";
240 status = sms_query("add_member_to_list", 3, membervec,
242 if (status == SMS_SUCCESS)
244 else if (status != SMS_LIST || memberstruct->type != M_ANY) {
245 com_err(whoami, status, " while adding member %s to %s",
246 memberstruct->name, listname);
250 membervec[1] = "STRING";
251 status = sms_query("add_member_to_list", 3, membervec,
253 if (status != SMS_SUCCESS)
254 com_err(whoami, status, " while adding member %s to %s",
255 memberstruct->name, listname);
259 /* Process the delete list */
260 while (sq_get_data(dellist, &memberstruct)) {
261 membervec[0] = listname;
262 membervec[2] = memberstruct->name;
263 if (verbose && syncflg) {
264 printf("Deleting member ");
265 show_list_member(memberstruct);
267 switch (memberstruct->type) {
270 membervec[1] = "USER";
271 status = sms_query("delete_member_from_list", 3, membervec,
273 if (status == SMS_SUCCESS)
275 else if ((status != SMS_USER && status != SMS_NO_MATCH) ||
276 memberstruct->type != M_ANY) {
277 com_err(whoami, status, " while deleteing member %s from %s",
278 memberstruct->name, listname);
282 membervec[1] = "LIST";
283 status = sms_query("delete_member_from_list", 3, membervec,
285 if (status == SMS_SUCCESS)
287 else if ((status != SMS_LIST && status != SMS_NO_MATCH) ||
288 memberstruct->type != M_ANY) {
289 com_err(whoami, status, " while deleteing member %s from %s",
290 memberstruct->name, listname);
294 membervec[1] = "STRING";
295 status = sms_query("delete_member_from_list", 3, membervec,
297 if (status == SMS_STRING && memberstruct->type == M_ANY)
298 com_err(whoami, 0, "Unable to find member %s to delete from %s",
299 memberstruct->name, listname);
300 else if (status != SMS_SUCCESS)
301 com_err(whoami, status, " while deleteing member %s from %s",
302 memberstruct->name, listname);
306 /* Display the members of the list now, if requested */
309 recursive_display_list_members();
311 status = sms_query("get_members_of_list", 1, &listname,
312 get_list_members, (char *)memberlist);
314 com_err(whoami, status, " while getting members of list %s",
316 while (sq_get_data(memberlist, &memberstruct))
317 show_list_member(memberstruct);
329 fprintf(stderr, "Usage: %s [options] listname [options]\n",argv[0]);
330 fprintf(stderr, "Options are\n");
331 fprintf(stderr, " -v | -verbose\n");
332 fprintf(stderr, " -m | -members\n");
333 fprintf(stderr, " -u | -users\n");
334 fprintf(stderr, " -l | -lists\n");
335 fprintf(stderr, " -s | -strings\n");
336 fprintf(stderr, " -i | -info\n");
337 fprintf(stderr, " -r | -recursive\n");
338 fprintf(stderr, " -a | -add member\n");
339 fprintf(stderr, " -d | -delete member\n");
340 fprintf(stderr, " -f | -file filename\n");
341 fprintf(stderr, " -n | -noauth\n");
342 fprintf(stderr, " -D | -debug\n");
347 /* Display the members stored in the queue */
349 show_list_member(memberstruct)
350 struct member *memberstruct;
354 switch (memberstruct->type) {
371 printf("%s\n", memberstruct->name);
376 printf("%s:%s\n", s, memberstruct->name);
378 if (memberstruct->type == M_LIST)
379 printf("LIST:%s\n", memberstruct->name);
380 else if (memberstruct->type == M_STRING &&
381 !index(memberstruct->name, '@'))
382 printf("STRING:%s\n", memberstruct->name);
384 printf("%s\n", memberstruct->name);
389 /* Show the retrieved information about a list */
391 show_list_info(argc, argv, hint)
396 printf("List: %s\n", argv[0]);
397 printf("Description: %s\n", argv[9]);
398 printf("Flags: %s, %s, and %s\n",
399 atoi(argv[1]) ? "active" : "inactive",
400 atoi(argv[2]) ? "public" : "private",
401 atoi(argv[3]) ? "hidden" : "visible");
402 printf("%s is %sa maillist and is %sa group", argv[0],
403 atoi(argv[4]) ? "" : "not ",
404 atoi(argv[5]) ? "" : "not ");
406 printf(" with GID %d\n", atoi(argv[6]));
409 printf("Owner: %s %s\n", argv[7], argv[8]);
410 printf("Last modified by %s with %s on %s\n", argv[11], argv[12], argv[10]);
415 /* Show the retrieve list member count */
417 show_list_count(argc, argv, hint)
422 printf("Members: %s\n", argv[0]);
426 /* Recursively find all of the members of listname, and then display them */
428 recursive_display_list_members()
430 int status, count, savecount;
431 struct save_queue *lists, *members;
432 struct member *m, *m1, *data;
435 members = sq_create();
436 m = (struct member *) malloc(sizeof(struct member));
439 sq_save_data(lists, m);
441 while (sq_get_data(lists, &m)) {
442 sq_destroy(memberlist);
443 memberlist = sq_create();
445 fprintf(stderr, "Fetching members of %s\n", m->name);
446 status = sms_query("get_members_of_list", 1, &(m->name),
447 get_list_members, (char *)memberlist);
449 com_err(whoami, status, " while getting members of list %s", m->name);
450 while (sq_get_data(memberlist, &m1)) {
451 if (m1->type == M_LIST)
452 unique_add_member(lists, m1);
454 unique_add_member(members, m1);
457 savecount = count = sq_count_elts(members);
458 data = (struct member *) malloc(count * sizeof(struct member));
460 while (sq_get_data(members, &m))
461 bcopy(m, &data[count++], sizeof(struct member));
462 qsort(data, count, sizeof(struct member), membercmp);
463 for (count = 0; count < savecount; count++) {
464 show_list_member(&data[count]);
469 /* add a struct member to a queue if that member isn't already there. */
471 unique_add_member(q, m)
472 struct save_queue *q;
475 struct save_queue *qp;
477 for (qp = q->q_next; qp != q; qp = qp->q_next) {
478 if (!membercmp(qp->q_data, m))
485 /* Collect the retrieved members of the list */
487 get_list_members(argc, argv, q)
490 struct save_queue *q;
494 m = (struct member *) malloc(sizeof(struct member));
495 switch (argv[0][0]) {
506 m->name = strsave(argv[1]);
512 /* Called only if a query returns a value that we weren't expecting */
516 fprintf(stderr, "Programmer botch\n");
521 /* Parse a line of input, fetching a member. NULL is returned if a member
522 * is not found. Only the first token on the line is parsed. ';' is a
526 struct member *parse_member(s)
529 register struct member *m;
532 while (*s && isspace(*s))
535 while (*p && isprint(*p) && !isspace(*p) && *p != ';')
538 if (p == s || strlen(s) == 0)
541 if ((m = (struct member *) malloc(sizeof(struct member))) == NULL)
544 if (p = index(s, ':')) {
547 if (!strcasecmp("user", s))
549 else if (!strcasecmp("list", s))
551 else if (!strcasecmp("string", s))
558 m->name = strsave(m->name);
561 m->name = strsave(s);
562 if (index(s, '@') || index(s, '!') || index(s, '%'))
571 * This routine two compares members by the following rules:
572 * 1. A USER is less than a LIST
573 * 2. A LIST is less than a STRING
574 * 3. If two members are of the same type, the one alphabetically first
575 * is less than the other
576 * It returs < 0 if the first member is less, 0 if they are identical, and
577 * > 0 if the second member is less (the first member is greater).
580 int membercmp(m1, m2)
581 struct member *m1, *m2;
583 if (m1->type == M_ANY || m2->type == M_ANY || (m1->type == m2->type))
584 return(strcmp(m1->name, m2->name));
586 return(m1->type - m2->type);
591 struct save_queue *q;
597 while (sq_get_data(q, &foo))