X-Git-Url: http://git.madism.org/?p=apps%2Fmadmutt.git;a=blobdiff_plain;f=lib-mx%2Fpop-new.c;h=fcc17df71e859c689d1792a4ebea58ec20458fec;hp=3936cbbfc6d44efa11af2c21a5bc22cae6a3bb2a;hb=806d13adf4c00b1c71c1c5eaca8431524ce1ec1e;hpb=6b4475312b058043257fba4526817f60d9e26baf diff --git a/lib-mx/pop-new.c b/lib-mx/pop-new.c index 3936cbb..fcc17df 100644 --- a/lib-mx/pop-new.c +++ b/lib-mx/pop-new.c @@ -20,24 +20,101 @@ */ #include +#include #include "pop.h" +#include "account.h" + +#define POP3_PORT 110 +#define POP3S_PORT 995 +/* maximal length of the server response (RFC1939) */ +#define POP_CMD_MAXLEN 512 + +enum pop_state { + POP_CONNECTING, + POP_CHECK_CAPA0, + POP_STLS, + POP_AUTHENTICATE, + POP_CHECK_CAPA1, + POP_STAT, + POP_READY, +}; + +typedef enum { + CMD_NOT_AVAILABLE, + CMD_AVAILABLE, + CMD_UNKNOWN, /* unknown whether it is available or not */ +} cmd_status; typedef struct pop_data_t { int refcnt; - job_t *w; + enum pop_state state; + + unsigned multiline : 1; + unsigned capa_done : 1; + unsigned use_stls : 2; + cmd_status cmd_capa : 2; /* optional command CAPA */ + cmd_status cmd_stls : 2; /* optional command STLS */ + cmd_status cmd_user : 2; /* optional command USER */ + ACCOUNT act; + + char *apop_token; + char *auth_mechs; + + volatile job_t *w; + buffer_t ibuf, obuf; } pop_data_t; -DO_INIT(pop_data_t, pop_data); -DO_WIPE(pop_data_t, pop_data); -DO_REFCNT(pop_data_t, pop_data); +static void pop_data_delete(pop_data_t **tp); DO_ARRAY_TYPE(pop_data_t, pop_data); DO_ARRAY_FUNCS(pop_data_t, pop_data, pop_data_delete); /****************************************************************************/ /* module */ -/****************************************************************************/ +/************************************************************************{{{*/ static pop_data_array conns; +static inline pop_data_t *pop_data_new(void) +{ + pop_data_t *res = p_new(pop_data_t, 1); + res->refcnt = 1; + pop_data_array_append(&conns, res); + return res; +} +static inline pop_data_t *pop_data_dup(pop_data_t *t) +{ + t->refcnt++; + return t; +} +static void pop_data_reset(pop_data_t *pd) +{ + p_delete(&pd->apop_token); + buffer_reset(&pd->ibuf); + buffer_reset(&pd->obuf); +} +static void pop_data_wipe(pop_data_t *pd) +{ + p_delete(&pd->apop_token); + if (pd->w) + IGNORE(el_job_release((job_t *)pd->w, EL_KILLED)); +} +static void pop_data_delete(pop_data_t **tp) +{ + if (!*tp) + return; + if (--(*tp)->refcnt > 0) { + *tp = NULL; + } else { + for (int i = 0; i < conns.len; i++) { + if (conns.arr[i] == *tp) { + pop_data_array_take(&conns, i); + break; + } + } + pop_data_wipe(*tp); + p_delete(tp); + } +} + static __init void pop_initialize(void) { pop_data_array_init(&conns); @@ -47,22 +124,175 @@ static __fini void pop_shutdown(void) pop_data_array_wipe(&conns); } -/****************************************************************************/ +/************************************************************************}}}*/ /* pop3 machine */ /****************************************************************************/ +#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(); + } +} + static int pop_setup(job_t *w, void *cfg) { - return 0; + pop_data_t *pd = w->ptr = pop_data_dup(cfg); + return el_job_connect2(w, &pd->act); } +static int pop_on_event(job_t *w, el_event evt) +{ + pop_data_t *pd = w->ptr; + switch (evt) { + case EL_EVT_RUNNING: + return el_job_setmode(w, EL_READING); + case EL_EVT_IN: + EL_JOB_CHECK(el_job_read(w, &pd->ibuf)); + if (pd->state <= POP_STAT) + return pop_do_connect(w, pd); + abort(); + + case EL_EVT_OUT: + EL_JOB_CHECK(el_job_write(w, &pd->obuf)); + if (pd->obuf.len == 0) + return el_job_setmode(w, EL_READING); + return 0; + + case EL_EVT_WAKEUP: + return 0; + + default: + abort(); + } +} static void pop_finalize(job_t *w, el_status reason) { + pop_data_t *pd = w->ptr; + switch (pd->state) { + case POP_CONNECTING: + case POP_CHECK_CAPA0: + case POP_STLS: + case POP_CHECK_CAPA1: + case POP_AUTHENTICATE: + case POP_STAT: + mutt_error(_("Error connecting to server: %s"), pd->act.host); + break; + + case POP_READY: + break; + } + pop_data_reset(pd); + pop_data_delete((pop_data_t **)(void *)&w->ptr); } static machine_t const pop_machine = { .name = "POP3", .setup = &pop_setup, + .on_event = &pop_on_event, .finalize = &pop_finalize, }; @@ -70,18 +300,85 @@ static machine_t const pop_machine = { /* pop3 mx driver */ /****************************************************************************/ +static int pop_parse_path(const char *path, ACCOUNT *act) +{ + ciss_url_t url; + char s[BUFSIZ]; + + /* Defaults */ + act->flags = 0; + act->port = POP3_PORT; + act->type = M_ACCT_TYPE_POP; + m_strcpy(s, sizeof(s), path); + url_parse_ciss(&url, s); + + if (url.scheme == U_POP || url.scheme == U_POPS) { + if (url.scheme == U_POPS) { + act->has_ssl = 1; + act->port = POP3S_PORT; + } + + if (m_strisempty(url.path) && !mutt_account_fromurl(act, &url)) + return 0; + } + + return -1; +} + +static pop_data_t *pop_find_conn(ACCOUNT *act) +{ + pop_data_t *pd = NULL; + + el_lock(); + for (int i = 0; i < conns.len; i++) { + if (mutt_account_match(act, &conns.arr[i]->act)) { + pd = pop_data_dup(conns.arr[i]); + break; + } + } + if (!pd) { + pd = pop_data_new(); + pd->act = *act; + } + if (!pd->w) { + pd->w = el_job_start(&pop_machine, pd); + } + while (pd->w && pd->state != POP_READY) { + el_wait(pd->w); + } + if (!pd->w) + pop_data_delete(&pd); + + el_unlock(); + return pd; +} + +static int pop_open_mailbox(CONTEXT *ctx) +{ + char buf[BUFSIZ]; + ACCOUNT act; + ciss_url_t url; + pop_data_t *pd = NULL; + + if (pop_parse_path(ctx->path, &act)) { + mutt_error(_("%s is an invalid POP path"), ctx->path); + mutt_sleep(2); + return -1; + } + + p_clear(&url, 1); + mutt_account_tourl(&act, &url); + + pd = pop_find_conn(&act); + if (pd) { + ctx->data = pd; + url_ciss_tostring(&url, buf, sizeof (buf), 0); + m_strreplace(&ctx->path, buf); + } + return 0; +} + mx_t const pop_mx_ng = { - M_POP, - 0, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, + .type = M_POP, + .mx_open_mailbox = pop_open_mailbox, };