masqmail

annotate src/smtp_in.c @ 31:0267fe9745d5

we should probably add an always-bcc feature
author meillo@marmaro.de
date Thu, 06 May 2010 13:02:40 +0200
parents 26e34ae9a3e3
children e5090ac234cf
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@0 22 /*
meillo@0 23 I always forget these rfc numbers:
meillo@0 24 RFC 821 (SMTP)
meillo@0 25 RFC 1869 (ESMTP)
meillo@0 26 RFC 1870 (ESMTP SIZE)
meillo@0 27 RFC 2197 (ESMTP PIPELINE)
meillo@0 28 RFC 2554 (ESMTP AUTH)
meillo@0 29 */
meillo@0 30
meillo@0 31 #ifdef ENABLE_SMTP_SERVER
meillo@0 32
meillo@10 33 smtp_cmd smtp_cmds[] = {
meillo@15 34 {SMTP_HELO, "HELO",},
meillo@15 35 {SMTP_EHLO, "EHLO",},
meillo@15 36 {SMTP_MAIL_FROM, "MAIL FROM:",},
meillo@15 37 {SMTP_RCPT_TO, "RCPT TO:",},
meillo@15 38 {SMTP_DATA, "DATA",},
meillo@15 39 {SMTP_QUIT, "QUIT",},
meillo@15 40 {SMTP_RSET, "RSET",},
meillo@15 41 {SMTP_NOOP, "NOOP",},
meillo@15 42 {SMTP_HELP, "HELP"},
meillo@0 43 };
meillo@0 44
meillo@10 45 static smtp_cmd_id
meillo@10 46 get_id(const gchar * line)
meillo@0 47 {
meillo@10 48 gint i;
meillo@10 49 for (i = 0; i < SMTP_NUM_IDS; i++) {
meillo@10 50 if (strncasecmp(smtp_cmds[i].cmd, line, strlen(smtp_cmds[i].cmd)) == 0)
meillo@10 51 return (smtp_cmd_id) i;
meillo@10 52 }
meillo@10 53 return SMTP_ERROR;
meillo@0 54 }
meillo@0 55
meillo@0 56 /* this is a quick hack: we expect the address to be syntactically correct
meillo@0 57 and containing the mailbox only:
meillo@0 58 */
meillo@10 59 static gboolean
meillo@10 60 get_address(gchar * line, gchar * addr)
meillo@0 61 {
meillo@10 62 gchar *p = line, *q = addr;
meillo@0 63
meillo@10 64 /* skip MAIL FROM: and RCPT TO: */
meillo@10 65 while (*p && (*p != ':'))
meillo@10 66 p++;
meillo@10 67 p++;
meillo@0 68
meillo@10 69 /* skip spaces: */
meillo@10 70 while (*p && isspace(*p))
meillo@10 71 p++;
meillo@0 72
meillo@10 73 /* get address: */
meillo@10 74 while (*p && !isspace(*p) && (q < addr + MAX_ADDRESS - 1))
meillo@10 75 *(q++) = *(p++);
meillo@10 76 *q = 0;
meillo@0 77
meillo@10 78 return TRUE;
meillo@0 79 }
meillo@0 80
meillo@10 81 static smtp_connection*
meillo@10 82 create_base(gchar * remote_host)
meillo@0 83 {
meillo@10 84 smtp_connection *base = g_malloc(sizeof(smtp_connection));
meillo@10 85 if (base) {
meillo@10 86 base->remote_host = g_strdup(remote_host);
meillo@0 87
meillo@10 88 base->prot = PROT_SMTP;
meillo@10 89 base->next_id = 0;
meillo@10 90 base->helo_seen = 0;
meillo@10 91 base->from_seen = 0;
meillo@10 92 base->rcpt_seen = 0;
meillo@10 93 base->msg = NULL;
meillo@0 94
meillo@10 95 return base;
meillo@10 96 }
meillo@10 97 return NULL;
meillo@0 98 }
meillo@0 99
meillo@10 100 static void
meillo@10 101 smtp_printf(FILE * out, gchar * fmt, ...)
meillo@0 102 {
meillo@10 103 va_list args;
meillo@10 104 va_start(args, fmt);
meillo@0 105
meillo@10 106 DEBUG(4) {
meillo@10 107 gchar buf[256];
meillo@10 108 va_list args_copy;
meillo@0 109
meillo@10 110 va_copy(args_copy, args);
meillo@10 111 vsnprintf(buf, 255, fmt, args_copy);
meillo@10 112 va_end(args_copy);
meillo@0 113
meillo@10 114 debugf(">>>%s", buf);
meillo@10 115 }
meillo@0 116
meillo@10 117 vfprintf(out, fmt, args);
meillo@10 118 fflush(out);
meillo@0 119
meillo@10 120 va_end(args);
meillo@0 121 }
meillo@0 122
meillo@10 123 void
meillo@10 124 smtp_in(FILE * in, FILE * out, gchar * remote_host, gchar * ident)
meillo@0 125 {
meillo@10 126 gchar *buffer;
meillo@10 127 smtp_cmd_id cmd_id;
meillo@10 128 message *msg = NULL;
meillo@10 129 smtp_connection *psc;
meillo@10 130 int len;
meillo@0 131
meillo@10 132 DEBUG(5) debugf("smtp_in entered, remote_host = %s\n", remote_host);
meillo@0 133
meillo@10 134 psc = create_base(remote_host);
meillo@10 135 psc->msg = msg;
meillo@0 136
meillo@10 137 buffer = (gchar *) g_malloc(BUF_LEN);
meillo@10 138 if (buffer) {
meillo@10 139 /* send greeting string, containing ESMTP: */
meillo@10 140 smtp_printf(out, "220 %s MasqMail %s ESMTP\r\n", conf.host_name, VERSION);
meillo@0 141
meillo@10 142 while ((len = read_sockline(in, buffer, BUF_LEN, 5 * 60, READSOCKL_CHUG)) >= 0) {
meillo@10 143 cmd_id = get_id(buffer);
meillo@10 144
meillo@10 145 switch (cmd_id) {
meillo@10 146 case SMTP_EHLO:
meillo@10 147 psc->prot = PROT_ESMTP;
meillo@10 148 /* fall through */
meillo@10 149 case SMTP_HELO:
meillo@10 150 psc->helo_seen = TRUE;
meillo@10 151
meillo@10 152 if (!conf.defer_all) { /* I need this to debug delivery failures */
meillo@10 153 if (psc->prot == PROT_ESMTP) {
meillo@10 154 smtp_printf(out, "250-%s Nice to meet you with ESMTP\r\n", conf.host_name);
meillo@10 155 /* not yet: fprintf(out, "250-SIZE\r\n"); */
meillo@10 156 smtp_printf(out, "250-PIPELINING\r\n" "250 HELP\r\n");
meillo@10 157 } else {
meillo@10 158 smtp_printf(out, "250 %s pretty old mailer, huh?\r\n", conf.host_name);
meillo@10 159 }
meillo@10 160 break;
meillo@10 161 } else {
meillo@10 162 smtp_printf(out, "421 %s service temporarily unavailable.\r\n", conf.host_name);
meillo@10 163 }
meillo@10 164
meillo@10 165 case SMTP_MAIL_FROM:
meillo@10 166 if (psc->helo_seen && !psc->from_seen) {
meillo@10 167 gchar buf[MAX_ADDRESS];
meillo@10 168 address *addr;
meillo@10 169
meillo@10 170 msg = create_message();
meillo@10 171 msg->received_host = remote_host ? g_strdup(remote_host) : NULL;
meillo@10 172 msg->received_prot = psc->prot;
meillo@10 173 msg->ident = ident ? g_strdup(ident) : NULL;
meillo@10 174 /* get transfer id and increment for next one */
meillo@10 175 msg->transfer_id = (psc->next_id)++;
meillo@10 176
meillo@10 177 get_address(buffer, buf);
meillo@10 178 if ((addr = remote_host
meillo@10 179 ? create_address(buf, TRUE)
meillo@10 180 : create_address_qualified(buf, TRUE, conf.host_name))) {
meillo@10 181 if (addr->domain != NULL) {
meillo@10 182 psc->from_seen = TRUE;
meillo@10 183 msg->return_path = addr;
meillo@10 184 smtp_printf(out, "250 OK %s is a nice guy.\r\n", addr->address);
meillo@10 185 } else {
meillo@10 186 smtp_printf(out, "501 return path must be qualified.\r\n", buf);
meillo@10 187 }
meillo@10 188 } else {
meillo@10 189 smtp_printf(out, "501 %s: syntax error.\r\n", buf);
meillo@10 190 }
meillo@10 191 } else {
meillo@10 192 if (!psc->helo_seen)
meillo@10 193 smtp_printf(out, "503 need HELO or EHLO\r\n");
meillo@10 194 else
meillo@10 195 smtp_printf(out, "503 MAIL FROM: already given.\r\n");
meillo@10 196 }
meillo@10 197 break;
meillo@10 198
meillo@10 199 case SMTP_RCPT_TO:
meillo@10 200
meillo@10 201 if (psc->helo_seen && psc->from_seen) {
meillo@10 202 char buf[MAX_ADDRESS];
meillo@10 203 address *addr;
meillo@10 204
meillo@10 205 get_address(buffer, buf);
meillo@10 206 if ((addr = remote_host
meillo@10 207 ? create_address(buf, TRUE)
meillo@10 208 : create_address_qualified(buf, TRUE, conf.host_name))) {
meillo@10 209 if (addr->local_part[0] != '|') {
meillo@10 210 if (addr->domain != NULL) {
meillo@10 211 gboolean do_relay = conf.do_relay;
meillo@10 212 if (!do_relay) {
meillo@10 213 if ((do_relay = addr_is_local(msg->return_path))) {
meillo@10 214 }
meillo@10 215 if (!do_relay) {
meillo@10 216 do_relay = addr_is_local(addr);
meillo@10 217 }
meillo@10 218 }
meillo@10 219 if (do_relay) {
meillo@10 220 psc->rcpt_seen = TRUE;
meillo@10 221 msg->rcpt_list = g_list_append(msg->rcpt_list, addr);
meillo@10 222 smtp_printf(out, "250 OK %s is our friend.\r\n", addr->address);
meillo@10 223 } else {
meillo@10 224 smtp_printf(out, "550 relaying to %s denied.\r\n", addr_string(addr));
meillo@10 225 }
meillo@10 226 } else {
meillo@10 227 smtp_printf(out, "501 recipient address must be qualified.\r\n", buf);
meillo@10 228 }
meillo@10 229 } else
meillo@10 230 smtp_printf(out, "501 %s: no pipe allowed for SMTP connections\r\n", buf);
meillo@10 231 } else {
meillo@10 232 smtp_printf(out, "501 %s: syntax error in address.\r\n", buf);
meillo@10 233 }
meillo@10 234 } else {
meillo@10 235
meillo@10 236 if (!psc->helo_seen)
meillo@10 237 smtp_printf(out, "503 need HELO or EHLO.\r\n");
meillo@10 238 else
meillo@10 239 smtp_printf(out, "503 need MAIL FROM: before RCPT TO:\r\n");
meillo@10 240 }
meillo@10 241 break;
meillo@10 242
meillo@10 243 case SMTP_DATA:
meillo@10 244 if (psc->helo_seen && psc->rcpt_seen) {
meillo@10 245 accept_error err;
meillo@10 246
meillo@10 247 smtp_printf(out, "354 okay, and do not forget the dot\r\n");
meillo@10 248
meillo@10 249 if ((err = accept_message(in, msg, conf.do_save_envelope_to ? ACC_SAVE_ENVELOPE_TO : 0)) == AERR_OK) {
meillo@10 250 if (spool_write(msg, TRUE)) {
meillo@10 251 pid_t pid;
meillo@10 252 smtp_printf(out, "250 OK id=%s\r\n", msg->uid);
meillo@10 253
meillo@10 254 if (remote_host != NULL)
meillo@10 255 logwrite(LOG_NOTICE, "%s <= <%s@%s> host=%s with %s\n", msg->uid, msg->return_path->local_part,
meillo@10 256 msg->return_path->domain, remote_host, prot_names[psc->prot]);
meillo@10 257 else
meillo@10 258 logwrite(LOG_NOTICE, "%s <= <%s@%s> with %s\n", msg->uid, msg->return_path->local_part,
meillo@10 259 msg->return_path->domain, prot_names[psc->prot]);
meillo@10 260
meillo@10 261 if (!conf.do_queue) {
meillo@10 262 if ((pid = fork()) == 0) {
meillo@10 263
meillo@10 264 if (deliver(msg))
meillo@10 265 _exit(EXIT_SUCCESS);
meillo@10 266 else
meillo@10 267 _exit(EXIT_FAILURE);
meillo@10 268
meillo@10 269 } else if (pid < 0) {
meillo@10 270 logwrite(LOG_ALERT, "could not fork for delivery, id = %s", msg->uid);
meillo@10 271 }
meillo@10 272 } else {
meillo@10 273 DEBUG(1) debugf("queuing forced by configuration or option.\n");
meillo@10 274 }
meillo@10 275 } else {
meillo@10 276 smtp_printf(out, "451 Could not write spool file\r\n");
meillo@10 277 return;
meillo@10 278 }
meillo@10 279 } else {
meillo@10 280 switch (err) {
meillo@10 281 case AERR_TIMEOUT:
meillo@10 282 return;
meillo@10 283 case AERR_EOF:
meillo@10 284 return;
meillo@10 285 default:
meillo@10 286 /* should never happen: */
meillo@10 287 smtp_printf(out, "451 Unknown error\r\n");
meillo@10 288 return;
meillo@10 289 }
meillo@10 290 }
meillo@10 291 psc->rcpt_seen = psc->from_seen = FALSE;
meillo@10 292 destroy_message(msg);
meillo@10 293 msg = NULL;
meillo@10 294 } else {
meillo@10 295 if (!psc->helo_seen)
meillo@10 296 smtp_printf(out, "503 need HELO or EHLO.\r\n");
meillo@10 297 else
meillo@10 298 smtp_printf(out, "503 need RCPT TO: before DATA\r\n");
meillo@10 299 }
meillo@10 300 break;
meillo@10 301 case SMTP_QUIT:
meillo@10 302 smtp_printf(out, "221 goodbye\r\n");
meillo@10 303 if (msg != NULL)
meillo@10 304 destroy_message(msg);
meillo@10 305 return;
meillo@10 306 case SMTP_RSET:
meillo@10 307 psc->from_seen = psc->rcpt_seen = FALSE;
meillo@10 308 if (msg != NULL)
meillo@10 309 destroy_message(msg);
meillo@10 310 msg = NULL;
meillo@10 311 smtp_printf(out, "250 OK\r\n");
meillo@10 312 break;
meillo@10 313 case SMTP_NOOP:
meillo@10 314 smtp_printf(out, "250 OK\r\n");
meillo@10 315 break;
meillo@10 316 case SMTP_HELP:
meillo@10 317 {
meillo@10 318 int i;
meillo@10 319
meillo@10 320 smtp_printf(out, "214-supported commands:\r\n");
meillo@10 321 for (i = 0; i < SMTP_NUM_IDS - 1; i++) {
meillo@10 322 smtp_printf(out, "214-%s\r\n", smtp_cmds[i].cmd);
meillo@10 323 }
meillo@10 324 smtp_printf(out, "214 %s\r\n", smtp_cmds[i].cmd);
meillo@10 325 }
meillo@10 326 break;
meillo@10 327 default:
meillo@10 328 smtp_printf(out, "501 command not recognized\r\n");
meillo@10 329 DEBUG(1) debugf("command not recognized, was '%s'\n", buffer);
meillo@10 330 break;
meillo@10 331 }
meillo@10 332 }
meillo@10 333 switch (len) {
meillo@10 334 case -3:
meillo@10 335 logwrite(LOG_NOTICE, "connection timed out\n");
meillo@10 336 break;
meillo@10 337 case -2:
meillo@10 338 logwrite(LOG_NOTICE, "line overflow\n");
meillo@10 339 break;
meillo@10 340 case -1:
meillo@10 341 logwrite(LOG_NOTICE, "received EOF\n");
meillo@10 342 break;
meillo@10 343 default:
meillo@10 344 break;
meillo@10 345 }
meillo@0 346 }
meillo@0 347 }
meillo@0 348 #endif