Exemple #1
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);
}
Exemple #2
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);
}
Exemple #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));
}
Exemple #4
0
/* Capability state 1. Parse capabilities and set flags. */
int
imap_state_capability1(struct account *a, struct fetch_ctx *fctx)
{
	struct fetch_imap_data	*data = a->data;
	char			*line, *ptr;

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

	/* Convert to uppercase. */
	for (ptr = line; *ptr != '\0'; ptr++)
		*ptr = toupper((u_char) *ptr);

	if (strstr(line, "IMAP4REV1") == NULL) {
		log_warnx("%s: no IMAP4rev1 capability: %s", a->name, line);
		return (FETCH_ERROR);
	}

	data->capa = 0;
	if (strstr(line, "AUTH=CRAM-MD5") != NULL)
		data->capa |= IMAP_CAPA_AUTH_CRAM_MD5;

	/* Use XYZZY to detect Google brokenness. */
	if (strstr(line, "XYZZY") != NULL)
		data->capa |= IMAP_CAPA_XYZZY;

	if (strstr(line, "STARTTLS") != NULL)
		data->capa |= IMAP_CAPA_STARTTLS;

	fctx->state = imap_state_capability2;
	return (FETCH_AGAIN);
}
Exemple #5
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);
}
Exemple #6
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);
}
Exemple #7
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);
}
Exemple #8
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);
}
Exemple #9
0
/* Expunge state. */
int
imap_state_expunge(struct account *a, struct fetch_ctx *fctx)
{
	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));

	fctx->state = imap_state_next;
	return (FETCH_AGAIN);
}
Exemple #10
0
/* Quit state. */
int
imap_state_quit(struct account *a, struct fetch_ctx *fctx)
{
	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));

	imap_abort(a);
	return (FETCH_EXIT);
}
Exemple #11
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);
}
Exemple #12
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);
}
Exemple #13
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);
}
Exemple #14
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);
}
Exemple #15
0
/* Commit state. */
int
imap_state_commit(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->committed++;

	fctx->state = imap_state_next;
	return (FETCH_AGAIN);
}
Exemple #16
0
/* Select state 3. Hold until select returns message count. */
int
imap_state_select3(struct account *a, struct fetch_ctx *fctx)
{
	struct fetch_imap_data	*data = a->data;
	char			*line;

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

		if (sscanf(line, "* %u EXISTS", &data->total) == 1)
			break;
	}

	fctx->state = imap_state_select4;
	return (FETCH_AGAIN);
}
Exemple #17
0
/* Mail state. */
int
imap_state_mail(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->capa & IMAP_CAPA_GMEXT) {
		fctx->state = imap_state_gmext_start;
		return (FETCH_AGAIN);
	}

	fctx->state = imap_state_next;
	return (FETCH_MAIL);
}
Exemple #18
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);
}
Exemple #19
0
/* Search state 2. */
int
imap_state_search2(struct account *a, struct fetch_ctx *fctx)
{
	struct fetch_imap_data	*data = a->data;
	char			*line, *ptr;
	u_int			 uid;

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

	/* Skip the header. */
	if (strncasecmp(line, "* SEARCH", 8) != 0)
		return (imap_bad(a, line));
	line += 8;

	/* Read each UID and save it. */
	do {
		while (isspace((u_char) *line))
			line++;
		ptr = strchr(line, ' ');
		if (ptr == NULL)
			ptr = strchr(line, '\0');
		if (ptr == line)
			break;

		if (sscanf(line, "%u", &uid) != 1)
			return (imap_bad(a, line));
		ARRAY_ADD(&data->wanted, uid);
		log_debug3("%s: fetching UID: %u", a->name, uid);

		line = ptr;
	} while (*line == ' ');

	fctx->state = imap_state_search3;
	return (FETCH_AGAIN);
}
Exemple #20
0
/* STARTTLS state. */
int
imap_state_starttls(struct account *a, struct fetch_ctx *fctx)
{
	struct fetch_imap_data	*data = a->data;
	char			*line, *cause;

	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->io->ssl = makessl(&data->server, data->io->fd,
	    conf.verify_certs && data->server.verify, conf.timeout, &cause);
	if (data->io->ssl == NULL) {
		log_warnx("%s: STARTTLS failed: %s", a->name, cause);
		xfree(cause);
		return (FETCH_ERROR);
	}

	return (imap_pick_auth(a, fctx));
}
Exemple #21
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);
}
Exemple #22
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);
}
Exemple #23
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);
}