source: trunk/third/moira/gen/mailhub.pc @ 23740

Revision 23740, 14.0 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: mailhub.pc,v 1.21 2008-08-22 17:49:11 zacheiss Exp $
2 *
3 * This generates the /usr/lib/aliases file for the mailhub.
4 *
5 * (c) Copyright 1988-1998 by the Massachusetts Institute of Technology.
6 * For copying and distribution information, please see the file
7 * <mit-copyright.h>.
8 */
9
10#include <mit-copyright.h>
11#include <moira.h>
12#include <moira_site.h>
13
14#include <sys/stat.h>
15
16#include <ctype.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20
21#include "util.h"
22
23EXEC SQL INCLUDE sqlca;
24
25RCSID("$Header: /afs/athena.mit.edu/astaff/project/moiradev/repository/moira/gen/mailhub.pc,v 1.21 2008-08-22 17:49:11 zacheiss Exp $");
26
27char *whoami = "mailhub.gen";
28char *db = "moira/moira";
29char *divide = "##############################################################";
30
31#define MAX_LINE_WIDTH  72
32#define MAX_ALIAS_WIDTH 592
33
34#define FALSE 0
35#define TRUE (!FALSE)
36
37FILE *out = stdout;
38
39struct hash *users, *machines, *strings, *lists;
40struct user {
41  char *login;
42  char *pobox;
43};
44struct member {
45  struct member *next;
46  char *name;
47  int list_id;
48};
49struct list {
50  char *name;
51  char maillist;
52  char *description;
53  char acl_t;
54  int acl_id;
55  char mailman;
56  char *mailman_server;
57  struct member *m;
58};
59
60void get_info(void);
61void save_mlist(int id, void *list, void *force);
62int check_string(char *s);
63void output_login(int dummy, void *names, void *out);
64void output_mlist(int id, struct list *l);
65void put_fill(FILE *aliases, char *string);
66void do_people(void);
67
68int incount = 0;
69
70int main(int argc, char **argv)
71{
72  time_t tm = time(NULL);
73  char filename[MAXPATHLEN], *targetfile;
74
75  EXEC SQL CONNECT :db;
76
77  if (argc == 2)
78    {
79      targetfile = argv[1];
80      sprintf(filename, "%s~", targetfile);
81      if (!(out = fopen(filename, "w")))
82        {
83          fprintf(stderr, "unable to open %s for output\n", filename);
84          exit(MR_OCONFIG);
85        }
86    }
87  else if (argc != 1)
88    {
89      fprintf(stderr, "usage: %s [outfile]\n", argv[0]);
90      exit(MR_ARGS);
91    }
92
93  fprintf(out, "%s\n# Aliases File Extract of %s", divide, ctime(&tm));
94  fprintf(out, "# This file is automatically generated, "
95          "do not edit it directly.\n%s\n\n", divide);
96
97  get_info();
98
99  EXEC SQL COMMIT;
100
101  incount = 0;
102  fprintf(out, "\n%s\n# Mailing lists\n%s\n\n", divide, divide);
103  hash_step(lists, save_mlist, FALSE);
104  fprintf(stderr, "Output %d lists\n", incount);
105
106  incount = 0;
107  fprintf(out, "\n%s\n# People\n%s\n\n", divide, divide);
108  hash_step(users, output_login, out);
109  fprintf(stderr, "Output %d users\n", incount);
110
111  fprintf(out, "\n%s\n# End of aliases file\n", divide);
112
113  if (fclose(out))
114    {
115      perror("close failed");
116      exit(MR_CCONFIG);
117    }
118
119  if (argc == 2)
120    fix_file(targetfile);
121  exit(MR_SUCCESS);
122}
123
124void get_info(void)
125{
126  EXEC SQL BEGIN DECLARE SECTION;
127  int id, pid, iid, bid, eid, cnt, maillistp, acl, mid, mailman;
128  char mname[MACHINE_NAME_SIZE], str[STRINGS_STRING_SIZE];
129  char login[USERS_LOGIN_SIZE], potype[USERS_POTYPE_SIZE];
130  char lname[LIST_NAME_SIZE], desc[LIST_DESCRIPTION_SIZE];
131  char type[LIST_ACL_TYPE_SIZE], mailman_server[MACHINE_NAME_SIZE];
132  EXEC SQL END DECLARE SECTION;
133  char *s;
134  struct user *u;
135  struct list *l, *memberlist;
136  struct member *m;
137
138  /* The following is declarative, not executed,
139   * and so is dependent on where it is in the file,
140   * not in the order of execution of statements.
141   */
142  EXEC SQL WHENEVER SQLERROR GOTO sqlerr;
143
144  cnt = 0;
145  machines = create_hash(100);
146
147  EXEC SQL DECLARE m_cursor CURSOR FOR
148    SELECT mach_id, name
149    FROM machine
150    WHERE status = 1
151    AND ( mach_id IN ( SELECT UNIQUE pop_id FROM users ) OR
152          mach_id IN ( SELECT UNIQUE mach_id FROM filesys
153                       WHERE type = 'IMAP' ) )   
154    ORDER BY mach_id;
155  EXEC SQL OPEN m_cursor;
156  while (1)
157    {
158      EXEC SQL FETCH m_cursor INTO :id, :mname;
159      if (sqlca.sqlcode)
160        break;
161      if ((s = strchr(mname, '.')))
162        *s = '\0';
163      else
164        strtrim(mname);
165#ifdef ATHENA
166      strcat(mname, ".LOCAL");
167#endif
168      if (hash_store(machines, id, strdup(mname)) < 0)
169        {
170          fprintf(stderr, "Out of memory!\n");
171          exit(MR_NO_MEM);
172        }
173      cnt++;
174    }
175  EXEC SQL CLOSE m_cursor;
176
177  EXEC SQL DECLARE e_cursor CURSOR FOR
178    SELECT mach_id, name
179    FROM machine
180    WHERE status = 1
181    AND mach_id in (SELECT UNIQUE exchange_id FROM users)
182    ORDER BY mach_id;
183  EXEC SQL OPEN e_cursor;
184  while (1)
185    {
186      EXEC SQL FETCH e_cursor INTO :id, :mname;
187      if (sqlca.sqlcode)
188        break;
189      strtrim(mname);
190      if (hash_store(machines, id, strdup(mname)) < 0)
191        {
192          fprintf(stderr, "Out of memory!\n");
193          exit(MR_NO_MEM);
194        }
195      cnt++;
196    }
197  EXEC SQL CLOSE e_cursor;
198       
199  fprintf(stderr, "Loaded %d machines\n", cnt);
200
201  cnt = 0;
202  strings = create_hash(11001);
203
204  EXEC SQL DECLARE s_cursor CURSOR FOR
205    SELECT string_id, string
206    FROM strings
207    ORDER BY string_id;
208  EXEC SQL OPEN s_cursor;
209  while (1)
210    {
211      EXEC SQL FETCH s_cursor INTO :id, :str;
212      if (sqlca.sqlcode)
213        break;
214      if (hash_store(strings, id, strdup(strtrim(str))) < 0)
215        {
216          fprintf(stderr, "Out of memory!\n");
217          exit(MR_NO_MEM);
218        }
219      cnt++;
220    }
221  EXEC SQL CLOSE s_cursor;
222
223  fprintf(stderr, "Loaded %d strings\n", cnt);
224
225  cnt = 0;
226  users = create_hash(13001);
227
228  EXEC SQL DECLARE u_cursor CURSOR FOR
229    SELECT users_id, login, potype, pop_id, imap_id, box_id, exchange_id
230    FROM users
231    WHERE status != 3
232    ORDER BY users_id;
233  EXEC SQL OPEN u_cursor;
234  while (1)
235    {
236      char *saddr = NULL, *paddr = NULL;
237
238      EXEC SQL FETCH u_cursor INTO :id, :login, :potype, :pid, :iid, :bid,
239        :eid;
240      if (sqlca.sqlcode)
241        break;
242      u = malloc(sizeof(struct user));
243      u->login = strdup(strtrim(login));
244
245      if (!strcmp(strtrim(potype), "NONE"))
246        u->pobox = NULL;
247      else
248        {
249          /* If SMTP or SPLIT, get SMTP address. */
250          if (potype[0] == 'S')
251            {
252              saddr = hash_lookup(strings, bid);
253
254              /* If SMTP, clear pid and iid. */
255              if (potype[1] == 'M')
256                pid = iid = eid = 0;
257            }
258
259          /* If IMAP, or SPLIT with IMAP, set pid to mach_id. */
260          if (potype[0] == 'I' || (potype[0] == 'S' && iid))
261            {
262              EXEC SQL SELECT mach_id INTO :pid FROM filesys
263                WHERE filsys_id = :iid;
264            }
265
266          /* If EXCHANGE or SPLIT with EXCHANGE, set pid to eid. */
267          if (potype[0] == 'E' || (potype[0] == 'S' && eid))
268            pid = eid;
269
270          if (pid && (s = hash_lookup(machines, pid)))
271            {
272              paddr = malloc(strlen(u->login) + strlen(s) + 2);
273              sprintf(paddr, "%s@%s", u->login, s);
274            }
275
276          if (paddr && saddr)
277            {
278              u->pobox = malloc(strlen(paddr) + strlen(saddr) + 3);
279              sprintf(u->pobox, "%s, %s", paddr, saddr);
280              free(paddr);
281            }
282          else if (paddr)
283            u->pobox = paddr;
284          else
285            u->pobox = saddr;
286        }
287
288      check_string(u->login);
289      if (hash_store(users, id, u) < 0)
290        {
291          fprintf(stderr, "Out of memory!\n");
292          exit(MR_NO_MEM);
293        }
294      cnt++;
295    }
296  EXEC SQL CLOSE u_cursor;
297  fprintf(stderr, "Loaded %d users\n", cnt);
298
299  cnt = 0;
300  lists = create_hash(15000);
301
302  EXEC SQL DECLARE l_cursor CURSOR FOR
303    SELECT l.list_id, l.name, l.maillist, l.description, l.acl_type, l.acl_id,
304    l.mailman, m.name
305    FROM list l, machine m
306    WHERE active != 0 AND l.mailman_id = m.mach_id
307    ORDER BY list_id;
308  EXEC SQL OPEN l_cursor;
309  while (1)
310    {
311      EXEC SQL FETCH l_cursor INTO :id, :lname, :maillistp, :desc, :type, :acl,
312        :mailman, :mailman_server;
313      if (sqlca.sqlcode)
314        break;
315      l = malloc(sizeof(struct list));
316      l->name = strdup(strtrim(lname));
317      l->maillist = maillistp;
318      l->description = strdup(strtrim(desc));
319      l->acl_t = type[0];
320      l->acl_id = acl;
321      l->mailman = mailman;
322      l->mailman_server = strdup(strtrim(mailman_server));
323      l->m = NULL;
324      if (hash_store(lists, id, l) < 0)
325        {
326          fprintf(stderr, "Out of memory!\n");
327          exit(MR_NO_MEM);
328        }
329      cnt++;
330    }
331  EXEC SQL CLOSE l_cursor;
332  fprintf(stderr, "Loaded %d lists\n", cnt);
333
334  cnt = 0;
335
336  EXEC SQL DECLARE m_cursor2 CURSOR FOR
337    SELECT list_id, member_type, member_id
338    FROM imembers
339    WHERE direct = 1
340    ORDER BY list_id;
341  EXEC SQL OPEN m_cursor2;
342  while (1)
343    {
344      EXEC SQL FETCH m_cursor2 INTO :id, :type, :mid;
345      if (sqlca.sqlcode)
346        break;
347      cnt++;
348      if ((l = hash_lookup(lists, id)))
349        {
350          m = malloc(sizeof(struct member));
351          if (type[0] == 'U' && (u = hash_lookup(users, mid)))
352            {
353              m->list_id = 0;
354              m->name = u->login;
355              m->next = l->m;
356              l->m = m;
357            }
358          else if (type[0] == 'L' && (memberlist = hash_lookup(lists, mid)))
359            {
360              m->list_id = mid;
361              m->name = memberlist->name;
362              m->next = l->m;
363              l->m = m;
364            }
365          else if (type[0] == 'S' && (s = hash_lookup(strings, mid)))
366            {
367              m->list_id = 0;
368              m->next = l->m;
369              l->m = m;
370              m->name = s;
371            }
372        }
373    }
374  EXEC SQL CLOSE m_cursor2;
375  fprintf(stderr, "Loaded %d members\n", cnt);
376
377  EXEC SQL COMMIT;
378  return;
379sqlerr:
380  db_error(sqlca.sqlcode);
381  exit(MR_DBMS_ERR);
382}
383
384
385void save_mlist(int id, void *list, void *force)
386{
387  struct member *m;
388  struct list *l = list, *l1;
389
390  if (l->maillist > 1 || (l->maillist == 0 && !force) ||
391      !check_string(l->name))
392    return;
393
394  /* If user group appears on list, replace with user. */
395  if (l->m && l->m->next == NULL && !strcasecmp(l->name, l->m->name))
396    {
397      l->maillist = 3;
398      return;
399    }
400  l->maillist = 2;
401  output_mlist(id, l);
402
403  if (l->acl_t == 'L' && (l1 = hash_lookup(lists, l->acl_id)))
404    save_mlist(0, l1, (void *)TRUE);
405
406  for (m = l->m; m; m = m->next)
407    {
408      if (m->list_id && (l1 = hash_lookup(lists, m->list_id)))
409        save_mlist(0, l1, (void *)TRUE);
410    }
411}
412
413void output_login(int dummy, void *user, void *out)
414{
415  struct user *u = user;
416
417  incount++;
418  if (u->pobox && check_string(u->login) && u->login[0] != '#')
419    fprintf(out, "%s: %s\n", u->login, u->pobox);
420}
421
422static const char *mailman_suffixes[] = { "-admin", "-owner", "-request",
423                                          "-bounces", "-confirm", "-join",
424                                          "-leave", "-subscribe",
425                                          "-unsubscribe", NULL };
426
427void output_mlist(int id, struct list *l)
428{
429  struct list *l1;
430  struct member *m;
431  struct user *u;
432  int line_width, alias_width, word_width, beginning;
433  static int cont = 1;
434  char str[8];
435  int i;
436
437  put_fill(out, l->description);
438
439  if (l->mailman && strcmp(l->mailman_server, "[NONE]"))
440    {
441      for (i = 0; mailman_suffixes[i]; i++)
442        fprintf(out, "%s%s: %s%s@%s\n", l->name, mailman_suffixes[i], l->name,
443                mailman_suffixes[i], l->mailman_server);
444      fprintf(out, "owner-%s: %s-owner@%s\n%s: ", l->name, l->name,
445              l->mailman_server, l->name);
446    }
447  else if (l->acl_t ==  'L' && (l1 = hash_lookup(lists, l->acl_id)))
448    fprintf(out, "owner-%s: %s\n%s: ", l->name, l1->name, l->name);
449  else if (l->acl_t ==  'U' && (u = hash_lookup(users, l->acl_id)))
450    fprintf(out, "owner-%s: %s\n%s: ", l->name, u->login, l->name);
451  else
452    fprintf(out, "%s: ", l->name);
453
454  alias_width = line_width = strlen(l->name) + 2;
455  beginning = 1;
456  for (m = l->m; m; m = m->next)
457    {
458      word_width = strlen(m->name);
459
460      if (!beginning && alias_width + word_width + 2 > MAX_ALIAS_WIDTH)
461        {
462          /* Make a continuation. */
463          sprintf(str, "%c%c%c%c%c%c", rand() % 26 + 97, rand() % 26 + 97,
464                  rand() % 26 + 97, rand() % 26 + 97,
465                  rand() % 26 + 97, rand() % 26 + 97);
466          fprintf(out, ",\n\tcont%d-%s\ncont%d-%s: ", cont, str, cont, str);
467          cont++;
468          alias_width = line_width = 17 + word_width;
469          fputs(m->name, out);
470        }
471      else if (beginning)
472        {
473          /* Beginning of alias, so don't wrap. */
474          line_width += word_width;
475          alias_width = line_width;
476          fputs(m->name, out);
477          beginning = 0;
478        }
479      else if (line_width + word_width + 2 > MAX_LINE_WIDTH)
480        {
481          /* Wrap. */
482          fprintf(out, ",\n\t%s", m->name);
483          alias_width += line_width + word_width + 2;
484          line_width = word_width + 8;
485        }
486      else
487        {
488          /* Continue line. */
489          line_width += word_width + 2;
490          fprintf(out, ", %s", m->name);
491        }
492    }
493  if (!l->m)
494    fprintf(out, "/dev/null");
495  fprintf(out, "\n\n");
496  incount++;
497}
498
499/* Write a word-wrapped list description to the aliases file as a
500 * comment. */
501void put_fill(FILE *aliases, char *string)
502{
503  char *c;
504  int line_width;
505  int word_width;
506
507  if (!string || !*string)
508    return;
509  fputs("#  ", aliases);
510  line_width = 3;
511
512  while (1)
513    {
514      while (*string == ' ')
515        string++;
516      c = strchr(string, ' ');
517      if (!c)
518        word_width = strlen(string);
519      else
520        {
521          word_width = c - string;
522          *c = '\0';
523        }
524
525      if (line_width + word_width > MAX_LINE_WIDTH)
526        {
527          fputs("\n#  ", aliases);
528          line_width = 3;
529          fputs(string, aliases);
530        }
531      else
532        fputs(string, aliases);
533
534      if (!c)
535        break;
536      /* add a space after the word */
537      fputc(' ', aliases);
538      word_width++;
539      line_width += word_width;
540      string += word_width;
541      /* add another if after a period */
542      if (*--c == '.')
543        {
544          fputc(' ', aliases);
545          line_width++;
546        }
547    }
548
549  fputc('\n', aliases);
550}
551
552
553/* Illegal chars: this no longer corresponds to the array
554 * in setup_alis.  '+' is a valid character in a string on
555 * a list, but is not a valid character in a listname.
556 */
557
558static int illegalchars[] = {
559  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^@ - ^O */
560  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P - ^_ */
561  1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, /* SPACE - / */
562  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, /* 0 - ? */
563  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* @ - O */
564  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* P - _ */
565  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ` - o */
566  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, /* p - ^? */
567  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
568  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
569  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
570  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
571  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
572  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
573  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
574  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
575};
576
577int check_string(char *s)
578{
579  for (; *s; s++)
580    {
581      if (isupper(*s))
582        *s = tolower(*s);
583
584      if (illegalchars[(unsigned) *s])
585        return 0;
586    }
587  return 1;
588}
Note: See TracBrowser for help on using the repository browser.