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