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

Revision 24319, 37.8 KB checked in by broder, 14 years ago (diff)
New Moira snapshot from SVN.
Line 
1/* $Id: blanche.c 3956 2010-01-05 20:56:56Z zacheiss $
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("$HeadURL: svn+ssh://svn.mit.edu/moira/trunk/moira/clients/blanche/blanche.c $ $Id: blanche.c 3956 2010-01-05 20:56:56Z zacheiss $");
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 || showmachines))
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[16];
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 delete list */
666  while (sq_get_data(dellist, &memberstruct))
667    {
668      membervec[0] = listname;
669      membervec[2] = memberstruct->name;
670      if (verbose)
671        {
672          printf("Deleting member ");
673          show_list_member(memberstruct);
674        }
675      switch (memberstruct->type)
676        {
677        case M_ANY:
678        case M_USER:
679          membervec[1] = "USER";
680          status = mr_query("delete_member_from_list", 3, membervec,
681                            NULL, NULL);
682          if (status == MR_SUCCESS)
683            break;
684          else if ((status != MR_USER && status != MR_NO_MATCH) ||
685                   memberstruct->type != M_ANY)
686            {
687              com_err(whoami, status, "while deleting member %s from %s",
688                      memberstruct->name, listname);
689              success = 0;
690              break;
691            }
692        case M_LIST:
693          membervec[1] = "LIST";
694          status = mr_query("delete_member_from_list", 3, membervec,
695                            NULL, NULL);
696          if (status == MR_SUCCESS)
697            break;
698          else if ((status != MR_LIST && status != MR_NO_MATCH) ||
699                   memberstruct->type != M_ANY)
700            {
701              if (status == MR_PERM && memberstruct->type == M_ANY &&
702                  !strcmp(membervec[2], get_username()))
703                {
704                  /* M_ANY means we've fallen through from the user
705                   * case. The user is trying to remove himself from a
706                   * list, but we got MR_USER or MR_NO_MATCH above,
707                   * meaning he's not really on it, and we got MR_PERM
708                   * when trying to remove LIST:$USER because he's not
709                   * on the acl. That error is useless, so return
710                   * MR_NO_MATCH instead. However, this will generate
711                   * the wrong error if the user was trying to remove
712                   * the list with his username from a list he doesn't
713                   * administrate without explicitly specifying
714                   * "list:".
715                   */
716                  status = MR_NO_MATCH;
717                }
718              com_err(whoami, status, "while deleting member %s from %s",
719                      memberstruct->name, listname);
720              success = 0;
721              break;
722            }
723        case M_STRING:
724          membervec[1] = "STRING";
725          status = mr_query("delete_member_from_list", 3, membervec,
726                            NULL, NULL);
727          if (status == MR_STRING && memberstruct->type == M_ANY)
728            {
729              com_err(whoami, 0, " Unable to find member %s to delete from %s",
730                      memberstruct->name, listname);
731              success = 0;
732              if (!strcmp(membervec[0], get_username()))
733                {
734                  fprintf(stderr, "(If you were trying to remove yourself "
735                          "from the list \"%s\",\n", membervec[2]);
736                  fprintf(stderr, "the correct command is \"blanche %s -d "
737                          "%s\".)\n", membervec[2], membervec[0]);
738                }
739            }
740          else if (status != MR_SUCCESS)
741            {
742              com_err(whoami, status, "while deleting member %s from %s",
743                      memberstruct->name, listname);
744              success = 0;
745            }
746          break;
747        case M_KERBEROS:
748          membervec[1] = "KERBEROS";
749          status = mr_query("delete_member_from_list", 3, membervec,
750                            NULL, NULL);
751          if (status == MR_STRING || status == MR_NO_MATCH)
752            {
753              /* Try canonicalizing the Kerberos principal and trying
754               * again.  If we succeed, print the message from mrcl.
755               * Otherwise, just pretend we never did this and print
756               * the original error message.
757               */
758              mrcl_validate_kerberos_member(membervec[2], &membervec[2]);
759              if (mrcl_get_message())
760                {
761                  if (mr_query("delete_member_from_list", 3, membervec,
762                               NULL, NULL) == MR_SUCCESS)
763                    mrcl_com_err(whoami);
764                  status = MR_SUCCESS;
765                }
766            }
767          if (status != MR_SUCCESS)
768            {
769              com_err(whoami, status, "while deleting member %s from %s",
770                      memberstruct->name, listname);
771              success = 0;
772            }
773          break;
774        case M_MACHINE:
775          membervec[1] = "MACHINE";
776          membervec[2] = canonicalize_hostname(memberstruct->name);
777          status = mr_query("delete_member_from_list", 3, membervec,
778                            NULL, NULL);
779          if (status != MR_SUCCESS)
780            {
781              com_err(whoami, status, "while deleting member %s from %s",
782                      memberstruct->name, listname);
783              success = 0;
784            }
785          free(membervec[2]);
786        }
787    }
788
789  /* Process the add list */
790  while (sq_get_data(addlist, &memberstruct))
791    {
792      /* canonicalize string if necessary */
793      if (memberstruct->type != M_KERBEROS &&
794          (p = strchr(memberstruct->name, '@')))
795        {
796          char *host = canonicalize_hostname(strdup(++p));
797          static char **mailhubs = NULL;
798          char *argv[4];
799          int i;
800
801          if (!mailhubs)
802            {
803              argv[0] = "mailhub";
804              argv[1] = "TYPE";
805              argv[2] = "*";
806              mailhubs = malloc(sizeof(char *));
807              mailhubs[0] = NULL;
808              status = mr_query("get_alias", 3, argv, collect,
809                                &mailhubs);
810              if (status != MR_SUCCESS && status != MR_NO_MATCH)
811                {
812                  com_err(whoami, status,
813                          " while reading list of MAILHUB servers");
814                  mailhubs[0] = NULL;
815                }
816            }
817          for (i = 0; (p = mailhubs[i]); i++)
818            {
819              if (!strcasecmp(p, host))
820                {
821                  host = strdup(memberstruct->name);
822                  *(strchr(memberstruct->name, '@')) = 0;
823                  if (memberstruct->type == M_STRING)
824                      memberstruct->type = M_ANY;
825                  fprintf(stderr, "Warning: \"%s\" converted to "
826                          "\"%s\" because it is a local name.\n",
827                          host, memberstruct->name);
828                  break;
829                }
830            }
831          free(host);
832        }
833      /* now continue adding member */
834      membervec[0] = listname;
835      membervec[2] = memberstruct->name;
836      membervec[3] = memberstruct->tag;
837      if (verbose)
838        {
839          printf("Adding member ");
840          show_list_member(memberstruct);
841        }
842      switch (memberstruct->type)
843        {
844        case M_ANY:
845        case M_USER:
846          membervec[1] = "USER";
847          status = mr_query("add_tagged_member_to_list", 4, membervec,
848                            NULL, NULL);
849          if (status == MR_SUCCESS)
850            break;
851          else if (status != MR_USER || memberstruct->type != M_ANY)
852            {
853              com_err(whoami, status, "while adding member %s to %s",
854                      memberstruct->name, listname);
855              success = 0;
856              break;
857            }
858        case M_LIST:
859          membervec[1] = "LIST";
860          status = mr_query("add_tagged_member_to_list", 4, membervec,
861                            NULL, NULL);
862          if (status == MR_SUCCESS)
863            {
864              if (!strcmp(membervec[0], get_username()))
865                {
866                  fprintf(stderr, "\nWARNING: \"LIST:%s\" was just added "
867                          "to list \"%s\".\n", membervec[2], membervec[0]);
868                  fprintf(stderr, "If you meant to add yourself to the list "
869                          "\"%s\", type:\n", membervec[2]);
870                  fprintf(stderr, "\tblanche %s -d %s\t(to undo this)\n",
871                          membervec[0], membervec[2]);
872                  fprintf(stderr, "\tblanche %s -a %s\t(to add yourself to "
873                          "that list)\n", membervec[2], membervec[0]);
874                }
875              break;
876            }
877          else if (status != MR_LIST || memberstruct->type != M_ANY)
878            {
879              com_err(whoami, status, "while adding member %s to %s",
880                      memberstruct->name, listname);
881              success = 0;
882              break;
883            }
884        case M_STRING:
885          status = mrcl_validate_string_member(memberstruct->name);
886          if (memberstruct->type == M_ANY && status == MRCL_WARN)
887            {
888              /* if user is trying to add something which isn't a
889                 remote string, or a list, or a user, and didn't
890                 explicitly specify `STRING:', it's probably a typo */
891              com_err(whoami, MR_NO_MATCH, "while adding member %s to %s",
892                      memberstruct->name, listname);
893              success = 0;
894              break;
895            }
896          else
897            mrcl_com_err(whoami);
898
899          if (status == MRCL_REJECT)
900            {
901              success = 0;
902              break;
903            }
904
905          membervec[1] = "STRING";
906          status = mr_query("add_tagged_member_to_list", 4, membervec,
907                            NULL, NULL);
908          if (status != MR_SUCCESS)
909            {
910              com_err(whoami, status, "while adding member %s to %s",
911                      memberstruct->name, listname);
912              success = 0;
913            }
914          break;
915        case M_KERBEROS:
916          membervec[1] = "KERBEROS";
917          status = mrcl_validate_kerberos_member(membervec[2], &membervec[2]);
918          if (mrcl_get_message())
919            mrcl_com_err(whoami);
920          if (status == MRCL_REJECT)
921            {
922              success = 0;
923              break;
924            }
925          status = mr_query("add_tagged_member_to_list", 4, membervec,
926                            NULL, NULL);
927          if (status != MR_SUCCESS)
928            {
929              com_err(whoami, status, "while adding member %s to %s",
930                      memberstruct->name, listname);
931              success = 0;
932            }
933          free(membervec[2]);
934          break;
935        case M_MACHINE:
936          membervec[1] = "MACHINE";
937          membervec[2] = canonicalize_hostname(strdup(memberstruct->name));
938          status = mr_query("add_tagged_member_to_list", 4, membervec,
939                            NULL, NULL);
940          if (status != MR_SUCCESS)
941            {
942              com_err(whoami, status, "while adding member %s to %s",
943                      memberstruct->name, listname);
944              success = 0;
945            }
946          free(membervec[2]);
947        }
948    }
949
950  /* Process the tag list */
951  while (sq_get_data(taglist, &memberstruct))
952    {
953      membervec[0] = listname;
954      membervec[2] = memberstruct->name;
955      membervec[3] = memberstruct->tag;
956      if (verbose)
957        {
958          printf("Tagging member ");
959          show_list_member(memberstruct);
960        }
961      switch (memberstruct->type)
962        {
963        case M_ANY:
964        case M_USER:
965          membervec[1] = "USER";
966          status = mr_query("tag_member_of_list", 4, membervec,
967                            NULL, NULL);
968          if (status == MR_SUCCESS)
969            break;
970          else if ((status != MR_USER && status != MR_NO_MATCH) ||
971                   memberstruct->type != M_ANY)
972            {
973              com_err(whoami, status, "while changing tag on member %s of %s",
974                      memberstruct->name, listname);
975              success = 0;
976              break;
977            }
978        case M_LIST:
979          membervec[1] = "LIST";
980          status = mr_query("tag_member_of_list", 4, membervec,
981                            NULL, NULL);
982          if (status == MR_SUCCESS)
983            break;
984          else if ((status != MR_LIST && status != MR_NO_MATCH) ||
985                   memberstruct->type != M_ANY)
986            {
987              com_err(whoami, status, "while changing tag on member %s of %s",
988                      memberstruct->name, listname);
989              success = 0;
990              break;
991            }
992        case M_STRING:
993          membervec[1] = "STRING";
994          status = mr_query("tag_member_of_list", 4, membervec,
995                            NULL, NULL);
996          if (status == MR_STRING && memberstruct->type == M_ANY)
997            {
998              com_err(whoami, 0, " Unable to find member %s on list %s",
999                      memberstruct->name, listname);
1000              success = 0;
1001            }
1002          else if (status != MR_SUCCESS)
1003            {
1004              com_err(whoami, status, "while retagging member %s on %s",
1005                      memberstruct->name, listname);
1006              success = 0;
1007            }
1008          break;
1009        case M_KERBEROS:
1010          membervec[1] = "KERBEROS";
1011          status = mr_query("tag_member_of_list", 4, membervec,
1012                            NULL, NULL);
1013          if (status == MR_STRING || status == MR_NO_MATCH)
1014            {
1015              /* Try canonicalizing the Kerberos principal and trying
1016               * again.  If we succeed, print the message from mrcl.
1017               * Otherwise, just pretend we never did this and print
1018               * the original error message.
1019               */
1020              mrcl_validate_kerberos_member(membervec[2], &membervec[2]);
1021              if (mrcl_get_message())
1022                {
1023                  if (mr_query("tag_member_of_list", 4, membervec,
1024                               NULL, NULL) == MR_SUCCESS)
1025                    mrcl_com_err(whoami);
1026                  status = MR_SUCCESS;
1027                }
1028            }
1029          if (status != MR_SUCCESS)
1030            {
1031              com_err(whoami, status, "while changing tag on member %s of %s",
1032                      memberstruct->name, listname);
1033              success = 0;
1034            }
1035        case M_MACHINE:
1036          membervec[1] = "MACHINE";
1037          status = mr_query("tag_member_of_list", 4, membervec,
1038                            NULL, NULL);
1039          if (status != MR_SUCCESS)
1040            {
1041              com_err(whoami, status, "while adding member %s to %s",
1042                      memberstruct->name, listname);
1043              success = 0;
1044            }
1045        }
1046    }
1047
1048  /* Display the members of the list now, if requested */
1049  if (memberflg)
1050    {
1051      if (recursflg)
1052        recursive_display_list_members();
1053      else
1054        {
1055          status = mr_query(showtags ? "get_tagged_members_of_list" :
1056                            "get_members_of_list", 1, &listname,
1057                            get_list_members, memberlist);
1058          if (status)
1059            com_err(whoami, status, "while getting members of list %s",
1060                    listname);
1061          while (sq_get_data(memberlist, &memberstruct))
1062            show_list_member(memberstruct);
1063        }
1064    }
1065
1066  /* We're done! */
1067  mr_disconnect();
1068  exit(success ? 0 : 1);
1069}
1070
1071void usage(char **argv)
1072{
1073#define USAGE_OPTIONS_FORMAT "  %-39s%s\n"
1074  fprintf(stderr, "Usage: %s listname [options]\n", argv[0]);
1075  fprintf(stderr, "Options are\n");
1076  fprintf(stderr, USAGE_OPTIONS_FORMAT, "-v  | -verbose",
1077          "-C   | -create");
1078  fprintf(stderr, USAGE_OPTIONS_FORMAT, "-m  | -members",
1079          "-R   | -rename newname");
1080  fprintf(stderr, USAGE_OPTIONS_FORMAT, "-u  | -users",
1081          "-P   | -public");
1082  fprintf(stderr, USAGE_OPTIONS_FORMAT, "-l  | -lists",
1083          "-NP  | -private");
1084  fprintf(stderr, USAGE_OPTIONS_FORMAT, "-s  | -strings",
1085          "-A   | -active");
1086  fprintf(stderr, USAGE_OPTIONS_FORMAT, "-k  | -kerberos",
1087          "-I   | -inactive");
1088  fprintf(stderr, USAGE_OPTIONS_FORMAT, "-i  | -info",
1089          "-V   | -visible");
1090  fprintf(stderr, USAGE_OPTIONS_FORMAT, "-r  | -recursive",
1091          "-H   | -hidden");
1092  fprintf(stderr, USAGE_OPTIONS_FORMAT, "-a  | -add member",
1093          "-M   | -mail");
1094  fprintf(stderr, USAGE_OPTIONS_FORMAT, "-d  | -delete member",
1095          "-NM  | -notmail");
1096  fprintf(stderr, USAGE_OPTIONS_FORMAT, "-al | -addlist filename",
1097          "-G   | -group");
1098  fprintf(stderr, USAGE_OPTIONS_FORMAT, "-dl | -deletelist filename",
1099          "-NG  | -notgroup");
1100  fprintf(stderr, USAGE_OPTIONS_FORMAT, "-f  | -file filename",
1101          "-N   | -nfs");
1102  fprintf(stderr, USAGE_OPTIONS_FORMAT, "-at | -addtagged member tag",
1103          "-NN  | -notnfs");
1104  fprintf(stderr, USAGE_OPTIONS_FORMAT, "-ct | -changetag member tag",
1105          "-mm  | -mailman");
1106  fprintf(stderr, USAGE_OPTIONS_FORMAT, "-t  | -tags",
1107          "-nmm | -notmailman");
1108  fprintf(stderr, USAGE_OPTIONS_FORMAT, "-D  | -desc description",
1109          "-ms  | -mailman_server server");
1110  fprintf(stderr, USAGE_OPTIONS_FORMAT, "-O  | -owner owner",
1111          "-MA  | -memacl membership_acl");
1112  fprintf(stderr, USAGE_OPTIONS_FORMAT, "-n  | -noauth",
1113          "-db  | -database host[:port]");
1114  exit(1);
1115}
1116
1117
1118/* Display the members stored in the queue */
1119
1120void show_list_member(struct member *memberstruct)
1121{
1122  char *s = "";
1123
1124  switch (memberstruct->type)
1125    {
1126    case M_USER:
1127      if (!showusers)
1128        return;
1129      s = "USER";
1130      break;
1131    case M_LIST:
1132      if (!showlists)
1133        return;
1134      s = "LIST";
1135      break;
1136    case M_STRING:
1137      if (!showstrings)
1138        return;
1139      s = "STRING";
1140      break;
1141    case M_KERBEROS:
1142      if (!showkerberos)
1143        return;
1144      s = "KERBEROS";
1145      break;
1146    case M_MACHINE:
1147      if (!showmachines)
1148        return;
1149      s = "MACHINE";
1150      break;
1151    case M_ANY:
1152      printf("%s\n", memberstruct->name);
1153      return;
1154    }
1155
1156  if (verbose)
1157    printf("%s:%s", s, memberstruct->name);
1158  else
1159    {
1160      if (memberstruct->type == M_LIST)
1161        printf("LIST:%s", memberstruct->name);
1162      else if (memberstruct->type == M_KERBEROS)
1163        printf("KERBEROS:%s", memberstruct->name);
1164      else if (memberstruct->type == M_STRING &&
1165               !strchr(memberstruct->name, '@'))
1166        printf("STRING:%s", memberstruct->name);
1167      else if (memberstruct->type == M_MACHINE)
1168        printf("MACHINE:%s", memberstruct->name);
1169      else
1170        printf("%s", memberstruct->name);
1171    }
1172  if (showtags && *(memberstruct->tag))
1173    printf(" (%s)\n", memberstruct->tag);
1174  else
1175    printf("\n");
1176}
1177
1178
1179/* Show the retrieved information about a list */
1180
1181int show_list_info(int argc, char **argv, void *hint)
1182{
1183  printf("List: %s\n", argv[L_NAME]);
1184  printf("Description: %s\n", argv[L_DESC]);
1185  printf("Flags: %s, %s, and %s\n",
1186         atoi(argv[L_ACTIVE]) ? "active" : "inactive",
1187         atoi(argv[L_PUBLIC]) ? "public" : "private",
1188         atoi(argv[L_HIDDEN]) ? "hidden" : "visible");
1189  printf("%s is %sa maillist and is %sa group", argv[L_NAME],
1190         atoi(argv[L_MAILLIST]) ? "" : "not ",
1191         atoi(argv[L_GROUP]) ? "" : "not ");
1192  if (atoi(argv[L_GROUP]))
1193    {
1194      if (atoi(argv[L_NFSGROUP]))
1195        printf(" (and an NFS group)");
1196      printf(" with GID %d\n", atoi(argv[L_GID]));
1197    }
1198  else
1199    printf("\n");
1200  if (atoi(argv[L_MAILMAN]))
1201    printf("%s is a Mailman list on server %s\n", argv[L_NAME],
1202           argv[L_MAILMAN_SERVER]);
1203  printf("Owner: %s %s\n", argv[L_ACE_TYPE], argv[L_ACE_NAME]);
1204  if (strcmp(argv[L_MEMACE_TYPE], "NONE"))
1205    printf("Membership ACL: %s %s\n", argv[L_MEMACE_TYPE],
1206           argv[L_MEMACE_NAME]);
1207  printf("Last modified by %s with %s on %s\n",
1208         argv[L_MODBY], argv[L_MODWITH], argv[L_MODTIME]);
1209  return MR_CONT;
1210}
1211
1212
1213/* Copy retrieved information about a list into a new argv */
1214
1215int save_list_info(int argc, char **argv, void *hint)
1216{
1217  char **nargv = hint;
1218
1219  for (argc = 0; argc < 16; argc++)
1220    nargv[argc + 1] = strdup(argv[argc]);
1221  return MR_CONT;
1222}
1223
1224/* Show the retrieve list member count */
1225
1226int show_list_count(int argc, char **argv, void *hint)
1227{
1228  printf("Members: %s\n", argv[0]);
1229  return MR_CONT;
1230}
1231
1232
1233/* Recursively find all of the members of listname, and then display them */
1234
1235void recursive_display_list_members(void)
1236{
1237  int status, count, savecount;
1238  struct save_queue *lists, *members;
1239  struct member *m, *m1, *data;
1240
1241  lists = sq_create();
1242  members = sq_create();
1243  m = malloc(sizeof(struct member));
1244  m->type = M_LIST;
1245  m->name = listname;
1246  sq_save_data(lists, m);
1247
1248  while (sq_get_data(lists, &m))
1249    {
1250      sq_destroy(memberlist);
1251      memberlist = sq_create();
1252      status = mr_query("get_members_of_list", 1, &(m->name),
1253                        get_list_members, memberlist);
1254      if (status)
1255        com_err(whoami, status, "while getting members of list %s", m->name);
1256      while (sq_get_data(memberlist, &m1))
1257        {
1258          if (m1->type == M_LIST)
1259            unique_add_member(lists, m1);
1260          else
1261            unique_add_member(members, m1);
1262        }
1263    }
1264  savecount = count = sq_count_elts(members);
1265  data = malloc(count * sizeof(struct member));
1266  count = 0;
1267  while (sq_get_data(members, &m))
1268    memcpy(&data[count++], m, sizeof(struct member));
1269  qsort(data, count, sizeof(struct member), membercmp);
1270  for (count = 0; count < savecount; count++)
1271    show_list_member(&data[count]);
1272}
1273
1274
1275/* add a struct member to a queue if that member isn't already there. */
1276
1277void unique_add_member(struct save_queue *q, struct member *m)
1278{
1279  struct save_queue *qp;
1280
1281  for (qp = q->q_next; qp != q; qp = qp->q_next)
1282    {
1283      if (!membercmp(qp->q_data, m))
1284        return;
1285    }
1286  sq_save_data(q, m);
1287}
1288
1289
1290/* Collect the retrieved members of the list */
1291
1292int get_list_members(int argc, char **argv, void *sq)
1293{
1294  struct save_queue *q = sq;
1295  struct member *m;
1296
1297  m = malloc(sizeof(struct member));
1298  switch (argv[0][0])
1299    {
1300    case 'U':
1301      m->type = M_USER;
1302      break;
1303    case 'L':
1304      m->type = M_LIST;
1305      break;
1306    case 'S':
1307      m->type = M_STRING;
1308      break;
1309    case 'K':
1310      m->type = M_KERBEROS;
1311      break;
1312    case 'M':
1313      m->type = M_MACHINE;
1314      break;
1315    }
1316  m->name = strdup(argv[1]);
1317  if (argc == 3)
1318    m->tag = strdup(argv[2]);
1319  else
1320    m->tag = strdup("");
1321  sq_save_data(q, m);
1322  return MR_CONT;
1323}
1324
1325
1326/* Open file, parse members from file, and put them on the specified queue */
1327void get_members_from_file(char *filename, struct save_queue *queue)
1328{
1329  FILE *in;
1330  char buf[BUFSIZ];
1331  struct member *memberstruct;
1332
1333  if (!strcmp(filename, "-"))
1334    in = stdin;
1335  else
1336    {
1337      in = fopen(filename, "r");
1338      if (!in)
1339        {
1340          com_err(whoami, errno, "while opening %s for input", filename);
1341          exit(2);
1342        }
1343    }
1344
1345  while (fgets(buf, BUFSIZ, in))
1346    {
1347      if ((memberstruct = parse_member(buf)))
1348        sq_save_data(queue, memberstruct);
1349    }
1350  if (!feof(in))
1351    {
1352      com_err(whoami, errno, "while reading from %s", filename);
1353      exit(2);
1354    }
1355}
1356
1357
1358/* Collect the possible expansions of the alias MAILHUB */
1359
1360int collect(int argc, char **argv, void *l)
1361{
1362  char ***list = l;
1363  int i;
1364
1365  for (i = 0; (*list)[i]; i++)
1366    ;
1367  *list = realloc(*list, (i + 2) * sizeof(char *));
1368  (*list)[i] = strdup(argv[2]);
1369  (*list)[i + 1] = NULL;
1370  return MR_CONT;
1371}
1372
1373
1374/* Parse a line of input, fetching a member.  NULL is returned if a member
1375 * is not found.  ';' is a comment character.
1376 */
1377
1378struct member *parse_member(char *s)
1379{
1380  struct member *m;
1381  char *p, *lastchar;
1382
1383  while (*s && isspace(*s))
1384    s++;
1385  lastchar = p = s;
1386  while (*p && *p != '\n' && *p != ';')
1387    {
1388      if (isprint(*p) && !isspace(*p))
1389        lastchar = p++;
1390      else
1391        p++;
1392    }
1393  lastchar++;
1394  *lastchar = '\0';
1395  if (p == s || strlen(s) == 0)
1396    return NULL;
1397
1398  if (!(m = malloc(sizeof(struct member))))
1399    return NULL;
1400  m->tag = strdup("");
1401
1402  if ((p = strchr(s, ':')))
1403    {
1404      *p = '\0';
1405      m->name = ++p;
1406      if (!strcasecmp("user", s))
1407        m->type = M_USER;
1408      else if (!strcasecmp("list", s))
1409        m->type = M_LIST;
1410      else if (!strcasecmp("string", s))
1411        m->type = M_STRING;
1412      else if (!strcasecmp("kerberos", s))
1413        m->type = M_KERBEROS;
1414      else if (!strcasecmp("machine", s))
1415        m->type = M_MACHINE;
1416      else if (!strcasecmp("none", s))
1417        m->type = M_NONE;
1418      else
1419        {
1420          m->type = M_ANY;
1421          *(--p) = ':';
1422          m->name = s;
1423        }
1424      m->name = strdup(m->name);
1425    }
1426  else
1427    {
1428      m->name = strdup(s);
1429      m->type = strcasecmp(s, "none") ? M_ANY : M_NONE;
1430    }
1431  return m;
1432}
1433
1434
1435/*
1436 * This routine two compares members by the following rules:
1437 * 1.  A USER is less than a LIST
1438 * 2.  A LIST is less than a STRING
1439 * 3.  If two members are of the same type, the one alphabetically first
1440 *     is less than the other
1441 * It returs < 0 if the first member is less, 0 if they are identical, and
1442 * > 0 if the second member is less (the first member is greater).
1443 */
1444
1445int membercmp(const void *mem1, const void *mem2)
1446{
1447  const struct member *m1 = mem1, *m2 = mem2;
1448
1449  if (m1->type == M_ANY || m2->type == M_ANY || (m1->type == m2->type))
1450    return strcmp(m1->name, m2->name);
1451  else
1452    return m1->type - m2->type;
1453}
1454
1455
1456int sq_count_elts(struct save_queue *q)
1457{
1458  char *foo;
1459  int count;
1460
1461  count = 0;
1462  while (sq_get_data(q, &foo))
1463    count++;
1464  return count;
1465}
1466
1467char *get_username(void)
1468{
1469  char *username;
1470
1471  username = getenv("USER");
1472  if (!username)
1473    {
1474      username = mrcl_krb_user();
1475      if (!username)
1476        {
1477          com_err(whoami, 0, "Could not determine username");
1478          exit(1);
1479        }
1480    }
1481  return username;
1482}
Note: See TracBrowser for help on using the repository browser.