masqmail-0.2

annotate src/smtp_in.c @ 11:24872a9fe6e1

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