masqmail

view src/alias.c @ 421:f37384470855

Changed lockdir to /var/lock/masqmail; Create lockdir and piddir on startup. Moved the lockdir out of the spool dir. (When /var/lock is a ramdisk we do well to have the lock files there.) Added the new configure option --with-lockdir to change that location. Nontheless, if we run_as_user, then lock files are always stored in the spool dir directly. Instead of installing the lockdir and piddir at installation time, we create them on startup time now if they are missing. This is necessary if lockdir or piddir are a tmpfs.
author markus schnalke <meillo@marmaro.de>
date Wed, 30 May 2012 09:38:38 +0200
parents c7cc3c03193c
children
line source
1 /*
2 ** MasqMail
3 ** Copyright (C) 2000-2001 Oliver Kurth
4 ** Copyright (C) 2010 markus schnalke <meillo@marmaro.de>
5 **
6 ** This program is free software; you can redistribute it and/or modify
7 ** it under the terms of the GNU General Public License as published by
8 ** the Free Software Foundation; either version 2 of the License, or
9 ** (at your option) any later version.
10 **
11 ** This program is distributed in the hope that it will be useful,
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ** GNU General Public License for more details.
15 **
16 ** You should have received a copy of the GNU General Public License
17 ** along with this program; if not, write to the Free Software
18 ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
21 #include "masqmail.h"
22 #include <fnmatch.h>
24 gboolean
25 addr_is_local(address *addr)
26 {
27 GList *dom_node;
28 GList *addr_node;
29 address *a;
31 if (!addr->domain) {
32 return TRUE;
33 }
34 foreach(conf.local_hosts, dom_node) {
35 /* Note: FNM_CASEFOLD is a GNU extension */
36 if (fnmatch(dom_node->data, addr->domain, FNM_CASEFOLD)!=0) {
37 /* no match, try next */
38 continue;
39 }
40 foreach(conf.not_local_addresses, addr_node) {
41 a = create_address_qualified(addr_node->data, TRUE,
42 conf.host_name);
43 DEBUG(6) debugf("not_local_addresses: "
44 "addr_node->data=%s a->address=%s\n",
45 addr_node->data, a->address);
46 if (addr_isequal(a, addr, conf.localpartcmp)) {
47 /* also in not_local_addresses */
48 destroy_address(a);
49 return FALSE;
50 }
51 destroy_address(a);
52 }
53 /* in local_hosts */
54 return TRUE;
55 }
56 foreach(conf.local_addresses, addr_node) {
57 a = create_address_qualified(addr_node->data, TRUE,
58 conf.host_name);
59 DEBUG(6) debugf("local_addresses: addr_node->data=%s "
60 "a->address=%s\n",
61 addr_node->data, a->address);
62 if (addr_isequal(a, addr, conf.localpartcmp)) {
63 /* in local_addresses */
64 destroy_address(a);
65 return TRUE;
66 }
67 destroy_address(a);
68 }
69 return FALSE;
70 }
72 static GList*
73 parse_list(gchar *line)
74 {
75 GList *list = NULL;
76 gchar buf[256];
77 gchar *p, *q;
79 p = line;
80 while (*p) {
81 q = buf;
82 while (isspace(*p)) {
83 p++;
84 }
85 if (*p != '"') {
86 while (*p && (*p != ',') && (q < buf + 255)) {
87 *(q++) = *(p++);
88 }
89 *q = '\0';
90 } else {
91 gboolean escape = FALSE;
92 p++;
93 while (*p && (*p != '"' || escape) && (q < buf+255)) {
94 if ((*p == '\\') && !escape) {
95 escape = TRUE;
96 } else {
97 escape = FALSE;
98 *(q++) = *p;
99 }
100 p++;
101 }
102 *q = '\0';
103 while (*p && (*p != ',')) {
104 p++;
105 }
106 }
107 list = g_list_append(list, g_strdup(g_strchomp(buf)));
108 if (*p) {
109 p++;
110 }
111 }
112 return list;
113 }
115 static int
116 globaliascmp(const char *pattern, const char *addr)
117 {
118 if (conf.localpartcmp == strcasecmp) {
119 return fnmatch(pattern, addr, FNM_CASEFOLD);
120 } else if (strncasecmp(addr, "postmaster", 10)==0) {
121 /* postmaster must always be matched caseless
122 ** see RFC 822 and RFC 5321 */
123 return fnmatch(pattern, addr, FNM_CASEFOLD);
124 } else {
125 /* case-sensitive */
126 return fnmatch(pattern, addr, 0);
127 }
128 }
130 /*
131 ** addr is assumed to be local and no pipe address nor not-to-expand
132 */
133 static GList*
134 expand_one(GList *alias_table, address *addr, int doglob)
135 {
136 GList *val_list;
137 GList *val_node;
138 GList *alias_list = NULL;
139 GList *alias_node;
140 gchar *val;
141 char addrstr[BUFSIZ];
143 if (doglob) {
144 snprintf(addrstr, sizeof addrstr, "%s@%s",
145 addr->local_part, addr->domain);
146 } else {
147 snprintf(addrstr, sizeof addrstr, "%s", addr->local_part);
148 }
150 /* expand the local alias */
151 DEBUG(6) debugf("alias: '%s' is local and will get expanded\n",
152 addrstr);
154 if (doglob) {
155 val = (gchar *) table_find_func(alias_table, addrstr,
156 globaliascmp);
158 } else if (strcasecmp(addr->local_part, "postmaster") == 0) {
159 /* postmaster must always be matched caseless
160 ** see RFC 822 and RFC 5321 */
161 val = (gchar *) table_find_func(alias_table, addrstr,
162 strcasecmp);
163 } else {
164 val = (gchar *) table_find_func(alias_table, addrstr,
165 conf.localpartcmp);
166 }
167 if (!val) {
168 DEBUG(5) debugf("alias: '%s' is fully expanded, hence "
169 "completed\n", addrstr);
170 return g_list_append(NULL, addr);
171 }
173 DEBUG(5) debugf("alias: '%s' -> '%s'\n", addrstr, val);
174 val_list = parse_list(val);
175 alias_list = NULL;
177 foreach(val_list, val_node) {
178 gchar *val = (gchar *) (val_node->data);
179 address *alias_addr;
181 DEBUG(6) debugf("alias: processing '%s'\n", val);
183 if (*val == '\\') {
184 DEBUG(5) debugf("alias: '%s' is marked as final, "
185 "hence completed\n", val);
186 alias_addr = create_address_qualified(val+1, TRUE,
187 conf.host_name);
188 g_free(val);
189 DEBUG(6) debugf("alias: address generated: '%s'\n",
190 alias_addr->address);
191 alias_list = g_list_append(alias_list, alias_addr);
192 continue;
193 }
195 if (*val == '|') {
196 DEBUG(5) debugf("alias: '%s' is a pipe address\n",
197 val);
198 alias_addr = create_address_pipe(val);
199 g_free(val);
200 DEBUG(6) debugf("alias: pipe generated: %s\n",
201 alias_addr->local_part);
202 alias_list = g_list_append(alias_list, alias_addr);
203 continue;
204 }
206 alias_addr = create_address_qualified(val, TRUE,
207 conf.host_name);
208 g_free(val);
210 if (!addr_is_local(alias_addr)) {
211 DEBUG(5) debugf("alias: '%s' is non-local, "
212 "hence completed\n",
213 alias_addr->address);
214 alias_list = g_list_append(alias_list, alias_addr);
215 continue;
216 }
218 /* addr is local and to expand at this point */
219 /* but first ... search in parents for loops: */
220 if (addr_isequal_parent(addr, alias_addr, conf.localpartcmp)) {
221 /* loop detected, ignore this path */
222 logwrite(LOG_ALERT, "alias: detected loop, "
223 "hence ignoring '%s'\n",
224 alias_addr->address);
225 continue;
226 }
227 alias_addr->parent = addr;
229 /* recurse */
230 DEBUG(6) debugf("alias: >>\n");
231 alias_node = expand_one(alias_table, alias_addr, doglob);
232 DEBUG(6) debugf("alias: <<\n");
233 if (alias_node) {
234 alias_list = g_list_concat(alias_list, alias_node);
235 }
236 }
237 g_list_free(val_list);
238 addr->children = g_list_copy(alias_list);
240 return alias_list;
241 }
243 GList*
244 alias_expand(GList *alias_table, GList *rcpt_list, GList *non_rcpt_list,
245 int doglob)
246 {
247 GList *rcpt_node = NULL;
248 GList *alias_list = NULL;
249 GList *done_list = NULL;
250 GList *rcpt_node_next = NULL;
251 address *addr = NULL;
253 for (rcpt_node=g_list_copy(rcpt_list); rcpt_node;
254 rcpt_node=g_list_next(rcpt_node)) {
256 addr = (address *) (rcpt_node->data);
257 if (addr_is_local(addr)) {
258 DEBUG(5) debugf("alias: expand local '%s' "
259 "(orig rcpt addr)\n", addr->address);
260 alias_list = expand_one(alias_table, addr, doglob);
261 if (alias_list) {
262 done_list = g_list_concat(done_list,
263 alias_list);
264 }
265 } else {
266 DEBUG(5) debugf("alias: don't expand non-local '%s' "
267 "(orig rcpt addr)\n", addr->address);
268 done_list = g_list_append(done_list, addr);
269 }
270 }
272 /* we're done if we don't have to remove rcpts */
273 if (!non_rcpt_list) {
274 return done_list;
275 }
277 /* delete addresses of non_rcpt_list from done_list */
278 for (rcpt_node = g_list_first(done_list); rcpt_node;
279 rcpt_node = rcpt_node_next) {
280 address *addr = (address *) (rcpt_node->data);
281 GList *non_node;
283 rcpt_node_next = g_list_next(rcpt_node);
284 foreach(non_rcpt_list, non_node) {
285 address *non_addr = (address *) (non_node->data);
286 if (addr_isequal(addr, non_addr, conf.localpartcmp)) {
287 done_list = g_list_remove_link(done_list,
288 rcpt_node);
289 g_list_free_1(rcpt_node);
290 /*
291 ** this address is still in the children
292 ** lists of the original address, simply
293 ** mark them delivered
294 */
295 addr_mark_delivered(addr);
296 break;
297 }
298 }
299 }
300 return done_list;
301 }