masqmail
view src/alias.c @ 304:d5ce2ba71e7b
manual formating of Received: hdrs; changed hdr for local receival
Now the Received: headers are much friendlier to read.
About folding: We must fold any line at 998 chars before transfer.
We should fold the lines we produce at 78 chars. That is what RFC
2821 requests. We should think about it, somewhen.
The header for locally (i.e. non-SMTP) received mail is changed
to the format postfix uses. This matches RFC 2821 better. The
`from' clause should contain a domain or IP, not a user name. Also,
the `with' clause should contain a registered standard protocol
name, which ``local'' is not.
author | markus schnalke <meillo@marmaro.de> |
---|---|
date | Thu, 09 Dec 2010 18:28:11 -0300 |
parents | bc9d9cd9ee8e |
children | 273f6c9eb6a2 |
line source
1 /* MasqMail
2 Copyright (C) 2000-2001 Oliver Kurth
3 Copyright (C) 2010 markus schnalke <meillo@marmaro.de>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
20 #include "masqmail.h"
21 #include <fnmatch.h>
23 gboolean
24 addr_is_local(address * addr)
25 {
26 GList *dom_node;
27 GList *addr_node;
28 address *a;
30 if (addr->domain == NULL) {
31 return TRUE;
32 }
33 foreach(conf.local_hosts, dom_node) {
34 /* Note: FNM_CASEFOLD is a GNU extension */
35 if (fnmatch(dom_node->data, addr->domain, FNM_CASEFOLD) != 0) {
36 /* no match, try next */
37 continue;
38 }
39 foreach(conf.not_local_addresses, addr_node) {
40 a = create_address_qualified(addr_node->data, TRUE, conf.host_name);
41 DEBUG(6) debugf("not_local_addresses: addr_node->data=%s a->address=%s\n",
42 addr_node->data, a->address);
43 if (addr_isequal(a, addr, conf.localpartcmp)) {
44 destroy_address(a);
45 /* in local_hosts but also in not_local_addresses */
46 return FALSE;
47 }
48 destroy_address(a);
49 }
50 /* in local_hosts */
51 return TRUE;
52 }
53 foreach(conf.local_addresses, addr_node) {
54 a = create_address_qualified(addr_node->data, TRUE, conf.host_name);
55 DEBUG(6) debugf("local_addresses: addr_node->data=%s a->address=%s\n",
56 addr_node->data, a->address);
57 if (addr_isequal(a, addr, conf.localpartcmp)) {
58 destroy_address(a);
59 /* in local_addresses */
60 return TRUE;
61 }
62 destroy_address(a);
63 }
64 return FALSE;
65 }
67 static GList*
68 parse_list(gchar * line)
69 {
70 GList *list = NULL;
71 gchar buf[256];
72 gchar *p, *q;
74 p = line;
75 while (*p != '\0') {
76 q = buf;
77 while (isspace(*p))
78 p++;
79 if (*p != '"') {
80 while (*p && (*p != ',') && (q < buf + 255))
81 *(q++) = *(p++);
82 *q = '\0';
83 } else {
84 gboolean escape = FALSE;
85 p++;
86 while (*p && (*p != '"' || escape) && (q < buf + 255)) {
87 if ((*p == '\\') && !escape)
88 escape = TRUE;
89 else {
90 escape = FALSE;
91 *(q++) = *p;
92 }
93 p++;
94 }
95 *q = '\0';
96 while (*p && (*p != ','))
97 p++;
98 }
99 list = g_list_append(list, g_strdup(g_strchomp(buf)));
100 if (*p)
101 p++;
102 }
103 return list;
104 }
106 /*
107 addr is assumed to be local and no pipe address nor not-to-expand
108 */
109 static GList*
110 expand_one(GList* alias_table, address* addr)
111 {
112 GList *val_list;
113 GList *val_node;
114 GList *alias_list = NULL;
115 GList *alias_node;
116 gchar *val;
117 address* alias_addr;
119 /* expand the local alias */
120 DEBUG(6) debugf("alias: '%s' is local and will get expanded\n", addr->local_part);
122 if (strcasecmp(addr->local_part, "postmaster") == 0) {
123 /* postmaster must always be matched caseless
124 see RFC 822 and RFC 5321 */
125 val = (gchar *) table_find_func(alias_table, addr->local_part, strcasecmp);
126 } else {
127 val = (gchar *) table_find_func(alias_table, addr->local_part, conf.localpartcmp);
128 }
129 if (!val) {
130 DEBUG(5) debugf("alias: '%s' is fully expanded, hence completed\n",
131 addr->local_part);
132 return g_list_append(NULL, addr);
133 }
135 DEBUG(5) debugf("alias: '%s' -> '%s'\n", addr->local_part, val);
136 val_list = parse_list(val);
137 alias_list = NULL;
139 foreach(val_list, val_node) {
140 gchar *val = (gchar *) (val_node->data);
141 address *alias_addr;
142 address *addr_parent = NULL;
144 DEBUG(6) debugf("alias: processing '%s'\n", val);
146 if (val[0] == '\\') {
147 DEBUG(5) debugf("alias: '%s' is marked as final, hence completed\n", val);
148 alias_addr = create_address_qualified(val+1, TRUE, conf.host_name);
149 g_free(val);
150 DEBUG(6) debugf("alias: address generated: '%s'\n",
151 alias_addr->local_part);
152 alias_list = g_list_append(alias_list, alias_addr);
153 continue;
154 }
156 if (val[0] == '|') {
157 DEBUG(5) debugf("alias: '%s' is a pipe address\n", val);
158 alias_addr = create_address_pipe(val+1);
159 g_free(val);
160 DEBUG(6) debugf("alias: pipe generated: %s\n",
161 alias_addr->local_part);
162 alias_list = g_list_append(alias_list, alias_addr);
163 continue;
164 }
166 alias_addr = create_address_qualified(val, TRUE, conf.host_name);
167 g_free(val);
169 if (!addr_is_local(alias_addr)) {
170 DEBUG(5) debugf("alias: '%s@%s' is non-local, hence completed\n",
171 alias_addr->local_part, alias_addr->domain);
172 alias_list = g_list_append(alias_list, alias_addr);
173 continue;
174 }
176 /* addr is local and to expand at this point */
177 /* but first ... search in parents for loops: */
178 if (addr_isequal_parent(addr, alias_addr, conf.localpartcmp)) {
179 /* loop detected, ignore this path */
180 logwrite(LOG_ALERT, "alias: detected loop, hence ignoring '%s'\n",
181 alias_addr->local_part);
182 continue;
183 }
184 alias_addr->parent = addr;
186 /* recurse */
187 DEBUG(6) debugf("alias: >>\n");
188 alias_node = expand_one(alias_table, alias_addr);
189 DEBUG(6) debugf("alias: <<\n");
190 if (alias_node) {
191 alias_list = g_list_concat(alias_list, alias_node);
192 }
193 }
194 g_list_free(val_list);
195 addr->children = g_list_copy(alias_list);
197 return alias_list;
198 }
200 GList*
201 alias_expand(GList* alias_table, GList* rcpt_list, GList* non_rcpt_list)
202 {
203 GList *rcpt_node = NULL;
204 GList *alias_list = NULL;
205 GList *done_list = NULL;
206 GList *rcpt_node_next = NULL;
207 address *addr = NULL;
209 for (rcpt_node=g_list_copy(rcpt_list);
210 rcpt_node;
211 rcpt_node=g_list_next(rcpt_node)) {
213 addr = (address *) (rcpt_node->data);
214 if (addr_is_local(addr)) {
215 DEBUG(5) debugf("alias: (orig rcpt addr) expand local '%s'\n",
216 addr->local_part);
217 alias_list = expand_one(alias_table, addr);
218 if (alias_list) {
219 done_list = g_list_concat(done_list, alias_list);
220 }
221 } else {
222 DEBUG(5) debugf("alias: (orig rcpt addr) don't expand non-local '%s@%s'\n",
223 addr->local_part, addr->domain);
224 done_list = g_list_append(done_list, addr);
225 }
226 }
228 /* we're done if we don't have to remove rcpts */
229 if (!non_rcpt_list) {
230 return done_list;
231 }
233 /* delete addresses of non_rcpt_list from done_list */
234 for (rcpt_node = g_list_first(done_list); rcpt_node; rcpt_node = rcpt_node_next) {
235 address *addr = (address *) (rcpt_node->data);
236 GList *non_node;
238 rcpt_node_next = g_list_next(rcpt_node);
239 foreach(non_rcpt_list, non_node) {
240 address *non_addr = (address *) (non_node->data);
241 if (addr_isequal(addr, non_addr, conf.localpartcmp)) {
242 done_list = g_list_remove_link(done_list, rcpt_node);
243 g_list_free_1(rcpt_node);
244 /* this address is still in the children lists
245 of the original address, simply mark them delivered */
246 addr_mark_delivered(addr);
247 break;
248 }
249 }
250 }
251 return done_list;
252 }