]> andersk Git - moira.git/blame - gen/mailhub.pc
- Rewrite address output code to use more obvious variable names,
[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
5eaef520 37FILE *out = stdout;
bac4ceaa 38
7ac48069 39struct hash *users, *machines, *strings, *lists, *names;
40struct user {
41 char *login;
7ac48069 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 struct member *m;
56};
57struct names {
58 char *name;
59 struct names *next;
60 int keep;
61 int id;
62};
63
64void get_info(void);
65void save_mlist(int id, void *list, void *force);
66void insert_login(int id, void *user, void *hint);
7ac48069 67void insert_name(char *s, int id, int nodups, int copy);
68int hashstr(char *s);
69void sort_info(void);
70void output_data(int dummy, void *names, void *out);
71void output_mlist(int id, struct list *l);
7ac48069 72void put_fill(FILE *aliases, char *string);
73void do_people(void);
74
b033c6ed 75int incount = 0;
76
5eaef520 77int main(int argc, char **argv)
bac4ceaa 78{
b033c6ed 79 time_t tm = time(NULL);
dfaf9b68 80 char filename[MAXPATHLEN], *targetfile;
5eaef520 81 struct stat sb;
b033c6ed 82 int flag1, flag2;
5eaef520 83
84 EXEC SQL CONNECT :db;
85
86 if (argc == 2)
87 {
88 if (stat(argv[1], &sb) == 0)
89 {
b033c6ed 90 if (ModDiff(&flag1, "users", sb.st_mtime) ||
91 ModDiff(&flag2, "list", sb.st_mtime))
5eaef520 92 exit(MR_DATE);
b033c6ed 93 if (flag1 < 0 && flag2 < 0)
5eaef520 94 {
95 fprintf(stderr, "File %s does not need to be rebuilt.\n",
96 argv[1]);
97 exit(MR_NO_CHANGE);
bac4ceaa 98 }
99 }
5eaef520 100 targetfile = argv[1];
101 sprintf(filename, "%s~", targetfile);
102 if (!(out = fopen(filename, "w")))
103 {
104 fprintf(stderr, "unable to open %s for output\n", filename);
105 exit(MR_OCONFIG);
bac4ceaa 106 }
5eaef520 107 }
108 else if (argc != 1)
109 {
110 fprintf(stderr, "usage: %s [outfile]\n", argv[0]);
111 exit(MR_ARGS);
bac4ceaa 112 }
113
5eaef520 114 fprintf(out, "%s\n# Aliases File Extract of %s", divide, ctime(&tm));
115 fprintf(out, "# This file is automatically generated, "
116 "do not edit it directly.\n%s\n\n", divide);
bac4ceaa 117
5eaef520 118 get_info();
bac4ceaa 119
5eaef520 120 EXEC SQL COMMIT;
bac4ceaa 121
5eaef520 122 fprintf(stderr, "Sorting Info\n");
123 sort_info();
bac4ceaa 124
5eaef520 125 fprintf(stderr, "Dumping information\n");
126 do_people();
bac4ceaa 127
5eaef520 128 fprintf(out, "\n%s\n# End of aliases file\n", divide);
bac4ceaa 129
5eaef520 130 if (fclose(out))
131 {
132 perror("close failed");
133 exit(MR_CCONFIG);
bac4ceaa 134 }
135
5eaef520 136 if (argc == 2)
137 fix_file(targetfile);
138 exit(MR_SUCCESS);
bac4ceaa 139}
140
7ac48069 141void get_info(void)
bac4ceaa 142{
5eaef520 143 EXEC SQL BEGIN DECLARE SECTION;
144 int id, pid, bid, cnt, maillistp, acl, mid;
dfaf9b68 145 char mname[MACHINE_NAME_SIZE], str[STRINGS_STRING_SIZE];
146 char login[USERS_LOGIN_SIZE], potype[USERS_POTYPE_SIZE];
147 char lname[LIST_NAME_SIZE], desc[LIST_DESCRIPTION_SIZE];
148 char type[LIST_ACL_TYPE_SIZE];
5eaef520 149 EXEC SQL END DECLARE SECTION;
150 char *s;
44d12d58 151 struct user *u;
5eaef520 152 struct list *l, *memberlist;
44d12d58 153 struct member *m;
5eaef520 154
155 /* The following is declarative, not executed,
156 * and so is dependent on where it is in the file,
157 * not in the order of execution of statements.
158 */
159 EXEC SQL WHENEVER SQLERROR GOTO sqlerr;
160
161 cnt = 0;
b033c6ed 162 machines = create_hash(100);
5eaef520 163
164 EXEC SQL DECLARE m_cursor CURSOR FOR
165 SELECT mach_id, name
166 FROM machine
167 WHERE status = 1
b033c6ed 168 AND mach_id IN ( SELECT UNIQUE pop_id FROM users )
5eaef520 169 ORDER BY mach_id;
170 EXEC SQL OPEN m_cursor;
171 while (1)
172 {
dfaf9b68 173 EXEC SQL FETCH m_cursor INTO :id, :mname;
5eaef520 174 if (sqlca.sqlcode)
175 break;
dfaf9b68 176 if ((s = strchr(mname, '.')))
5eaef520 177 *s = '\0';
178 else
dfaf9b68 179 strtrim(mname);
bac4ceaa 180#ifdef ATHENA
dfaf9b68 181 strcat(mname, ".LOCAL");
bac4ceaa 182#endif
dfaf9b68 183 if (hash_store(machines, id, strdup(mname)) < 0)
5eaef520 184 {
185 fprintf(stderr, "Out of memory!\n");
186 exit(MR_NO_MEM);
bac4ceaa 187 }
5eaef520 188 cnt++;
bac4ceaa 189 }
5eaef520 190 EXEC SQL CLOSE m_cursor;
191
192 fprintf(stderr, "Loaded %d machines\n", cnt);
193
194 cnt = 0;
195 strings = create_hash(11001);
196
197 EXEC SQL DECLARE s_cursor CURSOR FOR
198 SELECT string_id, string
199 FROM strings
200 ORDER BY string_id;
201 EXEC SQL OPEN s_cursor;
202 while (1)
203 {
dfaf9b68 204 EXEC SQL FETCH s_cursor INTO :id, :str;
5eaef520 205 if (sqlca.sqlcode)
206 break;
dfaf9b68 207 if (hash_store(strings, id, strdup(strtrim(str))) < 0)
5eaef520 208 {
209 fprintf(stderr, "Out of memory!\n");
210 exit(MR_NO_MEM);
bac4ceaa 211 }
5eaef520 212 cnt++;
bac4ceaa 213 }
5eaef520 214 EXEC SQL CLOSE s_cursor;
215
216 fprintf(stderr, "Loaded %d strings\n", cnt);
217
218 cnt = 0;
219 users = create_hash(13001);
220
221 EXEC SQL DECLARE u_cursor CURSOR FOR
dfaf9b68 222 SELECT users_id, login, potype, pop_id, box_id
5eaef520 223 FROM users
224 WHERE status != 3
225 ORDER BY users_id;
226 EXEC SQL OPEN u_cursor;
227 while (1)
228 {
dfaf9b68 229 EXEC SQL FETCH u_cursor INTO :id, :login, :potype, :pid, :bid;
5eaef520 230 if (sqlca.sqlcode)
231 break;
7ac48069 232 u = malloc(sizeof(struct user));
dfaf9b68 233 u->login = strdup(strtrim(login));
5eaef520 234
dfaf9b68 235 if (potype[0] == 'P' && (s = hash_lookup(machines, pid)))
5eaef520 236 {
dfaf9b68 237 char *buf = malloc(strlen(u->login) + strlen(s) + 2);
5eaef520 238 sprintf(buf, "%s@%s", u->login, s);
dfaf9b68 239 u->pobox = buf;
5eaef520 240 }
dfaf9b68 241 else if (potype[0] == 'S')
5eaef520 242 u->pobox = hash_lookup(strings, bid);
243 else
244 u->pobox = NULL;
245 if (hash_store(users, id, u) < 0)
246 {
247 fprintf(stderr, "Out of memory!\n");
248 exit(MR_NO_MEM);
bac4ceaa 249 }
5eaef520 250 cnt++;
bac4ceaa 251 }
5eaef520 252 EXEC SQL CLOSE u_cursor;
253 fprintf(stderr, "Loaded %d users\n", cnt);
254
255 cnt = 0;
256 lists = create_hash(15000);
257
258 EXEC SQL DECLARE l_cursor CURSOR FOR
259 SELECT list_id, name, maillist, description, acl_type, acl_id
260 FROM list
261 WHERE active != 0
262 ORDER BY list_id;
263 EXEC SQL OPEN l_cursor;
264 while (1)
265 {
dfaf9b68 266 EXEC SQL FETCH l_cursor INTO :id, :lname, :maillistp, :desc, :type, :acl;
5eaef520 267 if (sqlca.sqlcode)
268 break;
7ac48069 269 l = malloc(sizeof(struct list));
dfaf9b68 270 l->name = strdup(strtrim(lname));
5eaef520 271 l->maillist = maillistp;
dfaf9b68 272 l->description = strdup(strtrim(desc));
5eaef520 273 l->acl_t = type[0];
274 l->acl_id = acl;
275 l->m = NULL;
276 if (hash_store(lists, id, l) < 0)
277 {
278 fprintf(stderr, "Out of memory!\n");
279 exit(MR_NO_MEM);
bac4ceaa 280 }
5eaef520 281 cnt++;
bac4ceaa 282 }
5eaef520 283 EXEC SQL CLOSE l_cursor;
284 fprintf(stderr, "Loaded %d lists\n", cnt);
285
286 cnt = 0;
287
288 EXEC SQL DECLARE m_cursor2 CURSOR FOR
289 SELECT list_id, member_type, member_id
290 FROM imembers
291 WHERE direct = 1
292 ORDER BY list_id;
293 EXEC SQL OPEN m_cursor2;
294 while (1)
295 {
296 EXEC SQL FETCH m_cursor2 INTO :id, :type, :mid;
297 if (sqlca.sqlcode)
298 break;
299 cnt++;
7ac48069 300 if ((l = hash_lookup(lists, id)))
5eaef520 301 {
7ac48069 302 m = malloc(sizeof(struct member));
303 if (type[0] == 'U' && (u = hash_lookup(users, mid)))
5eaef520 304 {
305 m->list_id = 0;
306 m->name = u->login;
307 m->next = l->m;
308 l->m = m;
309 }
7ac48069 310 else if (type[0] == 'L' && (memberlist = hash_lookup(lists, mid)))
5eaef520 311 {
312 m->list_id = mid;
313 m->name = memberlist->name;
314 m->next = l->m;
315 l->m = m;
316 }
317 else if (type[0] == 'S' && (s = hash_lookup(strings, mid)))
318 {
319 m->list_id = 0;
320 m->next = l->m;
321 l->m = m;
322 m->name = s;
bac4ceaa 323 }
324 }
325 }
5eaef520 326 EXEC SQL CLOSE m_cursor2;
327 fprintf(stderr, "Loaded %d members\n", cnt);
328
329 EXEC SQL COMMIT;
330 return;
331sqlerr:
332 db_error(sqlca.sqlcode);
333 exit(MR_DBMS_ERR);
bac4ceaa 334}
335
336
7ac48069 337void save_mlist(int id, void *list, void *force)
bac4ceaa 338{
44d12d58 339 struct member *m;
7ac48069 340 struct list *l = list, *l1;
bac4ceaa 341
5eaef520 342 if (l->maillist > 1 || (l->maillist == 0 && !force))
343 return;
bac4ceaa 344
5eaef520 345 if (l->m && l->m->next == NULL && !strcasecmp(l->name, l->m->name))
346 {
347 l->maillist = 3;
348 return;
bac4ceaa 349 }
5eaef520 350 l->maillist = 2;
351 insert_name(l->name, -1, TRUE, FALSE);
352 output_mlist(id, l);
353
7ac48069 354 if (l->acl_t == 'L' && (l1 = hash_lookup(lists, l->acl_id)))
355 save_mlist(0, l1, (void *)TRUE);
5eaef520 356
357 for (m = l->m; m; m = m->next)
358 {
7ac48069 359 if (m->list_id && (l1 = hash_lookup(lists, m->list_id)))
360 save_mlist(0, l1, (void *)TRUE);
bac4ceaa 361 }
362}
363
364
7ac48069 365void insert_login(int id, void *user, void *hint)
bac4ceaa 366{
7ac48069 367 struct user *u = user;
5eaef520 368 if (u->pobox && u->login[0] != '#')
369 insert_name(u->login, id, TRUE, FALSE);
bac4ceaa 370}
371
7ac48069 372void insert_name(char *s, int id, int nodups, int copy)
bac4ceaa 373{
5eaef520 374 int code;
44d12d58 375 struct names *ns;
5eaef520 376
377 incount++;
378 code = hashstr(s);
7ac48069 379 ns = hash_lookup(names, code);
5eaef520 380 if (!ns)
381 {
7ac48069 382 if (!(ns = malloc(sizeof(struct names))))
5eaef520 383 {
384 fprintf(stderr, "ran out of memory inserting name (sorting)\n");
385 exit(MR_NO_MEM);
bac4ceaa 386 }
5eaef520 387 if (copy)
7ac48069 388 ns->name = strdup(s);
5eaef520 389 else
390 ns->name = s;
391 ns->keep = nodups;
392 ns->id = id;
393 ns->next = NULL;
394 if (hash_store(names, code, ns) < 0)
395 {
396 fprintf(stderr, "Out of memory!\n");
397 exit(MR_NO_MEM);
bac4ceaa 398 }
5eaef520 399 return;
bac4ceaa 400 }
5eaef520 401 if (strcasecmp(ns->name, s))
402 {
403 while (ns->next)
404 {
405 ns = ns->next;
406 if (!strcasecmp(ns->name, s))
407 goto foundns;
bac4ceaa 408 }
7ac48069 409 if (!(ns->next = malloc(sizeof(struct names))))
5eaef520 410 {
411 fprintf(stderr, "ran out of memory insterting name (sorting)\n");
412 exit(MR_NO_MEM);
bac4ceaa 413 }
5eaef520 414 ns = ns->next;
415 if (copy)
7ac48069 416 ns->name = strdup(s);
5eaef520 417 else
418 ns->name = s;
419 ns->keep = nodups;
420 ns->id = id;
421 ns->next = NULL;
422 return;
bac4ceaa 423 }
5eaef520 424foundns:
425 if (nodups || ns->keep)
426 {
427 if (nodups && ns->keep)
428 fprintf(stderr, "duplicated name: %s\n", s);
429 return;
bac4ceaa 430 }
5eaef520 431 ns->id = 0;
bac4ceaa 432}
433
434
3ce369ab 435/* Illegal chars: ! " % ( ) , / : ; < = > @ [ \ ] ^ { | } */
bac4ceaa 436
437static int illegalchars[] = {
5eaef520 438 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^@ - ^O */
439 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P - ^_ */
440 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, /* SPACE - / */
441 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, /* 0 - ? */
442 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* @ - O */
443 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, /* P - _ */
444 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ` - o */
445 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, /* p - ^? */
446 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
447 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
448 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
449 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
450 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
451 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
452 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
453 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
bac4ceaa 454};
455
456
457/* While hashing the string, punt any illegal characters */
458
44d12d58 459int hashstr(char *s)
bac4ceaa 460{
44d12d58 461 int result;
462 int c;
5eaef520 463
7ac48069 464 for (result = 0; (c = *s); s++)
5eaef520 465 {
466 if (illegalchars[c])
467 {
44d12d58 468 char *p;
5eaef520 469 for (p = s; *p; p++)
470 *p = p[1];
471 continue;
bac4ceaa 472 }
5eaef520 473 if (isupper(c))
474 c = *s = tolower(c);
475 result = (result << 5) - result + c - '`';
bac4ceaa 476 }
5eaef520 477 return result < 0 ? -result : result;
bac4ceaa 478}
479
480
7ac48069 481void sort_info(void)
bac4ceaa 482{
5eaef520 483 names = create_hash(20001);
484 hash_step(users, insert_login, NULL);
485 incount = 0;
486 fprintf(out, "\n%s\n# Mailing lists\n%s\n", divide, divide);
487 hash_step(lists, save_mlist, FALSE);
488 fprintf(stderr, "Output %d lists\n", incount);
bac4ceaa 489}
490
491
7ac48069 492void output_data(int dummy, void *names, void *out)
bac4ceaa 493{
7ac48069 494 struct names *ns, *nms = names;
44d12d58 495 struct user *u;
5eaef520 496
497 incount++;
498 for (ns = nms; ns; ns = ns->next)
499 {
500 if (!ns->name[0] || !ns->name[1])
501 {
502 fprintf(stderr, "punting %s due to short name\n", ns->name);
503 continue;
bac4ceaa 504 }
5eaef520 505 if (ns->id > 0)
506 {
7ac48069 507 u = hash_lookup(users, ns->id);
5eaef520 508 if (u->pobox)
509 fprintf(out, "%s: %s\n", ns->name, u->pobox);
bac4ceaa 510 }
511 }
512}
513
514
7ac48069 515void output_mlist(int id, struct list *l)
bac4ceaa 516{
5eaef520 517 struct list *l1;
44d12d58 518 struct member *m;
519 struct user *u;
b033c6ed 520 int line_width, alias_width, word_width, beginning;
521 static int cont = 1;
522 char str[8];
5eaef520 523
524 put_fill(out, l->description);
7ac48069 525 if (l->acl_t == 'L' && (l1 = hash_lookup(lists, l->acl_id)))
5eaef520 526 fprintf(out, "owner-%s: %s\n%s: ", l->name, l1->name, l->name);
7ac48069 527 else if (l->acl_t == 'U' && (u = hash_lookup(users, l->acl_id)))
5eaef520 528 fprintf(out, "owner-%s: %s\n%s: ", l->name, u->login, l->name);
529 else
530 fprintf(out, "%s: ", l->name);
531
b033c6ed 532 alias_width = line_width = strlen(l->name) + 2;
533 beginning = 1;
5eaef520 534 for (m = l->m; m; m = m->next)
b033c6ed 535 {
536 word_width = strlen(m->name);
537
538 if (!beginning && alias_width + word_width + 2 > MAX_ALIAS_WIDTH)
539 {
540 /* Make a continuation. */
541 sprintf(str, "%c%c%c%c%c%c", rand() % 26 + 97, rand() % 26 + 97,
542 rand() % 26 + 97, rand() % 26 + 97,
543 rand() % 26 + 97, rand() % 26 + 97);
544 fprintf(out, ",\n\tcont%d-%s\ncont%d-%s: ", cont, str, cont, str);
545 cont++;
546 alias_width = line_width = 17 + word_width;
547 fputs(m->name, out);
548 }
549 else if (beginning)
550 {
551 /* Beginning of alias, so don't wrap. */
552 line_width += word_width;
553 alias_width = line_width;
554 fputs(m->name, out);
555 beginning = 0;
556 }
557 else if (line_width + word_width + 2 > MAX_LINE_WIDTH)
558 {
559 /* Wrap. */
560 fprintf(out, ",\n\t%s", m->name);
561 alias_width += line_width + word_width + 2;
562 line_width = word_width + 8;
563 }
564 else
565 {
566 /* Continue line. */
567 line_width += word_width + 2;
568 fprintf(out, ", %s", m->name);
569 }
570 }
5eaef520 571 if (!l->m)
572 fprintf(out, "/dev/null");
573 fprintf(out, "\n\n");
574 incount++;
bac4ceaa 575}
576
b033c6ed 577/* Write a word-wrapped list description to the aliases file as a
578 * comment. */
7ac48069 579void put_fill(FILE *aliases, char *string)
492f333e 580{
44d12d58 581 char *c;
b033c6ed 582 int line_width;
583 int word_width;
492f333e 584
5eaef520 585 if (!string || !*string)
586 return;
587 fputs("# ", aliases);
b033c6ed 588 line_width = 3;
5eaef520 589
590 while (1)
591 {
592 while (*string == ' ')
593 string++;
594 c = strchr(string, ' ');
595 if (!c)
b033c6ed 596 word_width = strlen(string);
5eaef520 597 else
598 {
b033c6ed 599 word_width = c - string;
5eaef520 600 *c = '\0';
492f333e 601 }
602
b033c6ed 603 if (line_width + word_width > MAX_LINE_WIDTH)
5eaef520 604 {
605 fputs("\n# ", aliases);
b033c6ed 606 line_width = 3;
5eaef520 607 fputs(string, aliases);
608 }
609 else
610 fputs(string, aliases);
611
612 if (!c)
613 break;
614 /* add a space after the word */
615 fputc(' ', aliases);
b033c6ed 616 word_width++;
617 line_width += word_width;
618 string += word_width;
5eaef520 619 /* add another if after a period */
620 if (*--c == '.')
621 {
622 fputc(' ', aliases);
b033c6ed 623 line_width++;
492f333e 624 }
625 }
626
5eaef520 627 fputc('\n', aliases);
492f333e 628}
629
630
7ac48069 631void do_people(void)
bac4ceaa 632{
5eaef520 633 incount = 0;
634 fprintf(out, "\n%s\n# People\n%s\n", divide, divide);
635 hash_step(names, output_data, out);
636 fprintf(stderr, "Output %d entries\n", incount);
bac4ceaa 637}
This page took 0.16937 seconds and 5 git commands to generate.