source: trunk/third/moira/clients/blanche/blanche.c @ 23740

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