/* $Id$ */
/*
* Copyright (c) 2006 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "fdm.h"
#include "deliver.h"
int deliver_smtp_deliver(struct deliver_ctx *, struct actitem *);
void deliver_smtp_desc(struct actitem *, char *, size_t);
int deliver_smtp_code(char *);
struct deliver deliver_smtp = {
"smtp",
DELIVER_ASUSER,
deliver_smtp_deliver,
deliver_smtp_desc
};
int
deliver_smtp_code(char *line)
{
char ch;
const char *errstr;
int n;
size_t len;
len = strspn(line, "0123456789");
if (len == 0)
return (-1);
ch = line[len];
line[len] = '\0';
n = strtonum(line, 100, 999, &errstr);
line[len] = ch;
if (errstr != NULL)
return (-1);
return (n);
}
int
deliver_smtp_deliver(struct deliver_ctx *dctx, struct actitem *ti)
{
struct account *a = dctx->account;
struct mail *m = dctx->mail;
struct deliver_smtp_data *data = ti->data;
int done, code;
struct io *io;
char *cause, *to, *from, *line, *ptr, *lbuf;
enum deliver_smtp_state state;
size_t len, llen;
io = connectproxy(&data->server,
conf.verify_certs, conf.proxy, IO_CRLF, conf.timeout, &cause);
if (io == NULL) {
log_warnx("%s: %s", a->name, cause);
xfree(cause);
return (DELIVER_FAILURE);
}
if (conf.debug > 3 && !conf.syslog)
io->dup_fd = STDOUT_FILENO;
llen = IO_LINESIZE;
lbuf = xmalloc(llen);
if (conf.host_fqdn != NULL)
xasprintf(&ptr, "%s@%s", dctx->udata->name, conf.host_fqdn);
else
xasprintf(&ptr, "%s@%s", dctx->udata->name, conf.host_name);
if (data->to.str == NULL)
to = xstrdup(ptr);
else {
to = replacestr(&data->to, m->tags, m, &m->rml);
if (to == NULL || *to == '\0') {
xasprintf(&cause, "%s: empty to", a->name);
from = NULL;
goto error;
}
}
if (data->from.str == NULL)
from = xstrdup(ptr);
else {
from = replacestr(&data->from, m->tags, m, &m->rml);
if (from == NULL || *from == '\0') {
xasprintf(&cause, "%s: empty from", a->name);
goto error;
}
}
xfree(ptr);
state = SMTP_CONNECTING;
line = NULL;
done = 0;
do {
switch (io_pollline2(io,
&line, &lbuf, &llen, conf.timeout, &cause)) {
case 0:
cause = xstrdup("connection unexpectedly closed");
goto error;
case -1:
goto error;
}
code = deliver_smtp_code(line);
cause = NULL;
switch (state) {
case SMTP_CONNECTING:
if (code != 220)
goto error;
state = SMTP_HELO;
if (conf.host_fqdn != NULL)
io_writeline(io, "HELO %s", conf.host_fqdn);
else
io_writeline(io, "HELO %s", conf.host_name);
break;
case SMTP_HELO:
if (code != 250)
goto error;
state = SMTP_FROM;
io_writeline(io, "MAIL FROM:<%s>", from);
break;
case SMTP_FROM:
if (code != 250)
goto error;
state = SMTP_TO;
io_writeline(io, "RCPT TO:<%s>", to);
break;
case SMTP_TO:
if (code != 250)
goto error;
state = SMTP_DATA;
io_writeline(io, "DATA");
break;
case SMTP_DATA:
if (code != 354)
goto error;
line_init(m, &ptr, &len);
while (ptr != NULL) {
if (len > 1) {
if (*ptr == '.')
io_write(io, ".", 1);
io_write(io, ptr, len - 1);
}
io_writeline(io, NULL);
/* Update if necessary. */
if (io_update(io, conf.timeout, &cause) != 1)
goto error;
line_next(m, &ptr, &len);
}
state = SMTP_DONE;
io_writeline(io, ".");
io_flush(io, conf.timeout, NULL);
break;
case SMTP_DONE:
if (code != 250)
goto error;
state = SMTP_QUIT;
io_writeline(io, "QUIT");
break;
case SMTP_QUIT:
/*
* Exchange sometimes refuses to accept QUIT as a valid
* command, but since we got a 250 the mail has been
* accepted. So, allow 500 here too.
*/
if (code != 500 && code != 221)
goto error;
done = 1;
break;
}
} while (!done);
xfree(lbuf);
xfree(from);
xfree(to);
io_close(io);
io_free(io);
return (DELIVER_SUCCESS);
error:
if (cause != NULL) {
log_warnx("%s: %s", a->name, cause);
xfree(cause);
} else
log_warnx("%s: unexpected response: %s", a->name, line);
io_writeline(io, "QUIT");
io_flush(io, conf.timeout, NULL);
xfree(lbuf);
if (from != NULL)
xfree(from);
if (to != NULL)
xfree(to);
io_close(io);
io_free(io);
return (DELIVER_FAILURE);
}
void
deliver_smtp_desc(struct actitem *ti, char *buf, size_t len)
{
struct deliver_smtp_data *data = ti->data;
xsnprintf(buf, len, "smtp%s server \"%s\" port %s to \"%s\"",
data->server.ssl ? "s" : "", data->server.host, data->server.port,
data->to.str);
}