2 * Copyright (C) 2000-2003 Vsevolod Volkov <vvv@mutt.org.ua>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
28 # include "mutt_ssl.h"
36 /* given an POP mailbox name, return host, port, username and password */
37 int pop_parse_path (const char* path, ACCOUNT* acct)
41 struct servent *service;
45 acct->type = M_ACCT_TYPE_POP;
48 c = safe_strdup (path);
49 url_parse_ciss (&url, c);
51 if ((url.scheme != U_POP && url.scheme != U_POPS) ||
52 mutt_account_fromurl (acct, &url) < 0)
55 mutt_error(_("Invalid POP URL: %s\n"), path);
60 if (url.scheme == U_POPS)
61 acct->flags |= M_ACCT_SSL;
63 service = getservbyname (url.scheme == U_POP ? "pop3" : "pop3s", "tcp");
66 acct->port = ntohs (service->s_port);
68 acct->port = url.scheme == U_POP ? POP_PORT : POP_SSL_PORT;;
75 /* Copy error message to err_msg buffer */
76 void pop_error (POP_DATA *pop_data, char *msg)
80 t = strchr (pop_data->err_msg, '\0');
83 if (!mutt_strncmp (msg, "-ERR ", 5))
92 strfcpy (t, c, sizeof (pop_data->err_msg) - strlen (pop_data->err_msg));
93 mutt_remove_trailing_ws (pop_data->err_msg);
96 /* Parse CAPA output */
97 static int fetch_capa (char *line, void *data)
99 POP_DATA *pop_data = (POP_DATA *)data;
102 if (!ascii_strncasecmp (line, "SASL", 4))
104 FREE (&pop_data->auth_list);
107 pop_data->auth_list = safe_strdup (c);
110 else if (!ascii_strncasecmp (line, "STLS", 4))
111 pop_data->cmd_stls = 1;
113 else if (!ascii_strncasecmp (line, "USER", 4))
114 pop_data->cmd_user = 1;
116 else if (!ascii_strncasecmp (line, "UIDL", 4))
117 pop_data->cmd_uidl = 1;
119 else if (!ascii_strncasecmp (line, "TOP", 3))
120 pop_data->cmd_top = 1;
125 /* Fetch list of the authentication mechanisms */
126 static int fetch_auth (char *line, void *data)
128 POP_DATA *pop_data = (POP_DATA *)data;
130 if (!pop_data->auth_list)
132 pop_data->auth_list = safe_malloc (strlen (line) + 1);
133 *pop_data->auth_list = '\0';
137 safe_realloc (&pop_data->auth_list,
138 strlen (pop_data->auth_list) + strlen (line) + 2);
139 strcat (pop_data->auth_list, " "); /* __STRCAT_CHECKED__ */
141 strcat (pop_data->auth_list, line); /* __STRCAT_CHECKED__ */
149 * -1 - conection lost,
150 * -2 - execution error.
152 static int pop_capabilities (POP_DATA *pop_data, int mode)
154 char buf[LONG_STRING];
156 /* don't check capabilities on reconnect */
157 if (pop_data->capabilities)
160 /* init capabilities */
163 pop_data->cmd_capa = 0;
164 pop_data->cmd_stls = 0;
165 pop_data->cmd_user = 0;
166 pop_data->cmd_uidl = 0;
167 pop_data->cmd_top = 0;
168 pop_data->resp_codes = 0;
169 pop_data->expire = 1;
170 pop_data->login_delay = 0;
171 FREE (&pop_data->auth_list);
174 /* Execute CAPA command */
175 if (mode == 0 || pop_data->cmd_capa)
177 strfcpy (buf, "CAPA\r\n", sizeof (buf));
178 switch (pop_fetch_data (pop_data, buf, NULL, fetch_capa, pop_data))
182 pop_data->cmd_capa = 1;
190 /* CAPA not supported, use defaults */
191 if (mode == 0 && !pop_data->cmd_capa)
193 pop_data->cmd_user = 2;
194 pop_data->cmd_uidl = 2;
195 pop_data->cmd_top = 2;
197 strfcpy (buf, "AUTH\r\n", sizeof (buf));
198 if (pop_fetch_data (pop_data, buf, NULL, fetch_auth, pop_data) == -1)
202 /* Check capabilities */
207 if (!pop_data->expire)
208 msg = _("Unable to leave messages on server.");
209 if (!pop_data->cmd_top)
210 msg = _("Command TOP is not supported by server.");
211 if (!pop_data->cmd_uidl)
212 msg = _("Command UIDL is not supported by server.");
213 if (msg && pop_data->cmd_capa)
218 pop_data->capabilities = 1;
227 * -1 - conection lost,
228 * -2 - invalid response.
230 int pop_connect (POP_DATA *pop_data)
232 char buf[LONG_STRING];
234 pop_data->status = POP_NONE;
235 if (mutt_socket_open (pop_data->conn) < 0 ||
236 mutt_socket_readln (buf, sizeof (buf), pop_data->conn) < 0)
238 mutt_error (_("Error connecting to server: %s"), pop_data->conn->account.host);
242 pop_data->status = POP_CONNECTED;
244 if (mutt_strncmp (buf, "+OK", 3))
246 *pop_data->err_msg = '\0';
247 pop_error (pop_data, buf);
248 mutt_error ("%s", pop_data->err_msg);
252 pop_apop_timestamp (pop_data, buf);
258 * Open connection and authenticate
260 * -1 - conection lost,
261 * -2 - invalid command or execution error,
262 * -3 - authentication canceled.
264 int pop_open_connection (POP_DATA *pop_data)
267 unsigned int n, size;
268 char buf[LONG_STRING];
270 ret = pop_connect (pop_data);
277 ret = pop_capabilities (pop_data, 0);
287 /* Attempt STLS if available and desired. */
288 if (!pop_data->conn->ssf && (pop_data->cmd_stls || option(OPTSSLFORCETLS)))
290 if (option(OPTSSLFORCETLS))
291 pop_data->use_stls = 2;
292 if (pop_data->use_stls == 0)
294 ret = query_quadoption (OPT_SSLSTARTTLS,
295 _("Secure connection with TLS?"));
298 pop_data->use_stls = 1;
300 pop_data->use_stls = 2;
302 if (pop_data->use_stls == 2)
304 strfcpy (buf, "STLS\r\n", sizeof (buf));
305 ret = pop_query (pop_data, buf, sizeof (buf));
310 mutt_error ("%s", pop_data->err_msg);
313 else if (mutt_ssl_starttls (pop_data->conn))
315 mutt_error (_("Could not negotiate TLS connection"));
321 /* recheck capabilities after STLS completes */
322 ret = pop_capabilities (pop_data, 1);
334 if (option(OPTSSLFORCETLS) && !pop_data->conn->ssf)
336 mutt_error _("Encrypted connection unavailable");
342 ret = pop_authenticate (pop_data);
350 /* recheck capabilities after authentication */
351 ret = pop_capabilities (pop_data, 2);
360 /* get total size of mailbox */
361 strfcpy (buf, "STAT\r\n", sizeof (buf));
362 ret = pop_query (pop_data, buf, sizeof (buf));
367 mutt_error ("%s", pop_data->err_msg);
372 sscanf (buf, "+OK %u %u", &n, &size);
373 pop_data->size = size;
377 pop_data->status = POP_DISCONNECTED;
378 mutt_error _("Server closed connection!");
383 /* logout from POP server */
384 void pop_logout (CONTEXT *ctx)
387 char buf[LONG_STRING];
388 POP_DATA *pop_data = (POP_DATA *)ctx->data;
390 if (pop_data->status == POP_CONNECTED)
392 mutt_message _("Closing connection to POP server...");
396 strfcpy (buf, "RSET\r\n", sizeof (buf));
397 ret = pop_query (pop_data, buf, sizeof (buf));
402 strfcpy (buf, "QUIT\r\n", sizeof (buf));
403 pop_query (pop_data, buf, sizeof (buf));
409 pop_data->status = POP_DISCONNECTED;
414 * Send data from buffer and receive answer to the same buffer
416 * -1 - conection lost,
417 * -2 - invalid command or execution error.
419 int pop_query_d (POP_DATA *pop_data, char *buf, size_t buflen, char *msg)
421 int dbg = M_SOCK_LOG_CMD;
424 if (pop_data->status != POP_CONNECTED)
428 /* print msg instaed of real command */
431 dbg = M_SOCK_LOG_FULL;
432 dprint (M_SOCK_LOG_CMD, (debugfile, "> %s", msg));
436 mutt_socket_write_d (pop_data->conn, buf, -1, dbg);
438 c = strpbrk (buf, " \r\n");
440 snprintf (pop_data->err_msg, sizeof (pop_data->err_msg), "%s: ", buf);
442 if (mutt_socket_readln (buf, buflen, pop_data->conn) < 0)
444 pop_data->status = POP_DISCONNECTED;
447 if (!mutt_strncmp (buf, "+OK", 3))
450 pop_error (pop_data, buf);
455 * This function calls funct(*line, *data) for each received line,
456 * funct(NULL, *data) if rewind(*data) needs, exits when fail or done.
459 * -1 - conection lost,
460 * -2 - invalid command or execution error,
461 * -3 - error in funct(*line, *data)
463 int pop_fetch_data (POP_DATA *pop_data, char *query, progress_t *progressbar,
464 int (*funct) (char *, void *), void *data)
466 char buf[LONG_STRING];
473 strfcpy (buf, query, sizeof (buf));
474 ret = pop_query (pop_data, buf, sizeof (buf));
478 inbuf = safe_malloc (sizeof (buf));
482 chunk = mutt_socket_readln_d (buf, sizeof (buf), pop_data->conn, M_SOCK_LOG_HDR);
485 pop_data->status = POP_DISCONNECTED;
491 if (!lenbuf && buf[0] == '.')
498 strfcpy (inbuf + lenbuf, p, sizeof (buf));
501 /* cast is safe since we break out of the loop when chunk<=0 */
502 if ((size_t)chunk >= sizeof (buf))
504 lenbuf += strlen (p);
509 mutt_progress_update (progressbar, pos, -1);
510 if (ret == 0 && funct (inbuf, data) < 0)
515 safe_realloc (&inbuf, lenbuf + sizeof (buf));
522 /* find message with this UIDL and set refno */
523 static int check_uidl (char *line, void *data)
527 CONTEXT *ctx = (CONTEXT *)data;
529 sscanf (line, "%u %s", &index, line);
530 for (i = 0; i < ctx->msgcount; i++)
532 if (!mutt_strcmp (ctx->hdrs[i]->data, line))
534 ctx->hdrs[i]->refno = index;
542 /* reconnect and verify idnexes if connection was lost */
543 int pop_reconnect (CONTEXT *ctx)
546 POP_DATA *pop_data = (POP_DATA *)ctx->data;
547 progress_t progressbar;
549 if (pop_data->status == POP_CONNECTED)
551 if (pop_data->status == POP_BYE)
556 mutt_socket_close (pop_data->conn);
558 ret = pop_open_connection (pop_data);
563 mutt_progress_init (&progressbar, _("Verifying message indexes..."),
564 M_PROGRESS_SIZE, NetInc, 0);
566 for (i = 0; i < ctx->msgcount; i++)
567 ctx->hdrs[i]->refno = -1;
569 ret = pop_fetch_data (pop_data, "UIDL\r\n", &progressbar, check_uidl, ctx);
572 mutt_error ("%s", pop_data->err_msg);
584 if (query_quadoption (OPT_POPRECONNECT,
585 _("Connection lost. Reconnect to POP server?")) != M_YES)