Пример #1
0
/* Try an authentication method. */
int
imap_pick_auth(struct account *a, struct fetch_ctx *fctx)
{
	struct fetch_imap_data	*data = a->data;

	/* Try CRAM-MD5, if server supports it and user allows it. */
	if (!data->nocrammd5 && (data->capa & IMAP_CAPA_AUTH_CRAM_MD5)) {
		if (imap_putln(a,
		    "%u AUTHENTICATE CRAM-MD5", ++data->tag) != 0)
			return (FETCH_ERROR);
		fctx->state = imap_state_cram_md5_auth;
		return (FETCH_BLOCK);
	}

	/* Fall back to LOGIN, unless config disallows it. */
	if (!data->nologin) {
		if (imap_putln(a,
		    "%u LOGIN {%zu}", ++data->tag, strlen(data->user)) != 0)
			return (FETCH_ERROR);
		fctx->state = imap_state_login;
		return (FETCH_BLOCK);
	}

	log_warnx("%s: no authentication methods", a->name);
	return (FETCH_ERROR);
}
Пример #2
0
/* Search state 3. */
int
imap_state_search3(struct account *a, struct fetch_ctx *fctx)
{
	struct fetch_imap_data	*data = a->data;
	char			*line;

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

	/* Save the total. */
	data->total = ARRAY_LENGTH(&data->wanted);

	/* Update grand total. */
	data->folders_total += data->total;

	/* If no mails, or polling, stop here. */
	if (data->total == 0 || fctx->flags & FETCH_POLL) {
		if (imap_putln(a, "%u CLOSE", ++data->tag) != 0)
			return (FETCH_ERROR);
		fctx->state = imap_state_close;
		return (FETCH_BLOCK);
	}

	fctx->state = imap_state_next;
	return (FETCH_AGAIN);
}
Пример #3
0
/* Capability state 2. Check capabilities and choose login type. */
int
imap_state_capability2(struct account *a, struct fetch_ctx *fctx)
{
	struct fetch_imap_data	*data = a->data;
	char			*line;

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

	if (data->starttls) {
		if (!(data->capa & IMAP_CAPA_STARTTLS)) {
			log_warnx("%s: server doesn't support STARTTLS",
			    a->name);
			return (FETCH_ERROR);
		}
		if (imap_putln(a, "%u STARTTLS", ++data->tag) != 0)
			return (FETCH_ERROR);
		fctx->state = imap_state_starttls;
		return (FETCH_BLOCK);
	}

	return (imap_pick_auth(a, fctx));
}
Пример #4
0
/* Connected state: wait for initial line from server. */
int
imap_state_connected(struct account *a, struct fetch_ctx *fctx)
{
	struct fetch_imap_data	*data = a->data;
	char			*line;

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

	if (strncmp(line, "* PREAUTH", 9) == 0) {
		fctx->state = imap_state_select1;
		return (FETCH_AGAIN);
	}
	if (data->user == NULL || data->pass == NULL) {
		log_warnx("%s: not PREAUTH and no user or password", a->name);
		return (FETCH_ERROR);
	}

	if (imap_putln(a, "%u CAPABILITY", ++data->tag) != 0)
		return (FETCH_ERROR);
	fctx->state = imap_state_capability1;
	return (FETCH_BLOCK);
}
Пример #5
0
/* Select state 1. */
int
imap_state_select1(struct account *a, struct fetch_ctx *fctx)
{
	struct fetch_imap_data	*data = a->data;

	if (imap_putln(a, "%u SELECT {%zu}",
	    ++data->tag, strlen(ARRAY_ITEM(data->folders, data->folder))) != 0)
		return (FETCH_ERROR);
	fctx->state = imap_state_select2;
	return (FETCH_BLOCK);
}
Пример #6
0
/* GMail extensions start state. */
int
imap_state_gmext_start(struct account *a, struct fetch_ctx *fctx)
{
	struct fetch_imap_data	*data = a->data;
	struct mail		*m = fctx->mail;
	struct fetch_imap_mail	*aux = m->auxdata;

	if (imap_putln(a, "%u FETCH %u (X-GM-MSGID X-GM-THRID X-GM-LABELS)",
	    ++data->tag, aux->uid) != 0)
		return (FETCH_ERROR);

	fctx->state = imap_state_gmext_body;
	return (FETCH_AGAIN);
}
Пример #7
0
/* Login state. */
int
imap_state_login(struct account *a, struct fetch_ctx *fctx)
{
	struct fetch_imap_data	*data = a->data;
	char			*line;
	size_t			 passlen;

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

	passlen = strlen(data->pass);
	if (data->capa & IMAP_CAPA_NOSPACE) {
		if (imap_putln(a, "%s{%zu}", data->user, passlen) != 0)
			return (FETCH_ERROR);
	} else {
		if (imap_putln(a, "%s {%zu}", data->user, passlen) != 0)
			return (FETCH_ERROR);
	}
	fctx->state = imap_state_user;
	return (FETCH_BLOCK);
}
Пример #8
0
/* Search state 1. Request list of mail required. */
int
imap_state_search1(struct account *a, struct fetch_ctx *fctx)
{
	struct fetch_imap_data	*data = a->data;

	/* Search for a list of the mail UIDs to fetch. */
	switch (data->only) {
	case FETCH_ONLY_NEW:
		if (imap_putln(a, "%u UID SEARCH UNSEEN", ++data->tag) != 0)
			return (FETCH_ERROR);
		break;
	case FETCH_ONLY_OLD:
		if (imap_putln(a, "%u UID SEARCH SEEN", ++data->tag) != 0)
			return (FETCH_ERROR);
		break;
	default:
		if (imap_putln(a, "%u UID SEARCH ALL", ++data->tag) != 0)
			return (FETCH_ERROR);
		break;
	}

	fctx->state = imap_state_search2;
	return (FETCH_BLOCK);
}
Пример #9
0
/* Select state 2. Wait for continuation and send folder name. */
int
imap_state_select2(struct account *a, struct fetch_ctx *fctx)
{
	struct fetch_imap_data	*data = a->data;
	char			*line;

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

	if (imap_putln(a, "%s", ARRAY_ITEM(data->folders, data->folder)) != 0)
		return (FETCH_ERROR);
	fctx->state = imap_state_select3;
	return (FETCH_BLOCK);
}
Пример #10
0
/* User state. */
int
imap_state_user(struct account *a, struct fetch_ctx *fctx)
{
	struct fetch_imap_data	*data = a->data;
	char			*line;

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

	if (imap_putln(a, "%s", data->pass) != 0)
		return (FETCH_ERROR);
	fctx->state = imap_state_pass;
	return (FETCH_BLOCK);
}
Пример #11
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);
}
Пример #12
0
/* User state. */
int
imap_state_user(struct account *a, struct fetch_ctx *fctx)
{
	struct fetch_imap_data	*data = a->data;
	char			*line;
	int                      tag;

	if (data->capa & IMAP_CAPA_NOSPACE) {
		if (imap_getln(a, fctx, IMAP_CONTINUE, &line) != 0)
			return (FETCH_ERROR);
		if (line == NULL)
			return (FETCH_BLOCK);
	} else {
		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)
				continue;
			if (tag == IMAP_TAG_CONTINUE || tag == data->tag)
				break;
			return (FETCH_ERROR);
		}
		if (tag != IMAP_TAG_CONTINUE) {
			log_debug("%s: didn't accept user (%s); "
			    "trying without space", a->name, line);
			data->capa |= IMAP_CAPA_NOSPACE;
			return (imap_pick_auth(a, fctx));
		}
	}

	if (imap_putln(a, "%s", data->pass) != 0)
		return (FETCH_ERROR);
	fctx->state = imap_state_pass;
	return (FETCH_BLOCK);
}
Пример #13
0
/* Close state. */
int
imap_state_close(struct account *a, struct fetch_ctx *fctx)
{
	struct fetch_imap_data	*data = a->data;
	char			*line;

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

	data->folder++;
	if (data->folder != ARRAY_LENGTH(data->folders)) {
		fctx->state = imap_state_select1;
		return (FETCH_AGAIN);
	}

	if (imap_putln(a, "%u LOGOUT", ++data->tag) != 0)
		return (FETCH_ERROR);
	fctx->state = imap_state_quit;
	return (FETCH_BLOCK);
}
Пример #14
0
/* Select state 4. Hold until select completes. */
int
imap_state_select4(struct account *a, struct fetch_ctx *fctx)
{
	struct fetch_imap_data	*data = a->data;
	char			*line;

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

	/* If no mails, stop early. */
	if (data->total == 0) {
		if (imap_putln(a, "%u CLOSE", ++data->tag) != 0)
			return (FETCH_ERROR);
		fctx->state = imap_state_close;
		return (FETCH_BLOCK);
	}

	fctx->state = imap_state_search1;
	return (FETCH_AGAIN);
}
Пример #15
0
/*
 * Next state. Get next mail. This is also the idle state when completed, so
 * check for finished mail, exiting, and so on.
 */
int
imap_state_next(struct account *a, struct fetch_ctx *fctx)
{
	struct fetch_imap_data	*data = a->data;

	/* Handle dropped and kept mail. */
	if (!ARRAY_EMPTY(&data->dropped)) {
		if (imap_putln(a, "%u UID STORE %u +FLAGS.SILENT (\\Deleted)",
		    ++data->tag, ARRAY_FIRST(&data->dropped)) != 0)
			return (FETCH_ERROR);
		ARRAY_REMOVE(&data->dropped, 0);
		fctx->state = imap_state_commit;
		return (FETCH_BLOCK);
	}
	if (!ARRAY_EMPTY(&data->kept)) {
		/*
		 * GMail is broken and does not set the \Seen flag after mail
		 * is fetched, so set it explicitly for kept mail.
		 */
		if (imap_putln(a, "%u UID STORE %u +FLAGS.SILENT (\\Seen)",
		    ++data->tag, ARRAY_FIRST(&data->kept)) != 0)
			return (FETCH_ERROR);
		ARRAY_REMOVE(&data->kept, 0);
		fctx->state = imap_state_commit;
		return (FETCH_BLOCK);
	}

	/* Need to purge, switch to purge state. */
	if (fctx->flags & FETCH_PURGE) {
		/*
		 * If can't purge now, loop through this state until there is
		 * no mail on the dropped queue and FETCH_EMPTY is set. Can't
		 * have a seperate state to loop through without returning
		 * here: mail could potentially be added to the dropped list
		 * while in that state.
		 */
		if (fctx->flags & FETCH_EMPTY) {
			fctx->flags &= ~FETCH_PURGE;

			if (imap_putln(a, "%u EXPUNGE", ++data->tag) != 0)
				return (FETCH_ERROR);
			fctx->state = imap_state_expunge;
			return (FETCH_BLOCK);
		}

		/*
		 * Must be waiting for delivery, so permit blocking even though
		 * we (fetch) aren't waiting for any data.
		 */
		return (FETCH_BLOCK);
	}

	/* If last mail, wait for everything to be committed then close down. */
	if (ARRAY_EMPTY(&data->wanted)) {
		if (data->committed != data->total)
			return (FETCH_BLOCK);
		if (imap_putln(a, "%u CLOSE", ++data->tag) != 0)
			return (FETCH_ERROR);
		fctx->state = imap_state_close;
		return (FETCH_BLOCK);
	}

	/* Fetch the next mail. */
	if (imap_putln(a, "%u "
	    "UID FETCH %u BODY[]",++data->tag, ARRAY_FIRST(&data->wanted)) != 0)
		return (FETCH_ERROR);
	fctx->state = imap_state_body;
	return (FETCH_BLOCK);
}
Пример #16
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);
}