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.
18 #include <lib-lib/lib-lib.h>
23 #if defined (USE_SSL) || defined (USE_GNUTLS)
24 # include <lib-sys/mutt_ssl.h>
27 /* given an POP mailbox name, return host, port, username and password */
28 int pop_parse_path (const char *path, ACCOUNT * act)
37 act->type = M_ACCT_TYPE_POP;
40 url_parse_ciss (&url, c);
42 if (url.scheme == U_POP || url.scheme == U_POPS) {
43 if (url.scheme == U_POPS) {
44 act->flags |= M_ACCT_SSL;
45 act->port = POP_SSL_PORT;
48 if ((!url.path || !*url.path) && mutt_account_fromurl (act, &url) == 0)
56 /* Copy error message to err_msg buffer */
57 void pop_error (POP_DATA * pop_data, char *msg)
61 t = strchr (pop_data->err_msg, '\0');
64 if (!m_strncmp(msg, "-ERR ", 5)) {
65 c2 = vskipspaces(msg + 5);
70 m_strcpy(t, sizeof(pop_data->err_msg) - strlen(pop_data->err_msg), c);
71 m_strrtrim(pop_data->err_msg);
74 /* Parse CAPA output */
75 static int fetch_capa (char *line, void *data)
77 POP_DATA *pop_data = (POP_DATA *) data;
80 if (!ascii_strncasecmp (line, "SASL", 4)) {
81 p_delete(&pop_data->auth_list);
82 c = vskipspaces(line + 4);
83 pop_data->auth_list = m_strdup(c);
86 else if (!ascii_strncasecmp (line, "STLS", 4))
87 pop_data->cmd_stls = CMD_AVAILABLE;
89 else if (!ascii_strncasecmp (line, "USER", 4))
90 pop_data->cmd_user = CMD_AVAILABLE;
92 else if (!ascii_strncasecmp (line, "UIDL", 4))
93 pop_data->cmd_uidl = CMD_AVAILABLE;
95 else if (!ascii_strncasecmp (line, "TOP", 3))
96 pop_data->cmd_top = CMD_AVAILABLE;
101 /* Fetch list of the authentication mechanisms */
102 static int fetch_auth (char *line, void *data)
104 POP_DATA *pop_data = (POP_DATA *) data;
106 if (!pop_data->auth_list) {
107 pop_data->auth_list = p_new(char, strlen(line) + 1);
109 p_realloc(&pop_data->auth_list,
110 strlen(pop_data->auth_list) + strlen(line) + 2);
111 strcat (pop_data->auth_list, " "); /* __STRCAT_CHECKED__ */
113 strcat (pop_data->auth_list, line); /* __STRCAT_CHECKED__ */
121 * -1 - conection lost,
122 * -2 - execution error.
124 static pop_query_status pop_capabilities (POP_DATA * pop_data, int mode)
126 char buf[LONG_STRING];
128 /* don't check capabilities on reconnect */
129 if (pop_data->capabilities)
132 /* init capabilities */
134 pop_data->cmd_capa = CMD_NOT_AVAILABLE;
135 pop_data->cmd_stls = CMD_NOT_AVAILABLE;
136 pop_data->cmd_user = CMD_NOT_AVAILABLE;
137 pop_data->cmd_uidl = CMD_NOT_AVAILABLE;
138 pop_data->cmd_top = CMD_NOT_AVAILABLE;
139 pop_data->resp_codes = 0;
140 pop_data->expire = 1;
141 pop_data->login_delay = 0;
142 p_delete(&pop_data->auth_list);
145 /* Execute CAPA command */
146 if (mode == 0 || pop_data->cmd_capa != CMD_NOT_AVAILABLE) {
147 m_strcpy(buf, sizeof(buf), "CAPA\r\n");
148 switch (pop_fetch_data (pop_data, buf, NULL, fetch_capa, pop_data)) {
151 pop_data->cmd_capa = CMD_AVAILABLE;
154 case PFD_FUNCT_ERROR:
157 pop_data->cmd_capa = CMD_NOT_AVAILABLE;
160 case PQ_NOT_CONNECTED:
161 return PQ_NOT_CONNECTED;
165 /* CAPA not supported, use defaults */
166 if (mode == 0 && pop_data->cmd_capa == CMD_NOT_AVAILABLE) {
167 pop_data->cmd_user = CMD_UNKNOWN;
168 pop_data->cmd_uidl = CMD_UNKNOWN;
169 pop_data->cmd_top = CMD_UNKNOWN;
171 m_strcpy(buf, sizeof(buf), "AUTH\r\n");
172 if (pop_fetch_data (pop_data, buf, NULL, fetch_auth, pop_data) == PQ_NOT_CONNECTED)
173 return PQ_NOT_CONNECTED;
176 /* Check capabilities */
180 if (!pop_data->expire)
181 msg = _("Unable to leave messages on server.");
182 if (pop_data->cmd_top == CMD_NOT_AVAILABLE)
183 msg = _("Command TOP is not supported by server.");
184 if (pop_data->cmd_uidl == CMD_NOT_AVAILABLE)
185 msg = _("Command UIDL is not supported by server.");
186 if (msg && pop_data->cmd_capa != CMD_AVAILABLE) {
190 pop_data->capabilities = 1;
199 * -1 - conection lost,
200 * -2 - invalid response.
202 pop_query_status pop_connect (POP_DATA * pop_data)
204 char buf[LONG_STRING];
206 pop_data->status = POP_NONE;
207 if (mutt_socket_open (pop_data->conn) < 0 ||
208 mutt_socket_readln (buf, sizeof (buf), pop_data->conn) < 0) {
209 mutt_error (_("Error connecting to server: %s"),
210 pop_data->conn->account.host);
211 return PQ_NOT_CONNECTED;
214 pop_data->status = POP_CONNECTED;
216 if (m_strncmp(buf, "+OK", 3)) {
217 *pop_data->err_msg = '\0';
218 pop_error (pop_data, buf);
219 mutt_error ("%s", pop_data->err_msg);
223 pop_apop_timestamp (pop_data, buf);
229 * Open connection and authenticate
231 * -1 - conection lost,
232 * -2 - invalid command or execution error,
233 * -3 - authentication canceled.
235 pop_query_status pop_open_connection (POP_DATA * pop_data)
237 pop_query_status ret;
238 unsigned int n, size;
239 char buf[LONG_STRING];
241 ret = pop_connect (pop_data);
247 ret = pop_capabilities (pop_data, 0);
248 if (ret == PQ_NOT_CONNECTED)
255 #if (defined(USE_SSL) || defined(USE_GNUTLS))
256 /* Attempt STLS if available and desired. */
257 if (!pop_data->conn->ssf && (pop_data->cmd_stls || option(OPTSSLFORCETLS))) {
258 if (option (OPTSSLFORCETLS))
259 pop_data->use_stls = 2;
260 if (pop_data->use_stls == 0) {
261 ret = query_quadoption (OPT_SSLSTARTTLS,
262 _("Secure connection with TLS?"));
265 pop_data->use_stls = 1;
267 pop_data->use_stls = 2;
269 if (pop_data->use_stls == 2) {
270 m_strcpy(buf, sizeof(buf), "STLS\r\n");
271 ret = pop_query (pop_data, buf, sizeof (buf));
272 if (ret == PQ_NOT_CONNECTED)
275 mutt_error ("%s", pop_data->err_msg);
278 #if defined (USE_SSL) || defined (USE_GNUTLS)
279 else if (mutt_ssl_starttls (pop_data->conn))
282 mutt_error (_("Could not negotiate TLS connection"));
287 /* recheck capabilities after STLS completes */
288 ret = pop_capabilities (pop_data, 1);
289 if (ret == PQ_NOT_CONNECTED)
299 if (option(OPTSSLFORCETLS) && !pop_data->conn->ssf) {
300 mutt_error _("Encrypted connection unavailable");
306 ret = pop_authenticate (pop_data);
307 if (ret == PQ_NOT_CONNECTED)
309 if (ret == PFD_FUNCT_ERROR)
314 /* recheck capabilities after authentication */
315 ret = pop_capabilities (pop_data, 2);
316 if (ret == PQ_NOT_CONNECTED)
323 /* get total size of mailbox */
324 m_strcpy(buf, sizeof(buf), "STAT\r\n");
325 ret = pop_query (pop_data, buf, sizeof (buf));
326 if (ret == PQ_NOT_CONNECTED)
329 mutt_error ("%s", pop_data->err_msg);
334 sscanf (buf, "+OK %u %u", &n, &size);
335 pop_data->size = size;
339 pop_data->status = POP_DISCONNECTED;
340 mutt_error _("Server closed connection!");
343 return PQ_NOT_CONNECTED;
346 /* logout from POP server */
347 void pop_logout (CONTEXT * ctx)
349 pop_query_status ret = 0;
350 char buf[LONG_STRING];
351 POP_DATA *pop_data = (POP_DATA *) ctx->data;
353 if (pop_data->status == POP_CONNECTED) {
354 mutt_message _("Closing connection to POP server...");
357 m_strcpy(buf, sizeof(buf), "RSET\r\n");
358 ret = pop_query (pop_data, buf, sizeof (buf));
361 if (ret != PQ_NOT_CONNECTED) {
362 m_strcpy(buf, sizeof(buf), "QUIT\r\n");
363 pop_query (pop_data, buf, sizeof (buf));
369 pop_data->status = POP_DISCONNECTED;
374 * Send data from buffer and receive answer to the same buffer
376 * -1 - conection lost,
377 * -2 - invalid command or execution error.
379 pop_query_status pop_query_d (POP_DATA * pop_data, char *buf, size_t buflen, const char *msg)
383 if (pop_data->status != POP_CONNECTED)
384 return PQ_NOT_CONNECTED;
386 mutt_socket_write(pop_data->conn, buf);
388 c = strpbrk (buf, " \r\n");
390 snprintf (pop_data->err_msg, sizeof (pop_data->err_msg), "%s: ", buf);
392 if (mutt_socket_readln (buf, buflen, pop_data->conn) < 0) {
393 pop_data->status = POP_DISCONNECTED;
394 return PQ_NOT_CONNECTED;
396 if (!m_strncmp(buf, "+OK", 3))
399 pop_error (pop_data, buf);
404 * This function calls funct(*line, *data) for each received line,
405 * funct(NULL, *data) if rewind(*data) needs, exits when fail or done.
408 * -1 - conection lost,
409 * -2 - invalid command or execution error,
410 * -3 - error in funct(*line, *data)
412 pop_query_status pop_fetch_data (POP_DATA * pop_data, const char *query, progress_t* bar,
413 int (*funct) (char *, void *), void *data)
415 char buf[LONG_STRING];
418 pop_query_status ret;
423 m_strcpy(buf, sizeof(buf), query);
424 ret = pop_query (pop_data, buf, sizeof (buf));
428 inbuf = p_new(char, sizeof(buf));
432 mutt_socket_readln(buf, sizeof (buf), pop_data->conn);
434 pop_data->status = POP_DISCONNECTED;
435 ret = PQ_NOT_CONNECTED;
440 if (!lenbuf && buf[0] == '.') {
446 m_strcpy(inbuf + lenbuf,sizeof(buf), p);
449 if (chunk >= ssizeof(buf)) {
450 lenbuf += strlen (p);
453 mutt_progress_bar (bar, pos);
454 if (ret == 0 && funct (inbuf, data) < 0)
455 ret = PFD_FUNCT_ERROR;
459 p_realloc(&inbuf, lenbuf + sizeof(buf));
466 /* find message with this UIDL and set refno */
467 static int check_uidl (char *line, void *data)
470 CONTEXT *ctx = (CONTEXT *)data;
472 sscanf (line, "%u %s", &idx, line);
473 for (i = 0; i < ctx->msgcount; i++) {
474 if (!m_strcmp(ctx->hdrs[i]->data, line)) {
475 ctx->hdrs[i]->refno = idx;
483 /* reconnect and verify indexes if connection was lost */
484 pop_query_status pop_reconnect (CONTEXT * ctx)
486 pop_query_status ret;
487 POP_DATA *pop_data = (POP_DATA *) ctx->data;
490 if (pop_data->status == POP_CONNECTED)
492 if (pop_data->status == POP_BYE)
493 return PQ_NOT_CONNECTED;
496 mutt_socket_close (pop_data->conn);
498 ret = pop_open_connection (pop_data);
502 bar.msg = _("Verifying message indexes...");
504 mutt_progress_bar (&bar, 0);
506 for (i = 0; i < ctx->msgcount; i++)
507 ctx->hdrs[i]->refno = -1;
509 ret = pop_fetch_data(pop_data, "UIDL\r\n", &bar, check_uidl, ctx);
511 mutt_error ("%s", pop_data->err_msg);
521 return PQ_NOT_CONNECTED;
523 if (query_quadoption (OPT_POPRECONNECT,
524 _("Connection lost. Reconnect to POP server?")) !=
526 return PQ_NOT_CONNECTED;