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