Codebase list fdm / ef7ed019-1390-4663-b347-846e7bbcf2a8/main deliver-imap.c
ef7ed019-1390-4663-b347-846e7bbcf2a8/main

Tree @ef7ed019-1390-4663-b347-846e7bbcf2a8/main (Download .tar.gz)

deliver-imap.c @ef7ed019-1390-4663-b347-846e7bbcf2a8/main

479c18f
 
 
766fcf6
479c18f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f12745e
479c18f
 
 
 
 
 
 
 
 
 
 
 
 
 
f12745e
479c18f
 
f12745e
479c18f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f12745e
479c18f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7735db4
479c18f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f12745e
b2130d1
 
 
 
 
479c18f
b2130d1
 
 
 
 
eaf5ef5
 
 
 
 
 
 
479c18f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f12745e
479c18f
 
 
 
 
 
f12745e
479c18f
 
 
 
 
 
 
 
 
f12745e
479c18f
 
 
 
 
 
 
 
 
 
 
 
 
 
b952bf6
 
 
479c18f
 
 
f4a998a
479c18f
 
 
 
 
 
f12745e
479c18f
f12745e
479c18f
f4a998a
 
 
 
 
 
 
 
 
 
 
479c18f
 
 
 
 
 
 
 
 
 
 
/* $Id$ */

/*
 * Copyright (c) 2008 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"
#include "fetch.h"

/*
 * This file is a bit of a mishmash, so that we can use some bits from
 * the IMAP fetching code.
 *
 * All needs to be straightened out sometime.
 */

int	 deliver_imap_deliver(struct deliver_ctx *, struct actitem *);
void	 deliver_imap_desc(struct actitem *, char *, size_t);

int	 deliver_imap_poll(struct account *, struct io *);
int	 deliver_imap_pollto(int (*)(struct account *, struct fetch_ctx *),
	     struct account *, struct io *, struct fetch_ctx *);
int	 deliver_imap_waitokay(struct account *, struct fetch_ctx *,
	     struct io *, char **);
int	 deliver_imap_waitcontinue(struct account *, struct fetch_ctx *,
	     struct io *, char **);
int	 deliver_imap_waitappend(struct account *, struct fetch_ctx *,
	     struct io *, char **);

struct deliver deliver_imap = {
	"imap",
	DELIVER_ASUSER,
	deliver_imap_deliver,
	deliver_imap_desc
};

/* Poll for data from/to server. */
int
deliver_imap_poll(struct account *a, struct io *io)
{
	char	*cause;

	switch (io_poll(io, conf.timeout, &cause)) {
	case 0:
		log_warnx("%s: connection unexpectedly closed", a->name);
		return (1);
	case -1:
		log_warnx("%s: %s", a->name, cause);
		xfree(cause);
		return (1);
	}
	return (0);
}

/* Poll through the IMAP fetch states until a particular one is reached. */
int
deliver_imap_pollto(int (*state)(struct account *, struct fetch_ctx *),
    struct account *a, struct io *io, struct fetch_ctx *fctx)
{
	while (state == NULL || fctx->state != state) {
		switch (fctx->state(a, fctx)) {
		case FETCH_AGAIN:
			continue;
		case FETCH_ERROR:
			return (1);
		case FETCH_EXIT:
			return (0);
		}
		if (deliver_imap_poll(a, io) != 0)
			return (1);
	}
	return (0);
}

/* Wait for okay. */
int
deliver_imap_waitokay(struct account *a, struct fetch_ctx *fctx, struct io *io,
    char **line)
{
	do {
		if (deliver_imap_poll(a, io) != 0)
			return (1);
		if (imap_getln(a, fctx, IMAP_TAGGED, line) != 0)
			return (1);
	} while (*line == NULL);

	if (!imap_okay(*line)) {
		imap_bad(a, *line);
		return (1);
	}
	return (0);
}

/* Wait for continuation. */
int
deliver_imap_waitcontinue(struct account *a, struct fetch_ctx *fctx,
    struct io *io, char **line)
{
	do {
		if (deliver_imap_poll(a, io) != 0)
			return (1);
		if (imap_getln(a, fctx, IMAP_CONTINUE, line) != 0)
			return (1);
	} while (*line == NULL);

	return (0);
}

/* Wait for append response. */
int
deliver_imap_waitappend(struct account *a, struct fetch_ctx *fctx,
    struct io *io, char **line)
{
	struct fetch_imap_data	*data = a->data;
	int			 tag;

	for (;;) {
		if (deliver_imap_poll(a, io) != 0) {
			line = NULL;
			return (IMAP_TAG_ERROR);
		}
		if (data->getln(a, fctx, line) != 0) {
			line = NULL;
			return (IMAP_TAG_ERROR);
		}
		if (*line == NULL)
			continue;

		tag = imap_tag(*line);
		if (tag != IMAP_TAG_NONE)
			break;
	}

	if (tag == IMAP_TAG_CONTINUE)
		return (IMAP_TAG_CONTINUE);
	if (tag != data->tag)
		return (IMAP_TAG_ERROR);
	return (tag);
}

int
deliver_imap_deliver(struct deliver_ctx *dctx, struct actitem *ti)
{
	struct account			*a = dctx->account;
	struct mail			*m = dctx->mail;
	struct deliver_imap_data	*data = ti->data;
	struct io			*io;
	struct fetch_ctx		 fctx;
	struct fetch_imap_data		 fdata;
	char				*cause, *folder, *ptr, *line;
	size_t				 len, maillen;
	u_int				 total, body;

	/* Connect to the IMAP server. */
	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;

	/* Work out the folder name. */
	folder = replacestr(&data->folder, m->tags, m, &m->rml);
	if (folder == NULL || *folder == '\0') {
		log_warnx("%s: empty folder", a->name);
		goto error;
	}

	/* Fake up the fetch context for the fetch code. */
	memset(&fdata, 0, sizeof fdata);
	fdata.user = data->user;
	fdata.pass = data->pass;
	fdata.nocrammd5 = data->nocrammd5;
	fdata.nologin = data->nologin;
	memcpy(&fdata.server, &data->server, sizeof fdata.server);
	fdata.io = io;
	fdata.only = FETCH_ONLY_ALL;
	a->data = &fdata;
	fetch_imap_state_init(a, &fctx);
	fctx.state = imap_state_connected;
	fctx.llen = IO_LINESIZE;
	fctx.lbuf = xmalloc(fctx.llen);

	/* Use the fetch code until the select1 state is reached. */
	if (deliver_imap_pollto(imap_state_select1, a, io, &fctx) != 0)
		goto error;

retry:
	/* Send an append command. */
	if (imap_putln(a, "%u APPEND {%zu}", ++fdata.tag, strlen(folder)) != 0)
		goto error;
	switch (deliver_imap_waitappend(a, &fctx, io, &line)) {
	case IMAP_TAG_ERROR:
		if (line != NULL)
			imap_invalid(a, line);
		goto error;
	case IMAP_TAG_CONTINUE:
		break;
	default:
		if (imap_no(line) && strstr(line, "[TRYCREATE]") != NULL)
			goto try_create;
		imap_invalid(a, line);
		goto error;
	}

	/*
	 * Send the mail size, not forgetting lines are CRLF terminated. The
	 * Google IMAP server is written strangely, so send the size as if
	 * every CRLF was a CR if the server has XYZZY.
	 */
	count_lines(m, &total, &body);
	maillen = m->size + total - 1;
	if (fdata.capa & IMAP_CAPA_XYZZY) {
		log_debug2("%s: adjusting size: actual %zu", a->name, maillen);
		maillen = m->size;
	}
	if (fdata.capa & IMAP_CAPA_NOSPACE) {
		if (imap_putln(a, "%s{%zu}", folder, maillen) != 0)
			goto error;
	} else {
		if (imap_putln(a, "%s {%zu}", folder, maillen) != 0)
			goto error;
	}
	switch (deliver_imap_waitappend(a, &fctx, io, &line)) {
	case IMAP_TAG_ERROR:
		if (line != NULL)
			imap_invalid(a, line);
		goto error;
	case IMAP_TAG_CONTINUE:
		break;
	default:
		if (imap_no(line) && strstr(line, "[TRYCREATE]") != NULL)
			goto try_create;
		imap_invalid(a, line);
		goto error;
	}

	/* Send the mail data. */
	line_init(m, &ptr, &len);
	while (ptr != NULL) {
		if (len > 1)
			io_write(io, ptr, len - 1);
		io_writeline(io, NULL);

		/* Update if necessary. */
		if (io_update(io, conf.timeout, &cause) != 1) {
			log_warnx("%s: %s", a->name, cause);
			xfree(cause);
			goto error;
		}

		line_next(m, &ptr, &len);
	}

	/* Wait for an okay from the server. */
	switch (deliver_imap_waitappend(a, &fctx, io, &line)) {
	case IMAP_TAG_ERROR:
	case IMAP_TAG_CONTINUE:
		if (line != NULL)
			imap_invalid(a, line);
		goto error;
	default:
		if (imap_okay(line))
			break;
		if (strstr(line, "[TRYCREATE]") != NULL)
			goto try_create;
		imap_invalid(a, line);
		goto error;
	}

	if (imap_putln(a, "%u LOGOUT", ++fdata.tag) != 0)
		goto error;
	if (deliver_imap_waitokay(a, &fctx, io, &line) != 0)
		goto error;

	xfree(fctx.lbuf);
	xfree(folder);

	fdata.disconnect(a);
	return (DELIVER_SUCCESS);

try_create:	/* XXX function? */
	/* Try to create the folder. */
	if (imap_putln(a, "%u CREATE {%zu}", ++fdata.tag, strlen(folder)) != 0)
		goto error;
	if (deliver_imap_waitcontinue(a, &fctx, io, &line) != 0)
		goto error;
	if (imap_putln(a, "%s", folder) != 0)
		goto error;
	if (deliver_imap_waitokay(a, &fctx, io, &line) != 0)
		goto error;
	goto retry;

error:
	io_writeline(io, "QUIT");
	io_flush(io, conf.timeout, NULL);

	xfree(fctx.lbuf);
	if (folder != NULL)
		xfree(folder);

	fdata.disconnect(a);
	return (DELIVER_FAILURE);
}

void
deliver_imap_desc(struct actitem *ti, char *buf, size_t len)
{
	struct deliver_imap_data	*data = ti->data;

	xsnprintf(buf, len, "imap%s server \"%s\" port %s folder \"%s\"",
	    data->server.ssl ? "s" : "", data->server.host, data->server.port,
	    data->folder.str);
}