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