masqmail

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