masqmail

view src/conf.c @ 222:8cddc65765bd

added support for STARTTLS wrappers added the route config option `instant_helo' which causes masqmail, as SMTP client, not to wait for the server's 220 greeting. Instead if says EHLO right at once. You'll need this for STARTTLS wrappers that usually eat the greeting line.
author meillo@marmaro.de
date Fri, 23 Jul 2010 10:57:53 +0200
parents 0241aaccfcdb
children 996b53a50f55
line source
1 /* MasqMail
2 Copyright (C) 1999-2001 Oliver Kurth
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
19 #include <pwd.h>
20 #include <grp.h>
22 #include "masqmail.h"
24 masqmail_conf conf;
26 void
27 init_conf()
28 {
29 struct passwd *passwd;
30 struct group *group;
32 memset(&conf, 0, sizeof(masqmail_conf));
34 conf.orig_uid = getuid();
35 conf.orig_gid = getgid();
37 if ((passwd = getpwnam(DEF_MAIL_USER)))
38 conf.mail_uid = passwd->pw_uid;
39 else {
40 fprintf(stderr, "user %s not found! (terminating)\n", DEF_MAIL_USER);
41 exit(EXIT_FAILURE);
42 }
43 if ((group = getgrnam(DEF_MAIL_GROUP)))
44 conf.mail_gid = group->gr_gid;
45 else {
46 fprintf(stderr, "group %s not found! (terminating)\n", DEF_MAIL_GROUP);
47 exit(EXIT_FAILURE);
48 }
49 }
51 static gchar* true_strings[] = {
52 "yes", "on", "true", NULL
53 };
55 static gchar *false_strings[] = {
56 "no", "off", "false", NULL
57 };
59 static gboolean
60 parse_boolean(gchar * rval)
61 {
62 gchar **str;
64 DEBUG(6) fprintf(stderr, "parse_boolean: %s\n", rval);
66 str = true_strings;
67 while (*str) {
68 if (strncasecmp(*str, rval, strlen(*str)) == 0)
69 return TRUE;
70 str++;
71 }
73 str = false_strings;
74 while (*str) {
75 if (strncasecmp(*str, rval, strlen(*str)) == 0)
76 return FALSE;
77 str++;
78 }
80 fprintf(stderr, "cannot parse value '%s'\n", rval);
81 exit(EXIT_FAILURE);
82 }
84 /* make a list from each line in a file */
85 static GList*
86 parse_list_file(gchar * fname)
87 {
88 GList *list = NULL;
89 FILE *fptr;
91 if ((fptr = fopen(fname, "rt")) == NULL) {
92 logwrite(LOG_ALERT, "could not open %s for reading: %s\n", fname, strerror(errno));
93 exit(EXIT_FAILURE);
94 }
96 gchar buf[256];
98 while (!feof(fptr)) {
99 fgets(buf, 255, fptr);
100 if (buf[0] && (buf[0] != '#') && (buf[0] != '\n')) {
101 g_strchomp(buf);
102 DEBUG(6) fprintf(stderr,"parse_list_file: item = %s\n", buf);
103 list = g_list_append(list, g_strdup(buf));
104 }
105 }
106 fclose(fptr);
108 return list;
109 }
111 /* given a semicolon separated string, this function makes a GList out of it. */
112 GList*
113 parse_list(gchar * line, gboolean read_file)
114 {
115 GList *list = NULL;
116 gchar buf[256];
117 gchar *p, *q;
119 DEBUG(6) fprintf(stderr, "parsing list %s, file?:%d\n", line, read_file);
121 p = line;
122 while (*p != '\0') {
123 q = buf;
125 while (*p && (*p != ';') && (q < buf + 255))
126 *(q++) = *(p++);
127 *q = '\0';
129 if ((buf[0] == '/') && (read_file))
130 /* item is a filename, include its contents */
131 list = g_list_concat(list, parse_list_file(buf));
132 else
133 /* just a normal item */
134 list = g_list_append(list, g_strdup(buf));
136 DEBUG(6) fprintf(stderr, "item = %s\n", buf);
138 if (*p)
139 p++;
140 }
141 return list;
142 }
144 static GList*
145 parse_address_list(gchar * line, gboolean read_file)
146 {
147 GList *plain_list = parse_list(line, read_file);
148 GList *node;
149 GList *list = NULL;
151 foreach(plain_list, node) {
152 gchar *item = (gchar *) (node->data);
153 address *addr = create_address(item, TRUE);
154 if (addr)
155 list = g_list_append(list, addr);
156 g_free(item);
157 }
158 g_list_free(plain_list);
160 return list;
161 }
163 static GList*
164 parse_resolve_list(gchar * line)
165 {
166 GList *list;
167 GList *list_node;
168 GList *res_list = NULL;
170 list = parse_list(line, FALSE);
171 if (!list) {
172 return NULL;
173 }
175 foreach(list, list_node) {
176 gchar *item = (gchar *) (list_node->data);
177 if (strcmp(item, "byname") == 0) {
178 res_list = g_list_append(res_list, resolve_byname);
179 #ifdef ENABLE_RESOLVER
180 } else if (strcmp(item, "dns_a") == 0) {
181 res_list = g_list_append(res_list, resolve_dns_a);
182 } else if (strcmp(item, "dns_mx") == 0) {
183 res_list = g_list_append(res_list, resolve_dns_mx);
184 #endif
185 } else {
186 logwrite(LOG_ALERT, "unknown resolver %s\n", item);
187 exit(EXIT_FAILURE);
188 }
189 g_free(item);
190 }
191 g_list_free(list);
192 return res_list;
193 }
195 static interface*
196 parse_interface(gchar * line, gint def_port)
197 {
198 gchar buf[256];
199 gchar *p, *q;
200 interface *iface;
202 DEBUG(6) fprintf(stderr, "parse_interface: %s\n", line);
204 p = line;
205 q = buf;
206 while ((*p != '\0') && (*p != ':') && (q < buf + 255))
207 *(q++) = *(p++);
208 *q = '\0';
210 iface = g_malloc(sizeof(interface));
211 iface->address = g_strdup(buf);
213 if (*p) {
214 p++;
215 iface->port = atoi(p);
216 } else
217 iface->port = def_port;
218 DEBUG(6) fprintf(stderr,"rval=%s, address:port=%s:%i\n",line, iface->address, iface->port);
220 return iface;
221 }
223 #ifdef ENABLE_IDENT /* so far used for that only */
224 static struct in_addr*
225 parse_network(gchar * line, gint def_port)
226 {
227 gchar buf[256];
228 gchar *p, *q;
229 struct in_addr addr, mask_addr, net_addr, *p_net_addr;
230 guint n;
232 DEBUG(6) fprintf(stderr, "parse_network: %s\n", line);
234 p = line;
235 q = buf;
236 while ((*p != '\0') && (*p != '/') && (q < buf + 255))
237 *(q++) = *(p++);
238 *q = '\0';
240 if ((addr.s_addr = inet_addr(buf)) == INADDR_NONE) {
241 fprintf(stderr, "'%s' is not a valid address (must be ip)\n", buf);
242 exit(EXIT_FAILURE);
243 }
245 if (*p) {
246 guint i;
247 p++;
248 i = atoi(p);
249 if ((i >= 0) && (i <= 32))
250 n = i ? ~((1 << (32 - i)) - 1) : 0;
251 else {
252 fprintf(stderr, "'%d' is not a valid net mask (must be >= 0 and <= 32)\n", i);
253 exit(EXIT_FAILURE);
254 }
255 } else
256 n = 0;
258 mask_addr.s_addr = htonl(n);
259 net_addr.s_addr = mask_addr.s_addr & addr.s_addr;
261 p_net_addr = g_malloc(sizeof(struct in_addr));
262 p_net_addr->s_addr = net_addr.s_addr;
263 return p_net_addr;
264 }
265 #endif
267 static gboolean
268 eat_comments(FILE * in)
269 {
270 gint c;
272 for (c = fgetc(in); (c == '#' || isspace(c)) && c != EOF;
273 c = fgetc(in)) {
274 if (c == '#') {
275 gint c;
276 for (c = fgetc(in); (c != '\n') && (c != EOF); c = fgetc(in));
277 }
278 }
279 if (c == EOF)
280 return FALSE;
281 ungetc(c, in);
282 return TRUE;
283 }
285 /* after parsing, eat trailing character until LF */
286 static gboolean
287 eat_line_trailing(FILE * in)
288 {
289 gint c;
291 for (c = fgetc(in); c != EOF && c != '\n'; c = fgetc(in));
292 if (c == EOF)
293 return FALSE;
294 return TRUE;
295 }
297 static gboolean
298 eat_spaces(FILE * in)
299 {
300 gint c;
302 for (c = fgetc(in); c != EOF && isspace(c); c = fgetc(in)) {
303 /* empty */
304 }
305 if (c == EOF)
306 return FALSE;
307 ungetc(c, in);
308 return TRUE;
309 }
311 static gboolean
312 read_lval(FILE * in, gchar * buf, gint size)
313 {
314 gint c;
315 gchar *ptr = buf;
317 DEBUG(6) fprintf(stderr, "read_lval()\n");
319 if (!eat_spaces(in))
320 return FALSE;
322 c = fgetc(in);
323 DEBUG(6) fprintf(stderr, "read_lval() 2\n");
324 while ((isalnum(c) || c == '_' || c == '-' || c == '.')
325 && (ptr < buf + size - 1)
326 && (c != EOF)) {
327 *ptr = c;
328 ptr++;
329 c = fgetc(in);
330 }
331 *ptr = '\0';
332 ungetc(c, in);
334 if (c == EOF) {
335 fprintf(stderr, "unexpected EOF after %s\n", buf);
336 return FALSE;
337 } else if (ptr >= buf + size - 1) {
338 fprintf(stderr, "lval too long\n");
339 }
341 eat_spaces(in);
343 DEBUG(6) fprintf(stderr, "lval = %s\n", buf);
345 return buf[0] != '\0';
346 }
348 static gboolean
349 read_rval(FILE * in, gchar * buf, gint size)
350 {
351 gint c;
352 gchar *ptr = buf;
354 DEBUG(6) fprintf(stderr, "read_rval()\n");
356 if (!eat_spaces(in))
357 return FALSE;
359 c = fgetc(in);
360 if (c != '\"') {
361 while ((isalnum(c) || c == '_' || c == '-' || c == '.'
362 || c == '/' || c == '@' || c == ';' || c == ':')
363 && (ptr < buf + size - 1)
364 && (c != EOF)) {
365 *ptr = c;
366 ptr++;
367 c = fgetc(in);
368 }
369 *ptr = '\0';
370 ungetc(c, in);
371 } else {
372 gboolean escape = FALSE;
373 c = fgetc(in);
374 while (((c != '\"') || escape) && (ptr < buf + size - 1)) {
375 if (c != '\n') { /* ignore line breaks */
376 if ((c == '\\') && (!escape)) {
377 escape = TRUE;
378 } else {
379 *ptr = c;
380 ptr++;
381 escape = FALSE;
382 }
383 }
384 c = fgetc(in);
385 }
386 *ptr = '\0';
387 }
389 eat_line_trailing(in);
391 DEBUG(6) fprintf(stderr, "rval = %s\n", buf);
393 return TRUE;
394 }
396 static gboolean
397 read_statement(FILE * in, gchar * lval, gint lsize, gchar * rval, gint rsize)
398 {
399 gint c;
401 DEBUG(6) fprintf(stderr, "read_statement()\n");
403 /* eat comments and empty lines: */
404 if (!eat_comments(in))
405 return FALSE;
407 if (!read_lval(in, lval, lsize)) {
408 return FALSE;
409 }
411 DEBUG(6) fprintf(stderr, " lval = %s\n", lval);
412 if ((c = fgetc(in) == '=')) {
413 if (read_rval(in, rval, rsize)) {
414 DEBUG(6) fprintf(stderr, " rval = %s\n", rval);
415 return TRUE;
416 }
417 } else {
418 DEBUG(6) fprintf(stderr," '=' expected after %s, char was '%c'\n", lval, c);
419 fprintf(stderr, "'=' expected after %s, char was '%c'\n", lval, c);
420 }
421 return FALSE;
422 }
424 gboolean
425 read_conf(gchar * filename)
426 {
427 FILE *in;
429 conf.log_max_pri = 7;
430 conf.do_relay = TRUE;
431 conf.alias_local_cmp = strcmp;
432 conf.max_defer_time = 86400 * 4; /* 4 days */
433 conf.max_msg_size = 0; /* no limit on msg size */
434 conf.spool_dir = SPOOL_DIR;
435 conf.mail_dir = "/var/mail";
436 /* we use 127.0.0.1 because `localhost' could be bound to some
437 other IP address. This is unlikely but could be. Using
438 127.0.0.1 is more safe. See mailing list for details */
439 conf.listen_addresses = g_list_append(NULL, parse_interface("127.0.0.1", 25));
441 if ((in = fopen(filename, "r")) == NULL) {
442 logwrite(LOG_ALERT, "could not open config file %s: %s\n", filename, strerror(errno));
443 return FALSE;
444 }
446 gchar lval[256], rval[2048];
447 while (read_statement(in, lval, 256, rval, 2048)) {
448 DEBUG(6) fprintf(stderr,"read_conf(): lval=%s\n", lval);
449 if (strcmp(lval, "debug_level") == 0)
450 conf.debug_level = atoi(rval);
451 else if (strcmp(lval, "run_as_user") == 0) {
452 if (!conf.run_as_user) /* you should not be able to reset that flag */
453 conf.run_as_user = parse_boolean(rval);
454 } else if (strcmp(lval, "use_syslog") == 0)
455 conf.use_syslog = parse_boolean(rval);
456 else if (strcmp(lval, "mail_dir") == 0)
457 conf.mail_dir = g_strdup(rval);
458 else if (strcmp(lval, "lock_dir") == 0)
459 conf.lock_dir = g_strdup(rval);
460 else if (strcmp(lval, "spool_dir") == 0)
461 conf.spool_dir = g_strdup(rval);
462 else if (strcmp(lval, "log_dir") == 0)
463 conf.log_dir = g_strdup(rval);
464 else if (strcmp(lval, "host_name") == 0) {
465 if (rval[0] != '/')
466 conf.host_name = g_strdup(rval);
467 else {
468 char buf[256];
469 FILE *fptr = fopen(rval, "rt");
470 if (fptr) {
471 logwrite(LOG_ALERT, "could not open %s: %s\n", rval, strerror(errno));
472 return FALSE;
473 }
474 fgets(buf, 255, fptr);
475 g_strchomp(buf);
476 conf.host_name = g_strdup(buf);
477 fclose(fptr);
478 }
479 } else if (strcmp(lval, "local_hosts") == 0)
480 conf.local_hosts = parse_list(rval, FALSE);
481 else if (strcmp(lval, "local_addresses") == 0)
482 conf.local_addresses = parse_list(rval, TRUE);
483 else if (strcmp(lval, "not_local_addresses") == 0)
484 conf.not_local_addresses = parse_list(rval, TRUE);
485 else if (strcmp(lval, "local_nets") == 0)
486 conf.local_nets = parse_list(rval, FALSE);
487 else if (strcmp(lval, "do_save_envelope_to") == 0)
488 conf.do_save_envelope_to = parse_boolean(rval);
489 else if (strcmp(lval, "defer_all") == 0)
490 conf.defer_all = parse_boolean(rval);
491 else if (strcmp(lval, "do_relay") == 0)
492 conf.do_relay = parse_boolean(rval);
493 else if (strcmp(lval, "alias_file") == 0) {
494 conf.alias_file = g_strdup(rval);
495 } else if (strcmp(lval, "alias_local_caseless") == 0) {
496 conf.alias_local_cmp = parse_boolean(rval) ? strcasecmp : strcmp;
497 } else if (strcmp(lval, "mbox_default") == 0) {
498 conf.mbox_default = g_strdup(rval);
499 } else if (strcmp(lval, "mbox_users") == 0) {
500 conf.mbox_users = parse_list(rval, TRUE);
501 } else if (strcmp(lval, "mda_users") == 0) {
502 conf.mda_users = parse_list(rval, TRUE);
503 } else if (strcmp(lval, "mda") == 0) {
504 conf.mda = g_strdup(rval);
505 } else if (strcmp(lval, "mda_fromline") == 0) {
506 conf.mda_fromline = parse_boolean(rval);
507 } else if (strcmp(lval, "mda_fromhack") == 0) {
508 conf.mda_fromhack = parse_boolean(rval);
509 } else if (strcmp(lval, "pipe_fromline") == 0) {
510 conf.pipe_fromline = parse_boolean(rval);
511 } else if (strcmp(lval, "pipe_fromhack") == 0) {
512 conf.pipe_fromhack = parse_boolean(rval);
513 } else if (strcmp(lval, "listen_addresses") == 0) {
514 GList *node;
515 GList *tmp_list = parse_list(rval, FALSE);
517 conf.listen_addresses = NULL;
518 foreach(tmp_list, node) {
519 conf.listen_addresses = g_list_append(conf.listen_addresses, parse_interface((gchar *) (node-> data), 25));
520 g_free(node->data);
521 }
522 g_list_free(tmp_list);
523 } else if (strcmp(lval, "ident_trusted_nets") == 0) {
524 #ifdef ENABLE_IDENT
525 GList *node;
526 GList *tmp_list = parse_list(rval, FALSE);
528 conf.ident_trusted_nets = NULL;
529 foreach(tmp_list, node) {
530 conf.ident_trusted_nets = g_list_append(conf.ident_trusted_nets, parse_network((gchar *) (node->data), 25));
531 g_free(node->data);
532 }
533 g_list_free(tmp_list);
534 #else
535 logwrite(LOG_WARNING, "%s ignored: not compiled with ident support\n", lval);
536 #endif
537 } else if ((strncmp(lval, "connect_route.", 14) == 0)
538 || (strncmp(lval, "online_routes.", 14) == 0)) {
539 GList *file_list = parse_list(rval, FALSE);
540 table_pair *pair = create_pair(&(lval[14]), file_list);
541 conf.connect_routes = g_list_append(conf.connect_routes, pair);
542 } else if (strcmp(lval, "local_net_route") == 0) {
543 conf.local_net_routes = parse_list(rval, FALSE);
544 } else if (strcmp(lval, "online_detect") == 0)
545 conf.online_detect = g_strdup(rval);
546 else if (strcmp(lval, "online_file") == 0)
547 conf.online_file = g_strdup(rval);
548 else if (strcmp(lval, "online_pipe") == 0)
549 conf.online_pipe = g_strdup(rval);
550 else if (strcmp(lval, "do_queue") == 0)
551 conf.do_queue = parse_boolean(rval);
552 else if (strcmp(lval, "errmsg_file") == 0)
553 conf.errmsg_file = g_strdup(rval);
554 else if (strcmp(lval, "warnmsg_file") == 0)
555 conf.warnmsg_file = g_strdup(rval);
556 else if (strcmp(lval, "warn_intervals") == 0)
557 conf.warn_intervals = parse_list(rval, FALSE);
558 else if (strcmp(lval, "max_defer_time") == 0) {
559 gint dummy;
560 gint ival = time_interval(rval, &dummy);
561 if (ival < 0)
562 logwrite(LOG_WARNING, "invalid time interval for 'max_defer_time': %s\n", rval);
563 else
564 conf.max_defer_time = ival;
565 } else if (strcmp(lval, "log_user") == 0)
566 conf.log_user = g_strdup(rval);
567 else if(strcmp(lval, "max_msg_size") == 0) {
568 conf.max_msg_size = atol(rval);
569 DEBUG(6) fprintf(stderr,"rval=%s, conf.max_msg_size=%ld\n",
570 rval, conf.max_msg_size);
571 }
572 else
573 logwrite(LOG_WARNING, "var '%s' not (yet) known, ignored\n", lval);
574 }
575 fclose(in);
577 if (!conf.host_name) {
578 logwrite(LOG_ALERT, "`host_name' MUST be set in masqmail.conf. See man page\n");
579 return FALSE;
580 }
582 if (conf.errmsg_file == NULL)
583 conf.errmsg_file = g_strdup(DATA_DIR "/tpl/failmsg.tpl");
584 if (conf.warnmsg_file == NULL)
585 conf.warnmsg_file = g_strdup(DATA_DIR "/tpl/warnmsg.tpl");
587 if (conf.lock_dir == NULL)
588 conf.lock_dir = g_strdup_printf("%s/lock/", conf.spool_dir);
590 if (conf.mbox_default == NULL)
591 conf.mbox_default = g_strdup("mbox");
593 if (conf.warn_intervals == NULL)
594 conf.warn_intervals = parse_list("1h;4h;8h;1d;2d;3d", FALSE);
596 if (!conf.local_hosts) {
597 char* shortname = strdup(conf.host_name);
598 char* p = strchr(shortname, '.');
599 if (p) {
600 *p = '\0';
601 }
602 /* we don't care if shortname and conf.host_name are the same */
603 char* local_hosts_str = g_strdup_printf("localhost;%s;%s", shortname, conf.host_name);
604 conf.local_hosts = parse_list(local_hosts_str, FALSE);
605 free(shortname);
606 free(local_hosts_str);
607 }
610 return TRUE;
611 }
613 connect_route*
614 read_route(gchar * filename, gboolean is_local_net)
615 {
616 gboolean ok = FALSE;
617 FILE *in;
619 connect_route *route = g_malloc(sizeof(connect_route));
620 memset(route, 0, sizeof(connect_route));
622 DEBUG(5) debugf("read_route, filename = %s\n", filename);
624 route->filename = g_strdup(filename);
625 route->name = g_strdup(filename); /* quick hack */
627 route->protocol = g_strdup("smtp");
628 route->expand_h_sender_address = TRUE;
630 route->is_local_net = is_local_net;
632 route->do_pipelining = TRUE;
634 if ((in = fopen(route->filename, "r")) == NULL) {
635 logwrite(LOG_ALERT, "could not open route file %s: %s\n", route->filename, strerror(errno));
636 g_free(route);
637 return NULL;
638 }
640 gchar lval[256], rval[2048];
641 while (read_statement(in, lval, 256, rval, 2048)) {
642 if (strcmp(lval, "protocol") == 0)
643 route->protocol = g_strdup(rval);
644 else if (strcmp(lval, "mail_host") == 0)
645 route->mail_host = parse_interface(rval, 25);
646 else if (strcmp(lval, "helo_name") == 0)
647 route->helo_name = g_strdup(rval);
648 else if (strcmp(lval, "wrapper") == 0)
649 route->wrapper = g_strdup(rval);
650 else if (strcmp(lval, "connect_error_fail") == 0)
651 route->connect_error_fail = parse_boolean(rval);
652 else if (strcmp(lval, "do_correct_helo") == 0)
653 route->do_correct_helo = parse_boolean(rval);
654 else if (strcmp(lval, "instant_helo") == 0)
655 route->instant_helo = parse_boolean(rval);
656 else if (strcmp(lval, "do_pipelining") == 0)
657 route->do_pipelining = parse_boolean(rval);
658 else if (strcmp(lval, "allowed_return_paths") == 0)
659 route->allowed_return_paths = parse_address_list(rval, TRUE);
660 else if (strcmp(lval, "allowed_mail_locals") == 0)
661 route->allowed_mail_locals = parse_list(rval, TRUE);
662 else if (strcmp(lval, "not_allowed_return_paths") == 0)
663 route->not_allowed_return_paths = parse_address_list(rval, TRUE);
664 else if (strcmp(lval, "not_allowed_mail_locals") == 0)
665 route->not_allowed_mail_locals = parse_list(rval, TRUE);
666 else if (strcmp(lval, "allowed_rcpt_domains") == 0)
667 route->allowed_rcpt_domains = parse_list(rval, TRUE);
668 else if (strcmp(lval, "not_allowed_rcpt_domains") == 0)
669 route->not_allowed_rcpt_domains = parse_list(rval, TRUE);
670 else if (strcmp(lval, "set_h_from_domain") == 0)
671 route->set_h_from_domain = g_strdup(rval);
672 else if (strcmp(lval, "set_h_reply_to_domain") == 0)
673 route->set_h_reply_to_domain = g_strdup(rval);
674 else if (strcmp(lval, "set_return_path_domain") == 0)
675 route->set_return_path_domain = g_strdup(rval);
676 else if (strcmp(lval, "map_return_path_addresses") == 0) {
677 GList *node, *list;
679 list = parse_list(rval, TRUE);
680 foreach(list, node) {
681 gchar *item = (gchar *) (node->data);
682 table_pair *pair = parse_table_pair(item, ':');
683 address *addr = create_address((gchar *) (pair->value), TRUE);
684 g_free(pair->value);
685 pair->value = (gpointer *) addr;
686 route->map_return_path_addresses = g_list_append(route->map_return_path_addresses, pair);
687 g_free(item);
688 }
689 g_list_free(list);
690 } else if (strcmp(lval, "map_h_from_addresses") == 0) {
691 GList *list, *node;
693 list = parse_list(rval, TRUE);
694 foreach(list, node) {
695 gchar *item = (gchar *) (node->data);
696 table_pair *pair = parse_table_pair(item, ':');
697 route->map_h_from_addresses = g_list_append(route->map_h_from_addresses, pair);
698 g_free(item);
699 }
700 g_list_free(list);
701 } else if (strcmp(lval, "map_h_reply_to_addresses") == 0) {
702 GList *list, *node;
704 list = parse_list(rval, TRUE);
705 foreach(list, node) {
706 gchar *item = (gchar *) (node->data);
707 table_pair *pair = parse_table_pair(item, ':');
708 route->map_h_reply_to_addresses = g_list_append(route->map_h_reply_to_addresses, pair);
709 g_free(item);
710 }
711 g_list_free(list);
712 } else if (strcmp(lval, "map_h_mail_followup_to_addresses") == 0) {
713 GList *list, *node;
715 list = parse_list(rval, TRUE);
716 foreach(list, node) {
717 gchar *item = (gchar *) (node->data);
718 table_pair *pair = parse_table_pair(item, ':');
719 route->map_h_mail_followup_to_addresses = g_list_append(route->map_h_mail_followup_to_addresses, pair);
720 g_free(item);
721 }
722 g_list_free(list);
723 } else if (strcmp(lval, "expand_h_sender_domain") == 0) {
724 route->expand_h_sender_domain = parse_boolean(rval);
725 } else if (strcmp(lval, "expand_h_sender_address") == 0) {
726 route->expand_h_sender_address = parse_boolean(rval);
727 } else if (strcmp(lval, "resolve_list") == 0)
728 route->resolve_list = parse_resolve_list(rval);
729 else if (strcmp(lval, "do_ssl") == 0) {
730 /* we ignore this. This option is used by sqilconf */
731 ;
732 }
733 #ifdef ENABLE_AUTH
734 else if (strcmp(lval, "auth_name") == 0) {
735 route->auth_name = g_strdup(rval);
736 } else if (strcmp(lval, "auth_login") == 0) {
737 route->auth_login = g_strdup(rval);
738 } else if (strcmp(lval, "auth_secret") == 0) {
739 route->auth_secret = g_strdup(rval);
740 }
741 #else
742 else if ((strcmp(lval, "auth_name") == 0)
743 || (strcmp(lval, "auth_login") == 0)
744 || (strcmp(lval, "auth_secret") == 0)) {
745 logwrite(LOG_WARNING, "%s ignored: not compiled with auth support.\n", lval);
746 }
747 #endif
748 else if (strcmp(lval, "pipe") == 0) {
749 route->pipe = g_strdup(rval);
750 } else if (strcmp(lval, "pipe_fromline") == 0) {
751 route->pipe_fromline = parse_boolean(rval);
752 } else if (strcmp(lval, "pipe_fromhack") == 0) {
753 route->pipe_fromhack = parse_boolean(rval);
754 } else if (strcmp(lval, "last_route") == 0) {
755 route->last_route = parse_boolean(rval);
756 } else
757 logwrite(LOG_WARNING, "var '%s' not (yet) known, ignored\n", lval);
758 }
760 if (route->resolve_list == NULL) {
761 if (is_local_net) {
762 route->resolve_list = g_list_append(NULL, resolve_byname);
763 } else {
764 #ifdef ENABLE_RESOLVER
765 route->resolve_list = g_list_append(route->resolve_list, resolve_dns_mx);
766 route->resolve_list = g_list_append(route->resolve_list, resolve_dns_a);
767 #endif
768 route->resolve_list = g_list_append(route->resolve_list, resolve_byname);
769 }
770 }
771 fclose(in);
772 ok = TRUE;
774 /* warn user about misconfigurations: */
775 if ((route->map_h_from_addresses != NULL) && (route->set_h_from_domain != NULL)) {
776 logwrite(LOG_WARNING, "'map_h_from_addresses' overrides 'set_h_from_domain'\n");
777 g_free(route->set_h_from_domain);
778 route->set_h_from_domain = NULL;
779 }
780 if ((route->map_h_reply_to_addresses != NULL) && (route->set_h_reply_to_domain != NULL)) {
781 logwrite(LOG_WARNING, "'map_h_reply_to_addresses' overrides 'set_h_reply_to_domain'\n");
782 g_free(route->set_h_reply_to_domain);
783 route->set_h_reply_to_domain = NULL;
784 }
786 if (!ok) {
787 g_free(route);
788 route = NULL;
789 }
791 return route;
792 }
794 static void
795 _g_list_free_all(GList * list)
796 {
797 GList *node;
798 if (list) {
799 foreach(list, node)
800 g_free(node->data);
801 g_list_free(list);
802 }
803 }
805 void
806 destroy_route(connect_route * r)
807 {
808 if (r->filename)
809 g_free(r->filename);
810 if (r->protocol)
811 g_free(r->protocol);
812 if (r->mail_host) {
813 g_free(r->mail_host->address);
814 g_free(r->mail_host);
815 }
816 if (r->wrapper)
817 g_free(r->wrapper);
818 if (r->helo_name)
819 g_free(r->helo_name);
820 _g_list_free_all(r->allowed_mail_locals);
821 _g_list_free_all(r->not_allowed_mail_locals);
822 _g_list_free_all(r->allowed_rcpt_domains);
823 _g_list_free_all(r->not_allowed_rcpt_domains);
824 if (r->set_h_from_domain)
825 g_free(r->set_h_from_domain);
826 if (r->set_h_reply_to_domain)
827 g_free(r->set_h_reply_to_domain);
828 if (r->set_return_path_domain)
829 g_free(r->set_return_path_domain);
830 if (r->map_h_reply_to_addresses)
831 destroy_table(r->map_h_reply_to_addresses);
832 if (r->resolve_list)
833 g_list_free(r->resolve_list);
834 #ifdef ENABLE_AUTH
835 if (r->auth_name)
836 g_free(r->auth_name);
837 if (r->auth_login)
838 g_free(r->auth_login);
839 if (r->auth_secret)
840 g_free(r->auth_secret);
841 #endif
842 if (r->pipe)
843 g_free(r->pipe);
844 g_free(r);
845 }
847 GList*
848 read_route_list(GList * rf_list, gboolean is_local_net)
849 {
850 GList *list = NULL;
851 GList *node;
852 uid_t saved_uid, saved_gid;
854 if (!conf.run_as_user) {
855 set_euidgid(0, 0, &saved_uid, &saved_gid);
856 }
858 foreach(rf_list, node) {
859 gchar *fname = (gchar *) (node->data);
860 connect_route *route = read_route(fname, is_local_net);
861 if (route)
862 list = g_list_append(list, route);
863 else
864 logwrite(LOG_ALERT, "could not read route configuration %s\n", fname);
865 }
867 /* set uid and gid back */
868 if (!conf.run_as_user) {
869 set_euidgid(saved_uid, saved_gid, NULL, NULL);
870 }
872 return list;
873 }
875 void
876 destroy_route_list(GList * list)
877 {
878 GList *node;
880 foreach(list, node) {
881 connect_route *route = (connect_route *) (node->data);
882 destroy_route(route);
883 }
884 g_list_free(list);
885 }
887 connect_route*
888 create_local_route()
889 {
890 connect_route *route;
892 route = g_malloc(sizeof(connect_route));
893 if (!route) {
894 return NULL;
895 }
896 memset(route, 0, sizeof(connect_route));
897 route->protocol = g_strdup("smtp");
898 route->is_local_net = TRUE;
899 route->name = g_strdup("local_net (default)");
900 route->expand_h_sender_address = TRUE;
901 route->resolve_list = g_list_append(NULL, resolve_byname);
902 route->connect_error_fail = TRUE;
903 return route;
904 }