Пример #1
0
/* Body state. */
int
imap_state_body(struct account *a, struct fetch_ctx *fctx)
{
	struct fetch_imap_data	*data = a->data;
	struct mail		*m = fctx->mail;
	struct fetch_imap_mail	*aux;
	char			*line, *ptr;
	u_int			 n;

	if (imap_getln(a, fctx, IMAP_UNTAGGED, &line) != 0)
		return (FETCH_ERROR);
	if (line == NULL)
		return (FETCH_BLOCK);

	if (sscanf(line, "* %u FETCH (", &n) != 1)
		return (imap_invalid(a, line));
	if ((ptr = strstr(line, "BODY[] {")) == NULL)
		return (imap_invalid(a, line));

	if (sscanf(ptr, "BODY[] {%zu}", &data->size) != 1)
		return (imap_invalid(a, line));
	data->lines = 0;

	/* Fill in local data. */
	aux = xcalloc(1, sizeof *aux);
	aux->uid = ARRAY_FIRST(&data->wanted);
	m->auxdata = aux;
	m->auxfree = imap_free;
	ARRAY_REMOVE(&data->wanted, 0);

	/* Open the mail. */
	if (mail_open(m, data->size) != 0) {
		log_warnx("%s: failed to create mail", a->name);
		return (FETCH_ERROR);
	}
	m->size = 0;

	/* Tag mail. */
	default_tags(&m->tags, data->src);
	if (data->server.host != NULL) {
		add_tag(&m->tags, "server", "%s", data->server.host);
		add_tag(&m->tags, "port", "%s", data->server.port);
	}
	add_tag(&m->tags, "server_uid", "%u", aux->uid);
	add_tag(&m->tags,
	    "folder", "%s", ARRAY_ITEM(data->folders, data->folder));

	/* If we already know the mail is oversize, start off flushing it. */
	data->flushing = data->size > conf.max_size;

	fctx->state = imap_state_line;
	return (FETCH_AGAIN);
}
Пример #2
0
/* GMail extensions body state. */
int
imap_state_gmext_body(struct account *a, struct fetch_ctx *fctx)
{
	struct fetch_imap_data	*data = a->data;
	struct mail		*m = fctx->mail;
	char			*line, *lb;
	int     	         tag;
	u_int			 n;
	uint64_t		 thrid, msgid;
	size_t			 lblen;

	for (;;) {
		if (imap_getln(a, fctx, IMAP_RAW, &line) != 0)
			return (FETCH_ERROR);
		if (line == NULL)
			return (FETCH_BLOCK);
		tag = imap_tag(line);
		if (tag == IMAP_TAG_NONE)
			break;
		if (tag == data->tag) {
			fctx->state = imap_state_next;
			return (FETCH_MAIL);
		}
		return (FETCH_ERROR);
	}

	if (sscanf(line, "* %u FETCH (X-GM-THRID %llu X-GM-MSGID %llu ", &n,
	    &thrid, &msgid) != 3)
		return (imap_invalid(a, line));
	if ((lb = strstr(line, "X-GM-LABELS")) == NULL)
		return (imap_invalid(a, line));
	if ((lb = strchr(lb, '(')) == NULL)
		return (imap_invalid(a, line));
	lb++; /* drop '(' */
	lblen = strlen(lb);
	if (lblen < 2 || lb[lblen - 1] != ')' || lb[lblen - 2] != ')')
		return (imap_invalid(a, line));
	lblen -= 2; /* drop '))' from the end */

	add_tag(&m->tags, "gmail_msgid", "%llu", msgid);
	add_tag(&m->tags, "gmail_thrid", "%llu", thrid);
	add_tag(&m->tags, "gmail_labels", "%.*s", (int)lblen, lb);

	fctx->state = imap_state_gmext_done;
	return (FETCH_AGAIN);
}
Пример #3
0
/* Line state. */
int
imap_state_line(struct account *a, struct fetch_ctx *fctx)
{
	struct fetch_imap_data	*data = a->data;
	struct mail		*m = fctx->mail;
	char			*line;
	size_t			 used, size, left;

	for (;;) {
		if (imap_getln(a, fctx, IMAP_RAW, &line) != 0)
			return (FETCH_ERROR);
		if (line == NULL)
			return (FETCH_BLOCK);

		if (data->flushing)
			continue;

		/* Check if this line would exceed the expected size. */
		used = m->size + data->lines;
		size = strlen(line);
		if (used + size + 2 > data->size)
			break;

		if (append_line(m, line, size) != 0) {
			log_warnx("%s: failed to resize mail", a->name);
			return (FETCH_ERROR);
		}
		data->lines++;
	}

	/*
	 * Calculate the number of bytes still needed. The current line must
	 * include at least that much data. Some servers include UID or FLAGS
	 * after the message: we don't care about these so just ignore them and
	 * make sure there is a terminating ).
	 */
	left = data->size - used;
	if (line[size - 1] != ')' && size <= left)
		return (imap_invalid(a, line));

	/* If there was data left, add it as a new line without trailing \n. */
	if (left > 0) {
		if (append_line(m, line, left) != 0) {
			log_warnx("%s: failed to resize mail", a->name);
			return (FETCH_ERROR);
		}
		data->lines++;

		/* Wipe out the trailing \n. */
		m->size--;
	}

	fctx->state = imap_state_mail;
	return (FETCH_AGAIN);
}
Пример #4
0
/* CRAM-MD5 auth state. */
int
imap_state_cram_md5_auth(struct account *a, struct fetch_ctx *fctx)
{
	struct fetch_imap_data	*data = a->data;
	char			*line, *ptr, *src, *b64;
	char			 out[EVP_MAX_MD_SIZE * 2 + 1];
	u_char			 digest[EVP_MAX_MD_SIZE];
	u_int			 i, n;

	if (imap_getln(a, fctx, IMAP_CONTINUE, &line) != 0)
		return (FETCH_ERROR);
	if (line == NULL)
		return (FETCH_BLOCK);

	ptr = line + 1;
	while (isspace((u_char) *ptr))
		ptr++;
	if (*ptr == '\0')
		return (imap_invalid(a, line));

	b64 = imap_base64_decode(ptr);
	HMAC(EVP_md5(),
	    data->pass, strlen(data->pass), b64, strlen(b64), digest, &n);
	xfree(b64);

	for (i = 0; i < n; i++)
		xsnprintf(out + i * 2, 3, "%02hhx", digest[i]);
	xasprintf(&src, "%s %s", data->user, out);
	b64 = imap_base64_encode(src);
	xfree(src);

	if (imap_putln(a, "%s", b64) != 0) {
		xfree(b64);
		return (FETCH_ERROR);
	}
	xfree(b64);

	fctx->state = imap_state_pass;
	return (FETCH_BLOCK);
}
Пример #5
0
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;
	}

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

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

	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);
}