2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
7 * This program is distributed in the hope that it will be useful, but
8 * WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 * Copyright © 2007 Pierre Habouzit
19 * Copyright © 2000-2002 Vsevolod Volkov <vvv@mutt.org.ua>
22 #include <lib-sys/evtloop.h>
23 #include <lib-sys/mutt_socket.h>
28 #define POP3S_PORT 995
29 /* maximal length of the server response (RFC1939) */
30 #define POP_CMD_MAXLEN 512
45 CMD_UNKNOWN, /* unknown whether it is available or not */
48 typedef struct pop_data_t {
52 unsigned multiline : 1;
53 unsigned capa_done : 1;
54 unsigned use_stls : 2;
55 cmd_status cmd_capa : 2; /* optional command CAPA */
56 cmd_status cmd_stls : 2; /* optional command STLS */
57 cmd_status cmd_user : 2; /* optional command USER */
66 static void pop_data_delete(pop_data_t **tp);
67 DO_ARRAY_TYPE(pop_data_t, pop_data);
68 DO_ARRAY_FUNCS(pop_data_t, pop_data, pop_data_delete);
70 /****************************************************************************/
72 /************************************************************************{{{*/
74 static pop_data_array conns;
76 static inline pop_data_t *pop_data_new(void)
78 pop_data_t *res = p_new(pop_data_t, 1);
80 pop_data_array_append(&conns, res);
83 static inline pop_data_t *pop_data_dup(pop_data_t *t)
88 static void pop_data_reset(pop_data_t *pd)
90 p_delete(&pd->apop_token);
91 buffer_reset(&pd->ibuf);
92 buffer_reset(&pd->obuf);
94 static void pop_data_wipe(pop_data_t *pd)
96 p_delete(&pd->apop_token);
98 IGNORE(el_job_release((job_t *)pd->w, EL_KILLED));
100 static void pop_data_delete(pop_data_t **tp)
104 if (--(*tp)->refcnt > 0) {
107 for (int i = 0; i < conns.len; i++) {
108 if (conns.arr[i] == *tp) {
109 pop_data_array_take(&conns, i);
118 static __init void pop_initialize(void)
120 pop_data_array_init(&conns);
122 static __fini void pop_shutdown(void)
124 pop_data_array_wipe(&conns);
127 /************************************************************************}}}*/
129 /****************************************************************************/
131 #define POP_CHECK_EOL(w, pd) \
133 const char *__eol = strchr((pd)->ibuf.data, '\n'); \
135 if ((pd)->ibuf.len > POP_CMD_MAXLEN) \
136 return el_job_release((w), EL_ERROR); \
142 static int pop_parse_capa(job_t *w, pop_data_t *pd, const char *eol)
144 const char *p = pd->ibuf.data;
146 if (!pd->multiline) {
147 if (m_strncmp(pd->ibuf.data, "+OK", 3)) {
148 buffer_consume_upto(&pd->ibuf, eol);
151 pd->multiline = true;
156 const char *q = strchr(p, '\n');
158 buffer_consume_upto(&pd->ibuf, p);
161 if (p[0] == '.' && p[1] != '.') {
162 buffer_consume_upto(&pd->ibuf, q);
165 if (m_strcasestart(p, "SASL", &p)) {
167 pd->auth_mechs = p_dupstr(p, q - p);
168 } else if (m_strcasestart(p, "STLS", NULL)) {
169 pd->cmd_stls = CMD_AVAILABLE;
175 static int pop_do_connect(job_t *w, pop_data_t *pd)
177 const char *eol = POP_CHECK_EOL(w, pd);
182 if (m_strncmp(pd->ibuf.data, "+OK", 3))
183 return el_job_release(w, EL_ERROR);
185 p = memchr(pd->ibuf.data, '<', eol - pd->ibuf.data);
186 if (p && (q = memchr(p + 1, '>', eol - p - 1))) {
187 pd->apop_token = p_dupstr(p, q + 1 - p);
189 buffer_consume_upto(&pd->ibuf, eol);
192 pd->state = POP_CHECK_CAPA0;
193 buffer_addstr(&pd->obuf, "CAPA\r\n");
194 return el_job_setmode(w, EL_WRITING);
196 case POP_CHECK_CAPA0:
197 if (!pop_parse_capa(w, pd, eol))
201 pd->multiline = false;
202 if (pd->act.has_ssl || (!pd->cmd_stls && !mod_ssl.force_tls))
203 goto do_authenticate;
204 pd->state = POP_STLS;
205 buffer_addstr(&pd->obuf, "STLS\r\n");
206 return el_job_setmode(w, EL_WRITING);
209 if (m_strncmp(pd->ibuf.data, "+OK", 3)) {
210 pd->cmd_stls = CMD_NOT_AVAILABLE;
211 buffer_consume_upto(&pd->ibuf, eol);
212 goto do_authenticate;
215 pd->state = POP_AUTHENTICATE;
216 return el_job_starttls(w);
218 case POP_AUTHENTICATE:
220 pd->state = POP_CHECK_CAPA1;
221 buffer_addstr(&pd->obuf, "CAPA\r\n");
222 return el_job_setmode(w, EL_WRITING);
224 case POP_CHECK_CAPA1:
225 if (!pop_parse_capa(w, pd, eol))
227 pd->capa_done = true;
228 pd->state = POP_STAT;
229 buffer_addstr(&pd->obuf, "STAT\r\n");
230 return el_job_setmode(w, EL_WRITING);
233 pd->state = POP_READY;
234 return el_job_setmode(w, EL_IDLE);
241 static int pop_setup(job_t *w, void *cfg)
243 pop_data_t *pd = w->ptr = pop_data_dup(cfg);
244 return el_job_connect2(w, &pd->act);
246 static int pop_on_event(job_t *w, el_event evt)
248 pop_data_t *pd = w->ptr;
251 return el_job_setmode(w, EL_READING);
254 EL_JOB_CHECK(el_job_read(w, &pd->ibuf));
255 if (pd->state <= POP_STAT)
256 return pop_do_connect(w, pd);
260 EL_JOB_CHECK(el_job_write(w, &pd->obuf));
261 if (pd->obuf.len == 0)
262 return el_job_setmode(w, EL_READING);
272 static void pop_finalize(job_t *w, el_status reason)
274 pop_data_t *pd = w->ptr;
277 case POP_CHECK_CAPA0:
279 case POP_CHECK_CAPA1:
280 case POP_AUTHENTICATE:
282 mutt_error(_("Error connecting to server: %s"), pd->act.host);
289 pop_data_delete((pop_data_t **)(void *)&w->ptr);
292 static machine_t const pop_machine = {
295 .on_event = &pop_on_event,
296 .finalize = &pop_finalize,
299 /****************************************************************************/
301 /****************************************************************************/
303 static int pop_parse_path(const char *path, ACCOUNT *act)
310 act->port = POP3_PORT;
311 act->type = M_ACCT_TYPE_POP;
312 m_strcpy(s, sizeof(s), path);
313 url_parse_ciss(&url, s);
315 if (url.scheme == U_POP || url.scheme == U_POPS) {
316 if (url.scheme == U_POPS) {
318 act->port = POP3S_PORT;
321 if (m_strisempty(url.path) && !mutt_account_fromurl(act, &url))
328 static pop_data_t *pop_find_conn(ACCOUNT *act)
330 pop_data_t *pd = NULL;
333 for (int i = 0; i < conns.len; i++) {
334 if (mutt_account_match(act, &conns.arr[i]->act)) {
335 pd = pop_data_dup(conns.arr[i]);
344 pd->w = el_job_start(&pop_machine, pd);
346 while (pd->w && pd->state != POP_READY) {
350 pop_data_delete(&pd);
356 static int pop_open_mailbox(CONTEXT *ctx)
361 pop_data_t *pd = NULL;
363 if (pop_parse_path(ctx->path, &act)) {
364 mutt_error(_("%s is an invalid POP path"), ctx->path);
370 mutt_account_tourl(&act, &url);
372 pd = pop_find_conn(&act);
375 url_ciss_tostring(&url, buf, sizeof (buf), 0);
376 m_strreplace(&ctx->path, buf);
381 mx_t const pop_mx_ng = {
383 .mx_open_mailbox = pop_open_mailbox,