]> andersk Git - moira.git/blob - clients/blanche/blanche.c
Default to [ANY] for mailman_server in the ulis case if we're setting
[moira.git] / clients / blanche / blanche.c
1 /* $Id$
2  *
3  * Command line oriented Moira List tool.
4  *
5  * by Mark Rosenstein, September 1988.
6  *
7  * Copyright (C) 1988-1998 by the Massachusetts Institute of Technology.
8  * For copying and distribution information, please see the file
9  * <mit-copyright.h>.
10  */
11
12 #include <mit-copyright.h>
13 #include <moira.h>
14 #include <moira_site.h>
15 #include <mrclient.h>
16
17 #include <ctype.h>
18 #include <errno.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22
23 RCSID("$Header$");
24
25 struct member {
26   int type;
27   char *name, *tag;
28 };
29
30 /* It is important to membercmp that M_USER < M_LIST < M_STRING */
31 #define M_ANY           0
32 #define M_USER          1
33 #define M_LIST          2
34 #define M_STRING        3
35 #define M_KERBEROS      4
36 #define M_MACHINE       5
37 #define M_NONE          6
38
39 char *typename[] = { "ANY", "USER", "LIST", "STRING", "KERBEROS", "MACHINE",
40                      "NONE" };
41
42 /* argument parsing macro */
43 #define argis(a, b) (!strcmp(*arg + 1, a) || !strcmp(*arg + 1, b))
44
45 /* flags from command line */
46 int infoflg, verbose, syncflg, memberflg, recursflg, noauth;
47 int showusers, showstrings, showkerberos, showlists, showtags, showmachines;
48 int createflag, setinfo, active, public, hidden, maillist, grouplist;
49 int nfsgroup, mailman;
50 struct member *owner, *memacl;
51 char *desc, *newname, *mailman_server;
52
53 /* various member lists */
54 struct save_queue *addlist, *dellist, *memberlist, *synclist, *taglist;
55
56 char *listname, *whoami;
57
58 void usage(char **argv);
59 void show_list_member(struct member *memberstruct);
60 int show_list_info(int argc, char **argv, void *hint);
61 int save_list_info(int argc, char **argv, void *hint);
62 int show_list_count(int argc, char **argv, void *hint);
63 void recursive_display_list_members(void);
64 void unique_add_member(struct save_queue *q, struct member *m);
65 int get_list_members(int argc, char **argv, void *sq);
66 void get_members_from_file(char *filename, struct save_queue *queue);
67 int collect(int argc, char **argv, void *l);
68 struct member *parse_member(char *s);
69 int membercmp(const void *mem1, const void *mem2);
70 int sq_count_elts(struct save_queue *q);
71 char *get_username(void);
72
73 int main(int argc, char **argv)
74 {
75   int status, success;
76   char **arg = argv;
77   char *membervec[4];
78   struct member *memberstruct;
79   char *server = NULL, *p;
80
81   /* clear all flags & lists */
82   infoflg = verbose = syncflg = memberflg = recursflg = 0;
83   noauth = showusers = showstrings = showkerberos = showlists = 0;
84   showtags = showmachines = createflag = setinfo = 0;
85   active = public = hidden = maillist = grouplist = nfsgroup = mailman = -1;
86   listname = newname = desc = NULL;
87   owner = NULL;
88   memacl = NULL;
89   addlist = sq_create();
90   dellist = sq_create();
91   memberlist = sq_create();
92   synclist = sq_create();
93   taglist = sq_create();
94   whoami = argv[0];
95
96   success = 1;
97
98   /* parse args, building addlist, dellist, & synclist */
99   while (++arg - argv < argc)
100     {
101       if (**arg == '-')
102         {
103           if (argis("m", "members"))
104             memberflg++;
105           else if (argis("u", "users"))
106             showusers++;
107           else if (argis("s", "strings"))
108             showstrings++;
109           else if (argis("l", "lists"))
110             showlists++;
111           else if (argis("k", "kerberos"))
112             showkerberos++;
113           else if (argis("t", "tags"))
114             showtags++;
115           else if (argis("i", "info"))
116             infoflg++;
117           else if (argis("n", "noauth"))
118             noauth++;
119           else if (argis("v", "verbose"))
120             verbose++;
121           else if (argis("r", "recursive"))
122             recursflg++;
123           else if (argis("S", "server") || argis("db", "database"))
124             {
125               if (arg - argv < argc - 1)
126                 {
127                   ++arg;
128                   server = *arg;
129                 }
130               else
131                 usage(argv);
132             }
133           else if (argis("a", "add"))
134             {
135               if (arg - argv < argc - 1)
136                 {
137                   ++arg;
138                   if ((memberstruct = parse_member(*arg)))
139                     sq_save_data(addlist, memberstruct);
140                 }
141               else
142                 usage(argv);
143             }
144           else if (argis("at", "addtagged"))
145             {
146               if (arg - argv < argc - 2)
147                 {
148                   ++arg;
149                   if ((memberstruct = parse_member(*arg)))
150                     sq_save_data(addlist, memberstruct);
151                   memberstruct->tag = *++arg;
152                 }
153               else
154                 usage(argv);
155             }
156           else if (argis("al", "addlist"))
157             {
158               if (arg - argv < argc - 1)
159                 {
160                   ++arg;
161                   get_members_from_file(*arg, addlist);
162                 }
163               else
164                 usage(argv);
165             }
166           else if (argis("d", "delete"))
167             {
168               if (arg - argv < argc - 1)
169                 {
170                   ++arg;
171                   if ((memberstruct = parse_member(*arg)))
172                     sq_save_data(dellist, memberstruct);
173                 }
174               else
175                 usage(argv);
176             }
177           else if (argis("dl", "deletelist"))
178             {
179               if (arg - argv < argc - 1)
180                 {
181                   ++arg;
182                   get_members_from_file(*arg, dellist);
183                 }
184               else
185                 usage(argv);
186             }
187           else if (argis("f", "file"))
188             {
189               if (arg - argv < argc - 1)
190                 {
191                   syncflg++;
192                   ++arg;
193                   get_members_from_file(*arg, synclist);
194                 }
195               else
196                 usage(argv);
197             }
198           else if (argis("ct", "changetag"))
199             {
200               if (arg - argv < argc - 2)
201                 {
202                   ++arg;
203                   if ((memberstruct = parse_member(*arg)))
204                     sq_save_data(taglist, memberstruct);
205                   memberstruct->tag = *++arg;
206                 }
207               else
208                 usage(argv);
209             }
210           else if (argis("C", "create"))
211             createflag++;
212           else if (argis("P", "public"))
213             {
214               setinfo++;
215               public = 1;
216             }
217           else if (argis("NP", "private"))
218             {
219               setinfo++;
220               public = 0;
221             }
222           else if (argis("A", "active"))
223             {
224               setinfo++;
225               active = 1;
226             }
227           else if (argis("I", "inactive"))
228             {
229               setinfo++;
230               active = 0;
231             }
232           else if (argis("V", "visible"))
233             {
234               setinfo++;
235               hidden = 0;
236             }
237           else if (argis("H", "hidden"))
238             {
239               setinfo++;
240               hidden = 1;
241             }
242           else if (argis("M", "mail"))
243             {
244               setinfo++;
245               maillist = 1;
246             }
247           else if (argis("NM", "notmail"))
248             {
249               setinfo++;
250               maillist = 0;
251             }
252           else if (argis("G", "group"))
253             {
254               setinfo++;
255               grouplist = 1;
256             }
257           else if (argis("NG", "notgroup"))
258             {
259               setinfo++;
260               grouplist = 0;
261             }
262           else if (argis("N", "nfs"))
263             {
264               setinfo++;
265               nfsgroup = 1;
266             }
267           else if (argis("NN", "notnfs"))
268             {
269               setinfo++;
270               nfsgroup = 0;
271             }
272           else if (argis("mm", "mailman"))
273             {
274               setinfo++;
275               mailman = 1;
276             }
277           else if (argis("nmm", "notmailman"))
278             {
279               setinfo++;
280               mailman = 0;
281             }
282           else if (argis("ms", "mailman_server"))
283             {
284               if (arg - argv < argc - 1)
285                 {
286                   setinfo++;
287                   ++arg;
288                   mailman_server = canonicalize_hostname(strdup(*arg));
289                 }
290               else
291                 usage(argv);
292             }
293           else if (argis("D", "desc"))
294             {
295               if (arg - argv < argc - 1)
296                 {
297                   setinfo++;
298                   ++arg;
299                   desc = *arg;
300                 }
301               else
302                 usage(argv);
303             }
304           else if (argis("O", "owner"))
305             {
306               if (arg - argv < argc - 1)
307                 {
308                   setinfo++;
309                   ++arg;
310                   owner = parse_member(*arg);
311                 }
312               else
313                 usage(argv);
314             }
315           else if (argis("MA", "memacl"))
316             {
317               if (arg - argv < argc -1)
318                 {
319                   setinfo++;
320                   ++arg;
321                   memacl = parse_member(*arg);
322                 }
323               else
324                 usage(argv);
325             }
326           else if (argis("R", "rename"))
327             {
328               if (arg - argv < argc - 1)
329                 {
330                   setinfo++;
331                   ++arg;
332                   newname = *arg;
333                 }
334               else
335                 usage(argv);
336             }
337           else
338             usage(argv);
339         }
340       else if (listname == NULL)
341         listname = *arg;
342       else
343         usage(argv);
344     }
345   if (listname == NULL)
346     usage(argv);
347
348   /* if no other options specified, turn on list members flag */
349   if (!(infoflg || syncflg || createflag || setinfo ||
350         addlist->q_next != addlist || dellist->q_next != dellist ||
351         taglist->q_next != taglist))
352     memberflg++;
353
354   /* If none of {users,strings,lists,kerberos,machines} specified, 
355      turn them all on */
356   if (!(showusers || showstrings || showlists || showkerberos))
357     showusers = showstrings = showlists = showkerberos = showmachines = 1;
358
359   /* fire up Moira */
360   status = mrcl_connect(server, "blanche", 10, !noauth);
361   if (status == MRCL_AUTH_ERROR)
362     {
363       com_err(whoami, 0, "Authentication error while working on list %s",
364               listname);
365       com_err(whoami, 0, "Try the -noauth flag if you don't "
366               "need authentication.");
367     }
368   if (status)
369     exit(2);
370
371   /* check for username/listname clash */
372   if (createflag || (setinfo && newname && strcmp(newname, listname)))
373     {
374       status = mr_query("get_user_account_by_login", 1,
375                         createflag ? &listname : &newname,
376                         NULL, NULL);
377       if (status != MR_NO_MATCH)
378         fprintf(stderr, "WARNING: A user by that name already exists.\n");
379     }
380
381   /* create if needed */
382   if (createflag)
383     {
384       char *argv[13];
385
386       argv[L_NAME] = listname;
387       argv[L_ACTIVE] = (active == 0) ? "0" : "1";
388       argv[L_PUBLIC] = (public == 1) ? "1" : "0";
389       argv[L_HIDDEN] = (hidden == 1) ? "1" : "0";
390       argv[L_MAILLIST] = (maillist == 0) ? "0" : "1";
391       argv[L_GROUP] = (grouplist == 1) ? "1" : "0";
392       argv[L_GID] = UNIQUE_GID;
393       argv[L_NFSGROUP] = (nfsgroup == 1) ? "1" : "0";
394       argv[L_MAILMAN] = (mailman == 1) ? "1" : "0";
395       argv[L_DESC] = desc ? desc : "none";
396
397       if (mailman)
398         argv[L_MAILMAN_SERVER] = mailman_server ? mailman_server : "[ANY]";
399       else
400         argv[L_MAILMAN_SERVER] = "[NONE]";
401
402       if (memacl)
403         {
404           if (memacl->type == M_ANY)
405             {
406               status = mr_query("get_user_account_by_login", 1,
407                                 &memacl->name, NULL, NULL);
408               if (status == MR_NO_MATCH)
409                 memacl->type = M_LIST;
410               else
411                 memacl->type = M_USER;
412             }
413           argv[L_MEMACE_TYPE] = typename[memacl->type];
414           argv[L_MEMACE_NAME] = memacl->name;
415           if (memacl->type == M_KERBEROS)
416             {
417               status = mrcl_validate_kerberos_member(argv[L_MEMACE_NAME],
418                                                      &argv[L_MEMACE_NAME]);
419               if (mrcl_get_message())
420                 mrcl_com_err(whoami);
421               if (status == MRCL_REJECT)
422                 exit(1);
423             }
424         }
425       else 
426         argv[L_MEMACE_TYPE] = argv[L_MEMACE_NAME] = "NONE";
427
428       if (owner)
429         {
430           argv[L_ACE_NAME] = owner->name;
431           switch (owner->type)
432             {
433             case M_ANY:
434             case M_USER:
435               argv[L_ACE_TYPE] = "USER";
436               status = mr_query("add_list", 15, argv, NULL, NULL);
437               if (owner->type != M_ANY || status != MR_USER)
438                 break;
439
440             case M_LIST:
441               argv[L_ACE_TYPE] = "LIST";
442               status = mr_query("add_list", 15, argv, NULL, NULL);
443               break;
444
445             case M_KERBEROS:
446               argv[L_ACE_TYPE] = "KERBEROS";
447               status = mrcl_validate_kerberos_member(argv[L_ACE_NAME], 
448                                                      &argv[L_ACE_NAME]);
449               if (mrcl_get_message())
450                 mrcl_com_err(whoami);
451               if (status == MRCL_REJECT)
452                 exit(1);
453               status = mr_query("add_list", 15, argv, NULL, NULL);
454               break;
455             case M_NONE:
456               argv[L_ACE_TYPE] = argv[L_ACE_NAME] = "NONE";
457               status = mr_query("add_list", 15, argv, NULL, NULL);
458               break;
459             }
460         }
461       else
462         {
463           argv[L_ACE_TYPE] = "USER";
464           argv[L_ACE_NAME] = get_username();
465
466           status = mr_query("add_list", 15, argv, NULL, NULL);
467         }
468
469       if (status)
470         {
471           com_err(whoami, status, "while creating list.");
472           exit(1);
473         }
474     }
475   else if (setinfo)
476     {
477       char *argv[14];
478
479       status = mr_query("get_list_info", 1, &listname,
480                         save_list_info, argv);
481       if (status)
482         {
483           com_err(whoami, status, "while getting list information");
484           exit(1);
485         }
486
487       argv[0] = listname;
488       if (newname)
489         argv[L_NAME + 1] = newname;
490       if (active != -1)
491         argv[L_ACTIVE + 1] = active ? "1" : "0";
492       if (public != -1)
493         argv[L_PUBLIC + 1] = public ? "1" : "0";
494       if (hidden != -1)
495         argv[L_HIDDEN + 1] = hidden ? "1" : "0";
496       if (maillist != -1)
497         argv[L_MAILLIST + 1] = maillist ? "1" : "0";
498       if (grouplist != -1)
499         argv[L_GROUP + 1] = grouplist ? "1" : "0";
500       if (nfsgroup != -1)
501         argv[L_NFSGROUP + 1] = nfsgroup ? "1" : "0";
502       if (mailman != -1)
503         argv[L_MAILMAN + 1] = mailman ? "1" : "0";
504
505       /* If someone toggled the mailman bit, but didn't specify a server,
506        * default to [ANY].
507        */
508       if (mailman_server)
509         argv[L_MAILMAN_SERVER + 1] = mailman_server;
510       else if ((mailman == 1) && !strcmp(argv[L_MAILMAN_SERVER + 1], "[NONE]"))
511         argv[L_MAILMAN_SERVER + 1] = "[ANY]";
512
513       if (desc)
514         argv[L_DESC + 1] = desc;
515
516       if (memacl)
517         {
518           if (memacl->type == M_ANY)
519             {
520               status = mr_query("get_user_account_by_login", 1,
521                                 &memacl->name, NULL, NULL);
522               if (status == MR_NO_MATCH)
523                 memacl->type = M_LIST;
524               else
525                 memacl->type = M_USER;
526             }
527           argv[L_MEMACE_TYPE + 1] = typename[memacl->type];
528           argv[L_MEMACE_NAME + 1] = memacl->name;
529           if (memacl->type == M_KERBEROS)
530             {
531               status = mrcl_validate_kerberos_member(argv[L_MEMACE_NAME + 1],
532                                                      &argv[L_MEMACE_NAME + 1]);
533               if (mrcl_get_message())
534                 mrcl_com_err(whoami);
535               if (status == MRCL_REJECT)
536                 exit(1);
537             }
538         }
539
540       if (owner)
541         {
542           argv[L_ACE_NAME + 1] = owner->name;
543           switch (owner->type)
544             {
545             case M_ANY:
546             case M_USER:
547               argv[L_ACE_TYPE + 1] = "USER";
548               status = mr_query("update_list", 16, argv, NULL, NULL);
549               if (owner->type != M_ANY || status != MR_USER)
550                 break;
551
552             case M_LIST:
553               argv[L_ACE_TYPE + 1] = "LIST";
554               status = mr_query("update_list", 16, argv, NULL, NULL);
555               break;
556
557             case M_KERBEROS:
558               argv[L_ACE_TYPE + 1] = "KERBEROS";
559               status = mrcl_validate_kerberos_member(argv[L_ACE_NAME + 1],
560                                                      &argv[L_ACE_NAME + 1]);
561               if (mrcl_get_message())
562                 mrcl_com_err(whoami);
563               if (status == MRCL_REJECT)
564                 exit(1);
565               status = mr_query("update_list", 16, argv, NULL, NULL);
566               break;
567             case M_NONE:
568               argv[L_ACE_TYPE + 1] = argv[L_ACE_NAME + 1] = "NONE";
569               status = mr_query("update_list", 16, argv, NULL, NULL);
570               break;
571             }
572         }
573       else
574         status = mr_query("update_list", 16, argv, NULL, NULL);
575
576       if (status)
577         {
578           com_err(whoami, status, "while updating list.");
579           success = 0;
580         }
581       else if (newname)
582         listname = newname;
583     }
584
585   /* display list info if requested to */
586   if (infoflg)
587     {
588       status = mr_query("get_list_info", 1, &listname, show_list_info, NULL);
589       if (status)
590         {
591           com_err(whoami, status, "while getting list information");
592           success = 0;
593         }
594       if (verbose && !memberflg)
595         {
596           status = mr_query("count_members_of_list", 1, &listname,
597                             show_list_count, NULL);
598           if (status)
599             {
600               com_err(whoami, status, "while getting list count");
601               success = 0;
602             }
603         }
604     }
605
606   /* if we're synchronizing to a file, we need to:
607    *  get the current members of the list
608    *    for each member of the sync file
609    *       if they are on the list, remove them from the in-memory copy
610    *       if they're not on the list, add them to add-list
611    *    if anyone is left on the in-memory copy, put them on the delete-list
612    * lastly, reset memberlist so we can use it again later
613    */
614   if (syncflg)
615     {
616       status = mr_query("get_members_of_list", 1, &listname,
617                         get_list_members, memberlist);
618       if (status)
619         {
620           com_err(whoami, status, "getting members of list %s", listname);
621           exit(2);
622         }
623       while (sq_get_data(synclist, &memberstruct))
624         {
625           struct save_queue *q;
626           int removed = 0;
627
628           for (q = memberlist->q_next; q != memberlist; q = q->q_next)
629             {
630               if (membercmp(q->q_data, memberstruct) == 0)
631                 {
632                   q->q_prev->q_next = q->q_next;
633                   q->q_next->q_prev = q->q_prev;
634                   removed++;
635                   break;
636                 }
637             }
638           if (!removed)
639             sq_save_data(addlist, memberstruct);
640         }
641       while (sq_get_data(memberlist, &memberstruct))
642         sq_save_data(dellist, memberstruct);
643       sq_destroy(memberlist);
644       memberlist = sq_create();
645     }
646
647   /* Process the add list */
648   while (sq_get_data(addlist, &memberstruct))
649     {
650       /* canonicalize string if necessary */
651       if (memberstruct->type != M_KERBEROS &&
652           (p = strchr(memberstruct->name, '@')))
653         {
654           char *host = canonicalize_hostname(strdup(++p));
655           static char **mailhubs = NULL;
656           char *argv[4];
657           int i;
658
659           if (!mailhubs)
660             {
661               argv[0] = "mailhub";
662               argv[1] = "TYPE";
663               argv[2] = "*";
664               mailhubs = malloc(sizeof(char *));
665               mailhubs[0] = NULL;
666               status = mr_query("get_alias", 3, argv, collect,
667                                 &mailhubs);
668               if (status != MR_SUCCESS && status != MR_NO_MATCH)
669                 {
670                   com_err(whoami, status,
671                           " while reading list of MAILHUB servers");
672                   mailhubs[0] = NULL;
673                 }
674             }
675           for (i = 0; (p = mailhubs[i]); i++)
676             {
677               if (!strcasecmp(p, host))
678                 {
679                   host = strdup(memberstruct->name);
680                   *(strchr(memberstruct->name, '@')) = 0;
681                   if (memberstruct->type == M_STRING)
682                       memberstruct->type = M_ANY;
683                   fprintf(stderr, "Warning: \"%s\" converted to "
684                           "\"%s\" because it is a local name.\n",
685                           host, memberstruct->name);
686                   break;
687                 }
688             }
689           free(host);
690         }
691       /* now continue adding member */
692       membervec[0] = listname;
693       membervec[2] = memberstruct->name;
694       membervec[3] = memberstruct->tag;
695       if (verbose)
696         {
697           printf("Adding member ");
698           show_list_member(memberstruct);
699         }
700       switch (memberstruct->type)
701         {
702         case M_ANY:
703         case M_USER:
704           membervec[1] = "USER";
705           status = mr_query("add_tagged_member_to_list", 4, membervec,
706                             NULL, NULL);
707           if (status == MR_SUCCESS)
708             break;
709           else if (status != MR_USER || memberstruct->type != M_ANY)
710             {
711               com_err(whoami, status, "while adding member %s to %s",
712                       memberstruct->name, listname);
713               success = 0;
714               break;
715             }
716         case M_LIST:
717           membervec[1] = "LIST";
718           status = mr_query("add_tagged_member_to_list", 4, membervec,
719                             NULL, NULL);
720           if (status == MR_SUCCESS)
721             {
722               if (!strcmp(membervec[0], get_username()))
723                 {
724                   fprintf(stderr, "\nWARNING: \"LIST:%s\" was just added "
725                           "to list \"%s\".\n", membervec[2], membervec[0]);
726                   fprintf(stderr, "If you meant to add yourself to the list "
727                           "\"%s\", type:\n", membervec[2]);
728                   fprintf(stderr, "\tblanche %s -d %s\t(to undo this)\n",
729                           membervec[0], membervec[2]);
730                   fprintf(stderr, "\tblanche %s -a %s\t(to add yourself to "
731                           "that list)\n", membervec[2], membervec[0]);
732                 }
733               break;
734             }
735           else if (status != MR_LIST || memberstruct->type != M_ANY)
736             {
737               com_err(whoami, status, "while adding member %s to %s",
738                       memberstruct->name, listname);
739               success = 0;
740               break;
741             }
742         case M_STRING:
743           status = mrcl_validate_string_member(memberstruct->name);
744           if (memberstruct->type == M_ANY && status == MRCL_WARN)
745             {
746               /* if user is trying to add something which isn't a
747                  remote string, or a list, or a user, and didn't
748                  explicitly specify `STRING:', it's probably a typo */
749               com_err(whoami, MR_NO_MATCH, "while adding member %s to %s",
750                       memberstruct->name, listname);
751               success = 0;
752               break;
753             }
754           else
755             mrcl_com_err(whoami);
756
757           if (status == MRCL_REJECT)
758             {
759               success = 0;
760               break;
761             }
762
763           membervec[1] = "STRING";
764           status = mr_query("add_tagged_member_to_list", 4, membervec,
765                             NULL, NULL);
766           if (status != MR_SUCCESS)
767             {
768               com_err(whoami, status, "while adding member %s to %s",
769                       memberstruct->name, listname);
770               success = 0;
771             }
772           break;
773         case M_KERBEROS:
774           membervec[1] = "KERBEROS";
775           status = mrcl_validate_kerberos_member(membervec[2], &membervec[2]);
776           if (mrcl_get_message())
777             mrcl_com_err(whoami);
778           if (status == MRCL_REJECT)
779             {
780               success = 0;
781               break;
782             }
783           status = mr_query("add_tagged_member_to_list", 4, membervec,
784                             NULL, NULL);
785           if (status != MR_SUCCESS)
786             {
787               com_err(whoami, status, "while adding member %s to %s",
788                       memberstruct->name, listname);
789               success = 0;
790             }
791           free(membervec[2]);
792           break;
793         case M_MACHINE:
794           membervec[1] = "MACHINE";
795           membervec[2] = canonicalize_hostname(strdup(memberstruct->name));
796           status = mr_query("add_tagged_member_to_list", 4, membervec,
797                             NULL, NULL);
798           if (status != MR_SUCCESS)
799             {
800               com_err(whoami, status, "while adding member %s to %s",
801                       memberstruct->name, listname);
802               success = 0;
803             }
804           free(membervec[2]);
805         }
806     }
807
808   /* Process the delete list */
809   while (sq_get_data(dellist, &memberstruct))
810     {
811       membervec[0] = listname;
812       membervec[2] = memberstruct->name;
813       if (verbose)
814         {
815           printf("Deleting member ");
816           show_list_member(memberstruct);
817         }
818       switch (memberstruct->type)
819         {
820         case M_ANY:
821         case M_USER:
822           membervec[1] = "USER";
823           status = mr_query("delete_member_from_list", 3, membervec,
824                             NULL, NULL);
825           if (status == MR_SUCCESS)
826             break;
827           else if ((status != MR_USER && status != MR_NO_MATCH) ||
828                    memberstruct->type != M_ANY)
829             {
830               com_err(whoami, status, "while deleting member %s from %s",
831                       memberstruct->name, listname);
832               success = 0;
833               break;
834             }
835         case M_LIST:
836           membervec[1] = "LIST";
837           status = mr_query("delete_member_from_list", 3, membervec,
838                             NULL, NULL);
839           if (status == MR_SUCCESS)
840             break;
841           else if ((status != MR_LIST && status != MR_NO_MATCH) ||
842                    memberstruct->type != M_ANY)
843             {
844               if (status == MR_PERM && memberstruct->type == M_ANY &&
845                   !strcmp(membervec[2], get_username()))
846                 {
847                   /* M_ANY means we've fallen through from the user
848                    * case. The user is trying to remove himself from
849                    * a list, but we got MR_USER or MR_NO_MATCH above,
850                    * meaning he's not really on it, and we got MR_PERM
851                    * when trying to remove LIST:$USER because he's not
852                    * on the acl. That error is useless, so return
853                    * MR_NO_MATCH instead. However, this will generate the
854                    * wrong error if the user was trying to remove the list
855                    * with his username from a list he doesn't administrate
856                    * without explicitly specifying "list:".
857                    */
858                   status = MR_NO_MATCH;
859                 }
860               com_err(whoami, status, "while deleting member %s from %s",
861                       memberstruct->name, listname);
862               success = 0;
863               break;
864             }
865         case M_STRING:
866           membervec[1] = "STRING";
867           status = mr_query("delete_member_from_list", 3, membervec,
868                             NULL, NULL);
869           if (status == MR_STRING && memberstruct->type == M_ANY)
870             {
871               com_err(whoami, 0, " Unable to find member %s to delete from %s",
872                       memberstruct->name, listname);
873               success = 0;
874               if (!strcmp(membervec[0], get_username()))
875                 {
876                   fprintf(stderr, "(If you were trying to remove yourself "
877                           "from the list \"%s\",\n", membervec[2]);
878                   fprintf(stderr, "the correct command is \"blanche %s -d "
879                           "%s\".)\n", membervec[2], membervec[0]);
880                 }
881             }
882           else if (status != MR_SUCCESS)
883             {
884               com_err(whoami, status, "while deleting member %s from %s",
885                       memberstruct->name, listname);
886               success = 0;
887             }
888           break;
889         case M_KERBEROS:
890           membervec[1] = "KERBEROS";
891           status = mr_query("delete_member_from_list", 3, membervec,
892                             NULL, NULL);
893           if (status == MR_STRING || status == MR_NO_MATCH)
894             {
895               /* Try canonicalizing the Kerberos principal and trying
896                * again.  If we succeed, print the message from mrcl.
897                * Otherwise, just pretend we never did this and print 
898                * the original error message.
899                */
900               mrcl_validate_kerberos_member(membervec[2], &membervec[2]);
901               if (mrcl_get_message())
902                 {
903                   if (mr_query("delete_member_from_list", 3, membervec,
904                                NULL, NULL) == MR_SUCCESS)
905                     mrcl_com_err(whoami);
906                   status = MR_SUCCESS;
907                 }
908             }
909           if (status != MR_SUCCESS)
910             {
911               com_err(whoami, status, "while deleting member %s from %s",
912                       memberstruct->name, listname);
913               success = 0;
914             }
915           break;
916         case M_MACHINE:
917           membervec[1] = "MACHINE";
918           membervec[2] = canonicalize_hostname(memberstruct->name);
919           status = mr_query("delete_member_from_list", 3, membervec,
920                             NULL, NULL);
921           if (status != MR_SUCCESS)
922             {
923               com_err(whoami, status, "while deleting member %s from %s",
924                       memberstruct->name, listname);
925               success = 0;
926             }
927           free(membervec[2]);
928         }
929     }
930
931   /* Process the tag list */
932   while (sq_get_data(taglist, &memberstruct))
933     {
934       membervec[0] = listname;
935       membervec[2] = memberstruct->name;
936       membervec[3] = memberstruct->tag;
937       if (verbose)
938         {
939           printf("Tagging member ");
940           show_list_member(memberstruct);
941         }
942       switch (memberstruct->type)
943         {
944         case M_ANY:
945         case M_USER:
946           membervec[1] = "USER";
947           status = mr_query("tag_member_of_list", 4, membervec,
948                             NULL, NULL);
949           if (status == MR_SUCCESS)
950             break;
951           else if ((status != MR_USER && status != MR_NO_MATCH) ||
952                    memberstruct->type != M_ANY)
953             {
954               com_err(whoami, status, "while changing tag on member %s of %s",
955                       memberstruct->name, listname);
956               success = 0;
957               break;
958             }
959         case M_LIST:
960           membervec[1] = "LIST";
961           status = mr_query("tag_member_of_list", 4, membervec,
962                             NULL, NULL);
963           if (status == MR_SUCCESS)
964             break;
965           else if ((status != MR_LIST && status != MR_NO_MATCH) ||
966                    memberstruct->type != M_ANY)
967             {
968               com_err(whoami, status, "while changing tag on member %s of %s",
969                       memberstruct->name, listname);
970               success = 0;
971               break;
972             }
973         case M_STRING:
974           membervec[1] = "STRING";
975           status = mr_query("tag_member_of_list", 4, membervec,
976                             NULL, NULL);
977           if (status == MR_STRING && memberstruct->type == M_ANY)
978             {
979               com_err(whoami, 0, " Unable to find member %s on list %s",
980                       memberstruct->name, listname);
981               success = 0;
982             }
983           else if (status != MR_SUCCESS)
984             {
985               com_err(whoami, status, "while retagging member %s on %s",
986                       memberstruct->name, listname);
987               success = 0;
988             }
989           break;
990         case M_KERBEROS:
991           membervec[1] = "KERBEROS";
992           status = mr_query("tag_member_of_list", 4, membervec,
993                             NULL, NULL);
994           if (status == MR_STRING || status == MR_NO_MATCH)
995             {
996               /* Try canonicalizing the Kerberos principal and trying
997                * again.  If we succeed, print the message from mrcl.
998                * Otherwise, just pretend we never did this and print 
999                * the original error message.
1000                */
1001               mrcl_validate_kerberos_member(membervec[2], &membervec[2]);
1002               if (mrcl_get_message())
1003                 {
1004                   if (mr_query("tag_member_of_list", 4, membervec,
1005                                NULL, NULL) == MR_SUCCESS)
1006                     mrcl_com_err(whoami);
1007                   status = MR_SUCCESS;
1008                 }
1009             }
1010           if (status != MR_SUCCESS)
1011             {
1012               com_err(whoami, status, "while changing tag on member %s of %s",
1013                       memberstruct->name, listname);
1014               success = 0;
1015             }
1016         }
1017     }
1018
1019   /* Display the members of the list now, if requested */
1020   if (memberflg)
1021     {
1022       if (recursflg)
1023         recursive_display_list_members();
1024       else
1025         {
1026           status = mr_query(showtags ? "get_tagged_members_of_list" :
1027                             "get_members_of_list", 1, &listname,
1028                             get_list_members, memberlist);
1029           if (status)
1030             com_err(whoami, status, "while getting members of list %s",
1031                     listname);
1032           while (sq_get_data(memberlist, &memberstruct))
1033             show_list_member(memberstruct);
1034         }
1035     }
1036
1037   /* We're done! */
1038   mr_disconnect();
1039   exit(success ? 0 : 1);
1040 }
1041
1042 void usage(char **argv)
1043 {
1044 #define USAGE_OPTIONS_FORMAT "  %-39s%s\n"
1045   fprintf(stderr, "Usage: %s listname [options]\n", argv[0]);
1046   fprintf(stderr, "Options are\n");
1047   fprintf(stderr, USAGE_OPTIONS_FORMAT, "-v  | -verbose",
1048           "-C   | -create");
1049   fprintf(stderr, USAGE_OPTIONS_FORMAT, "-m  | -members",
1050           "-R   | -rename newname");
1051   fprintf(stderr, USAGE_OPTIONS_FORMAT, "-u  | -users",
1052           "-P   | -public");
1053   fprintf(stderr, USAGE_OPTIONS_FORMAT, "-l  | -lists",
1054           "-NP  | -private");
1055   fprintf(stderr, USAGE_OPTIONS_FORMAT, "-s  | -strings",
1056           "-A   | -active");
1057   fprintf(stderr, USAGE_OPTIONS_FORMAT, "-k  | -kerberos",
1058           "-I   | -inactive");
1059   fprintf(stderr, USAGE_OPTIONS_FORMAT, "-i  | -info",
1060           "-V   | -visible");
1061   fprintf(stderr, USAGE_OPTIONS_FORMAT, "-r  | -recursive",
1062           "-H   | -hidden");
1063   fprintf(stderr, USAGE_OPTIONS_FORMAT, "-a  | -add member",
1064           "-M   | -mail");
1065   fprintf(stderr, USAGE_OPTIONS_FORMAT, "-d  | -delete member",
1066           "-NM  | -notmail");
1067   fprintf(stderr, USAGE_OPTIONS_FORMAT, "-al | -addlist filename",
1068           "-G   | -group");
1069   fprintf(stderr, USAGE_OPTIONS_FORMAT, "-dl | -deletelist filename",
1070           "-NG  | -notgroup");
1071   fprintf(stderr, USAGE_OPTIONS_FORMAT, "-f  | -file filename",
1072           "-N   | -nfs");
1073   fprintf(stderr, USAGE_OPTIONS_FORMAT, "-at | -addtagged member tag",
1074           "-NN  | -notnfs");
1075   fprintf(stderr, USAGE_OPTIONS_FORMAT, "-ct | -changetag member tag",
1076           "-mm  | -mailman");
1077   fprintf(stderr, USAGE_OPTIONS_FORMAT, "-t  | -tags",
1078           "-nmm | -notmailman");
1079   fprintf(stderr, USAGE_OPTIONS_FORMAT, "-D  | -desc description",
1080           "-ms  | -mailman_server server");
1081   fprintf(stderr, USAGE_OPTIONS_FORMAT, "-O  | -owner owner",
1082           "-MA  | -memacl membership_acl"); 
1083   fprintf(stderr, USAGE_OPTIONS_FORMAT, "-n  | -noauth",
1084           "-db  | -database host[:port]");
1085   exit(1);
1086 }
1087
1088
1089 /* Display the members stored in the queue */
1090
1091 void show_list_member(struct member *memberstruct)
1092 {
1093   char *s = "";
1094
1095   switch (memberstruct->type)
1096     {
1097     case M_USER:
1098       if (!showusers)
1099         return;
1100       s = "USER";
1101       break;
1102     case M_LIST:
1103       if (!showlists)
1104         return;
1105       s = "LIST";
1106       break;
1107     case M_STRING:
1108       if (!showstrings)
1109         return;
1110       s = "STRING";
1111       break;
1112     case M_KERBEROS:
1113       if (!showkerberos)
1114         return;
1115       s = "KERBEROS";
1116       break;
1117     case M_MACHINE:
1118       if (!showmachines)
1119         return;
1120       s = "MACHINE";
1121       break;
1122     case M_ANY:
1123       printf("%s\n", memberstruct->name);
1124       return;
1125     }
1126
1127   if (verbose)
1128     printf("%s:%s", s, memberstruct->name);
1129   else
1130     {
1131       if (memberstruct->type == M_LIST)
1132         printf("LIST:%s", memberstruct->name);
1133       else if (memberstruct->type == M_KERBEROS)
1134         printf("KERBEROS:%s", memberstruct->name);
1135       else if (memberstruct->type == M_STRING &&
1136                !strchr(memberstruct->name, '@'))
1137         printf("STRING:%s", memberstruct->name);
1138       else if (memberstruct->type == M_MACHINE)
1139         printf("MACHINE:%s", memberstruct->name);
1140       else
1141         printf("%s", memberstruct->name);
1142     }
1143   if (showtags && *(memberstruct->tag))
1144     printf(" (%s)\n", memberstruct->tag);
1145   else
1146     printf("\n");
1147 }
1148
1149
1150 /* Show the retrieved information about a list */
1151
1152 int show_list_info(int argc, char **argv, void *hint)
1153 {
1154   printf("List: %s\n", argv[L_NAME]);
1155   printf("Description: %s\n", argv[L_DESC]);
1156   printf("Flags: %s, %s, and %s\n",
1157          atoi(argv[L_ACTIVE]) ? "active" : "inactive",
1158          atoi(argv[L_PUBLIC]) ? "public" : "private",
1159          atoi(argv[L_HIDDEN]) ? "hidden" : "visible");
1160   printf("%s is %sa maillist and is %sa group", argv[L_NAME],
1161          atoi(argv[L_MAILLIST]) ? "" : "not ",
1162          atoi(argv[L_GROUP]) ? "" : "not ");
1163   if (atoi(argv[L_GROUP]))
1164     {
1165       if (atoi(argv[L_NFSGROUP]))
1166         printf(" (and an NFS group)");
1167       printf(" with GID %d\n", atoi(argv[L_GID]));
1168     }
1169   else
1170     printf("\n");
1171   if (atoi(argv[L_MAILMAN]))
1172     printf("%s is a Mailman list on server %s\n", argv[L_NAME],
1173            argv[L_MAILMAN_SERVER]);
1174   printf("Owner: %s %s\n", argv[L_ACE_TYPE], argv[L_ACE_NAME]);
1175   if (strcmp(argv[L_MEMACE_TYPE], "NONE"))
1176     printf("Membership ACL: %s %s\n", argv[L_MEMACE_TYPE], 
1177            argv[L_MEMACE_NAME]);
1178   printf("Last modified by %s with %s on %s\n", 
1179          argv[L_MODBY], argv[L_MODWITH], argv[L_MODTIME]);
1180   return MR_CONT;
1181 }
1182
1183
1184 /* Copy retrieved information about a list into a new argv */
1185
1186 int save_list_info(int argc, char **argv, void *hint)
1187 {
1188   char **nargv = hint;
1189
1190   for (argc = 0; argc < 16; argc++)
1191     nargv[argc + 1] = strdup(argv[argc]);
1192   return MR_CONT;
1193 }
1194
1195 /* Show the retrieve list member count */
1196
1197 int show_list_count(int argc, char **argv, void *hint)
1198 {
1199   printf("Members: %s\n", argv[0]);
1200   return MR_CONT;
1201 }
1202
1203
1204 /* Recursively find all of the members of listname, and then display them */
1205
1206 void recursive_display_list_members(void)
1207 {
1208   int status, count, savecount;
1209   struct save_queue *lists, *members;
1210   struct member *m, *m1, *data;
1211
1212   lists = sq_create();
1213   members = sq_create();
1214   m = malloc(sizeof(struct member));
1215   m->type = M_LIST;
1216   m->name = listname;
1217   sq_save_data(lists, m);
1218
1219   while (sq_get_data(lists, &m))
1220     {
1221       sq_destroy(memberlist);
1222       memberlist = sq_create();
1223       status = mr_query("get_members_of_list", 1, &(m->name),
1224                         get_list_members, memberlist);
1225       if (status)
1226         com_err(whoami, status, "while getting members of list %s", m->name);
1227       while (sq_get_data(memberlist, &m1))
1228         {
1229           if (m1->type == M_LIST)
1230             unique_add_member(lists, m1);
1231           else
1232             unique_add_member(members, m1);
1233         }
1234     }
1235   savecount = count = sq_count_elts(members);
1236   data = malloc(count * sizeof(struct member));
1237   count = 0;
1238   while (sq_get_data(members, &m))
1239     memcpy(&data[count++], m, sizeof(struct member));
1240   qsort(data, count, sizeof(struct member), membercmp);
1241   for (count = 0; count < savecount; count++)
1242     show_list_member(&data[count]);
1243 }
1244
1245
1246 /* add a struct member to a queue if that member isn't already there. */
1247
1248 void unique_add_member(struct save_queue *q, struct member *m)
1249 {
1250   struct save_queue *qp;
1251
1252   for (qp = q->q_next; qp != q; qp = qp->q_next)
1253     {
1254       if (!membercmp(qp->q_data, m))
1255         return;
1256     }
1257   sq_save_data(q, m);
1258 }
1259
1260
1261 /* Collect the retrieved members of the list */
1262
1263 int get_list_members(int argc, char **argv, void *sq)
1264 {
1265   struct save_queue *q = sq;
1266   struct member *m;
1267
1268   m = malloc(sizeof(struct member));
1269   switch (argv[0][0])
1270     {
1271     case 'U':
1272       m->type = M_USER;
1273       break;
1274     case 'L':
1275       m->type = M_LIST;
1276       break;
1277     case 'S':
1278       m->type = M_STRING;
1279       break;
1280     case 'K':
1281       m->type = M_KERBEROS;
1282       break;
1283     case 'M':
1284       m->type = M_MACHINE;
1285       break;
1286     }
1287   m->name = strdup(argv[1]);
1288   if (argc == 3)
1289     m->tag = strdup(argv[2]);
1290   else
1291     m->tag = strdup("");
1292   sq_save_data(q, m);
1293   return MR_CONT;
1294 }
1295
1296
1297 /* Open file, parse members from file, and put them on the specified queue */
1298 void get_members_from_file(char *filename, struct save_queue *queue)
1299 {
1300   FILE *in;
1301   char buf[BUFSIZ];
1302   struct member *memberstruct;
1303
1304   if (!strcmp(filename, "-"))
1305     in = stdin;
1306   else
1307     {
1308       in = fopen(filename, "r");
1309       if (!in)
1310         {
1311           com_err(whoami, errno, "while opening %s for input", filename);
1312           exit(2);
1313         }
1314     }
1315
1316   while (fgets(buf, BUFSIZ, in))
1317     {
1318       if ((memberstruct = parse_member(buf)))
1319         sq_save_data(queue, memberstruct);
1320     }
1321   if (!feof(in))
1322     {
1323       com_err(whoami, errno, "while reading from %s", filename);
1324       exit(2);
1325     }
1326 }
1327
1328
1329 /* Collect the possible expansions of the alias MAILHUB */
1330
1331 int collect(int argc, char **argv, void *l)
1332 {
1333   char ***list = l;
1334   int i;
1335
1336   for (i = 0; (*list)[i]; i++)
1337     ;
1338   *list = realloc(*list, (i + 2) * sizeof(char *));
1339   (*list)[i] = strdup(argv[2]);
1340   (*list)[i + 1] = NULL;
1341   return MR_CONT;
1342 }
1343
1344
1345 /* Parse a line of input, fetching a member.  NULL is returned if a member
1346  * is not found.  ';' is a comment character.
1347  */
1348
1349 struct member *parse_member(char *s)
1350 {
1351   struct member *m;
1352   char *p, *lastchar;
1353
1354   while (*s && isspace(*s))
1355     s++;
1356   lastchar = p = s;
1357   while (*p && *p != '\n' && *p != ';')
1358     {
1359       if (isprint(*p) && !isspace(*p))
1360         lastchar = p++;
1361       else
1362         p++;
1363     }
1364   lastchar++;
1365   *lastchar = '\0';
1366   if (p == s || strlen(s) == 0)
1367     return NULL;
1368
1369   if (!(m = malloc(sizeof(struct member))))
1370     return NULL;
1371   m->tag = strdup("");
1372
1373   if ((p = strchr(s, ':')))
1374     {
1375       *p = '\0';
1376       m->name = ++p;
1377       if (!strcasecmp("user", s))
1378         m->type = M_USER;
1379       else if (!strcasecmp("list", s))
1380         m->type = M_LIST;
1381       else if (!strcasecmp("string", s))
1382         m->type = M_STRING;
1383       else if (!strcasecmp("kerberos", s))
1384         m->type = M_KERBEROS;
1385       else if (!strcasecmp("machine", s))
1386         m->type = M_MACHINE;
1387       else if (!strcasecmp("none", s))
1388         m->type = M_NONE;
1389       else
1390         {
1391           m->type = M_ANY;
1392           *(--p) = ':';
1393           m->name = s;
1394         }
1395       m->name = strdup(m->name);
1396     }
1397   else
1398     {
1399       m->name = strdup(s);
1400       m->type = strcasecmp(s, "none") ? M_ANY : M_NONE;
1401     }
1402   return m;
1403 }
1404
1405
1406 /*
1407  * This routine two compares members by the following rules:
1408  * 1.  A USER is less than a LIST
1409  * 2.  A LIST is less than a STRING
1410  * 3.  If two members are of the same type, the one alphabetically first
1411  *     is less than the other
1412  * It returs < 0 if the first member is less, 0 if they are identical, and
1413  * > 0 if the second member is less (the first member is greater).
1414  */
1415
1416 int membercmp(const void *mem1, const void *mem2)
1417 {
1418   const struct member *m1 = mem1, *m2 = mem2;
1419
1420   if (m1->type == M_ANY || m2->type == M_ANY || (m1->type == m2->type))
1421     return strcmp(m1->name, m2->name);
1422   else
1423     return m1->type - m2->type;
1424 }
1425
1426
1427 int sq_count_elts(struct save_queue *q)
1428 {
1429   char *foo;
1430   int count;
1431
1432   count = 0;
1433   while (sq_get_data(q, &foo))
1434     count++;
1435   return count;
1436 }
1437
1438 char *get_username(void)
1439 {
1440   char *username;
1441
1442   username = getenv("USER");
1443   if (!username)
1444     {
1445       username = mrcl_krb_user();
1446       if (!username)
1447         {
1448           com_err(whoami, 0, "Could not determine username");
1449           exit(1);
1450         }
1451     }
1452   return username;
1453 }
This page took 0.155592 seconds and 5 git commands to generate.