2 * Copyright notice from original mutt:
3 * Copyright (C) 2000-2003 Vsevolod Volkov <vvv@mutt.org.ua>
5 * This file is part of mutt-ng, see http://www.muttng.org/.
6 * It's licensed under the GNU General Public License,
7 * please see the file GPL in the top level source directory.
10 #include <lib-lib/lib-lib.h>
11 #include <lib-mx/mx.h>
16 /* given an POP mailbox name, return host, port, username and password */
17 int pop_parse_path (const char *path, ACCOUNT * act)
26 act->type = M_ACCT_TYPE_POP;
29 url_parse_ciss (&url, c);
31 if (url.scheme == U_POP || url.scheme == U_POPS) {
32 if (url.scheme == U_POPS) {
33 act->flags |= M_ACCT_SSL;
34 act->port = POP_SSL_PORT;
37 if ((!url.path || !*url.path) && mutt_account_fromurl (act, &url) == 0)
45 /* Copy error message to err_msg buffer */
46 void pop_error (POP_DATA * pop_data, char *msg)
50 t = strchr (pop_data->err_msg, '\0');
53 if (!m_strncmp(msg, "-ERR ", 5)) {
54 c2 = vskipspaces(msg + 5);
59 m_strcpy(t, sizeof(pop_data->err_msg) - strlen(pop_data->err_msg), c);
60 m_strrtrim(pop_data->err_msg);
63 /* Parse CAPA output */
64 static int fetch_capa (char *line, void *data)
66 POP_DATA *pop_data = (POP_DATA *) data;
69 if (!ascii_strncasecmp (line, "SASL", 4)) {
70 p_delete(&pop_data->auth_list);
71 c = vskipspaces(line + 4);
72 pop_data->auth_list = m_strdup(c);
75 else if (!ascii_strncasecmp (line, "STLS", 4))
76 pop_data->cmd_stls = CMD_AVAILABLE;
78 else if (!ascii_strncasecmp (line, "USER", 4))
79 pop_data->cmd_user = CMD_AVAILABLE;
81 else if (!ascii_strncasecmp (line, "UIDL", 4))
82 pop_data->cmd_uidl = CMD_AVAILABLE;
84 else if (!ascii_strncasecmp (line, "TOP", 3))
85 pop_data->cmd_top = CMD_AVAILABLE;
90 /* Fetch list of the authentication mechanisms */
91 static int fetch_auth (char *line, void *data)
93 POP_DATA *pop_data = (POP_DATA *) data;
94 ssize_t auth_list_len;
96 if (!pop_data->auth_list) {
97 auth_list_len = m_strlen(line) + 1;
98 pop_data->auth_list = p_new(char, auth_list_len);
100 auth_list_len = m_strlen(pop_data->auth_list) + m_strlen(line) + 2;
101 p_realloc(&pop_data->auth_list, auth_list_len);
102 m_strcat(pop_data->auth_list, auth_list_len, " ");
104 m_strcat(pop_data->auth_list, auth_list_len, line);
112 * -1 - conection lost,
113 * -2 - execution error.
115 static pop_query_status pop_capabilities (POP_DATA * pop_data, int mode)
117 char buf[LONG_STRING];
119 /* don't check capabilities on reconnect */
120 if (pop_data->capabilities)
123 /* init capabilities */
125 pop_data->cmd_capa = CMD_NOT_AVAILABLE;
126 pop_data->cmd_stls = CMD_NOT_AVAILABLE;
127 pop_data->cmd_user = CMD_NOT_AVAILABLE;
128 pop_data->cmd_uidl = CMD_NOT_AVAILABLE;
129 pop_data->cmd_top = CMD_NOT_AVAILABLE;
130 pop_data->resp_codes = 0;
131 pop_data->expire = 1;
132 pop_data->login_delay = 0;
133 p_delete(&pop_data->auth_list);
136 /* Execute CAPA command */
137 if (mode == 0 || pop_data->cmd_capa != CMD_NOT_AVAILABLE) {
138 m_strcpy(buf, sizeof(buf), "CAPA\r\n");
139 switch (pop_fetch_data (pop_data, buf, NULL, fetch_capa, pop_data)) {
142 pop_data->cmd_capa = CMD_AVAILABLE;
145 case PFD_FUNCT_ERROR:
148 pop_data->cmd_capa = CMD_NOT_AVAILABLE;
151 case PQ_NOT_CONNECTED:
152 return PQ_NOT_CONNECTED;
156 /* CAPA not supported, use defaults */
157 if (mode == 0 && pop_data->cmd_capa == CMD_NOT_AVAILABLE) {
158 pop_data->cmd_user = CMD_UNKNOWN;
159 pop_data->cmd_uidl = CMD_UNKNOWN;
160 pop_data->cmd_top = CMD_UNKNOWN;
162 m_strcpy(buf, sizeof(buf), "AUTH\r\n");
163 if (pop_fetch_data (pop_data, buf, NULL, fetch_auth, pop_data) == PQ_NOT_CONNECTED)
164 return PQ_NOT_CONNECTED;
167 /* Check capabilities */
171 if (!pop_data->expire)
172 msg = _("Unable to leave messages on server.");
173 if (pop_data->cmd_top == CMD_NOT_AVAILABLE)
174 msg = _("Command TOP is not supported by server.");
175 if (pop_data->cmd_uidl == CMD_NOT_AVAILABLE)
176 msg = _("Command UIDL is not supported by server.");
177 if (msg && pop_data->cmd_capa != CMD_AVAILABLE) {
181 pop_data->capabilities = 1;
190 * -1 - conection lost,
191 * -2 - invalid response.
193 pop_query_status pop_connect (POP_DATA * pop_data)
195 char buf[LONG_STRING];
197 pop_data->status = POP_NONE;
198 if (mutt_socket_open (pop_data->conn) < 0 ||
199 mutt_socket_readln (buf, sizeof (buf), pop_data->conn) < 0) {
200 mutt_error (_("Error connecting to server: %s"),
201 pop_data->conn->account.host);
202 return PQ_NOT_CONNECTED;
205 pop_data->status = POP_CONNECTED;
207 if (m_strncmp(buf, "+OK", 3)) {
208 *pop_data->err_msg = '\0';
209 pop_error (pop_data, buf);
210 mutt_error ("%s", pop_data->err_msg);
214 pop_apop_timestamp (pop_data, buf);
220 * Open connection and authenticate
222 * -1 - conection lost,
223 * -2 - invalid command or execution error,
224 * -3 - authentication canceled.
226 pop_query_status pop_open_connection (POP_DATA * pop_data)
228 pop_query_status ret;
229 unsigned int n, size;
230 char buf[LONG_STRING];
232 ret = pop_connect (pop_data);
238 ret = pop_capabilities (pop_data, 0);
239 if (ret == PQ_NOT_CONNECTED)
246 /* Attempt STLS if available and desired. */
247 if (!pop_data->conn->ssf && (pop_data->cmd_stls || mod_ssl.force_tls)) {
248 if (mod_ssl.force_tls)
249 pop_data->use_stls = 2;
250 if (pop_data->use_stls == 0) {
251 pop_data->use_stls = 1;
252 if (mod_ssl.starttls)
253 pop_data->use_stls = 2;
255 if (pop_data->use_stls == 2) {
256 m_strcpy(buf, sizeof(buf), "STLS\r\n");
257 ret = pop_query (pop_data, buf, sizeof (buf));
258 if (ret == PQ_NOT_CONNECTED)
261 mutt_error ("%s", pop_data->err_msg);
264 else if (mutt_ssl_starttls (pop_data->conn))
266 mutt_error (_("Could not negotiate TLS connection"));
271 /* recheck capabilities after STLS completes */
272 ret = pop_capabilities (pop_data, 1);
273 if (ret == PQ_NOT_CONNECTED)
283 if (mod_ssl.force_tls && !pop_data->conn->ssf) {
284 mutt_error _("Encrypted connection unavailable");
289 ret = pop_authenticate (pop_data);
290 if (ret == PQ_NOT_CONNECTED)
292 if (ret == PFD_FUNCT_ERROR)
297 /* recheck capabilities after authentication */
298 ret = pop_capabilities (pop_data, 2);
299 if (ret == PQ_NOT_CONNECTED)
306 /* get total size of mailbox */
307 m_strcpy(buf, sizeof(buf), "STAT\r\n");
308 ret = pop_query (pop_data, buf, sizeof (buf));
309 if (ret == PQ_NOT_CONNECTED)
312 mutt_error ("%s", pop_data->err_msg);
317 sscanf (buf, "+OK %u %u", &n, &size);
318 pop_data->size = size;
322 pop_data->status = POP_DISCONNECTED;
323 mutt_error _("Server closed connection!");
326 return PQ_NOT_CONNECTED;
329 /* logout from POP server */
330 void pop_logout (CONTEXT * ctx)
332 pop_query_status ret = 0;
333 char buf[LONG_STRING];
334 POP_DATA *pop_data = (POP_DATA *) ctx->data;
336 if (pop_data->status == POP_CONNECTED) {
337 mutt_message _("Closing connection to POP server...");
340 m_strcpy(buf, sizeof(buf), "RSET\r\n");
341 ret = pop_query (pop_data, buf, sizeof (buf));
344 if (ret != PQ_NOT_CONNECTED) {
345 m_strcpy(buf, sizeof(buf), "QUIT\r\n");
346 pop_query (pop_data, buf, sizeof (buf));
352 pop_data->status = POP_DISCONNECTED;
357 * Send data from buffer and receive answer to the same buffer
359 * -1 - conection lost,
360 * -2 - invalid command or execution error.
362 pop_query_status pop_query (POP_DATA * pop_data, char *buf, size_t buflen)
366 if (pop_data->status != POP_CONNECTED)
367 return PQ_NOT_CONNECTED;
369 mutt_socket_write(pop_data->conn, buf);
371 c = strpbrk (buf, " \r\n");
373 snprintf (pop_data->err_msg, sizeof (pop_data->err_msg), "%s: ", buf);
375 if (mutt_socket_readln (buf, buflen, pop_data->conn) < 0) {
376 pop_data->status = POP_DISCONNECTED;
377 return PQ_NOT_CONNECTED;
379 if (!m_strncmp(buf, "+OK", 3))
382 pop_error (pop_data, buf);
387 * This function calls funct(*line, *data) for each received line,
388 * funct(NULL, *data) if rewind(*data) needs, exits when fail or done.
391 * -1 - conection lost,
392 * -2 - invalid command or execution error,
393 * -3 - error in funct(*line, *data)
395 pop_query_status pop_fetch_data (POP_DATA * pop_data, const char *query, progress_t* bar,
396 int (*funct) (char *, void *), void *data)
398 char buf[LONG_STRING];
401 pop_query_status ret;
406 m_strcpy(buf, sizeof(buf), query);
407 ret = pop_query (pop_data, buf, sizeof (buf));
411 inbuf = p_new(char, sizeof(buf));
415 mutt_socket_readln(buf, sizeof (buf), pop_data->conn);
417 pop_data->status = POP_DISCONNECTED;
418 ret = PQ_NOT_CONNECTED;
423 if (!lenbuf && buf[0] == '.') {
429 m_strcpy(inbuf + lenbuf,sizeof(buf), p);
432 if (chunk >= ssizeof(buf)) {
433 lenbuf += strlen (p);
436 mutt_progress_bar (bar, pos);
437 if (ret == 0 && funct (inbuf, data) < 0)
438 ret = PFD_FUNCT_ERROR;
442 p_realloc(&inbuf, lenbuf + sizeof(buf));
449 /* find message with this UIDL and set refno */
450 static int check_uidl (char *line, void *data)
453 CONTEXT *ctx = (CONTEXT *)data;
455 sscanf (line, "%u %s", &idx, line);
456 for (i = 0; i < ctx->msgcount; i++) {
457 if (!m_strcmp(ctx->hdrs[i]->data, line)) {
458 ctx->hdrs[i]->refno = idx;
466 /* reconnect and verify indexes if connection was lost */
467 pop_query_status pop_reconnect (CONTEXT * ctx)
469 pop_query_status ret;
470 POP_DATA *pop_data = (POP_DATA *) ctx->data;
473 if (pop_data->status == POP_CONNECTED)
475 if (pop_data->status == POP_BYE)
476 return PQ_NOT_CONNECTED;
479 mutt_socket_close (pop_data->conn);
481 ret = pop_open_connection (pop_data);
485 bar.msg = _("Verifying message indexes...");
487 mutt_progress_bar (&bar, 0);
489 for (i = 0; i < ctx->msgcount; i++)
490 ctx->hdrs[i]->refno = -1;
492 ret = pop_fetch_data(pop_data, "UIDL\r\n", &bar, check_uidl, ctx);
494 mutt_error ("%s", pop_data->err_msg);
504 return PQ_NOT_CONNECTED;
506 if (query_quadoption (OPT_POPRECONNECT,
507 _("Connection lost. Reconnect to POP server?")) !=
509 return PQ_NOT_CONNECTED;