masqmail

annotate src/accept.c @ 91:3e7136221104

correct masqmail path in rmail script; remove docs on uninstall on install the correct path to the masqmail executable gets inserted into the rmail script now. now documentation, examples, and the templates are removed on uninstall. Empty directories are the only thing that may remain if one installs masqmail into an unusual path.
author meillo@marmaro.de
date Mon, 21 Jun 2010 09:40:16 +0200
parents 7c1635972aa7
children f4719cffc48c
rev   line source
meillo@0 1 /* MasqMail
meillo@0 2 Copyright (C) 1999-2001 Oliver Kurth
meillo@0 3
meillo@0 4 This program is free software; you can redistribute it and/or modify
meillo@0 5 it under the terms of the GNU General Public License as published by
meillo@0 6 the Free Software Foundation; either version 2 of the License, or
meillo@0 7 (at your option) any later version.
meillo@0 8
meillo@0 9 This program is distributed in the hope that it will be useful,
meillo@0 10 but WITHOUT ANY WARRANTY; without even the implied warranty of
meillo@0 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
meillo@0 12 GNU General Public License for more details.
meillo@0 13
meillo@0 14 You should have received a copy of the GNU General Public License
meillo@0 15 along with this program; if not, write to the Free Software
meillo@0 16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
meillo@0 17 */
meillo@0 18
meillo@0 19 #include "masqmail.h"
meillo@0 20 #include "readsock.h"
meillo@0 21
meillo@10 22 gchar *prot_names[] = {
meillo@10 23 "local",
meillo@10 24 "bsmtp",
meillo@10 25 "smtp",
meillo@10 26 "esmtp",
meillo@10 27 "pop3",
meillo@10 28 "apop",
meillo@10 29 "(unknown)" /* should not happen, but better than crashing. */
meillo@0 30 };
meillo@0 31
meillo@10 32 static gchar*
meillo@10 33 string_base62(gchar * res, guint value, gchar len)
meillo@0 34 {
meillo@10 35 static gchar base62_chars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
meillo@10 36 gchar *p = res + len;
meillo@14 37 *p = '\0';
meillo@10 38 while (p > res) {
meillo@10 39 *(--p) = base62_chars[value % 62];
meillo@10 40 value /= 62;
meillo@10 41 }
meillo@10 42 return res;
meillo@0 43 }
meillo@0 44
meillo@10 45 static gint
meillo@10 46 _g_list_addr_isequal(gconstpointer a, gconstpointer b)
meillo@0 47 {
meillo@10 48 address *addr1 = (address *) a;
meillo@10 49 address *addr2 = (address *) b;
meillo@10 50 int ret;
meillo@0 51
meillo@10 52 if ((ret = strcasecmp(addr1->domain, addr2->domain)) == 0)
meillo@10 53 return strcmp(addr1->local_part, addr2->local_part);
meillo@10 54 else
meillo@10 55 return ret;
meillo@0 56 }
meillo@0 57
meillo@0 58 /* accept message from anywhere.
meillo@0 59 A locally originating message is indicated by msg->recieved_host == NULL
meillo@0 60
meillo@0 61 If the flags ACC_DEL_RCPTS is set, recipients in the msg->rcpt_list is
meillo@0 62 copied and items occuring in it will be removed from the newly constructed
meillo@0 63 (from To/Cc/Bcc headers if ACC_RCPT_TO is set) rcpt_list.
meillo@0 64 */
meillo@0 65
meillo@10 66 accept_error
meillo@10 67 accept_message_stream(FILE * in, message * msg, guint flags)
meillo@0 68 {
meillo@10 69 gchar *line, *line1;
meillo@10 70 int line_size = MAX_DATALINE;
meillo@10 71 gboolean in_headers = TRUE;
meillo@10 72 header *hdr = NULL;
meillo@10 73 gint line_cnt = 0, data_size = 0;
meillo@0 74
meillo@10 75 line = g_malloc(line_size);
meillo@14 76 line[0] = '\0';
meillo@0 77
meillo@10 78 while (TRUE) {
meillo@10 79 int len = read_sockline1(in, &line, &line_size, 5 * 60, READSOCKL_CVT_CRLF);
meillo@0 80
meillo@10 81 line1 = line;
meillo@0 82
meillo@10 83 if ((line[0] == '.') && (!(flags & ACC_NODOT_TERM))) {
meillo@10 84 if (line[1] == '\n') {
meillo@10 85 g_free(line);
meillo@10 86 break;
meillo@10 87 }
meillo@10 88 line1++;
meillo@10 89 }
meillo@10 90
meillo@10 91 if (len <= 0) {
meillo@10 92 if ((len == -1) && ((flags & ACC_NODOT_TERM) || (flags & ACC_NODOT_RELAX))) {
meillo@10 93 /* we got an EOF, and the last line was not terminated by a CR */
meillo@10 94 gint len1 = strlen(line1);
meillo@10 95 if (len1 > 0) { /* == 0 is 'normal' (EOF after a CR) */
meillo@10 96 if (line1[len1 - 1] != '\n') { /* some mail clients allow unterminated lines */
meillo@10 97 line1[len1] = '\n';
meillo@14 98 line1[len1 + 1] = '\0';
meillo@10 99 msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1));
meillo@10 100 data_size += strlen(line1);
meillo@10 101 line_cnt++;
meillo@10 102 }
meillo@10 103 }
meillo@10 104 break;
meillo@10 105 } else {
meillo@10 106 g_free(line);
meillo@10 107 if (len == -1) {
meillo@10 108 return AERR_EOF;
meillo@10 109 } else if (len == -2) {
meillo@10 110 /* should not happen any more */
meillo@10 111 return AERR_OVERFLOW;
meillo@10 112 } else if (len == -3) {
meillo@10 113 return AERR_TIMEOUT;
meillo@10 114 } else {
meillo@10 115 /* does not happen */
meillo@10 116 DEBUG(5) debugf("read_sockline returned %d\n", len);
meillo@10 117 return AERR_UNKNOWN;
meillo@10 118 }
meillo@10 119 }
meillo@10 120 } else {
meillo@10 121 if (in_headers) {
meillo@10 122
meillo@10 123 /* some pop servers send the 'From ' line, skip it: */
meillo@10 124 if (msg->hdr_list == NULL)
meillo@10 125 if (strncmp(line1, "From ", 5) == 0)
meillo@10 126 continue;
meillo@10 127
meillo@10 128 if (line1[0] == ' ' || line1[0] == '\t') {
meillo@10 129 /* continuation of 'folded' header: */
meillo@10 130 if (hdr) {
meillo@22 131 hdr->header = g_strconcat(hdr->header, line1, NULL);
meillo@10 132 }
meillo@10 133
meillo@10 134 } else if (line1[0] == '\n') {
meillo@10 135 /* an empty line marks end of headers */
meillo@10 136 in_headers = FALSE;
meillo@10 137 } else {
meillo@10 138 /* in all other cases we expect another header */
meillo@10 139 if ((hdr = get_header(line1)))
meillo@10 140 msg->hdr_list = g_list_append(msg->hdr_list, hdr);
meillo@10 141 else {
meillo@10 142 /* if get_header() returns NULL, no header was recognized,
meillo@10 143 so this seems to be the first data line of a broken mailer
meillo@10 144 which does not send an empty line after the headers */
meillo@10 145 in_headers = FALSE;
meillo@10 146 msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1));
meillo@10 147 }
meillo@10 148 }
meillo@10 149 } else {
meillo@10 150 msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1));
meillo@10 151 data_size += strlen(line1);
meillo@10 152 line_cnt++;
meillo@10 153 }
meillo@10 154 }
meillo@0 155 }
meillo@0 156
meillo@10 157 if (msg->data_list != NULL)
meillo@10 158 msg->data_list = g_list_reverse(msg->data_list);
meillo@10 159 else
meillo@10 160 /* make sure data list is not NULL: */
meillo@10 161 msg->data_list = g_list_append(NULL, g_strdup(""));
meillo@0 162
meillo@10 163 DEBUG(4) debugf("received %d lines of data (%d bytes)\n", line_cnt, data_size);
meillo@10 164 /* we get here after we succesfully received the mail data */
meillo@0 165
meillo@10 166 msg->data_size = data_size;
meillo@10 167 msg->received_time = time(NULL);
meillo@0 168
meillo@10 169 return AERR_OK;
meillo@0 170 }
meillo@0 171
meillo@10 172 accept_error
meillo@10 173 accept_message_prepare(message * msg, guint flags)
meillo@0 174 {
meillo@10 175 struct passwd *passwd = NULL;
meillo@10 176 GList *non_rcpt_list = NULL;
meillo@10 177 time_t rec_time = time(NULL);
meillo@0 178
meillo@10 179 DEBUG(5) debugf("accept_message_prepare()\n");
meillo@0 180
meillo@10 181 /* create unique message id */
meillo@10 182 msg->uid = g_malloc(14);
meillo@0 183
meillo@10 184 string_base62(msg->uid, rec_time, 6);
meillo@10 185 msg->uid[6] = '-';
meillo@10 186 string_base62(&(msg->uid[7]), getpid(), 3);
meillo@10 187 msg->uid[10] = '-';
meillo@10 188 string_base62(&(msg->uid[11]), msg->transfer_id, 2);
meillo@10 189 msg->uid[13] = 0;
meillo@0 190
meillo@10 191 /* if local, get password entry */
meillo@10 192 if (msg->received_host == NULL) {
meillo@10 193 passwd = g_memdup(getpwuid(geteuid()), sizeof(struct passwd));
meillo@10 194 msg->ident = g_strdup(passwd->pw_name);
meillo@10 195 }
meillo@0 196
meillo@10 197 /* set return path if local */
meillo@22 198 if (msg->return_path == NULL && msg->received_host == NULL) {
meillo@22 199 gchar *path = g_strdup_printf("<%s@%s>", passwd->pw_name, conf.host_name);
meillo@22 200 DEBUG(3) debugf("setting return_path for local accept: %s\n", path);
meillo@22 201 msg->return_path = create_address(path, TRUE);
meillo@22 202 g_free(path);
meillo@10 203 }
meillo@0 204
meillo@10 205 /* -t option */
meillo@10 206 if (flags & ACC_DEL_RCPTS) {
meillo@10 207 non_rcpt_list = msg->rcpt_list;
meillo@10 208 msg->rcpt_list = NULL;
meillo@10 209 }
meillo@0 210
meillo@10 211 /* scan headers */
meillo@10 212 {
meillo@10 213 gboolean has_id = FALSE;
meillo@10 214 gboolean has_date = FALSE;
meillo@10 215 gboolean has_sender = FALSE;
meillo@10 216 gboolean has_from = FALSE;
meillo@10 217 gboolean has_rcpt = FALSE;
meillo@10 218 gboolean has_to_or_cc = FALSE;
meillo@10 219 GList *hdr_node, *hdr_node_next;
meillo@10 220 header *hdr;
meillo@0 221
meillo@10 222 for (hdr_node = g_list_first(msg->hdr_list);
meillo@10 223 hdr_node != NULL; hdr_node = hdr_node_next) {
meillo@10 224 hdr_node_next = g_list_next(hdr_node);
meillo@10 225 hdr = ((header *) (hdr_node->data));
meillo@10 226 DEBUG(5) debugf("scanning headers: %s", hdr->header);
meillo@10 227 switch (hdr->id) {
meillo@10 228 case HEAD_MESSAGE_ID:
meillo@10 229 has_id = TRUE;
meillo@10 230 break;
meillo@10 231 case HEAD_DATE:
meillo@10 232 has_date = TRUE;
meillo@10 233 break;
meillo@10 234 case HEAD_FROM:
meillo@10 235 has_from = TRUE;
meillo@10 236 break;
meillo@10 237 case HEAD_SENDER:
meillo@10 238 has_sender = TRUE;
meillo@10 239 break;
meillo@10 240 case HEAD_TO:
meillo@10 241 case HEAD_CC:
meillo@10 242 case HEAD_BCC:
meillo@10 243 has_rcpt = TRUE;
meillo@10 244 if (flags & ACC_RCPT_FROM_HEAD) {
meillo@10 245 DEBUG(5) debugf("hdr->value = %s\n", hdr->value);
meillo@10 246 if (hdr->value) {
meillo@10 247 msg->rcpt_list = addr_list_append_rfc822(msg->rcpt_list, hdr->value, conf.host_name);
meillo@10 248 }
meillo@10 249 }
meillo@10 250 if ((flags & ACC_DEL_BCC) && (hdr->id == HEAD_BCC)) {
meillo@10 251 DEBUG(3) debugf("removing 'Bcc' header\n");
meillo@10 252 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
meillo@10 253 g_list_free_1(hdr_node);
meillo@10 254 destroy_header(hdr);
meillo@10 255 } else
meillo@10 256 has_to_or_cc = TRUE;
meillo@10 257 break;
meillo@10 258 case HEAD_ENVELOPE_TO:
meillo@10 259 if (flags & ACC_SAVE_ENVELOPE_TO) {
meillo@10 260 DEBUG(3) debugf("creating 'X-Orig-Envelope-To' header\n");
meillo@22 261 msg->hdr_list = g_list_prepend(msg->hdr_list, create_header(HEAD_UNKNOWN,
meillo@22 262 "X-Orig-Envelope-to: %s", hdr->value));
meillo@10 263 }
meillo@10 264 DEBUG(3) debugf("removing 'Envelope-To' header\n");
meillo@10 265 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
meillo@10 266 g_list_free_1(hdr_node);
meillo@10 267 destroy_header(hdr);
meillo@10 268 break;
meillo@10 269 case HEAD_RETURN_PATH:
meillo@10 270 if (flags & ACC_MAIL_FROM_HEAD) {
meillo@10 271 /* usually POP3 accept */
meillo@10 272 msg->return_path = create_address_qualified(hdr->value, TRUE, msg->received_host);
meillo@10 273 DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path));
meillo@10 274 }
meillo@10 275 DEBUG(3) debugf("removing 'Return-Path' header\n");
meillo@10 276 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
meillo@10 277 g_list_free_1(hdr_node);
meillo@10 278 destroy_header(hdr);
meillo@10 279 break;
meillo@10 280 default:
meillo@10 281 break; /* make compiler happy */
meillo@10 282 }
meillo@10 283 }
meillo@10 284
meillo@10 285 if (msg->return_path == NULL) {
meillo@10 286 /* this can happen for pop3 accept only and if no Return-path: header was given */
meillo@10 287 GList *hdr_list;
meillo@10 288 header *hdr;
meillo@10 289
meillo@10 290 DEBUG(3) debugf("return_path == NULL\n");
meillo@10 291
meillo@10 292 hdr_list = find_header(msg->hdr_list, HEAD_SENDER, NULL);
meillo@10 293 if (!hdr_list)
meillo@10 294 hdr_list = find_header(msg->hdr_list, HEAD_FROM, NULL);
meillo@10 295 if (hdr_list) {
meillo@10 296 gchar *addr;
meillo@10 297 hdr = (header *) (g_list_first(hdr_list)->data);
meillo@10 298
meillo@10 299 DEBUG(5) debugf("hdr->value = '%s'\n", hdr->value);
meillo@10 300
meillo@10 301 addr = g_strdup(hdr->value);
meillo@10 302 g_strchomp(addr);
meillo@10 303
meillo@10 304 if ((msg->return_path = create_address_qualified(addr, FALSE, msg->received_host)) != NULL) {
meillo@10 305 DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path));
meillo@13 306 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_UNKNOWN,
meillo@13 307 "X-Warning: return path set from %s address\n",
meillo@13 308 hdr->id == HEAD_SENDER ? "Sender:" : "From:"));
meillo@10 309 }
meillo@10 310 g_free(addr);
meillo@10 311 }
meillo@10 312 if (msg->return_path == NULL) { /* no Sender: or From: or create_address_qualified failed */
meillo@10 313 msg->return_path = create_address_qualified("postmaster", TRUE, conf.host_name);
meillo@10 314 DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path));
meillo@13 315 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_UNKNOWN,
meillo@25 316 "X-Warning: real return path is unknown\n"));
meillo@10 317 }
meillo@10 318 }
meillo@10 319
meillo@10 320 if (flags & ACC_DEL_RCPTS) {
meillo@10 321 GList *rcpt_node;
meillo@10 322 foreach(non_rcpt_list, rcpt_node) {
meillo@10 323 address *rcpt = (address *) (rcpt_node->data);
meillo@10 324 GList *node;
meillo@10 325 if ((node = g_list_find_custom(msg->rcpt_list, rcpt, _g_list_addr_isequal))) {
meillo@10 326 DEBUG(3) debugf("removing rcpt address %s\n", addr_string(node->data));
meillo@10 327 msg->rcpt_list = g_list_remove_link(msg->rcpt_list, node);
meillo@10 328 destroy_address((address *) (node->data));
meillo@10 329 g_list_free_1(node);
meillo@10 330 }
meillo@10 331 }
meillo@10 332 }
meillo@10 333
meillo@10 334 /* here we should have our recipients, fail if not: */
meillo@10 335 if (msg->rcpt_list == NULL) {
meillo@10 336 logwrite(LOG_WARNING, "no recipients found in message\n");
meillo@10 337 return AERR_NORCPT;
meillo@10 338 }
meillo@10 339
meillo@10 340 if (!(has_sender || has_from)) {
meillo@10 341 DEBUG(3) debugf("adding 'From' header\n");
meillo@10 342 msg->hdr_list = g_list_append(msg->hdr_list,
meillo@10 343 msg->full_sender_name
meillo@10 344 ?
meillo@13 345 create_header(HEAD_FROM, "From: \"%s\" <%s@%s>\n", msg->full_sender_name,
meillo@13 346 msg->return_path->local_part, msg->return_path->domain)
meillo@10 347 :
meillo@10 348 create_header(HEAD_FROM, "From: <%s@%s>\n",
meillo@13 349 msg->return_path->local_part, msg->return_path->domain)
meillo@10 350 );
meillo@10 351 }
meillo@10 352 if ((flags & ACC_HEAD_FROM_RCPT) && !has_rcpt) {
meillo@10 353 GList *node;
meillo@10 354 DEBUG(3) debugf("adding 'To' header(s)\n");
meillo@10 355 for (node = g_list_first(msg->rcpt_list); node; node = g_list_next(node)) {
meillo@13 356 msg->hdr_list = g_list_append(msg->hdr_list,
meillo@13 357 create_header(HEAD_TO, "To: %s\n", addr_string(msg-> return_path)));
meillo@10 358 }
meillo@10 359 }
meillo@10 360 if ((flags & ACC_DEL_BCC) && !has_to_or_cc) {
meillo@10 361 /* Bcc headers have been removed, and there are no remaining rcpt headers */
meillo@10 362 DEBUG(3) debugf("adding empty 'Bcc:' header\n");
meillo@10 363 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_BCC, "Bcc:\n"));
meillo@10 364 }
meillo@10 365 if (!has_date) {
meillo@10 366 DEBUG(3) debugf("adding 'Date:' header\n");
meillo@10 367 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_DATE, "Date: %s\n", rec_timestamp()));
meillo@10 368 }
meillo@10 369 if (!has_id) {
meillo@10 370 DEBUG(3) debugf("adding 'Message-ID:' header\n");
meillo@13 371 msg->hdr_list = g_list_append(msg->hdr_list,
meillo@13 372 create_header(HEAD_MESSAGE_ID, "Message-ID: <%s@%s>\n", msg->uid, conf.host_name));
meillo@10 373 }
meillo@0 374 }
meillo@10 375
meillo@10 376 /* Received header: */
meillo@10 377 /* At this point because we have to know the rcpts for the 'for' part */
meillo@10 378 if (!(flags & ACC_NO_RECVD_HDR)) {
meillo@10 379 gchar *for_string = NULL;
meillo@10 380 header *hdr = NULL;
meillo@10 381
meillo@10 382 DEBUG(3) debugf("adding 'Received:' header\n");
meillo@10 383
meillo@10 384 if (g_list_length(msg->rcpt_list) == 1) {
meillo@10 385 address *addr = (address *) (g_list_first(msg->rcpt_list)->data);
meillo@10 386 for_string = g_strdup_printf(" for %s", addr_string(addr));
meillo@10 387 }
meillo@10 388
meillo@10 389 if (msg->received_host == NULL) {
meillo@13 390 hdr = create_header(HEAD_RECEIVED, "Received: from %s by %s with %s (%s %s) id %s%s; %s\n",
meillo@13 391 passwd->pw_name, conf.host_name, prot_names[msg->received_prot],
meillo@13 392 PACKAGE, VERSION, msg->uid, for_string ? for_string : "", rec_timestamp());
meillo@10 393 } else {
meillo@10 394 #ifdef ENABLE_IDENT
meillo@10 395 DEBUG(5) debugf("adding 'Received:' header (5)\n");
meillo@13 396 hdr = create_header(HEAD_RECEIVED, "Received: from %s (ident=%s) by %s with %s (%s %s) id %s%s; %s\n",
meillo@13 397 msg->received_host, msg->ident ? msg->ident : "unknown", conf.host_name,
meillo@13 398 prot_names[msg->received_prot], PACKAGE, VERSION, msg->uid, for_string ? for_string : "",
meillo@10 399 rec_timestamp());
meillo@10 400 #else
meillo@13 401 hdr = create_header(HEAD_RECEIVED, "Received: from %s by %s with %s (%s %s) id %s%s; %s\n",
meillo@13 402 msg->received_host, conf.host_name, prot_names[msg->received_prot],
meillo@13 403 PACKAGE, VERSION, msg->uid, for_string ? for_string : "", rec_timestamp());
meillo@10 404 #endif
meillo@10 405 }
meillo@10 406 header_fold(hdr);
meillo@10 407 msg->hdr_list = g_list_prepend(msg->hdr_list, hdr);
meillo@10 408
meillo@10 409 if (for_string)
meillo@10 410 g_free(for_string);
meillo@0 411 }
meillo@0 412
meillo@10 413 /* write message to spool: */
meillo@10 414 /* accept is no longer responsible for this
meillo@13 415 if (!spool_write(msg, TRUE))
meillo@13 416 return AERR_NOSPOOL;
meillo@10 417 */
meillo@10 418 return AERR_OK;
meillo@0 419 }
meillo@0 420
meillo@10 421 accept_error
meillo@10 422 accept_message(FILE * in, message * msg, guint flags)
meillo@0 423 {
meillo@10 424 accept_error err;
meillo@0 425
meillo@10 426 err = accept_message_stream(in, msg, flags);
meillo@10 427 if (err == AERR_OK)
meillo@10 428 err = accept_message_prepare(msg, flags);
meillo@0 429
meillo@10 430 return err;
meillo@0 431 }