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>
15 #if defined (USE_SSL) || defined (USE_GNUTLS)
16 # include <lib-sys/mutt_ssl.h>
19 /* given an POP mailbox name, return host, port, username and password */
20 int pop_parse_path (const char *path, ACCOUNT * act)
29 act->type = M_ACCT_TYPE_POP;
32 url_parse_ciss (&url, c);
34 if (url.scheme == U_POP || url.scheme == U_POPS) {
35 if (url.scheme == U_POPS) {
36 act->flags |= M_ACCT_SSL;
37 act->port = POP_SSL_PORT;
40 if ((!url.path || !*url.path) && mutt_account_fromurl (act, &url) == 0)
48 /* Copy error message to err_msg buffer */
49 void pop_error (POP_DATA * pop_data, char *msg)
53 t = strchr (pop_data->err_msg, '\0');
56 if (!m_strncmp(msg, "-ERR ", 5)) {
57 c2 = vskipspaces(msg + 5);
62 m_strcpy(t, sizeof(pop_data->err_msg) - strlen(pop_data->err_msg), c);
63 m_strrtrim(pop_data->err_msg);
66 /* Parse CAPA output */
67 static int fetch_capa (char *line, void *data)
69 POP_DATA *pop_data = (POP_DATA *) data;
72 if (!ascii_strncasecmp (line, "SASL", 4)) {
73 p_delete(&pop_data->auth_list);
74 c = vskipspaces(line + 4);
75 pop_data->auth_list = m_strdup(c);
78 else if (!ascii_strncasecmp (line, "STLS", 4))
79 pop_data->cmd_stls = CMD_AVAILABLE;
81 else if (!ascii_strncasecmp (line, "USER", 4))
82 pop_data->cmd_user = CMD_AVAILABLE;
84 else if (!ascii_strncasecmp (line, "UIDL", 4))
85 pop_data->cmd_uidl = CMD_AVAILABLE;
87 else if (!ascii_strncasecmp (line, "TOP", 3))
88 pop_data->cmd_top = CMD_AVAILABLE;
93 /* Fetch list of the authentication mechanisms */
94 static int fetch_auth (char *line, void *data)
96 POP_DATA *pop_data = (POP_DATA *) data;
97 ssize_t auth_list_len;
99 if (!pop_data->auth_list) {
100 auth_list_len = m_strlen(line) + 1;
101 pop_data->auth_list = p_new(char, auth_list_len);
103 auth_list_len = m_strlen(pop_data->auth_list) + m_strlen(line) + 2;
104 p_realloc(&pop_data->auth_list, auth_list_len);
105 m_strcat(pop_data->auth_list, auth_list_len, " ");
107 m_strcat(pop_data->auth_list, auth_list_len, line);
115 * -1 - conection lost,
116 * -2 - execution error.
118 static pop_query_status pop_capabilities (POP_DATA * pop_data, int mode)
120 char buf[LONG_STRING];
122 /* don't check capabilities on reconnect */
123 if (pop_data->capabilities)
126 /* init capabilities */
128 pop_data->cmd_capa = CMD_NOT_AVAILABLE;
129 pop_data->cmd_stls = CMD_NOT_AVAILABLE;
130 pop_data->cmd_user = CMD_NOT_AVAILABLE;
131 pop_data->cmd_uidl = CMD_NOT_AVAILABLE;
132 pop_data->cmd_top = CMD_NOT_AVAILABLE;
133 pop_data->resp_codes = 0;
134 pop_data->expire = 1;
135 pop_data->login_delay = 0;
136 p_delete(&pop_data->auth_list);
139 /* Execute CAPA command */
140 if (mode == 0 || pop_data->cmd_capa != CMD_NOT_AVAILABLE) {
141 m_strcpy(buf, sizeof(buf), "CAPA\r\n");
142 switch (pop_fetch_data (pop_data, buf, NULL, fetch_capa, pop_data)) {
145 pop_data->cmd_capa = CMD_AVAILABLE;
148 case PFD_FUNCT_ERROR:
151 pop_data->cmd_capa = CMD_NOT_AVAILABLE;
154 case PQ_NOT_CONNECTED:
155 return PQ_NOT_CONNECTED;
159 /* CAPA not supported, use defaults */
160 if (mode == 0 && pop_data->cmd_capa == CMD_NOT_AVAILABLE) {
161 pop_data->cmd_user = CMD_UNKNOWN;
162 pop_data->cmd_uidl = CMD_UNKNOWN;
163 pop_data->cmd_top = CMD_UNKNOWN;
165 m_strcpy(buf, sizeof(buf), "AUTH\r\n");
166 if (pop_fetch_data (pop_data, buf, NULL, fetch_auth, pop_data) == PQ_NOT_CONNECTED)
167 return PQ_NOT_CONNECTED;
170 /* Check capabilities */
174 if (!pop_data->expire)
175 msg = _("Unable to leave messages on server.");
176 if (pop_data->cmd_top == CMD_NOT_AVAILABLE)
177 msg = _("Command TOP is not supported by server.");
178 if (pop_data->cmd_uidl == CMD_NOT_AVAILABLE)
179 msg = _("Command UIDL is not supported by server.");
180 if (msg && pop_data->cmd_capa != CMD_AVAILABLE) {
184 pop_data->capabilities = 1;
193 * -1 - conection lost,
194 * -2 - invalid response.
196 pop_query_status pop_connect (POP_DATA * pop_data)
198 char buf[LONG_STRING];
200 pop_data->status = POP_NONE;
201 if (mutt_socket_open (pop_data->conn) < 0 ||
202 mutt_socket_readln (buf, sizeof (buf), pop_data->conn) < 0) {
203 mutt_error (_("Error connecting to server: %s"),
204 pop_data->conn->account.host);
205 return PQ_NOT_CONNECTED;
208 pop_data->status = POP_CONNECTED;
210 if (m_strncmp(buf, "+OK", 3)) {
211 *pop_data->err_msg = '\0';
212 pop_error (pop_data, buf);
213 mutt_error ("%s", pop_data->err_msg);
217 pop_apop_timestamp (pop_data, buf);
223 * Open connection and authenticate
225 * -1 - conection lost,
226 * -2 - invalid command or execution error,
227 * -3 - authentication canceled.
229 pop_query_status pop_open_connection (POP_DATA * pop_data)
231 pop_query_status ret;
232 unsigned int n, size;
233 char buf[LONG_STRING];
235 ret = pop_connect (pop_data);
241 ret = pop_capabilities (pop_data, 0);
242 if (ret == PQ_NOT_CONNECTED)
249 #if (defined(USE_SSL) || defined(USE_GNUTLS))
250 /* Attempt STLS if available and desired. */
251 if (!pop_data->conn->ssf && (pop_data->cmd_stls || option(OPTSSLFORCETLS))) {
252 if (option (OPTSSLFORCETLS))
253 pop_data->use_stls = 2;
254 if (pop_data->use_stls == 0) {
255 ret = query_quadoption (OPT_SSLSTARTTLS,
256 _("Secure connection with TLS?"));
259 pop_data->use_stls = 1;
261 pop_data->use_stls = 2;
263 if (pop_data->use_stls == 2) {
264 m_strcpy(buf, sizeof(buf), "STLS\r\n");
265 ret = pop_query (pop_data, buf, sizeof (buf));
266 if (ret == PQ_NOT_CONNECTED)
269 mutt_error ("%s", pop_data->err_msg);
272 #if defined (USE_SSL) || defined (USE_GNUTLS)
273 else if (mutt_ssl_starttls (pop_data->conn))
276 mutt_error (_("Could not negotiate TLS connection"));
281 /* recheck capabilities after STLS completes */
282 ret = pop_capabilities (pop_data, 1);
283 if (ret == PQ_NOT_CONNECTED)
293 if (option(OPTSSLFORCETLS) && !pop_data->conn->ssf) {
294 mutt_error _("Encrypted connection unavailable");
300 ret = pop_authenticate (pop_data);
301 if (ret == PQ_NOT_CONNECTED)
303 if (ret == PFD_FUNCT_ERROR)
308 /* recheck capabilities after authentication */
309 ret = pop_capabilities (pop_data, 2);
310 if (ret == PQ_NOT_CONNECTED)
317 /* get total size of mailbox */
318 m_strcpy(buf, sizeof(buf), "STAT\r\n");
319 ret = pop_query (pop_data, buf, sizeof (buf));
320 if (ret == PQ_NOT_CONNECTED)
323 mutt_error ("%s", pop_data->err_msg);
328 sscanf (buf, "+OK %u %u", &n, &size);
329 pop_data->size = size;
333 pop_data->status = POP_DISCONNECTED;
334 mutt_error _("Server closed connection!");
337 return PQ_NOT_CONNECTED;
340 /* logout from POP server */
341 void pop_logout (CONTEXT * ctx)
343 pop_query_status ret = 0;
344 char buf[LONG_STRING];
345 POP_DATA *pop_data = (POP_DATA *) ctx->data;
347 if (pop_data->status == POP_CONNECTED) {
348 mutt_message _("Closing connection to POP server...");
351 m_strcpy(buf, sizeof(buf), "RSET\r\n");
352 ret = pop_query (pop_data, buf, sizeof (buf));
355 if (ret != PQ_NOT_CONNECTED) {
356 m_strcpy(buf, sizeof(buf), "QUIT\r\n");
357 pop_query (pop_data, buf, sizeof (buf));
363 pop_data->status = POP_DISCONNECTED;
368 * Send data from buffer and receive answer to the same buffer
370 * -1 - conection lost,
371 * -2 - invalid command or execution error.
373 pop_query_status pop_query (POP_DATA * pop_data, char *buf, size_t buflen)
377 if (pop_data->status != POP_CONNECTED)
378 return PQ_NOT_CONNECTED;
380 mutt_socket_write(pop_data->conn, buf);
382 c = strpbrk (buf, " \r\n");
384 snprintf (pop_data->err_msg, sizeof (pop_data->err_msg), "%s: ", buf);
386 if (mutt_socket_readln (buf, buflen, pop_data->conn) < 0) {
387 pop_data->status = POP_DISCONNECTED;
388 return PQ_NOT_CONNECTED;
390 if (!m_strncmp(buf, "+OK", 3))
393 pop_error (pop_data, buf);
398 * This function calls funct(*line, *data) for each received line,
399 * funct(NULL, *data) if rewind(*data) needs, exits when fail or done.
402 * -1 - conection lost,
403 * -2 - invalid command or execution error,
404 * -3 - error in funct(*line, *data)
406 pop_query_status pop_fetch_data (POP_DATA * pop_data, const char *query, progress_t* bar,
407 int (*funct) (char *, void *), void *data)
409 char buf[LONG_STRING];
412 pop_query_status ret;
417 m_strcpy(buf, sizeof(buf), query);
418 ret = pop_query (pop_data, buf, sizeof (buf));
422 inbuf = p_new(char, sizeof(buf));
426 mutt_socket_readln(buf, sizeof (buf), pop_data->conn);
428 pop_data->status = POP_DISCONNECTED;
429 ret = PQ_NOT_CONNECTED;
434 if (!lenbuf && buf[0] == '.') {
440 m_strcpy(inbuf + lenbuf,sizeof(buf), p);
443 if (chunk >= ssizeof(buf)) {
444 lenbuf += strlen (p);
447 mutt_progress_bar (bar, pos);
448 if (ret == 0 && funct (inbuf, data) < 0)
449 ret = PFD_FUNCT_ERROR;
453 p_realloc(&inbuf, lenbuf + sizeof(buf));
460 /* find message with this UIDL and set refno */
461 static int check_uidl (char *line, void *data)
464 CONTEXT *ctx = (CONTEXT *)data;
466 sscanf (line, "%u %s", &idx, line);
467 for (i = 0; i < ctx->msgcount; i++) {
468 if (!m_strcmp(ctx->hdrs[i]->data, line)) {
469 ctx->hdrs[i]->refno = idx;
477 /* reconnect and verify indexes if connection was lost */
478 pop_query_status pop_reconnect (CONTEXT * ctx)
480 pop_query_status ret;
481 POP_DATA *pop_data = (POP_DATA *) ctx->data;
484 if (pop_data->status == POP_CONNECTED)
486 if (pop_data->status == POP_BYE)
487 return PQ_NOT_CONNECTED;
490 mutt_socket_close (pop_data->conn);
492 ret = pop_open_connection (pop_data);
496 bar.msg = _("Verifying message indexes...");
498 mutt_progress_bar (&bar, 0);
500 for (i = 0; i < ctx->msgcount; i++)
501 ctx->hdrs[i]->refno = -1;
503 ret = pop_fetch_data(pop_data, "UIDL\r\n", &bar, check_uidl, ctx);
505 mutt_error ("%s", pop_data->err_msg);
515 return PQ_NOT_CONNECTED;
517 if (query_quadoption (OPT_POPRECONNECT,
518 _("Connection lost. Reconnect to POP server?")) !=
520 return PQ_NOT_CONNECTED;