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
44 CMD_UNKNOWN, /* unknown whether it is available or not */
49 typedef struct pop_data_t {
53 unsigned multiline : 1;
54 unsigned known_capa : 1;
55 unsigned use_stls : 2;
56 cmd_status cmd_capa : 2; /* optional command CAPA */
57 cmd_status cmd_stls : 2; /* optional command STLS */
58 cmd_status cmd_uidl : 2; /* optional command UIDL */
59 cmd_status cmd_top : 2; /* optional command TOP */
60 cmd_status cmd_user : 2; /* optional command USER */
69 static void pop_data_delete(pop_data_t **tp);
70 DO_ARRAY_TYPE(pop_data_t, pop_data);
71 DO_ARRAY_FUNCS(pop_data_t, pop_data, pop_data_delete);
73 /****************************************************************************/
75 /************************************************************************{{{*/
77 static pop_data_array conns;
79 static inline pop_data_t *pop_data_new(void)
81 pop_data_t *res = p_new(pop_data_t, 1);
83 pop_data_array_append(&conns, res);
86 static inline pop_data_t *pop_data_dup(pop_data_t *t)
91 static void pop_data_reset(pop_data_t *pd)
93 p_delete(&pd->apop_token);
94 buffer_reset(&pd->ibuf);
95 buffer_reset(&pd->obuf);
97 static void pop_data_wipe(pop_data_t *pd)
99 p_delete(&pd->apop_token);
101 IGNORE(el_job_release((job_t *)pd->w, EL_KILLED));
103 static void pop_data_delete(pop_data_t **tp)
107 if (--(*tp)->refcnt > 0) {
110 for (int i = 0; i < conns.len; i++) {
111 if (conns.arr[i] == *tp) {
112 pop_data_array_take(&conns, i);
121 static __init void pop_initialize(void)
123 pop_data_array_init(&conns);
125 static __fini void pop_shutdown(void)
127 pop_data_array_wipe(&conns);
130 /************************************************************************}}}*/
132 /****************************************************************************/
134 #define POP_CHECK_EOL(w, pd) \
136 const char *__eol = strchr((pd)->ibuf.data, '\n'); \
138 if ((pd)->ibuf.len > POP_CMD_MAXLEN) \
139 return el_job_release((w), EL_ERROR); \
145 static int pop_read_banner(job_t *w, pop_data_t *pd)
147 const char *eol = POP_CHECK_EOL(w, pd);
150 if (m_strncmp(pd->ibuf.data, "+OK", 3))
151 return el_job_release(w, EL_ERROR);
153 p = memchr(pd->ibuf.data, '<', eol - pd->ibuf.data);
154 if (p && (q = memchr(p + 1, '>', eol - p - 1))) {
155 pd->apop_token = p_dupstr(p, q + 1 - p);
157 buffer_consume_upto(&pd->ibuf, eol);
159 /* TODO do not check capa again */
160 pd->state = POP_CHECK_CAPA0;
161 buffer_addstr(&pd->obuf, "CAPA\r\n");
162 return el_job_setmode(w, EL_WRITING);
165 static int pop_check_capa(job_t *w, pop_data_t *pd)
167 const char *p = POP_CHECK_EOL(w, pd);
169 if (!pd->multiline) {
170 if (m_strncmp(pd->ibuf.data, "+OK", 3)) {
171 pd->cmd_capa = CMD_NOT_AVAILABLE;
172 pd->cmd_stls = CMD_NOT_AVAILABLE;
175 pd->multiline = true;
181 const char *q = strchr(p, '\n');
183 buffer_consume_upto(&pd->ibuf, p);
186 if (p[0] == '.' && p[1] != '.') {
187 buffer_consume_upto(&pd->ibuf, q);
190 if (m_strcasestart(p, "SASL", &p)) {
192 pd->auth_mechs = p_dupstr(p, q - p);
193 } else if (m_strcasestart(p, "STLS", NULL)) {
194 pd->cmd_stls = CMD_AVAILABLE;
195 } else if (m_strcasestart(p, "UIDL", NULL)) {
196 pd->cmd_uidl = CMD_AVAILABLE;
197 } else if (m_strcasestart(p, "TOP", NULL)) {
198 pd->cmd_top = CMD_AVAILABLE;
204 pd->multiline = false;
206 case POP_CHECK_CAPA0:
207 if (!pd->act.has_ssl && (pd->cmd_stls || mod_ssl.force_tls)) {
208 buffer_addstr(&pd->obuf, "STLS\r\n");
209 pd->state = POP_STLS;
210 return el_job_setmode(w, EL_WRITING);
213 case POP_CHECK_CAPA1:
214 pd->state = POP_AUTHENTICATE;
218 case POP_CHECK_CAPA2:
219 pd->state = POP_STAT;
223 return el_job_release(w, EL_ERROR);
227 static int (*pop_actions[])(job_t *, pop_data_t *pd) = {
228 [POP_CONNECTING] = pop_read_banner,
229 [POP_CHECK_CAPA0] = pop_check_capa,
233 static int pop_setup(job_t *w, void *cfg)
235 pop_data_t *pd = w->ptr = pop_data_dup(cfg);
236 return el_job_connect2(w, &pd->act);
238 static int pop_on_event(job_t *w, el_event evt)
240 pop_data_t *pd = w->ptr;
243 return el_job_setmode(w, EL_READING);
246 EL_JOB_CHECK(el_job_read(w, &pd->ibuf));
247 EL_JOB_CHECK(pop_actions[pd->state](w, pd));
250 EL_JOB_CHECK(el_job_write(w, &pd->obuf));
251 if (pd->obuf.len == 0)
252 return el_job_setmode(w, EL_READING);
262 static void pop_finalize(job_t *w, el_status reason)
264 pop_data_t *pd = w->ptr;
267 case POP_CHECK_CAPA0:
269 case POP_CHECK_CAPA1:
270 case POP_AUTHENTICATE:
271 case POP_CHECK_CAPA2:
273 mutt_error(_("Error connecting to server: %s"), pd->act.host);
280 pop_data_delete((pop_data_t **)(void *)&w->ptr);
283 static machine_t const pop_machine = {
286 .on_event = &pop_on_event,
287 .finalize = &pop_finalize,
290 /****************************************************************************/
292 /****************************************************************************/
294 static int pop_parse_path(const char *path, ACCOUNT *act)
301 act->port = POP3_PORT;
302 act->type = M_ACCT_TYPE_POP;
303 m_strcpy(s, sizeof(s), path);
304 url_parse_ciss(&url, s);
306 if (url.scheme == U_POP || url.scheme == U_POPS) {
307 if (url.scheme == U_POPS) {
309 act->port = POP3S_PORT;
312 if (m_strisempty(url.path) && !mutt_account_fromurl(act, &url))
319 static pop_data_t *pop_find_conn(ACCOUNT *act)
321 pop_data_t *pd = NULL;
324 for (int i = 0; i < conns.len; i++) {
325 if (mutt_account_match(act, &conns.arr[i]->act)) {
326 pd = pop_data_dup(conns.arr[i]);
335 pd->w = el_job_start(&pop_machine, pd);
337 while (pd->w && pd->state != POP_READY) {
341 pop_data_delete(&pd);
347 static int pop_open_mailbox(CONTEXT *ctx)
352 pop_data_t *pd = NULL;
354 if (pop_parse_path(ctx->path, &act)) {
355 mutt_error(_("%s is an invalid POP path"), ctx->path);
361 mutt_account_tourl(&act, &url);
363 pd = pop_find_conn(&act);
366 url_ciss_tostring(&url, buf, sizeof (buf), 0);
367 m_strreplace(&ctx->path, buf);
372 mx_t const pop_mx_ng = {
374 .mx_open_mailbox = pop_open_mailbox,