]> git.llucax.com Git - software/mutt-debian.git/blob - pop_lib.c
new patch from upstream to fix the atime issue
[software/mutt-debian.git] / pop_lib.c
1 /*
2  * Copyright (C) 2000-2003 Vsevolod Volkov <vvv@mutt.org.ua>
3  * 
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.
8  * 
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.
13  * 
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.
17  */
18
19 #if HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22
23 #include "mutt.h"
24 #include "mx.h"
25 #include "url.h"
26 #include "pop.h"
27 #if defined(USE_SSL)
28 # include "mutt_ssl.h"
29 #endif
30
31 #include <string.h>
32 #include <unistd.h>
33 #include <ctype.h>
34 #include <netdb.h>
35
36 /* given an POP mailbox name, return host, port, username and password */
37 int pop_parse_path (const char* path, ACCOUNT* acct)
38 {
39   ciss_url_t url;
40   char *c;
41   struct servent *service;
42
43   /* Defaults */
44   acct->flags = 0;
45   acct->type = M_ACCT_TYPE_POP;
46
47   c = safe_strdup (path);
48   url_parse_ciss (&url, c);
49
50   if ((url.scheme != U_POP && url.scheme != U_POPS) ||
51       mutt_account_fromurl (acct, &url) < 0)
52   {
53     FREE(&c);
54     mutt_error(_("Invalid POP URL: %s\n"), path);
55     mutt_sleep(1);
56     return -1;
57   }
58
59   if (url.scheme == U_POPS)
60     acct->flags |= M_ACCT_SSL;
61
62   service = getservbyname (url.scheme == U_POP ? "pop3" : "pop3s", "tcp");
63   if (service)
64     acct->port = ntohs (service->s_port);
65   else
66     acct->port = url.scheme == U_POP ? POP_PORT : POP_SSL_PORT;;
67
68   FREE (&c);
69   return 0;
70 }
71
72 /* Copy error message to err_msg buffer */
73 void pop_error (POP_DATA *pop_data, char *msg)
74 {
75   char *t, *c, *c2;
76
77   t = strchr (pop_data->err_msg, '\0');
78   c = msg;
79
80   if (!mutt_strncmp (msg, "-ERR ", 5))
81   {
82     c2 = msg + 5;
83     SKIPWS (c2);
84
85     if (*c2)
86       c = c2;
87   }
88
89   strfcpy (t, c, sizeof (pop_data->err_msg) - strlen (pop_data->err_msg));
90   mutt_remove_trailing_ws (pop_data->err_msg);
91 }
92
93 /* Parse CAPA output */
94 static int fetch_capa (char *line, void *data)
95 {
96   POP_DATA *pop_data = (POP_DATA *)data;
97   char *c;
98
99   if (!ascii_strncasecmp (line, "SASL", 4))
100   {
101     FREE (&pop_data->auth_list);
102     c = line + 4;
103     SKIPWS (c);
104     pop_data->auth_list = safe_strdup (c);
105   }
106
107   else if (!ascii_strncasecmp (line, "STLS", 4))
108     pop_data->cmd_stls = 1;
109
110   else if (!ascii_strncasecmp (line, "USER", 4))
111     pop_data->cmd_user = 1;
112
113   else if (!ascii_strncasecmp (line, "UIDL", 4))
114     pop_data->cmd_uidl = 1;
115
116   else if (!ascii_strncasecmp (line, "TOP", 3))
117     pop_data->cmd_top = 1;
118
119   return 0;
120 }
121
122 /* Fetch list of the authentication mechanisms */
123 static int fetch_auth (char *line, void *data)
124 {
125   POP_DATA *pop_data = (POP_DATA *)data;
126
127   if (!pop_data->auth_list)
128   {
129     pop_data->auth_list = safe_malloc (strlen (line) + 1);
130     *pop_data->auth_list = '\0';
131   }
132   else
133   {
134     safe_realloc (&pop_data->auth_list,
135             strlen (pop_data->auth_list) + strlen (line) + 2);
136     strcat (pop_data->auth_list, " ");  /* __STRCAT_CHECKED__ */
137   }
138   strcat (pop_data->auth_list, line);   /* __STRCAT_CHECKED__ */
139
140   return 0;
141 }
142
143 /*
144  * Get capabilities
145  *  0 - successful,
146  * -1 - conection lost,
147  * -2 - execution error.
148 */
149 static int pop_capabilities (POP_DATA *pop_data, int mode)
150 {
151   char buf[LONG_STRING];
152
153   /* don't check capabilities on reconnect */
154   if (pop_data->capabilities)
155     return 0;
156
157   /* init capabilities */
158   if (mode == 0)
159   {
160     pop_data->cmd_capa = 0;
161     pop_data->cmd_stls = 0;
162     pop_data->cmd_user = 0;
163     pop_data->cmd_uidl = 0;
164     pop_data->cmd_top = 0;
165     pop_data->resp_codes = 0;
166     pop_data->expire = 1;
167     pop_data->login_delay = 0;
168     FREE (&pop_data->auth_list);
169   }
170
171   /* Execute CAPA command */
172   if (mode == 0 || pop_data->cmd_capa)
173   {
174     strfcpy (buf, "CAPA\r\n", sizeof (buf));
175     switch (pop_fetch_data (pop_data, buf, NULL, fetch_capa, pop_data))
176     {
177       case 0:
178       {
179         pop_data->cmd_capa = 1;
180         break;
181       }
182       case -1:
183         return -1;
184     }
185   }
186
187   /* CAPA not supported, use defaults */
188   if (mode == 0 && !pop_data->cmd_capa)
189   {
190     pop_data->cmd_user = 2;
191     pop_data->cmd_uidl = 2;
192     pop_data->cmd_top = 2;
193
194     strfcpy (buf, "AUTH\r\n", sizeof (buf));
195     if (pop_fetch_data (pop_data, buf, NULL, fetch_auth, pop_data) == -1)
196       return -1;
197   }
198
199   /* Check capabilities */
200   if (mode == 2)
201   {
202     char *msg = NULL;
203
204     if (!pop_data->expire)
205       msg = _("Unable to leave messages on server.");
206     if (!pop_data->cmd_top)
207       msg = _("Command TOP is not supported by server.");
208     if (!pop_data->cmd_uidl)
209       msg = _("Command UIDL is not supported by server.");
210     if (msg && pop_data->cmd_capa)
211     {
212       mutt_error (msg);
213       return -2;
214     }
215     pop_data->capabilities = 1;
216   }
217
218   return 0;
219 }
220
221 /*
222  * Open connection
223  *  0 - successful,
224  * -1 - conection lost,
225  * -2 - invalid response.
226 */
227 int pop_connect (POP_DATA *pop_data)
228 {
229   char buf[LONG_STRING];
230
231   pop_data->status = POP_NONE;
232   if (mutt_socket_open (pop_data->conn) < 0 ||
233       mutt_socket_readln (buf, sizeof (buf), pop_data->conn) < 0)
234   {
235     mutt_error (_("Error connecting to server: %s"), pop_data->conn->account.host);
236     return -1;
237   }
238
239   pop_data->status = POP_CONNECTED;
240
241   if (mutt_strncmp (buf, "+OK", 3))
242   {
243     *pop_data->err_msg = '\0';
244     pop_error (pop_data, buf);
245     mutt_error ("%s", pop_data->err_msg);
246     return -2;
247   }
248
249   pop_apop_timestamp (pop_data, buf);
250
251   return 0;
252 }
253
254 /*
255  * Open connection and authenticate
256  *  0 - successful,
257  * -1 - conection lost,
258  * -2 - invalid command or execution error,
259  * -3 - authentication canceled.
260 */
261 int pop_open_connection (POP_DATA *pop_data)
262 {
263   int ret;
264   unsigned int n, size;
265   char buf[LONG_STRING];
266
267   ret = pop_connect (pop_data);
268   if (ret < 0)
269   {
270     mutt_sleep (2);
271     return ret;
272   }
273
274   ret = pop_capabilities (pop_data, 0);
275   if (ret == -1)
276     goto err_conn;
277   if (ret == -2)
278   {
279     mutt_sleep (2);
280     return -2;
281   }
282
283 #if defined(USE_SSL)
284   /* Attempt STLS if available and desired. */
285   if (!pop_data->conn->ssf && (pop_data->cmd_stls || option(OPTSSLFORCETLS)))
286   {
287     if (option(OPTSSLFORCETLS))
288       pop_data->use_stls = 2;
289     if (pop_data->use_stls == 0)
290     {
291       ret = query_quadoption (OPT_SSLSTARTTLS,
292             _("Secure connection with TLS?"));
293       if (ret == -1)
294         return -2;
295       pop_data->use_stls = 1;
296       if (ret == M_YES)
297         pop_data->use_stls = 2;
298     }
299     if (pop_data->use_stls == 2)
300     {
301       strfcpy (buf, "STLS\r\n", sizeof (buf));
302       ret = pop_query (pop_data, buf, sizeof (buf));
303       if (ret == -1)
304         goto err_conn;
305       if (ret != 0)
306       {
307         mutt_error ("%s", pop_data->err_msg);
308         mutt_sleep (2);
309       }
310       else if (mutt_ssl_starttls (pop_data->conn))
311       {
312         mutt_error (_("Could not negotiate TLS connection"));
313         mutt_sleep (2);
314         return -2;
315       }
316       else
317       {
318         /* recheck capabilities after STLS completes */
319         ret = pop_capabilities (pop_data, 1);
320         if (ret == -1)
321           goto err_conn;
322         if (ret == -2)
323         {
324           mutt_sleep (2);
325           return -2;
326         }
327       }
328     }
329   }
330
331   if (option(OPTSSLFORCETLS) && !pop_data->conn->ssf)
332   {
333     mutt_error _("Encrypted connection unavailable");
334     mutt_sleep (1);
335     return -2;
336   }
337 #endif
338
339   ret = pop_authenticate (pop_data);
340   if (ret == -1)
341     goto err_conn;
342   if (ret == -3)
343     mutt_clear_error ();
344   if (ret != 0)
345     return ret;
346
347   /* recheck capabilities after authentication */
348   ret = pop_capabilities (pop_data, 2);
349   if (ret == -1)
350     goto err_conn;
351   if (ret == -2)
352   {
353     mutt_sleep (2);
354     return -2;
355   }
356
357   /* get total size of mailbox */
358   strfcpy (buf, "STAT\r\n", sizeof (buf));
359   ret = pop_query (pop_data, buf, sizeof (buf));
360   if (ret == -1)
361     goto err_conn;
362   if (ret == -2)
363   {
364     mutt_error ("%s", pop_data->err_msg);
365     mutt_sleep (2);
366     return ret;
367   }
368
369   sscanf (buf, "+OK %u %u", &n, &size);
370   pop_data->size = size;
371   return 0;
372
373 err_conn:
374   pop_data->status = POP_DISCONNECTED;
375   mutt_error _("Server closed connection!");
376   mutt_sleep (2);
377   return -1;
378 }
379
380 /* logout from POP server */
381 void pop_logout (CONTEXT *ctx)
382 {
383   int ret = 0;
384   char buf[LONG_STRING];
385   POP_DATA *pop_data = (POP_DATA *)ctx->data;
386
387   if (pop_data->status == POP_CONNECTED)
388   {
389     mutt_message _("Closing connection to POP server...");
390
391     if (ctx->readonly)
392     {
393       strfcpy (buf, "RSET\r\n", sizeof (buf));
394       ret = pop_query (pop_data, buf, sizeof (buf));
395     }
396
397     if (ret != -1)
398     {
399       strfcpy (buf, "QUIT\r\n", sizeof (buf));
400       pop_query (pop_data, buf, sizeof (buf));
401     }
402
403     mutt_clear_error ();
404   }
405
406   pop_data->status = POP_DISCONNECTED;
407   return;
408 }
409
410 /*
411  * Send data from buffer and receive answer to the same buffer
412  *  0 - successful,
413  * -1 - conection lost,
414  * -2 - invalid command or execution error.
415 */
416 int pop_query_d (POP_DATA *pop_data, char *buf, size_t buflen, char *msg)
417 {
418   int dbg = M_SOCK_LOG_CMD;
419   char *c;
420
421   if (pop_data->status != POP_CONNECTED)
422     return -1;
423
424 #ifdef DEBUG
425     /* print msg instaed of real command */
426     if (msg)
427     {
428       dbg = M_SOCK_LOG_FULL;
429       dprint (M_SOCK_LOG_CMD, (debugfile, "> %s", msg));
430     }
431 #endif
432
433   mutt_socket_write_d (pop_data->conn, buf, -1, dbg);
434
435   c = strpbrk (buf, " \r\n");
436   *c = '\0';
437   snprintf (pop_data->err_msg, sizeof (pop_data->err_msg), "%s: ", buf);
438
439   if (mutt_socket_readln (buf, buflen, pop_data->conn) < 0)
440   {
441     pop_data->status = POP_DISCONNECTED;
442     return -1;
443   }
444   if (!mutt_strncmp (buf, "+OK", 3))
445     return 0;
446
447   pop_error (pop_data, buf);
448   return -2;
449 }
450
451 /*
452  * This function calls  funct(*line, *data)  for each received line,
453  * funct(NULL, *data)  if  rewind(*data)  needs, exits when fail or done.
454  * Returned codes:
455  *  0 - successful,
456  * -1 - conection lost,
457  * -2 - invalid command or execution error,
458  * -3 - error in funct(*line, *data)
459  */
460 int pop_fetch_data (POP_DATA *pop_data, char *query, progress_t *progressbar,
461                     int (*funct) (char *, void *), void *data)
462 {
463   char buf[LONG_STRING];
464   char *inbuf;
465   char *p;
466   int ret, chunk = 0;
467   long pos = 0;
468   size_t lenbuf = 0;
469
470   strfcpy (buf, query, sizeof (buf));
471   ret = pop_query (pop_data, buf, sizeof (buf));
472   if (ret < 0)
473     return ret;
474
475   inbuf = safe_malloc (sizeof (buf));
476
477   FOREVER
478   {
479     chunk = mutt_socket_readln_d (buf, sizeof (buf), pop_data->conn, M_SOCK_LOG_HDR);
480     if (chunk < 0)
481     {
482       pop_data->status = POP_DISCONNECTED;
483       ret = -1;
484       break;
485     }
486
487     p = buf;
488     if (!lenbuf && buf[0] == '.')
489     {
490       if (buf[1] != '.')
491         break;
492       p++;
493     }
494
495     strfcpy (inbuf + lenbuf, p, sizeof (buf));
496     pos += chunk;
497
498     if (chunk >= sizeof (buf))
499     {
500       lenbuf += strlen (p);
501     }
502     else
503     {
504       if (progressbar)
505         mutt_progress_update (progressbar, pos, -1);
506       if (ret == 0 && funct (inbuf, data) < 0)
507         ret = -3;
508       lenbuf = 0;
509     }
510
511     safe_realloc (&inbuf, lenbuf + sizeof (buf));
512   }
513
514   FREE (&inbuf);
515   return ret;
516 }
517
518 /* find message with this UIDL and set refno */
519 static int check_uidl (char *line, void *data)
520 {
521   int i;
522   unsigned int index;
523   CONTEXT *ctx = (CONTEXT *)data;
524
525   sscanf (line, "%u %s", &index, line);
526   for (i = 0; i < ctx->msgcount; i++)
527   {
528     if (!mutt_strcmp (ctx->hdrs[i]->data, line))
529     {
530       ctx->hdrs[i]->refno = index;
531       break;
532     }
533   }
534
535   return 0;
536 }
537
538 /* reconnect and verify idnexes if connection was lost */
539 int pop_reconnect (CONTEXT *ctx)
540 {
541   int ret;
542   POP_DATA *pop_data = (POP_DATA *)ctx->data;
543   progress_t progressbar;
544
545   if (pop_data->status == POP_CONNECTED)
546     return 0;
547   if (pop_data->status == POP_BYE)
548     return -1;
549
550   FOREVER
551   {
552     mutt_socket_close (pop_data->conn);
553
554     ret = pop_open_connection (pop_data);
555     if (ret == 0)
556     {
557       int i;
558
559       mutt_progress_init (&progressbar, _("Verifying message indexes..."),
560                           M_PROGRESS_SIZE, NetInc, 0);
561
562       for (i = 0; i < ctx->msgcount; i++)
563         ctx->hdrs[i]->refno = -1;
564
565       ret = pop_fetch_data (pop_data, "UIDL\r\n", &progressbar, check_uidl, ctx);
566       if (ret == -2)
567       {
568         mutt_error ("%s", pop_data->err_msg);
569         mutt_sleep (2);
570       }
571     }
572     if (ret == 0)
573       return 0;
574
575     pop_logout (ctx);
576
577     if (ret < -1)
578       return -1;
579
580     if (query_quadoption (OPT_POPRECONNECT,
581                 _("Connection lost. Reconnect to POP server?")) != M_YES)
582       return -1;
583   }
584 }