+#define POP_CHECK_EOL(w, pd) \
+ ({ \
+ const char *__eol = strchr((pd)->ibuf.data, '\n'); \
+ if (!__eol) { \
+ if ((pd)->ibuf.len > POP_CMD_MAXLEN) \
+ return el_job_release((w), EL_ERROR); \
+ return 0; \
+ } \
+ __eol + 1; \
+ })
+
+static int pop_parse_capa(job_t *w, pop_data_t *pd, const char *eol)
+{
+ const char *p = pd->ibuf.data;
+
+ if (!pd->multiline) {
+ if (m_strncmp(pd->ibuf.data, "+OK", 3)) {
+ buffer_consume_upto(&pd->ibuf, eol);
+ return 1;
+ }
+ pd->multiline = true;
+ p = eol;
+ }
+
+ for (;;) {
+ const char *q = strchr(p, '\n');
+ if (!q) {
+ buffer_consume_upto(&pd->ibuf, p);
+ return 0;
+ }
+ if (p[0] == '.' && p[1] != '.') {
+ buffer_consume_upto(&pd->ibuf, q);
+ return 1;
+ }
+ if (m_strcasestart(p, "SASL", &p)) {
+ p = skipspaces(p);
+ pd->auth_mechs = p_dupstr(p, q - p);
+ } else if (m_strcasestart(p, "STLS", NULL)) {
+ pd->cmd_stls = CMD_AVAILABLE;
+ }
+ p = q;
+ }
+}
+
+static int pop_do_connect(job_t *w, pop_data_t *pd)
+{
+ const char *eol = POP_CHECK_EOL(w, pd);
+ const char *p, *q;
+
+ switch (pd->state) {
+ case POP_CONNECTING:
+ if (m_strncmp(pd->ibuf.data, "+OK", 3))
+ return el_job_release(w, EL_ERROR);
+
+ p = memchr(pd->ibuf.data, '<', eol - pd->ibuf.data);
+ if (p && (q = memchr(p + 1, '>', eol - p - 1))) {
+ pd->apop_token = p_dupstr(p, q + 1 - p);
+ }
+ buffer_consume_upto(&pd->ibuf, eol);
+ if (pd->capa_done)
+ goto capa0_choice;
+ pd->state = POP_CHECK_CAPA0;
+ buffer_addstr(&pd->obuf, "CAPA\r\n");
+ return el_job_setmode(w, EL_WRITING);
+
+ case POP_CHECK_CAPA0:
+ if (!pop_parse_capa(w, pd, eol))
+ return 0;
+
+ capa0_choice:
+ pd->multiline = false;
+ if (pd->act.has_ssl || (!pd->cmd_stls && !mod_ssl.force_tls))
+ goto do_authenticate;
+ pd->state = POP_STLS;
+ buffer_addstr(&pd->obuf, "STLS\r\n");
+ return el_job_setmode(w, EL_WRITING);
+
+ case POP_STLS:
+ if (m_strncmp(pd->ibuf.data, "+OK", 3)) {
+ pd->cmd_stls = CMD_NOT_AVAILABLE;
+ buffer_consume_upto(&pd->ibuf, eol);
+ goto do_authenticate;
+ }
+ do_authenticate:
+ pd->state = POP_AUTHENTICATE;
+ return el_job_starttls(w);
+
+ case POP_AUTHENTICATE:
+ abort();
+ pd->state = POP_CHECK_CAPA1;
+ buffer_addstr(&pd->obuf, "CAPA\r\n");
+ return el_job_setmode(w, EL_WRITING);
+
+ case POP_CHECK_CAPA1:
+ if (!pop_parse_capa(w, pd, eol))
+ return 0;
+ pd->capa_done = true;
+ pd->state = POP_STAT;
+ buffer_addstr(&pd->obuf, "STAT\r\n");
+ return el_job_setmode(w, EL_WRITING);
+
+ case POP_STAT:
+ pd->state = POP_READY;
+ return el_job_setmode(w, EL_IDLE);
+
+ default:
+ abort();
+ }
+}
+