]>
Commit | Line | Data |
---|---|---|
2d7360ca | 1 | /* $Header$ |
2 | * | |
3 | * Command line oriented SMS List tool. | |
4 | * | |
5 | * by Mark Rosenstein, September 1988. | |
6 | * | |
7 | * Copyright 1989 by the Massachusetts Institute of Technology. | |
1b6b0a57 | 8 | * |
9 | * (c) Copyright 1988 by the Massachusetts Institute of Technology. | |
10 | * For copying and distribution information, please see the file | |
11 | * <mit-copyright.h>. | |
2d7360ca | 12 | */ |
13 | ||
1b6b0a57 | 14 | /* ### Aren't there a lot of sq abstraction barrier violations here? |
15 | Do we need to improve the support for queue operations? */ | |
16 | ||
2d7360ca | 17 | #include <mit-copyright.h> |
18 | #include <stdio.h> | |
19 | #include <ctype.h> | |
20 | #include <sms.h> | |
21 | #include <sms_app.h> | |
22 | ||
23 | #ifndef LINT | |
24 | static char smslist_rcsid[] = "$Header$"; | |
25 | #endif | |
26 | ||
27 | ||
28 | struct member { | |
29 | int type; | |
30 | char *name; | |
31 | }; | |
32 | ||
1b6b0a57 | 33 | /* member types: ### This should be an enumerated type ? |
34 | It is important to membercmp that M_USER < M_LIST < M_STRING */ | |
2d7360ca | 35 | #define M_ANY 0 |
36 | #define M_USER 1 | |
37 | #define M_LIST 2 | |
38 | #define M_STRING 3 | |
39 | ||
1b6b0a57 | 40 | /* argument parsing macro */ |
41 | #define argis(a,b) ((strcmp(*arg+1, a) == 0) || (strcmp(*arg+1, b) == 0)) | |
42 | ||
2d7360ca | 43 | /* flags from command line */ |
1b6b0a57 | 44 | int infoflg, verbose, syncflg, memberflg, recursflg, debugflg; |
2d7360ca | 45 | |
46 | /* various member lists */ | |
47 | struct save_queue *addlist, *dellist, *memberlist, *synclist; | |
48 | ||
49 | char *listname, *whoami; | |
50 | ||
51 | extern char *index(); | |
52 | extern int errno; | |
53 | ||
54 | int show_list_info(), show_list_count(), get_list_members(), scream(); | |
1b6b0a57 | 55 | int show_list_members(); |
2d7360ca | 56 | struct member *parse_member(); |
57 | ||
58 | ||
59 | ||
60 | main(argc, argv) | |
61 | int argc; | |
62 | char **argv; | |
63 | { | |
64 | int status; | |
65 | char **arg = argv; | |
66 | char *membervec[3]; | |
67 | struct member *memberstruct; | |
68 | ||
69 | /* clear all flags & lists */ | |
1b6b0a57 | 70 | infoflg = verbose = syncflg = memberflg = debugflg = recursflg = 0; |
2d7360ca | 71 | listname = NULL; |
72 | addlist = sq_create(); | |
73 | dellist = sq_create(); | |
74 | memberlist = sq_create(); | |
75 | synclist = sq_create(); | |
76 | whoami = argv[0]; | |
77 | ||
78 | /* parse args, building addlist, dellist, & synclist */ | |
79 | while (++arg - argv < argc) { | |
80 | if (**arg == '-') | |
1b6b0a57 | 81 | { |
82 | if (argis("m", "members")) | |
83 | memberflg++; | |
84 | else if (argis("D", "debug")) | |
85 | debugflg++; | |
86 | else if (argis("i","information")) | |
2d7360ca | 87 | infoflg++; |
1b6b0a57 | 88 | else if (argis("v","verbose")) |
2d7360ca | 89 | verbose++; |
1b6b0a57 | 90 | else if (argis("r","recursive")) |
91 | recursflg++; | |
92 | else if (argis("a","add")) | |
93 | if (arg - argv < argc - 1) { | |
94 | ++arg; | |
95 | if (memberstruct = parse_member(*arg)) | |
96 | sq_save_data(addlist, memberstruct); | |
97 | } else | |
98 | usage(argv); | |
99 | else if (argis("d","delete")) | |
100 | if (arg - argv < argc - 1) { | |
101 | ++arg; | |
102 | if (memberstruct = parse_member(*arg)) | |
103 | sq_save_data(dellist, memberstruct); | |
104 | } else | |
105 | usage(argv); | |
106 | else if (argis("f","file")) | |
107 | if (arg - argv < argc - 1) { | |
108 | FILE *in; | |
109 | char buf[BUFSIZ]; | |
110 | ||
111 | syncflg++; | |
112 | ++arg; | |
113 | if (!strcmp(*arg, "-")) | |
114 | in = stdin; | |
115 | else { | |
116 | in = fopen(*arg, "r"); | |
117 | if (!in) { | |
118 | com_err(whoami, errno, | |
119 | " while opening %s for input", *arg); | |
120 | exit(2); | |
121 | } | |
122 | } | |
123 | while (fgets(buf, BUFSIZ, in)) | |
124 | if (memberstruct = parse_member(buf)) | |
125 | sq_save_data(synclist, memberstruct); | |
126 | if (!feof(in)) | |
127 | com_err(whoami, errno, " while reading from %s", *arg); | |
128 | } else | |
129 | usage(argv); | |
130 | else | |
2d7360ca | 131 | usage(argv); |
1b6b0a57 | 132 | } |
2d7360ca | 133 | else if (listname == NULL) |
134 | listname = *arg; | |
135 | else | |
136 | usage(argv); | |
137 | } | |
138 | if (listname == NULL) | |
139 | usage(argv); | |
140 | ||
141 | /* if no other options specified, turn on list members flag */ | |
142 | if (!(infoflg || syncflg || | |
143 | addlist->q_next != addlist || dellist->q_next != dellist)) | |
144 | memberflg++; | |
145 | ||
146 | /* fire up SMS */ | |
147 | if (status = sms_connect()) { | |
148 | com_err(whoami, status, " unable to connect to SMS"); | |
149 | exit(2); | |
150 | } | |
151 | if (status = sms_auth("blanche")) { | |
152 | com_err(whoami, status, " unable to authenticate to SMS"); | |
153 | exit(2); | |
154 | } | |
155 | ||
156 | /* display list info if requested to */ | |
157 | if (infoflg) { | |
158 | status = sms_query("get_list_info", 1, &listname, show_list_info,NULL); | |
159 | if (status) | |
160 | com_err(whoami, status, " while getting list information"); | |
161 | if (verbose && !memberflg) { | |
162 | status = sms_query("count_members_of_list", 1, &listname, | |
163 | show_list_count, NULL); | |
164 | if (status) | |
165 | com_err(whoami, status, " while getting list count"); | |
166 | } | |
167 | } | |
168 | ||
169 | /* if we're synchronizing to a file, we need to: | |
170 | * get the current members of the list | |
171 | * for each member of the sync file | |
172 | * if they are on the list, remove them from the in-memory copy | |
173 | * if they're not on the list, add them to add-list | |
174 | * if anyone is left on the in-memory copy, put them on the delete-list | |
175 | * lastly, reset memberlist so we can use it again later | |
176 | */ | |
177 | if (syncflg) { | |
178 | status = sms_query("get_members_of_list", 1, &listname, | |
1b6b0a57 | 179 | get_list_members, (char *)memberlist); |
2d7360ca | 180 | if (status) |
181 | com_err(whoami, status, " while getting members of list"); | |
182 | while (sq_get_data(synclist, &memberstruct)) { | |
183 | struct save_queue *q; | |
184 | int removed = 0; | |
185 | ||
186 | for (q = memberlist->q_next; q != memberlist; q = q->q_next) { | |
1b6b0a57 | 187 | if (membercmp(q->q_data, memberstruct) == 0) { |
2d7360ca | 188 | q->q_prev->q_next = q->q_next; |
189 | q->q_next->q_prev = q->q_prev; | |
190 | removed++; | |
191 | break; | |
192 | } | |
193 | } | |
194 | if (!removed) | |
195 | sq_save_data(addlist, memberstruct); | |
196 | } | |
197 | while (sq_get_data(memberlist, &memberstruct)) | |
198 | sq_save_data(dellist, memberstruct); | |
199 | sq_destroy(memberlist); | |
200 | memberlist = sq_create(); | |
201 | } | |
202 | ||
203 | /* Process the add list */ | |
204 | while (sq_get_data(addlist, &memberstruct)) { | |
205 | membervec[0] = listname; | |
206 | membervec[2] = memberstruct->name; | |
207 | switch (memberstruct->type) { | |
208 | case M_ANY: | |
209 | case M_USER: | |
210 | membervec[1] = "USER"; | |
211 | status = sms_query("add_member_to_list", 3, membervec, scream, NULL); | |
212 | if (status == SMS_SUCCESS) | |
213 | break; | |
214 | else if (status != SMS_USER || memberstruct->type != M_ANY) { | |
215 | com_err(whoami, status, " while adding member %s to %s", | |
216 | memberstruct->name, listname); | |
217 | break; | |
218 | } | |
219 | case M_LIST: | |
220 | membervec[1] = "LIST"; | |
221 | status = sms_query("add_member_to_list", 3, membervec, | |
222 | scream, NULL); | |
223 | if (status == SMS_SUCCESS) | |
224 | break; | |
225 | else if (status != SMS_LIST || memberstruct->type != M_ANY) { | |
226 | com_err(whoami, status, " while adding member %s to %s", | |
227 | memberstruct->name, listname); | |
228 | break; | |
229 | } | |
230 | case M_STRING: | |
231 | membervec[1] = "STRING"; | |
232 | status = sms_query("add_member_to_list", 3, membervec, | |
233 | scream, NULL); | |
234 | if (status != SMS_SUCCESS) | |
235 | com_err(whoami, status, " while adding member %s to %s", | |
236 | memberstruct->name, listname); | |
237 | } | |
238 | } | |
239 | ||
240 | /* Process the delete list */ | |
241 | while (sq_get_data(dellist, &memberstruct)) { | |
242 | membervec[0] = listname; | |
243 | membervec[2] = memberstruct->name; | |
244 | switch (memberstruct->type) { | |
245 | case M_ANY: | |
246 | case M_USER: | |
247 | membervec[1] = "USER"; | |
248 | status = sms_query("delete_member_from_list", 3, membervec, | |
249 | scream, NULL); | |
250 | if (status == SMS_SUCCESS) | |
251 | break; | |
252 | else if ((status != SMS_USER && status != SMS_NO_MATCH) || | |
253 | memberstruct->type != M_ANY) { | |
254 | com_err(whoami, status, " while deleteing member %s from %s", | |
255 | memberstruct->name, listname); | |
256 | break; | |
257 | } | |
258 | case M_LIST: | |
259 | membervec[1] = "LIST"; | |
260 | status = sms_query("delete_member_from_list", 3, membervec, | |
261 | scream, NULL); | |
262 | if (status == SMS_SUCCESS) | |
263 | break; | |
264 | else if ((status != SMS_LIST && status != SMS_NO_MATCH) || | |
265 | memberstruct->type != M_ANY) { | |
266 | com_err(whoami, status, " while deleteing member %s from %s", | |
267 | memberstruct->name, listname); | |
268 | break; | |
269 | } | |
270 | case M_STRING: | |
271 | membervec[1] = "STRING"; | |
272 | status = sms_query("delete_member_from_list", 3, membervec, | |
273 | scream, NULL); | |
274 | if (status == SMS_STRING && memberstruct->type == M_ANY) | |
275 | com_err(whoami, 0, "Unable to find member %s to delete from %s", | |
276 | memberstruct->name, listname); | |
277 | else if (status != SMS_SUCCESS) | |
278 | com_err(whoami, status, " while deleteing member %s from %s", | |
279 | memberstruct->name, listname); | |
280 | } | |
281 | } | |
282 | ||
283 | /* Display the members of the list now, if requested */ | |
1b6b0a57 | 284 | if (memberflg) |
285 | display_list_members(); | |
2d7360ca | 286 | |
287 | /* We're done! */ | |
288 | sms_disconnect(); | |
289 | exit(0); | |
290 | } | |
291 | ||
292 | usage(argv) | |
293 | char **argv; | |
294 | { | |
1b6b0a57 | 295 | fprintf(stderr, "Usage: %s [options] listname [options]\n",argv[0]); |
296 | fprintf(stderr, "Options are\n"); | |
297 | fprintf(stderr, " -v | -verbose\n"); | |
298 | fprintf(stderr, " -m | -members\n"); | |
299 | fprintf(stderr, " -i | -info\n"); | |
300 | fprintf(stderr, " -r | -recursive\n"); | |
301 | fprintf(stderr, " -a | -add member\n"); | |
302 | fprintf(stderr, " -d | -delete member\n"); | |
303 | fprintf(stderr, " -f | -file filename\n"); | |
304 | #ifdef notdef | |
305 | fprintf(stderr, " -D | -debug\n"); | |
306 | #endif | |
2d7360ca | 307 | exit(1); |
308 | } | |
309 | ||
310 | ||
1b6b0a57 | 311 | show_list_members(memberlist) |
312 | struct sq *memberlist; | |
313 | { | |
314 | struct member *memberstruct; | |
315 | ||
316 | while (sq_get_data(memberlist, &memberstruct)) { | |
317 | if (verbose) { | |
318 | char *s; | |
319 | switch (memberstruct->type) { | |
320 | case M_USER: | |
321 | s = "USER"; | |
322 | break; | |
323 | case M_LIST: | |
324 | s = "LIST"; | |
325 | break; | |
326 | case M_STRING: | |
327 | s = "STRING"; | |
328 | break; | |
329 | } | |
330 | printf("%s:%s\n", s, memberstruct->name); | |
331 | } else { | |
332 | if (memberstruct->type == M_LIST) | |
333 | printf("LIST:%s\n", memberstruct->name); | |
334 | else if (memberstruct->type == M_STRING && | |
335 | !index(memberstruct->name, '@')) | |
336 | printf("STRING:%s\n", memberstruct->name); | |
337 | else | |
338 | printf("%s\n", memberstruct->name); | |
339 | } | |
340 | } | |
341 | } | |
342 | ||
2d7360ca | 343 | show_list_info(argc, argv, hint) |
344 | int argc; | |
345 | char **argv; | |
346 | int hint; | |
347 | { | |
348 | printf("List: %s\n", argv[0]); | |
349 | printf("Description: %s\n", argv[9]); | |
350 | printf("Flags: %s, %s, and %s\n", | |
351 | atoi(argv[1]) ? "active" : "inactive", | |
352 | atoi(argv[2]) ? "public" : "private", | |
353 | atoi(argv[3]) ? "hidden" : "visible"); | |
1b6b0a57 | 354 | printf("%s is %sa maillist and is %sa group", argv[0], |
2d7360ca | 355 | atoi(argv[4]) ? "" : "not ", |
356 | atoi(argv[5]) ? "" : "not "); | |
357 | if (atoi(argv[5])) | |
358 | printf(" with GID %d\n", atoi(argv[6])); | |
359 | else | |
360 | printf("\n"); | |
361 | printf("Owner: %s %s\n", argv[7], argv[8]); | |
362 | printf("Last modified by %s with %s on %s\n", argv[11], argv[12], argv[10]); | |
363 | return(SMS_CONT); | |
364 | } | |
365 | ||
366 | ||
367 | show_list_count(argc, argv, hint) | |
368 | int argc; | |
369 | char **argv; | |
370 | int hint; | |
371 | { | |
372 | printf("Members: %s\n", argv[0]); | |
373 | } | |
374 | ||
375 | ||
1b6b0a57 | 376 | display_list_members() |
377 | { | |
378 | int status; | |
379 | ||
380 | status = sms_query("get_members_of_list", 1, &listname, | |
381 | get_list_members, (char *)memberlist); | |
382 | if (status) | |
383 | com_err(whoami, status, " while getting members of list"); | |
384 | if (recursflg) | |
385 | fprintf(stderr,"%s: The recursive flag is not yet implemented.\n", | |
386 | whoami); | |
387 | show_list_members(memberlist); | |
388 | } | |
389 | ||
2d7360ca | 390 | get_list_members(argc, argv, q) |
391 | int argc; | |
392 | char **argv; | |
393 | struct save_queue *q; | |
394 | { | |
395 | struct member *m; | |
396 | ||
397 | m = (struct member *) malloc(sizeof(struct member)); | |
398 | switch (argv[0][0]) { | |
399 | case 'U': | |
400 | m->type = M_USER; | |
401 | break; | |
402 | case 'L': | |
403 | m->type = M_LIST; | |
404 | break; | |
405 | case 'S': | |
406 | m->type = M_STRING; | |
407 | break; | |
408 | } | |
409 | m->name = strsave(argv[1]); | |
410 | sq_save_data(q, m); | |
411 | return(SMS_CONT); | |
412 | } | |
413 | ||
414 | ||
415 | scream() | |
416 | { | |
417 | fprintf(stderr, "Programmer botch\n"); | |
418 | exit(3); | |
419 | } | |
420 | ||
421 | ||
422 | struct member *parse_member(s) | |
423 | register char *s; | |
424 | { | |
425 | register struct member *m; | |
426 | char *p; | |
427 | ||
428 | while (*s && isspace(*s)) | |
429 | s++; | |
430 | p = s; | |
431 | while (*p && isprint(*p) && !isspace(*p) && *p != ';') | |
432 | p++; | |
433 | *p = 0; | |
434 | if (p == s || strlen(s) == 0) | |
435 | return(NULL); | |
436 | ||
437 | if ((m = (struct member *) malloc(sizeof(struct member))) == NULL) | |
438 | return(NULL); | |
439 | ||
440 | if (p = index(s, ':')) { | |
441 | *p = 0; | |
442 | m->name = ++p; | |
443 | if (!strcasecmp("user", s)) | |
444 | m->type = M_USER; | |
445 | else if (!strcasecmp("list", s)) | |
446 | m->type = M_LIST; | |
447 | else if (!strcasecmp("string", s)) | |
448 | m->type = M_STRING; | |
449 | else { | |
450 | m->type = M_STRING; | |
451 | *(--p) = ':'; | |
452 | m->name = s; | |
453 | } | |
454 | m->name = strsave(m->name); | |
455 | return(m); | |
456 | } | |
457 | m->name = strsave(s); | |
458 | if (index(s, '@') || index(s, '!') || index(s, '%')) | |
459 | m->type = M_STRING; | |
460 | else | |
461 | m->type = M_ANY; | |
462 | return(m); | |
463 | } | |
464 | ||
465 | ||
1b6b0a57 | 466 | int membercmp(m1, m2) |
467 | struct member *m1, *m2; | |
468 | /* | |
469 | * This routine two compares members by the following rules: | |
470 | * 1. A USER is less than a LIST | |
471 | * 2. A LIST is less than a STRING | |
472 | * 3. If two members are of the same type, the one alphabetically first | |
473 | * is less than the other | |
474 | * It returs < 0 if the first member is less, 0 if they are identical, and | |
475 | * > 0 if the second member is less (the first member is greater). | |
476 | */ | |
2d7360ca | 477 | { |
1b6b0a57 | 478 | if (m1->type == M_ANY || m2->type == M_ANY || (m1->type == m2->type)) |
479 | return(strcmp(m1->name, m2->name)); | |
2d7360ca | 480 | else |
1b6b0a57 | 481 | return(m1->type - m2->type); |
2d7360ca | 482 | } |