masqmail

changeset 80:e5090ac234cf

refactoring plus one small bugfix replaced deep nested conditionals with early exits fixed a small bug in the same go (Note: it is bad to fix bugs during refactoring): The SMTP_HELO case did not break in case of error. Now it does.
author meillo@marmaro.de
date Sat, 19 Jun 2010 11:11:28 +0200
parents d2bee9f4625c
children 71ce3a1568e9
files src/smtp_in.c
diffstat 1 files changed, 238 insertions(+), 212 deletions(-) [+]
line diff
     1.1 --- a/src/smtp_in.c	Sat Jun 19 10:52:24 2010 +0200
     1.2 +++ b/src/smtp_in.c	Sat Jun 19 11:11:28 2010 +0200
     1.3 @@ -1,5 +1,6 @@
     1.4  /*  MasqMail
     1.5      Copyright (C) 1999-2001 Oliver Kurth
     1.6 +    Copyright (C) 2010 markus schnalke <meillo@marmaro.de>
     1.7  
     1.8      This program is free software; you can redistribute it and/or modify
     1.9      it under the terms of the GNU General Public License as published by
    1.10 @@ -47,8 +48,9 @@
    1.11  {
    1.12  	gint i;
    1.13  	for (i = 0; i < SMTP_NUM_IDS; i++) {
    1.14 -		if (strncasecmp(smtp_cmds[i].cmd, line, strlen(smtp_cmds[i].cmd)) == 0)
    1.15 +		if (strncasecmp(smtp_cmds[i].cmd, line, strlen(smtp_cmds[i].cmd)) == 0) {
    1.16  			return (smtp_cmd_id) i;
    1.17 +		}
    1.18  	}
    1.19  	return SMTP_ERROR;
    1.20  }
    1.21 @@ -59,20 +61,24 @@
    1.22  static gboolean
    1.23  get_address(gchar * line, gchar * addr)
    1.24  {
    1.25 -	gchar *p = line, *q = addr;
    1.26 +	gchar *p = line;
    1.27 +	gchar *q = addr;
    1.28  
    1.29  	/* skip MAIL FROM: and RCPT TO: */
    1.30 -	while (*p && (*p != ':'))
    1.31 +	while (*p && (*p != ':')) {
    1.32  		p++;
    1.33 +	}
    1.34  	p++;
    1.35  
    1.36  	/* skip spaces: */
    1.37 -	while (*p && isspace(*p))
    1.38 +	while (*p && isspace(*p)) {
    1.39  		p++;
    1.40 +	}
    1.41  
    1.42  	/* get address: */
    1.43 -	while (*p && !isspace(*p) && (q < addr + MAX_ADDRESS - 1))
    1.44 +	while (*p && !isspace(*p) && (q < addr + MAX_ADDRESS - 1)) {
    1.45  		*(q++) = *(p++);
    1.46 +	}
    1.47  	*q = 0;
    1.48  
    1.49  	return TRUE;
    1.50 @@ -82,19 +88,20 @@
    1.51  create_base(gchar * remote_host)
    1.52  {
    1.53  	smtp_connection *base = g_malloc(sizeof(smtp_connection));
    1.54 -	if (base) {
    1.55 -		base->remote_host = g_strdup(remote_host);
    1.56 +	if (!base) {
    1.57 +		return NULL;
    1.58 +	}
    1.59  
    1.60 -		base->prot = PROT_SMTP;
    1.61 -		base->next_id = 0;
    1.62 -		base->helo_seen = 0;
    1.63 -		base->from_seen = 0;
    1.64 -		base->rcpt_seen = 0;
    1.65 -		base->msg = NULL;
    1.66 +	base->remote_host = g_strdup(remote_host);
    1.67  
    1.68 -		return base;
    1.69 -	}
    1.70 -	return NULL;
    1.71 +	base->prot = PROT_SMTP;
    1.72 +	base->next_id = 0;
    1.73 +	base->helo_seen = 0;
    1.74 +	base->from_seen = 0;
    1.75 +	base->rcpt_seen = 0;
    1.76 +	base->msg = NULL;
    1.77 +
    1.78 +	return base;
    1.79  }
    1.80  
    1.81  static void
    1.82 @@ -135,214 +142,233 @@
    1.83  	psc->msg = msg;
    1.84  
    1.85  	buffer = (gchar *) g_malloc(BUF_LEN);
    1.86 -	if (buffer) {
    1.87 -		/* send greeting string, containing ESMTP: */
    1.88 -		smtp_printf(out, "220 %s MasqMail %s ESMTP\r\n", conf.host_name, VERSION);
    1.89 +	if (!buffer) {
    1.90 +		/* this check is actually unneccessary as g_malloc()
    1.91 +		   aborts on failure */
    1.92 +		return;
    1.93 +	}
    1.94  
    1.95 -		while ((len = read_sockline(in, buffer, BUF_LEN, 5 * 60, READSOCKL_CHUG)) >= 0) {
    1.96 -			cmd_id = get_id(buffer);
    1.97 +	/* send greeting string, containing ESMTP: */
    1.98 +	smtp_printf(out, "220 %s MasqMail %s ESMTP\r\n", conf.host_name, VERSION);
    1.99  
   1.100 -			switch (cmd_id) {
   1.101 -			case SMTP_EHLO:
   1.102 -				psc->prot = PROT_ESMTP;
   1.103 -				/* fall through */
   1.104 -			case SMTP_HELO:
   1.105 -				psc->helo_seen = TRUE;
   1.106 +	while ((len = read_sockline(in, buffer, BUF_LEN, 5 * 60, READSOCKL_CHUG)) >= 0) {
   1.107 +		cmd_id = get_id(buffer);
   1.108  
   1.109 -				if (!conf.defer_all) {  /* I need this to debug delivery failures */
   1.110 -					if (psc->prot == PROT_ESMTP) {
   1.111 -						smtp_printf(out, "250-%s Nice to meet you with ESMTP\r\n", conf.host_name);
   1.112 -						/* not yet: fprintf(out, "250-SIZE\r\n"); */
   1.113 -						smtp_printf(out, "250-PIPELINING\r\n" "250 HELP\r\n");
   1.114 -					} else {
   1.115 -						smtp_printf(out, "250 %s pretty old mailer, huh?\r\n", conf.host_name);
   1.116 -					}
   1.117 -					break;
   1.118 -				} else {
   1.119 -					smtp_printf(out, "421 %s service temporarily unavailable.\r\n", conf.host_name);
   1.120 -				}
   1.121 +		switch (cmd_id) {
   1.122 +		case SMTP_EHLO:
   1.123 +			psc->prot = PROT_ESMTP;
   1.124 +			/* fall through */
   1.125 +		case SMTP_HELO:
   1.126 +			psc->helo_seen = TRUE;
   1.127  
   1.128 -			case SMTP_MAIL_FROM:
   1.129 -				if (psc->helo_seen && !psc->from_seen) {
   1.130 -					gchar buf[MAX_ADDRESS];
   1.131 -					address *addr;
   1.132 -
   1.133 -					msg = create_message();
   1.134 -					msg->received_host = remote_host ? g_strdup(remote_host) : NULL;
   1.135 -					msg->received_prot = psc->prot;
   1.136 -					msg->ident = ident ? g_strdup(ident) : NULL;
   1.137 -					/* get transfer id and increment for next one */
   1.138 -					msg->transfer_id = (psc->next_id)++;
   1.139 -
   1.140 -					get_address(buffer, buf);
   1.141 -					if ((addr = remote_host
   1.142 -					    ? create_address(buf, TRUE)
   1.143 -					    : create_address_qualified(buf, TRUE, conf.host_name))) {
   1.144 -						if (addr->domain != NULL) {
   1.145 -							psc->from_seen = TRUE;
   1.146 -							msg->return_path = addr;
   1.147 -							smtp_printf(out, "250 OK %s is a nice guy.\r\n", addr->address);
   1.148 -						} else {
   1.149 -							smtp_printf(out, "501 return path must be qualified.\r\n", buf);
   1.150 -						}
   1.151 -					} else {
   1.152 -						smtp_printf(out, "501 %s: syntax error.\r\n", buf);
   1.153 -					}
   1.154 -				} else {
   1.155 -					if (!psc->helo_seen)
   1.156 -						smtp_printf(out, "503 need HELO or EHLO\r\n");
   1.157 -					else
   1.158 -						smtp_printf(out, "503 MAIL FROM: already given.\r\n");
   1.159 -				}
   1.160 -				break;
   1.161 -
   1.162 -			case SMTP_RCPT_TO:
   1.163 -
   1.164 -				if (psc->helo_seen && psc->from_seen) {
   1.165 -					char buf[MAX_ADDRESS];
   1.166 -					address *addr;
   1.167 -
   1.168 -					get_address(buffer, buf);
   1.169 -					if ((addr = remote_host
   1.170 -					    ? create_address(buf, TRUE)
   1.171 -					    : create_address_qualified(buf, TRUE, conf.host_name))) {
   1.172 -						if (addr->local_part[0] != '|') {
   1.173 -							if (addr->domain != NULL) {
   1.174 -								gboolean do_relay = conf.do_relay;
   1.175 -								if (!do_relay) {
   1.176 -									if ((do_relay = addr_is_local(msg->return_path))) {
   1.177 -									}
   1.178 -									if (!do_relay) {
   1.179 -										do_relay = addr_is_local(addr);
   1.180 -									}
   1.181 -								}
   1.182 -								if (do_relay) {
   1.183 -									psc->rcpt_seen = TRUE;
   1.184 -									msg->rcpt_list = g_list_append(msg->rcpt_list, addr);
   1.185 -									smtp_printf(out, "250 OK %s is our friend.\r\n", addr->address);
   1.186 -								} else {
   1.187 -									smtp_printf(out, "550 relaying to %s denied.\r\n", addr_string(addr));
   1.188 -								}
   1.189 -							} else {
   1.190 -								smtp_printf(out, "501 recipient address must be qualified.\r\n", buf);
   1.191 -							}
   1.192 -						} else
   1.193 -							smtp_printf(out, "501 %s: no pipe allowed for SMTP connections\r\n", buf);
   1.194 -					} else {
   1.195 -						smtp_printf(out, "501 %s: syntax error in address.\r\n", buf);
   1.196 -					}
   1.197 -				} else {
   1.198 -
   1.199 -					if (!psc->helo_seen)
   1.200 -						smtp_printf(out, "503 need HELO or EHLO.\r\n");
   1.201 -					else
   1.202 -						smtp_printf(out, "503 need MAIL FROM: before RCPT TO:\r\n");
   1.203 -				}
   1.204 -				break;
   1.205 -
   1.206 -			case SMTP_DATA:
   1.207 -				if (psc->helo_seen && psc->rcpt_seen) {
   1.208 -					accept_error err;
   1.209 -
   1.210 -					smtp_printf(out, "354 okay, and do not forget the dot\r\n");
   1.211 -
   1.212 -					if ((err = accept_message(in, msg, conf.do_save_envelope_to ? ACC_SAVE_ENVELOPE_TO : 0)) == AERR_OK) {
   1.213 -						if (spool_write(msg, TRUE)) {
   1.214 -							pid_t pid;
   1.215 -							smtp_printf(out, "250 OK id=%s\r\n", msg->uid);
   1.216 -
   1.217 -							if (remote_host != NULL)
   1.218 -								logwrite(LOG_NOTICE, "%s <= <%s@%s> host=%s with %s\n", msg->uid, msg->return_path->local_part,
   1.219 -								         msg->return_path->domain, remote_host, prot_names[psc->prot]);
   1.220 -							else
   1.221 -								logwrite(LOG_NOTICE, "%s <= <%s@%s> with %s\n", msg->uid, msg->return_path->local_part,
   1.222 -								         msg->return_path->domain, prot_names[psc->prot]);
   1.223 -
   1.224 -							if (!conf.do_queue) {
   1.225 -								if ((pid = fork()) == 0) {
   1.226 -
   1.227 -									if (deliver(msg))
   1.228 -										_exit(EXIT_SUCCESS);
   1.229 -									else
   1.230 -										_exit(EXIT_FAILURE);
   1.231 -
   1.232 -								} else if (pid < 0) {
   1.233 -									logwrite(LOG_ALERT, "could not fork for delivery, id = %s", msg->uid);
   1.234 -								}
   1.235 -							} else {
   1.236 -								DEBUG(1) debugf("queuing forced by configuration or option.\n");
   1.237 -							}
   1.238 -						} else {
   1.239 -							smtp_printf(out, "451 Could not write spool file\r\n");
   1.240 -							return;
   1.241 -						}
   1.242 -					} else {
   1.243 -						switch (err) {
   1.244 -						case AERR_TIMEOUT:
   1.245 -							return;
   1.246 -						case AERR_EOF:
   1.247 -							return;
   1.248 -						default:
   1.249 -							/* should never happen: */
   1.250 -							smtp_printf(out, "451 Unknown error\r\n");
   1.251 -							return;
   1.252 -						}
   1.253 -					}
   1.254 -					psc->rcpt_seen = psc->from_seen = FALSE;
   1.255 -					destroy_message(msg);
   1.256 -					msg = NULL;
   1.257 -				} else {
   1.258 -					if (!psc->helo_seen)
   1.259 -						smtp_printf(out, "503 need HELO or EHLO.\r\n");
   1.260 -					else
   1.261 -						smtp_printf(out, "503 need RCPT TO: before DATA\r\n");
   1.262 -				}
   1.263 -				break;
   1.264 -			case SMTP_QUIT:
   1.265 -				smtp_printf(out, "221 goodbye\r\n");
   1.266 -				if (msg != NULL)
   1.267 -					destroy_message(msg);
   1.268 -				return;
   1.269 -			case SMTP_RSET:
   1.270 -				psc->from_seen = psc->rcpt_seen = FALSE;
   1.271 -				if (msg != NULL)
   1.272 -					destroy_message(msg);
   1.273 -				msg = NULL;
   1.274 -				smtp_printf(out, "250 OK\r\n");
   1.275 -				break;
   1.276 -			case SMTP_NOOP:
   1.277 -				smtp_printf(out, "250 OK\r\n");
   1.278 -				break;
   1.279 -			case SMTP_HELP:
   1.280 -				{
   1.281 -					int i;
   1.282 -
   1.283 -					smtp_printf(out, "214-supported commands:\r\n");
   1.284 -					for (i = 0; i < SMTP_NUM_IDS - 1; i++) {
   1.285 -						smtp_printf(out, "214-%s\r\n", smtp_cmds[i].cmd);
   1.286 -					}
   1.287 -					smtp_printf(out, "214 %s\r\n", smtp_cmds[i].cmd);
   1.288 -				}
   1.289 -				break;
   1.290 -			default:
   1.291 -				smtp_printf(out, "501 command not recognized\r\n");
   1.292 -				DEBUG(1) debugf("command not recognized, was '%s'\n", buffer);
   1.293 +			if (conf.defer_all) {  /* I need this to debug delivery failures */
   1.294 +				smtp_printf(out, "421 %s service temporarily unavailable.\r\n", conf.host_name);
   1.295  				break;
   1.296  			}
   1.297 -		}
   1.298 -		switch (len) {
   1.299 -		case -3:
   1.300 -			logwrite(LOG_NOTICE, "connection timed out\n");
   1.301 +
   1.302 +			if (psc->prot == PROT_ESMTP) {
   1.303 +				smtp_printf(out, "250-%s Nice to meet you with ESMTP\r\n", conf.host_name);
   1.304 +				/* not yet: fprintf(out, "250-SIZE\r\n"); */
   1.305 +				smtp_printf(out, "250-PIPELINING\r\n" "250 HELP\r\n");
   1.306 +			} else {
   1.307 +				smtp_printf(out, "250 %s pretty old mailer, huh?\r\n", conf.host_name);
   1.308 +			}
   1.309  			break;
   1.310 -		case -2:
   1.311 -			logwrite(LOG_NOTICE, "line overflow\n");
   1.312 +
   1.313 +		case SMTP_MAIL_FROM:
   1.314 +			{
   1.315 +				gchar buf[MAX_ADDRESS];
   1.316 +				address *addr;
   1.317 +	
   1.318 +				if (!psc->helo_seen) {
   1.319 +					smtp_printf(out, "503 need HELO or EHLO\r\n");
   1.320 +					break;
   1.321 +				}
   1.322 +				if (psc->from_seen) {
   1.323 +					smtp_printf(out, "503 MAIL FROM: already given.\r\n");
   1.324 +					break;
   1.325 +				}
   1.326 +	
   1.327 +				msg = create_message();
   1.328 +				msg->received_host = remote_host ? g_strdup(remote_host) : NULL;
   1.329 +				msg->received_prot = psc->prot;
   1.330 +				msg->ident = ident ? g_strdup(ident) : NULL;
   1.331 +				/* get transfer id and increment for next one */
   1.332 +				msg->transfer_id = (psc->next_id)++;
   1.333 +	
   1.334 +				get_address(buffer, buf);
   1.335 +				if (remote_host) {
   1.336 +					addr = create_address(buf, TRUE);
   1.337 +				} else {
   1.338 +					addr = create_address_qualified(buf, TRUE, conf.host_name);
   1.339 +				}
   1.340 +				if (!addr) {
   1.341 +					smtp_printf(out, "501 %s: syntax error.\r\n", buf);
   1.342 +				} else if (!addr->domain) {
   1.343 +					smtp_printf(out, "501 return path must be qualified.\r\n", buf);
   1.344 +				} else {
   1.345 +					psc->from_seen = TRUE;
   1.346 +					msg->return_path = addr;
   1.347 +					smtp_printf(out, "250 OK %s is a nice guy.\r\n", addr->address);
   1.348 +				}
   1.349 +			}
   1.350  			break;
   1.351 -		case -1:
   1.352 -			logwrite(LOG_NOTICE, "received EOF\n");
   1.353 +
   1.354 +		case SMTP_RCPT_TO:
   1.355 +			{
   1.356 +				char buf[MAX_ADDRESS];
   1.357 +				address *addr;
   1.358 +	
   1.359 +				if (!psc->helo_seen) {
   1.360 +					smtp_printf(out, "503 need HELO or EHLO.\r\n");
   1.361 +					break;
   1.362 +				}
   1.363 +				if (!psc->from_seen) {
   1.364 +					smtp_printf(out, "503 need MAIL FROM: before RCPT TO:\r\n");
   1.365 +					break;
   1.366 +				}
   1.367 +	
   1.368 +				get_address(buffer, buf);
   1.369 +				if (remote_host) {
   1.370 +					addr = create_address(buf, TRUE);
   1.371 +				} else {
   1.372 +					addr = create_address_qualified(buf, TRUE, conf.host_name);
   1.373 +				}
   1.374 +				if (!addr) {
   1.375 +					smtp_printf(out, "501 %s: syntax error in address.\r\n", buf);
   1.376 +					break;
   1.377 +				}
   1.378 +				if (addr->local_part[0] == '|') {
   1.379 +					smtp_printf(out, "501 %s: no pipe allowed for SMTP connections\r\n", buf);
   1.380 +					break;
   1.381 +				}
   1.382 +				if (!addr->domain) {
   1.383 +					smtp_printf(out, "501 recipient address must be qualified.\r\n", buf);
   1.384 +					break;
   1.385 +				}
   1.386 +				gboolean do_relay = conf.do_relay;
   1.387 +				if (!do_relay) {
   1.388 +					do_relay = addr_is_local(msg->return_path);
   1.389 +					if (!do_relay) {
   1.390 +						do_relay = addr_is_local(addr);
   1.391 +					}
   1.392 +				}
   1.393 +				if (!do_relay) {
   1.394 +					smtp_printf(out, "550 relaying to %s denied.\r\n", addr_string(addr));
   1.395 +					break;
   1.396 +				}
   1.397 +				psc->rcpt_seen = TRUE;
   1.398 +				msg->rcpt_list = g_list_append(msg->rcpt_list, addr);
   1.399 +				smtp_printf(out, "250 OK %s is our friend.\r\n", addr->address);
   1.400 +			}
   1.401  			break;
   1.402 +
   1.403 +		case SMTP_DATA:
   1.404 +			if (!psc->helo_seen) {
   1.405 +				smtp_printf(out, "503 need HELO or EHLO.\r\n");
   1.406 +				break;
   1.407 +			}
   1.408 +			if (!psc->rcpt_seen) {
   1.409 +				smtp_printf(out, "503 need RCPT TO: before DATA\r\n");
   1.410 +				break;
   1.411 +			}
   1.412 +			accept_error err;
   1.413 +
   1.414 +			smtp_printf(out, "354 okay, and do not forget the dot\r\n");
   1.415 +
   1.416 +			err = accept_message(in, msg, conf.do_save_envelope_to ? ACC_SAVE_ENVELOPE_TO : 0);
   1.417 +			if (err != AERR_OK) {
   1.418 +				if (err == AERR_TIMEOUT || err == AERR_EOF) {
   1.419 +					return;
   1.420 +				}
   1.421 +				/* should never happen: */
   1.422 +				smtp_printf(out, "451 Unknown error\r\n");
   1.423 +				return;
   1.424 +			}
   1.425 +
   1.426 +
   1.427 +			if (!spool_write(msg, TRUE)) {
   1.428 +				smtp_printf(out, "451 Could not write spool file\r\n");
   1.429 +				return;
   1.430 +			}
   1.431 +			pid_t pid;
   1.432 +			smtp_printf(out, "250 OK id=%s\r\n", msg->uid);
   1.433 +
   1.434 +			if (remote_host != NULL) {
   1.435 +				logwrite(LOG_NOTICE, "%s <= <%s@%s> host=%s with %s\n", msg->uid,
   1.436 +				         msg->return_path->local_part, msg->return_path->domain,
   1.437 +				         remote_host, prot_names[psc->prot]);
   1.438 +			} else {
   1.439 +				logwrite(LOG_NOTICE, "%s <= <%s@%s> with %s\n", msg->uid,
   1.440 +				         msg->return_path->local_part, msg->return_path->domain,
   1.441 +				         prot_names[psc->prot]);
   1.442 +			}
   1.443 +
   1.444 +			if (conf.do_queue) {
   1.445 +				DEBUG(1) debugf("queuing forced by configuration or option.\n");
   1.446 +			} else {
   1.447 +				pid = fork();
   1.448 +				if (pid == 0) {
   1.449 +					_exit(deliver(msg));
   1.450 +				} else if (pid < 0) {
   1.451 +					logwrite(LOG_ALERT, "could not fork for delivery, id = %s", msg->uid);
   1.452 +				}
   1.453 +			}
   1.454 +			psc->rcpt_seen = psc->from_seen = FALSE;
   1.455 +			destroy_message(msg);
   1.456 +			msg = NULL;
   1.457 +			break;
   1.458 +
   1.459 +		case SMTP_QUIT:
   1.460 +			smtp_printf(out, "221 goodbye\r\n");
   1.461 +			if (msg) {
   1.462 +				destroy_message(msg);
   1.463 +			}
   1.464 +			return;
   1.465 +
   1.466 +		case SMTP_RSET:
   1.467 +			psc->from_seen = psc->rcpt_seen = FALSE;
   1.468 +			if (msg) {
   1.469 +				destroy_message(msg);
   1.470 +				msg = NULL;
   1.471 +			}
   1.472 +			smtp_printf(out, "250 OK\r\n");
   1.473 +			break;
   1.474 +
   1.475 +		case SMTP_NOOP:
   1.476 +			smtp_printf(out, "250 OK\r\n");
   1.477 +			break;
   1.478 +
   1.479 +		case SMTP_HELP:
   1.480 +			{
   1.481 +				int i;
   1.482 +
   1.483 +				smtp_printf(out, "214-supported commands:\r\n");
   1.484 +				for (i = 0; i < SMTP_NUM_IDS - 1; i++) {
   1.485 +					smtp_printf(out, "214-%s\r\n", smtp_cmds[i].cmd);
   1.486 +				}
   1.487 +				smtp_printf(out, "214 %s\r\n", smtp_cmds[i].cmd);
   1.488 +			}
   1.489 +			break;
   1.490 +
   1.491  		default:
   1.492 +			smtp_printf(out, "501 command not recognized\r\n");
   1.493 +			DEBUG(1) debugf("command not recognized, was '%s'\n", buffer);
   1.494  			break;
   1.495  		}
   1.496  	}
   1.497 +	switch (len) {
   1.498 +	case -3:
   1.499 +		logwrite(LOG_NOTICE, "connection timed out\n");
   1.500 +		break;
   1.501 +	case -2:
   1.502 +		logwrite(LOG_NOTICE, "line overflow\n");
   1.503 +		break;
   1.504 +	case -1:
   1.505 +		logwrite(LOG_NOTICE, "received EOF\n");
   1.506 +		break;
   1.507 +	default:
   1.508 +		break;
   1.509 +	}
   1.510  }
   1.511  #endif