55943c5300f77f3a517e98ebd757238d259ee6a4
[apps/madmutt.git] / pop.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 2000-2002 Vsevolod Volkov <vvv@mutt.org.ua>
4  *
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.
8  */
9
10 #include <lib-lib/lib-lib.h>
11
12 #include <sasl/sasl.h>
13 #include <sasl/saslutil.h>
14
15 #include <lib-sys/mutt_socket.h>
16 #include <lib-ui/curses.h>
17
18 #include "crypt.h"
19 #include "mutt.h"
20 #include "mutt_sasl.h"
21 #include "pop.h"
22
23 #define POP_PORT            110
24 #define POP_SSL_PORT        995
25 #define POP_CACHE_LEN        10
26 /* maximal length of the server response (RFC1939) */
27 #define POP_CMD_RESPONSE    512
28
29 enum {
30     /* Status */
31     POP_NONE = 0,
32     POP_CONNECTED,
33     POP_DISCONNECTED,
34     POP_BYE
35 };
36
37 typedef enum {
38     POP_A_SUCCESS = 0,
39     POP_A_SOCKET,
40     POP_A_FAILURE,
41     POP_A_UNAVAIL
42 } pop_auth_res_t;
43
44 typedef struct {
45   int index;
46   char *path;
47 } POP_CACHE;
48
49 typedef enum {
50     /* pop_fetch_data uses pop_query_status and this return value */
51     PFD_FUNCT_ERROR = -3,
52     PQ_ERR = -2,
53     PQ_NOT_CONNECTED = -1,
54     PQ_OK = 0
55 } pop_query_status;
56
57 typedef enum {
58     CMD_NOT_AVAILABLE = 0,
59     CMD_AVAILABLE,
60     CMD_UNKNOWN /* unknown whether it is available or not */
61 } cmd_status;
62
63 typedef struct {
64     CONNECTION *conn;
65
66     unsigned status : 2;
67     unsigned capabilities : 1;
68     unsigned use_stls : 2;
69     cmd_status cmd_capa : 2;      /* optional command CAPA */
70     cmd_status cmd_stls : 2;      /* optional command STLS */
71     cmd_status cmd_user : 2;      /* optional command USER */
72     cmd_status cmd_uidl : 2;      /* optional command UIDL */
73     cmd_status cmd_top : 2;       /* optional command TOP */
74     unsigned resp_codes : 1;      /* server supports extended response codes */
75     unsigned expire : 1;          /* expire is greater than 0 */
76     unsigned clear_cache : 1;
77
78     ssize_t size;
79     time_t check_time;
80     time_t login_delay;           /* minimal login delay  capability */
81     char *auth_list;              /* list of auth mechanisms */
82     char *timestamp;
83     char err_msg[POP_CMD_RESPONSE];
84     POP_CACHE cache[POP_CACHE_LEN];
85 } pop_data_t;
86
87 /* pop low level functions {{{ */
88
89 static void pop_error(pop_data_t *pop_data, const char *msg)
90 {
91     if (!m_strncmp(msg, "-ERR ", 5)) {
92         const char *s = skipspaces(msg + 5);
93         if (*s)
94             msg = s;
95     }
96
97     m_strcat(pop_data->err_msg, sizeof(pop_data->err_msg), msg);
98     m_strrtrim(pop_data->err_msg);
99 }
100
101 /*
102  * Send data from buffer and receive answer to the same buffer
103  *  0 - successful,
104  * -1 - conection lost,
105  * -2 - invalid command or execution error.
106 */
107 static pop_query_status pop_query(pop_data_t *pop_data, char *buf, ssize_t buflen)
108 {
109     char *c;
110
111     if (pop_data->status != POP_CONNECTED)
112         return PQ_NOT_CONNECTED;
113
114     mutt_socket_write(pop_data->conn, buf);
115
116     c  = strpbrk(buf, " \r\n");
117     *c = '\0';
118     snprintf(pop_data->err_msg, sizeof(pop_data->err_msg), "%s: ", buf);
119
120     if (mutt_socket_readln(buf, buflen, pop_data->conn) < 0) {
121         pop_data->status = POP_DISCONNECTED;
122         return PQ_NOT_CONNECTED;
123     }
124     if (!m_strncmp(buf, "+OK", 3))
125         return PQ_OK;
126
127     pop_error(pop_data, buf);
128     return PQ_ERR;
129 }
130
131 /*
132  * Open connection
133  *  0 - successful,
134  * -1 - conection lost,
135  * -2 - invalid response.
136 */
137 static pop_query_status pop_connect(pop_data_t * pop_data)
138 {
139     char buf[LONG_STRING];
140     const char *p, *q;
141
142     if (mutt_socket_open(pop_data->conn) < 0
143     ||  mutt_socket_readln(buf, sizeof(buf), pop_data->conn) < 0)
144     {
145         mutt_error(_("Error connecting to server: %s"),
146                    pop_data->conn->account.host);
147         pop_data->status = POP_NONE;
148         return PQ_NOT_CONNECTED;
149     }
150
151     pop_data->status = POP_CONNECTED;
152     if (m_strncmp(buf, "+OK", 3)) {
153         pop_data->err_msg[0] = '\0';
154         pop_error(pop_data, buf);
155         mutt_error("%s", pop_data->err_msg);
156         return PQ_ERR;
157     }
158
159     p_delete(&pop_data->timestamp);
160     if ((p = strchr(buf, '<')) && (q = strchr(p, '>'))) {
161         pop_data->timestamp = p_dupstr(p, q - p);
162     }
163     return PQ_OK;
164 }
165
166 static void pop_logout (CONTEXT * ctx)
167 {
168   pop_query_status ret = 0;
169   char buf[LONG_STRING];
170   pop_data_t *pop_data = (pop_data_t *) ctx->data;
171
172   if (pop_data->status == POP_CONNECTED) {
173     mutt_message _("Closing connection to POP server...");
174
175     if (ctx->readonly) {
176       m_strcpy(buf, sizeof(buf), "RSET\r\n");
177       ret = pop_query (pop_data, buf, sizeof (buf));
178     }
179
180     if (ret != PQ_NOT_CONNECTED) {
181       m_strcpy(buf, sizeof(buf), "QUIT\r\n");
182       pop_query (pop_data, buf, sizeof (buf));
183     }
184
185     mutt_clear_error ();
186   }
187
188   pop_data->status = POP_DISCONNECTED;
189   return;
190 }
191
192 /* }}} */
193
194 /*
195  * This function calls  funct(*line, *data)  for each received line,
196  * funct(NULL, *data)  if  rewind(*data)  needs, exits when fail or done.
197  * Returned codes:
198  *  0 - successful,
199  * -1 - conection lost,
200  * -2 - invalid command or execution error,
201  * -3 - error in funct(*line, *data)
202  */
203 static pop_query_status
204 pop_fetch_data(pop_data_t *pop_data, const char *query, progress_t *bar,
205                int (*funct)(char *, void *), void *data)
206 {
207   char buf[LONG_STRING];
208   char *inbuf;
209   char *p;
210   pop_query_status ret;
211   int chunk = 0;
212   long pos = 0;
213   ssize_t lenbuf = 0;
214
215   m_strcpy(buf, sizeof(buf), query);
216   ret = pop_query (pop_data, buf, sizeof (buf));
217   if (ret != PQ_OK)
218     return ret;
219
220   inbuf = p_new(char, sizeof(buf));
221
222   for (;;) {
223     chunk =
224       mutt_socket_readln(buf, sizeof (buf), pop_data->conn);
225     if (chunk < 0) {
226       pop_data->status = POP_DISCONNECTED;
227       ret = PQ_NOT_CONNECTED;
228       break;
229     }
230
231     p = buf;
232     if (!lenbuf && buf[0] == '.') {
233       if (buf[1] != '.')
234         break;
235       p++;
236     }
237
238     m_strcpy(inbuf + lenbuf,sizeof(buf), p);
239     pos += chunk;
240
241     if (chunk >= ssizeof(buf)) {
242       lenbuf += strlen (p);
243     } else {
244       if (bar)
245         mutt_progress_bar (bar, pos);
246       if (ret == 0 && funct (inbuf, data) < 0)
247         ret = PFD_FUNCT_ERROR;
248       lenbuf = 0;
249     }
250
251     p_realloc(&inbuf, lenbuf + sizeof(buf));
252   }
253
254   p_delete(&inbuf);
255   return ret;
256 }
257
258 static int fetch_capa (char *line, void *data)
259 {
260   pop_data_t *pop_data = (pop_data_t *) data;
261   char *c;
262
263   if (!ascii_strncasecmp (line, "SASL", 4)) {
264     p_delete(&pop_data->auth_list);
265     c = vskipspaces(line + 4);
266     pop_data->auth_list = m_strdup(c);
267   }
268
269   else if (!ascii_strncasecmp (line, "STLS", 4))
270     pop_data->cmd_stls = CMD_AVAILABLE;
271
272   else if (!ascii_strncasecmp (line, "USER", 4))
273     pop_data->cmd_user = CMD_AVAILABLE;
274
275   else if (!ascii_strncasecmp (line, "UIDL", 4))
276     pop_data->cmd_uidl = CMD_AVAILABLE;
277
278   else if (!ascii_strncasecmp (line, "TOP", 3))
279     pop_data->cmd_top = CMD_AVAILABLE;
280
281   return 0;
282 }
283
284 static int fetch_auth (char *line, void *data)
285 {
286   pop_data_t *pop_data = (pop_data_t *) data;
287   ssize_t auth_list_len;
288
289   if (!pop_data->auth_list) {
290     auth_list_len = m_strlen(line) + 1;
291     pop_data->auth_list = p_new(char, auth_list_len);
292   } else {
293     auth_list_len = m_strlen(pop_data->auth_list) + m_strlen(line) + 2;
294     p_realloc(&pop_data->auth_list, auth_list_len);
295     m_strcat(pop_data->auth_list, auth_list_len, " ");
296   }
297   m_strcat(pop_data->auth_list, auth_list_len, line);
298
299   return 0;
300 }
301
302 /*
303  * Get capabilities
304  *  0 - successful,
305  * -1 - conection lost,
306  * -2 - execution error.
307 */
308 static pop_query_status pop_capabilities (pop_data_t * pop_data, int mode)
309 {
310   char buf[LONG_STRING];
311
312   /* don't check capabilities on reconnect */
313   if (pop_data->capabilities)
314     return 0;
315
316   /* init capabilities */
317   if (mode == 0) {
318     pop_data->cmd_capa = CMD_NOT_AVAILABLE;
319     pop_data->cmd_stls = CMD_NOT_AVAILABLE;
320     pop_data->cmd_user = CMD_NOT_AVAILABLE;
321     pop_data->cmd_uidl = CMD_NOT_AVAILABLE;
322     pop_data->cmd_top = CMD_NOT_AVAILABLE;
323     pop_data->resp_codes = 0;
324     pop_data->expire = 1;
325     pop_data->login_delay = 0;
326     p_delete(&pop_data->auth_list);
327   }
328
329   /* Execute CAPA command */
330   if (mode == 0 || pop_data->cmd_capa != CMD_NOT_AVAILABLE) {
331     m_strcpy(buf, sizeof(buf), "CAPA\r\n");
332     switch (pop_fetch_data (pop_data, buf, NULL, fetch_capa, pop_data)) {
333     case PQ_OK:
334       {
335         pop_data->cmd_capa = CMD_AVAILABLE;
336         break;
337       }
338     case PFD_FUNCT_ERROR:
339     case PQ_ERR:
340       {
341         pop_data->cmd_capa = CMD_NOT_AVAILABLE;
342         break;
343       }
344     case PQ_NOT_CONNECTED:
345       return PQ_NOT_CONNECTED;
346     }
347   }
348
349   /* CAPA not supported, use defaults */
350   if (mode == 0 && pop_data->cmd_capa == CMD_NOT_AVAILABLE) {
351     pop_data->cmd_user = CMD_UNKNOWN;
352     pop_data->cmd_uidl = CMD_UNKNOWN;
353     pop_data->cmd_top = CMD_UNKNOWN;
354
355     m_strcpy(buf, sizeof(buf), "AUTH\r\n");
356     if (pop_fetch_data (pop_data, buf, NULL, fetch_auth, pop_data) == PQ_NOT_CONNECTED)
357       return PQ_NOT_CONNECTED;
358   }
359
360   /* Check capabilities */
361   if (mode == 2) {
362     char *msg = NULL;
363
364     if (!pop_data->expire)
365       msg = _("Unable to leave messages on server.");
366     if (pop_data->cmd_top == CMD_NOT_AVAILABLE)
367       msg = _("Command TOP is not supported by server.");
368     if (pop_data->cmd_uidl == CMD_NOT_AVAILABLE)
369       msg = _("Command UIDL is not supported by server.");
370     if (msg && pop_data->cmd_capa != CMD_AVAILABLE) {
371       mutt_error (msg);
372       return PQ_ERR;
373     }
374     pop_data->capabilities = 1;
375   }
376
377   return PQ_OK;
378 }
379
380 /* Authentication {{{ */
381
382 /* SASL authenticator */
383 static pop_auth_res_t pop_auth_sasl (pop_data_t * pop_data, const char *method)
384 {
385   sasl_conn_t *saslconn;
386   sasl_interact_t *interaction = NULL;
387   int rc;
388   char buf[LONG_STRING];
389   char inbuf[LONG_STRING];
390   const char *mech;
391
392   const char *pc = NULL;
393   unsigned int len, olen;
394   unsigned char client_start;
395
396   if (mutt_sasl_client_new (pop_data->conn, &saslconn) < 0) {
397     return POP_A_FAILURE;
398   }
399
400   if (!method)
401     method = pop_data->auth_list;
402
403   for (;;) {
404     rc = sasl_client_start (saslconn, method, &interaction, &pc, &olen,
405                             &mech);
406     if (rc != SASL_INTERACT)
407       break;
408     mutt_sasl_interact (interaction);
409   }
410
411   if (rc != SASL_OK && rc != SASL_CONTINUE) {
412     /* SASL doesn't support suggested mechanisms, so fall back */
413     return POP_A_UNAVAIL;
414   }
415
416   client_start = (olen > 0);
417
418   mutt_message _("Authenticating (SASL)...");
419
420   snprintf (buf, sizeof (buf), "AUTH %s", mech);
421   olen = strlen (buf);
422
423   /* looping protocol */
424   for (;;) {
425     m_strcpy(buf + olen, sizeof(buf) - olen, "\r\n");
426     mutt_socket_write (pop_data->conn, buf);
427     if (mutt_socket_readln (inbuf, sizeof (inbuf), pop_data->conn) < 0) {
428       sasl_dispose (&saslconn);
429       pop_data->status = POP_DISCONNECTED;
430       return POP_A_SOCKET;
431     }
432
433     if (rc != SASL_CONTINUE)
434       break;
435
436     if (!m_strncmp(inbuf, "+ ", 2)
437         && sasl_decode64 (inbuf, strlen (inbuf), buf, LONG_STRING - 1,
438                           &len) != SASL_OK)
439     {
440       goto bail;
441     }
442
443     if (!client_start)
444       for (;;) {
445       rc = sasl_client_step (saslconn, buf, len, &interaction, &pc, &olen);
446       if (rc != SASL_INTERACT)
447         break;
448       mutt_sasl_interact (interaction);
449       }
450     else
451       client_start = 0;
452
453     if (rc != SASL_CONTINUE && (olen == 0 || rc != SASL_OK))
454       break;
455
456     /* send out response, or line break if none needed */
457     if (pc) {
458       if (sasl_encode64 (pc, olen, buf, sizeof (buf), &olen) != SASL_OK) {
459         goto bail;
460       }
461
462       /* sasl_client_st(art|ep) allocate pc with malloc, expect me to 
463        * free it */
464       p_delete((char **)&pc);
465     }
466   }
467
468   if (rc != SASL_OK)
469     goto bail;
470
471   if (!m_strncmp(inbuf, "+OK", 3)) {
472     mutt_sasl_setup_conn (pop_data->conn, saslconn);
473     return POP_A_SUCCESS;
474   }
475
476 bail:
477   sasl_dispose (&saslconn);
478
479   /* terminate SASL sessoin if the last responce is not +OK nor -ERR */
480   if (!m_strncmp(inbuf, "+ ", 2)) {
481     snprintf (buf, sizeof (buf), "*\r\n");
482     if (pop_query (pop_data, buf, sizeof (buf)) == PQ_NOT_CONNECTED)
483       return POP_A_SOCKET;
484   }
485
486   mutt_error _("SASL authentication failed.");
487
488   mutt_sleep (2);
489
490   return POP_A_FAILURE;
491 }
492
493 /* APOP authenticator */
494 static pop_auth_res_t pop_auth_apop (pop_data_t * pop_data,
495                                      const char *method __attribute__ ((unused)))
496 {
497   MD5_CTX mdContext;
498   unsigned char digest[16];
499   char hash[33];
500   char buf[LONG_STRING];
501   int i;
502
503   if (!pop_data->timestamp)
504     return POP_A_UNAVAIL;
505
506   mutt_message _("Authenticating (APOP)...");
507
508   /* Compute the authentication hash to send to the server */
509   MD5Init (&mdContext);
510   MD5Update (&mdContext, (unsigned char *) pop_data->timestamp,
511              strlen (pop_data->timestamp));
512   MD5Update (&mdContext, (unsigned char *) pop_data->conn->account.pass,
513              strlen (pop_data->conn->account.pass));
514   MD5Final (digest, &mdContext);
515
516   for (i = 0; i < ssizeof(digest); i++)
517     sprintf (hash + 2 * i, "%02x", digest[i]);
518
519   /* Send APOP command to server */
520   snprintf(buf, sizeof(buf), "APOP %s %s\r\n", pop_data->conn->account.user,
521            hash);
522
523   switch (pop_query (pop_data, buf, sizeof (buf))) {
524   case PQ_OK:
525     return POP_A_SUCCESS;
526   case PQ_NOT_CONNECTED:
527     return POP_A_SOCKET;
528   case PFD_FUNCT_ERROR:
529   case PQ_ERR:
530   default:
531     break;
532   }
533
534   mutt_error ("%s %s", _("APOP authentication failed."), pop_data->err_msg);
535   mutt_sleep (2);
536
537   return POP_A_FAILURE;
538 }
539
540 /* USER authenticator */
541 static pop_auth_res_t pop_auth_user (pop_data_t * pop_data,
542                                      const char *method __attribute__ ((unused)))
543 {
544   char buf[LONG_STRING];
545   pop_query_status ret;
546
547   if (pop_data->cmd_user == CMD_NOT_AVAILABLE)
548     return POP_A_UNAVAIL;
549
550   mutt_message _("Logging in...");
551
552   snprintf (buf, sizeof (buf), "USER %s\r\n", pop_data->conn->account.user);
553   ret = pop_query (pop_data, buf, sizeof (buf));
554
555   if (pop_data->cmd_user == CMD_UNKNOWN) {
556     if (ret == PQ_OK) {
557       pop_data->cmd_user = CMD_AVAILABLE;
558     }
559
560     if (ret == PQ_ERR) {
561       pop_data->cmd_user = CMD_NOT_AVAILABLE;
562
563       snprintf (pop_data->err_msg, sizeof (pop_data->err_msg),
564                 _("Command USER is not supported by server."));
565     }
566   }
567
568   if (ret == PQ_OK) {
569     snprintf (buf, sizeof (buf), "PASS %s\r\n", pop_data->conn->account.pass);
570     ret = pop_query (pop_data, buf, sizeof (buf));
571   }
572
573   switch (ret) {
574   case PQ_OK:
575     return POP_A_SUCCESS;
576   case PQ_NOT_CONNECTED:
577     return POP_A_SOCKET;
578   case PFD_FUNCT_ERROR:
579   case PQ_ERR:
580   default:
581     break;
582   }
583
584   mutt_error ("%s %s", _("Login failed."), pop_data->err_msg);
585   mutt_sleep (2);
586
587   return POP_A_FAILURE;
588 }
589
590 typedef struct {
591   /* do authentication, using named method or any available if method is NULL */
592   pop_auth_res_t (*authenticate) (pop_data_t *, const char *);
593   /* name of authentication method supported, NULL means variable. If this
594    * is not null, authenticate may ignore the second parameter. */
595   const char *method;
596 } pop_auth_t;
597
598 /*
599  * Authentication
600  *  0 - successful,
601  * -1 - conection lost,
602  * -2 - login failed,
603  * -3 - authentication canceled.
604 */
605 static pop_query_status pop_authenticate (pop_data_t * pop_data)
606 {
607   static pop_auth_t const pop_authenticators[] = {
608     {pop_auth_sasl, NULL},
609     {pop_auth_apop, "apop"},
610     {pop_auth_user, "user"},
611     {NULL, NULL}
612   };
613
614   ACCOUNT *act = &pop_data->conn->account;
615   const pop_auth_t *authenticator;
616   char *methods;
617   char *comma;
618   char *method;
619   int attempts = 0;
620   int ret = POP_A_UNAVAIL;
621
622   if (mutt_account_getuser (act) || !act->user[0] ||
623       mutt_account_getpass (act) || !act->pass[0])
624     return PFD_FUNCT_ERROR;
625
626   if (PopAuthenticators && *PopAuthenticators) {
627     /* Try user-specified list of authentication methods */
628     methods = m_strdup(PopAuthenticators);
629     method = methods;
630
631     while (method) {
632       comma = strchr (method, ':');
633       if (comma)
634         *comma++ = '\0';
635       authenticator = pop_authenticators;
636
637       while (authenticator->authenticate) {
638         if (!authenticator->method ||
639             !ascii_strcasecmp (authenticator->method, method)) {
640           ret = authenticator->authenticate (pop_data, method);
641           if (ret == POP_A_SOCKET) {
642             if (pop_connect(pop_data) == PQ_OK) {
643               ret = authenticator->authenticate (pop_data, method);
644             } else {
645               ret = POP_A_FAILURE;
646             }
647           }
648
649           if (ret != POP_A_UNAVAIL)
650             attempts++;
651           if (ret == POP_A_SUCCESS || ret == POP_A_SOCKET ||
652               (ret == POP_A_FAILURE && !option (OPTPOPAUTHTRYALL))) {
653             comma = NULL;
654             break;
655           }
656         }
657         authenticator++;
658       }
659
660       method = comma;
661     }
662
663     p_delete(&methods);
664   }
665   else {
666     /* Fall back to default: any authenticator */
667     authenticator = pop_authenticators;
668
669     while (authenticator->authenticate) {
670       ret = authenticator->authenticate (pop_data, authenticator->method);
671       if (ret == POP_A_SOCKET) {
672         if (pop_connect(pop_data) == PQ_OK) {
673           ret = authenticator->authenticate(pop_data, authenticator->method);
674         } else {
675           ret = POP_A_FAILURE;
676         }
677       }
678
679       if (ret != POP_A_UNAVAIL)
680         attempts++;
681       if (ret == POP_A_SUCCESS || ret == POP_A_SOCKET ||
682           (ret == POP_A_FAILURE && !option (OPTPOPAUTHTRYALL)))
683         break;
684
685       authenticator++;
686     }
687   }
688
689   switch (ret) {
690   case POP_A_SUCCESS:
691     return PQ_OK;
692   case POP_A_SOCKET:
693     return PQ_NOT_CONNECTED;
694   case POP_A_UNAVAIL:
695     if (!attempts)
696       mutt_error (_("No authenticators available"));
697   }
698
699   return PQ_ERR;
700 }
701
702 /* given an POP mailbox name, return host, port, username and password */
703 static int pop_parse_path (const char *path, ACCOUNT * act)
704 {
705   ciss_url_t url;
706   char *c;
707   int ret = -1;
708
709   /* Defaults */
710   act->flags = 0;
711   act->port = POP_PORT;
712   act->type = M_ACCT_TYPE_POP;
713
714   c = m_strdup(path);
715   url_parse_ciss (&url, c);
716
717   if (url.scheme == U_POP || url.scheme == U_POPS) {
718     if (url.scheme == U_POPS) {
719       act->flags |= M_ACCT_SSL;
720       act->port = POP_SSL_PORT;
721     }
722
723     if ((!url.path || !*url.path) && mutt_account_fromurl (act, &url) == 0)
724       ret = 0;
725   }
726
727   p_delete(&c);
728   return ret;
729 }
730
731 /* }}} */
732
733 /*
734  * Open connection and authenticate
735  *  0 - successful,
736  * -1 - conection lost,
737  * -2 - invalid command or execution error,
738  * -3 - authentication canceled.
739 */
740 static pop_query_status pop_open_connection (pop_data_t * pop_data)
741 {
742   pop_query_status ret;
743   unsigned int n, size;
744   char buf[LONG_STRING];
745
746   ret = pop_connect(pop_data);
747   if (ret != PQ_OK) {
748     mutt_sleep (2);
749     return ret;
750   }
751
752   ret = pop_capabilities (pop_data, 0);
753   if (ret == PQ_NOT_CONNECTED)
754     goto err_conn;
755   if (ret == PQ_ERR) {
756     mutt_sleep (2);
757     return PQ_ERR;
758   }
759
760   /* Attempt STLS if available and desired. */
761   if (!pop_data->conn->ssf && (pop_data->cmd_stls || mod_ssl.force_tls)) {
762     if (mod_ssl.force_tls)
763       pop_data->use_stls = 2;
764     if (pop_data->use_stls == 0) {
765       pop_data->use_stls = 1;
766       if (mod_ssl.starttls)
767         pop_data->use_stls = 2;
768     }
769     if (pop_data->use_stls == 2) {
770       m_strcpy(buf, sizeof(buf), "STLS\r\n");
771       ret = pop_query (pop_data, buf, sizeof (buf));
772       if (ret == PQ_NOT_CONNECTED)
773         goto err_conn;
774       if (ret != PQ_OK) {
775         mutt_error ("%s", pop_data->err_msg);
776         mutt_sleep (2);
777       }
778       else if (mutt_ssl_starttls (pop_data->conn))
779       {
780         mutt_error (_("Could not negotiate TLS connection"));
781         mutt_sleep (2);
782         return PQ_ERR;
783       }
784       else {
785         /* recheck capabilities after STLS completes */
786         ret = pop_capabilities (pop_data, 1);
787         if (ret == PQ_NOT_CONNECTED)
788           goto err_conn;
789         if (ret == PQ_ERR) {
790           mutt_sleep (2);
791           return PQ_ERR;
792         }
793       }
794     }
795   }
796
797   if (mod_ssl.force_tls && !pop_data->conn->ssf) {
798     mutt_error _("Encrypted connection unavailable");
799     mutt_sleep (1);
800     return -2;
801   }
802
803   ret = pop_authenticate (pop_data);
804   if (ret == PQ_NOT_CONNECTED)
805     goto err_conn;
806   if (ret == PFD_FUNCT_ERROR)
807     mutt_clear_error ();
808   if (ret != PQ_OK)
809     return ret;
810
811   /* recheck capabilities after authentication */
812   ret = pop_capabilities (pop_data, 2);
813   if (ret == PQ_NOT_CONNECTED)
814     goto err_conn;
815   if (ret == PQ_ERR) {
816     mutt_sleep (2);
817     return PQ_ERR;
818   }
819
820   /* get total size of mailbox */
821   m_strcpy(buf, sizeof(buf), "STAT\r\n");
822   ret = pop_query (pop_data, buf, sizeof (buf));
823   if (ret == PQ_NOT_CONNECTED)
824     goto err_conn;
825   if (ret == PQ_ERR) {
826     mutt_error ("%s", pop_data->err_msg);
827     mutt_sleep (2);
828     return ret;
829   }
830
831   sscanf (buf, "+OK %u %u", &n, &size);
832   pop_data->size = size;
833   return PQ_OK;
834
835 err_conn:
836   pop_data->status = POP_DISCONNECTED;
837   mutt_error _("Server closed connection!");
838
839   mutt_sleep (2);
840   return PQ_NOT_CONNECTED;
841 }
842
843 /* find message with this UIDL and set refno */
844 static int check_uidl (char *line, void *data)
845 {
846   int i, idx;
847   CONTEXT *ctx = (CONTEXT *)data;
848
849   sscanf (line, "%u %s", &idx, line);
850   for (i = 0; i < ctx->msgcount; i++) {
851     if (!m_strcmp(ctx->hdrs[i]->data, line)) {
852       ctx->hdrs[i]->refno = idx;
853       break;
854     }
855   }
856
857   return 0;
858 }
859
860 /* reconnect and verify indexes if connection was lost */
861 static pop_query_status pop_reconnect (CONTEXT * ctx)
862 {
863   pop_query_status ret;
864   pop_data_t *pop_data = (pop_data_t *) ctx->data;
865   progress_t bar;
866
867   if (pop_data->status == POP_CONNECTED)
868     return PQ_OK;
869   if (pop_data->status == POP_BYE)
870     return PQ_NOT_CONNECTED;
871
872   for (;;) {
873     mutt_socket_close (pop_data->conn);
874
875     ret = pop_open_connection(pop_data);
876     if (ret == PQ_OK) {
877       int i;
878
879       bar.msg = _("Verifying message indexes...");
880       bar.size = 0;
881       mutt_progress_bar (&bar, 0);
882
883       for (i = 0; i < ctx->msgcount; i++)
884         ctx->hdrs[i]->refno = -1;
885
886       ret = pop_fetch_data(pop_data, "UIDL\r\n", &bar, check_uidl, ctx);
887       if (ret == PQ_ERR) {
888         mutt_error ("%s", pop_data->err_msg);
889         mutt_sleep (2);
890       }
891     }
892     if (ret == PQ_OK)
893       return PQ_OK;
894
895     pop_logout (ctx);
896
897     if (ret == PQ_ERR)
898       return PQ_NOT_CONNECTED;
899
900     if (query_quadoption (OPT_POPRECONNECT,
901                           _("Connection lost. Reconnect to POP server?")) !=
902         M_YES)
903       return PQ_NOT_CONNECTED;
904   }
905 }
906
907 /* write line to file */
908 static int fetch_message (char *line, void *file)
909 {
910   FILE *f = (FILE *) file;
911
912   fputs (line, f);
913   if (fputc ('\n', f) == EOF)
914     return -1;
915
916   return 0;
917 }
918
919 /*
920  * Read header
921  * returns:
922  *  0 on success
923  * -1 - conection lost,
924  * -2 - invalid command or execution error,
925  * -3 - error writing to tempfile
926  */
927 static pop_query_status pop_read_header (pop_data_t * pop_data, HEADER * h)
928 {
929   FILE *f;
930   int idx;
931   pop_query_status ret;
932   long length;
933   char buf[LONG_STRING];
934   char tempfile[_POSIX_PATH_MAX];
935
936   f = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
937   if (!f) {
938     mutt_error(_("Could not create temporary file"));
939     return PFD_FUNCT_ERROR;
940   }
941
942   snprintf (buf, sizeof (buf), "LIST %d\r\n", h->refno);
943   ret = pop_query (pop_data, buf, sizeof (buf));
944   if (ret == PQ_OK) {
945     sscanf (buf, "+OK %d %ld", &idx, &length);
946
947     snprintf (buf, sizeof (buf), "TOP %d 0\r\n", h->refno);
948     ret = pop_fetch_data (pop_data, buf, NULL, fetch_message, f);
949
950     if (pop_data->cmd_top == CMD_UNKNOWN) {
951       if (ret == PQ_OK) {
952         pop_data->cmd_top = CMD_AVAILABLE;
953       }
954
955       if (ret == PQ_ERR) {
956         pop_data->cmd_top = CMD_NOT_AVAILABLE;
957         snprintf (pop_data->err_msg, sizeof (pop_data->err_msg),
958                   _("Command TOP is not supported by server."));
959       }
960     }
961   }
962
963   switch (ret) {
964   case PQ_OK:
965     {
966       rewind (f);
967       h->env = mutt_read_rfc822_header (f, h, 0, 0);
968       h->content->length = length - h->content->offset + 1;
969       rewind (f);
970       while (!feof (f)) {
971         h->content->length--;
972         fgets (buf, sizeof (buf), f);
973       }
974       break;
975     }
976   case PQ_ERR:
977     {
978       mutt_error ("%s", pop_data->err_msg);
979       break;
980     }
981   case PFD_FUNCT_ERROR:
982     {
983       mutt_error _("Can't write header to temporary file!");
984
985       break;
986     }
987   case PQ_NOT_CONNECTED:
988     {
989       mutt_error _("Can't fetch header: Not connected!");
990       break;
991     }
992   }
993
994   m_fclose(&f);
995   unlink (tempfile);
996   return ret;
997 }
998
999 /* parse UIDL */
1000 static int fetch_uidl (char *line, void *data)
1001 {
1002   int i, idx;
1003   CONTEXT *ctx = (CONTEXT *) data;
1004   pop_data_t *pop_data = (pop_data_t *) ctx->data;
1005
1006   sscanf (line, "%d %s", &idx, line);
1007   for (i = 0; i < ctx->msgcount; i++)
1008     if (!m_strcmp(line, ctx->hdrs[i]->data))
1009       break;
1010
1011   if (i == ctx->msgcount) {
1012     if (i >= ctx->hdrmax)
1013       mx_alloc_memory (ctx);
1014
1015     ctx->msgcount++;
1016     ctx->hdrs[i] = header_new();
1017     ctx->hdrs[i]->data = m_strdup(line);
1018   }
1019   else if (ctx->hdrs[i]->index != idx - 1)
1020     pop_data->clear_cache = 1;
1021
1022   ctx->hdrs[i]->refno = idx;
1023   ctx->hdrs[i]->index = idx - 1;
1024
1025   return 0;
1026 }
1027
1028 /*
1029  * Read headers
1030  * returns:
1031  *  0 on success
1032  * -1 - conection lost,
1033  * -2 - invalid command or execution error,
1034  * -3 - error writing to tempfile
1035  */
1036 static int pop_fetch_headers (CONTEXT * ctx)
1037 {
1038   int i, old_count, new_count;
1039   pop_query_status ret;
1040   pop_data_t *pop_data = (pop_data_t *) ctx->data;
1041
1042   time (&pop_data->check_time);
1043   pop_data->clear_cache = 0;
1044
1045   for (i = 0; i < ctx->msgcount; i++)
1046     ctx->hdrs[i]->refno = -1;
1047
1048   old_count = ctx->msgcount;
1049   ret = pop_fetch_data (pop_data, "UIDL\r\n", NULL, fetch_uidl, ctx);
1050   new_count = ctx->msgcount;
1051   ctx->msgcount = old_count;
1052
1053   if (pop_data->cmd_uidl == CMD_UNKNOWN) {
1054     if (ret == PQ_OK) {
1055       pop_data->cmd_uidl = CMD_AVAILABLE;
1056     }
1057
1058     if (ret == PQ_ERR && pop_data->cmd_uidl == CMD_UNKNOWN) {
1059       pop_data->cmd_uidl = CMD_NOT_AVAILABLE;
1060
1061       snprintf (pop_data->err_msg, sizeof (pop_data->err_msg),
1062                 _("Command UIDL is not supported by server."));
1063     }
1064   }
1065
1066   if (ret == PQ_OK) {
1067     for (i = 0; i < old_count; i++)
1068       if (ctx->hdrs[i]->refno == -1)
1069         ctx->hdrs[i]->deleted = 1;
1070
1071     for (i = old_count; i < new_count; i++) {
1072       mutt_message (_("Fetching message headers... [%d/%d]"),
1073                     i + 1 - old_count, new_count - old_count);
1074
1075       ret = pop_read_header (pop_data, ctx->hdrs[i]);
1076       if (ret != PQ_OK)
1077         break;
1078
1079       ctx->msgcount++;
1080     }
1081
1082     if (i > old_count)
1083       mx_update_context (ctx, i - old_count);
1084   }
1085
1086   if (ret != PQ_OK) {
1087     for (i = ctx->msgcount; i < new_count; i++)
1088       header_delete(&ctx->hdrs[i]);
1089     return ret;
1090   }
1091
1092   mutt_clear_error ();
1093   return (new_count - old_count);
1094 }
1095
1096 /* delete all cached messages */
1097 static void pop_clear_cache (pop_data_t * pop_data)
1098 {
1099   int i;
1100
1101   if (!pop_data->clear_cache)
1102     return;
1103
1104   for (i = 0; i < POP_CACHE_LEN; i++) {
1105     if (pop_data->cache[i].path) {
1106       unlink (pop_data->cache[i].path);
1107       p_delete(&pop_data->cache[i].path);
1108     }
1109   }
1110 }
1111
1112 /* pop_mx functions {{{ */
1113
1114 static int pop_is_magic(const char* path, struct stat* st)
1115 {
1116     url_scheme_t s = url_check_scheme(NONULL(path));
1117     return s == U_POP || s == U_POPS ? M_POP : -1;
1118 }
1119
1120 static int pop_open_mailbox (CONTEXT * ctx)
1121 {
1122   int ret;
1123   char buf[LONG_STRING];
1124   CONNECTION *conn;
1125   ACCOUNT act;
1126   pop_data_t *pop_data;
1127   ciss_url_t url;
1128
1129   if (pop_parse_path (ctx->path, &act)) {
1130     mutt_error (_("%s is an invalid POP path"), ctx->path);
1131     mutt_sleep (2);
1132     return -1;
1133   }
1134
1135   mutt_account_tourl (&act, &url);
1136   url.path = NULL;
1137   url_ciss_tostring (&url, buf, sizeof (buf), 0);
1138   conn = mutt_conn_find (NULL, &act);
1139   if (!conn)
1140     return -1;
1141
1142   p_delete(&ctx->path);
1143   ctx->path = m_strdup(buf);
1144
1145   pop_data = p_new(pop_data_t, 1);
1146   pop_data->conn = conn;
1147   ctx->data = pop_data;
1148
1149   if (pop_open_connection(pop_data) != PQ_OK)
1150     return -1;
1151
1152   conn->data = pop_data;
1153
1154   for (;;) {
1155     if (pop_reconnect (ctx) != PQ_OK)
1156       return -1;
1157
1158     ctx->size = pop_data->size;
1159
1160     mutt_message _("Fetching list of messages...");
1161
1162     ret = pop_fetch_headers (ctx);
1163
1164     if (ret >= 0)
1165       return 0;
1166
1167     if (ret < -1) {
1168       mutt_sleep (2);
1169       return -1;
1170     }
1171   }
1172 }
1173
1174 static int pop_acl_check(CONTEXT *ctx, int bit)
1175 {
1176     switch (bit) {
1177       case ACL_DELETE:    /* (un)deletion */
1178       case ACL_SEEN:      /* mark as read */
1179         return 1;
1180       case ACL_INSERT:    /* editing messages */
1181       case ACL_WRITE:     /* change importance */
1182       default:
1183         return 0;
1184     }
1185 }
1186
1187 static int pop_check_mailbox(CONTEXT * ctx, int *index_hint, int unused)
1188 {
1189   int ret;
1190   pop_data_t *pop_data = (pop_data_t *) ctx->data;
1191
1192   if ((pop_data->check_time + PopCheckTimeout) > time (NULL))
1193     return 0;
1194
1195   pop_logout (ctx);
1196
1197   mutt_socket_close (pop_data->conn);
1198
1199   if (pop_open_connection (pop_data) < 0)
1200     return -1;
1201
1202   ctx->size = pop_data->size;
1203
1204   mutt_message _("Checking for new messages...");
1205
1206   ret = pop_fetch_headers (ctx);
1207   pop_clear_cache (pop_data);
1208
1209   if (ret < 0)
1210     return -1;
1211
1212   if (ret > 0)
1213     return M_NEW_MAIL;
1214
1215   return 0;
1216 }
1217
1218 static void pop_close_mailbox (CONTEXT * ctx)
1219 {
1220   pop_data_t *pop_data = (pop_data_t *) ctx->data;
1221
1222   if (!pop_data)
1223     return;
1224
1225   pop_logout (ctx);
1226
1227   if (pop_data->status != POP_NONE)
1228     mutt_socket_close (pop_data->conn);
1229
1230   pop_data->status = POP_NONE;
1231
1232   pop_data->clear_cache = 1;
1233   pop_clear_cache (pop_data);
1234
1235   if (!pop_data->conn->data)
1236     mutt_socket_free (pop_data->conn);
1237
1238   return;
1239 }
1240
1241 static int pop_sync_mailbox(CONTEXT * ctx, int unused, int *index_hint)
1242 {
1243   int i;
1244   pop_query_status ret;
1245   char buf[LONG_STRING];
1246   pop_data_t *pop_data = (pop_data_t *) ctx->data;
1247
1248   pop_data->check_time = 0;
1249
1250   for (;;) {
1251     if (pop_reconnect (ctx) != PQ_OK)
1252       return PQ_NOT_CONNECTED;
1253
1254     mutt_message (_("Marking %d messages deleted..."), ctx->deleted);
1255
1256     for (i = 0, ret = 0; ret == 0 && i < ctx->msgcount; i++) {
1257       if (ctx->hdrs[i]->deleted) {
1258         snprintf (buf, sizeof (buf), "DELE %d\r\n", ctx->hdrs[i]->refno);
1259         ret = pop_query (pop_data, buf, sizeof (buf));
1260       }
1261     }
1262
1263     if (ret == PQ_OK) {
1264       m_strcpy(buf, sizeof(buf), "QUIT\r\n");
1265       ret = pop_query (pop_data, buf, sizeof (buf));
1266     }
1267
1268     if (ret == PQ_OK) {
1269       pop_data->clear_cache = 1;
1270       pop_clear_cache (pop_data);
1271       pop_data->status = POP_DISCONNECTED;
1272       return PQ_OK;
1273     }
1274
1275     if (ret == PQ_ERR) {
1276       mutt_error ("%s", pop_data->err_msg);
1277       mutt_sleep (2);
1278       return PQ_NOT_CONNECTED;
1279     }
1280   }
1281 }
1282
1283 /* }}} */
1284
1285 mx_t const pop_mx = {
1286     M_POP,
1287     0,
1288     pop_is_magic,
1289     NULL,
1290     NULL,
1291     pop_open_mailbox,
1292     NULL,
1293     pop_acl_check,
1294     pop_check_mailbox,
1295     pop_close_mailbox,
1296     pop_sync_mailbox,
1297     NULL,
1298 };
1299
1300 /* public API {{{ */
1301
1302 void pop_fetch_mail (void)
1303 {
1304   char buffer[LONG_STRING];
1305   char msgbuf[STRING];
1306   char *url, *p;
1307   int i, delanswer, last = 0, msgs, bytes, rset = 0;
1308   pop_query_status ret;
1309   CONNECTION *conn;
1310   CONTEXT ctx;
1311   MESSAGE *msg = NULL;
1312   ACCOUNT act;
1313   pop_data_t *pop_data;
1314   ssize_t plen;
1315
1316   if (!PopHost) {
1317     mutt_error _("POP host is not defined.");
1318
1319     return;
1320   }
1321
1322   plen = m_strlen(PopHost) + 7;
1323   url = p = p_new(char, plen);
1324   if (url_check_scheme (PopHost) == U_UNKNOWN) {
1325     plen -= m_strcpy(url, plen, "pop://");
1326     p += plen;
1327   }
1328   m_strcpy(p, plen, PopHost);
1329
1330   ret = pop_parse_path (url, &act);
1331   p_delete(&url);
1332   if (ret) {
1333     mutt_error (_("%s is an invalid POP path"), PopHost);
1334     return;
1335   }
1336
1337   conn = mutt_conn_find (NULL, &act);
1338   if (!conn)
1339     return;
1340
1341   pop_data = p_new(pop_data_t, 1);
1342   pop_data->conn = conn;
1343
1344   if (pop_open_connection (pop_data) < 0) {
1345     mutt_socket_free (pop_data->conn);
1346     p_delete(&pop_data);
1347     return;
1348   }
1349
1350   conn->data = pop_data;
1351
1352   mutt_message _("Checking for new messages...");
1353
1354   /* find out how many messages are in the mailbox. */
1355   m_strcpy(buffer, sizeof(buffer), "STAT\r\n");
1356   ret = pop_query (pop_data, buffer, sizeof (buffer));
1357   if (ret == PQ_NOT_CONNECTED)
1358     goto fail;
1359   if (ret == PQ_ERR) {
1360     mutt_error ("%s", pop_data->err_msg);
1361     goto finish;
1362   }
1363
1364   sscanf (buffer, "+OK %d %d", &msgs, &bytes);
1365
1366   /* only get unread messages */
1367   if (msgs > 0 && option (OPTPOPLAST)) {
1368     m_strcpy(buffer, sizeof(buffer), "LAST\r\n");
1369     ret = pop_query (pop_data, buffer, sizeof (buffer));
1370     if (ret == PQ_NOT_CONNECTED)
1371       goto fail;
1372     if (ret == PQ_OK)
1373       sscanf (buffer, "+OK %d", &last);
1374   }
1375
1376   if (msgs <= last) {
1377     mutt_message _("No new mail in POP mailbox.");
1378
1379     goto finish;
1380   }
1381
1382   if (mx_open_mailbox (NONULL (Spoolfile), M_APPEND, &ctx) == NULL)
1383     goto finish;
1384
1385   delanswer =
1386     query_quadoption (OPT_POPDELETE, _("Delete messages from server?"));
1387
1388   snprintf (msgbuf, sizeof (msgbuf), _("Reading new messages (%d bytes)..."),
1389             bytes);
1390   mutt_message ("%s", msgbuf);
1391
1392   for (i = last + 1; i <= msgs; i++) {
1393     if ((msg = mx_open_new_message (&ctx, NULL, M_ADD_FROM)) == NULL)
1394       ret = -3;
1395     else {
1396       snprintf (buffer, sizeof (buffer), "RETR %d\r\n", i);
1397       ret = pop_fetch_data (pop_data, buffer, NULL, fetch_message, msg->fp);
1398       if (ret == PFD_FUNCT_ERROR)
1399         rset = 1;
1400
1401       if (ret == PQ_OK && mx_commit_message (msg, &ctx) != 0) {
1402         rset = 1;
1403         ret = PFD_FUNCT_ERROR;
1404       }
1405
1406       mx_close_message (&msg);
1407     }
1408
1409     if (ret == PQ_OK && delanswer == M_YES) {
1410       /* delete the message on the server */
1411       snprintf (buffer, sizeof (buffer), "DELE %d\r\n", i);
1412       ret = pop_query (pop_data, buffer, sizeof (buffer));
1413     }
1414
1415     if (ret == PQ_NOT_CONNECTED) {
1416       mx_close_mailbox (&ctx, NULL);
1417       goto fail;
1418     }
1419     if (ret == PQ_ERR) {
1420       mutt_error ("%s", pop_data->err_msg);
1421       break;
1422     }
1423     if (ret == -3) { /* this is -3 when ret != 0, because it will keep the value from before *gna* */
1424       mutt_error _("Error while writing mailbox!");
1425
1426       break;
1427     }
1428
1429     mutt_message (_("%s [%d of %d messages read]"), msgbuf, i - last,
1430                   msgs - last);
1431   }
1432
1433   mx_close_mailbox (&ctx, NULL);
1434
1435   if (rset) {
1436     /* make sure no messages get deleted */
1437     m_strcpy(buffer, sizeof(buffer), "RSET\r\n");
1438     if (pop_query (pop_data, buffer, sizeof (buffer)) == PQ_NOT_CONNECTED)
1439       goto fail;
1440   }
1441
1442 finish:
1443   /* exit gracefully */
1444   m_strcpy(buffer, sizeof(buffer), "QUIT\r\n");
1445   if (pop_query (pop_data, buffer, sizeof (buffer)) == PQ_NOT_CONNECTED)
1446     goto fail;
1447   mutt_socket_close (conn);
1448   p_delete(&pop_data);
1449   return;
1450
1451 fail:
1452   mutt_error _("Server closed connection!");
1453   mutt_socket_close (conn);
1454   p_delete(&pop_data);
1455 }
1456
1457 /* fetch message from POP server */
1458 int pop_fetch_message (MESSAGE * msg, CONTEXT * ctx, int msgno)
1459 {
1460   int ret;
1461   void *uidl;
1462   char buf[LONG_STRING];
1463   char path[_POSIX_PATH_MAX];
1464   progress_t bar;
1465   pop_data_t *pop_data = (pop_data_t *) ctx->data;
1466   POP_CACHE *cache;
1467   HEADER *h = ctx->hdrs[msgno];
1468
1469   /* see if we already have the message in our cache */
1470   cache = &pop_data->cache[h->index % POP_CACHE_LEN];
1471
1472   if (cache->path) {
1473     if (cache->index == h->index) {
1474       /* yes, so just return a pointer to the message */
1475       msg->fp = fopen (cache->path, "r");
1476       if (msg->fp)
1477         return 0;
1478
1479       mutt_perror (cache->path);
1480       mutt_sleep (2);
1481       return -1;
1482     }
1483     else {
1484       /* clear the previous entry */
1485       unlink (cache->path);
1486       p_delete(&cache->path);
1487     }
1488   }
1489
1490   for (;;) {
1491     if (pop_reconnect (ctx) != PQ_OK)
1492       return -1;
1493
1494     /* verify that massage index is correct */
1495     if (h->refno < 0) {
1496       mutt_error
1497         _("The message index is incorrect. Try reopening the mailbox.");
1498       mutt_sleep (2);
1499       return -1;
1500     }
1501
1502     bar.size = h->content->length + h->content->offset - 1;
1503     bar.msg = _("Fetching message...");
1504     mutt_progress_bar (&bar, 0);
1505
1506     msg->fp = m_tempfile(path, sizeof(path), NONULL(MCore.tmpdir), NULL);
1507     if (!msg->fp) {
1508       mutt_error(_("Could not create temporary file"));
1509       mutt_sleep(2);
1510       return -1;
1511     }
1512
1513     snprintf (buf, sizeof (buf), "RETR %d\r\n", h->refno);
1514
1515     ret = pop_fetch_data (pop_data, buf, &bar, fetch_message, msg->fp);
1516     if (ret == PQ_OK)
1517       break;
1518
1519     m_fclose(&msg->fp);
1520     unlink (path);
1521
1522     if (ret == PQ_ERR) {
1523       mutt_error ("%s", pop_data->err_msg);
1524       mutt_sleep (2);
1525       return -1;
1526     }
1527
1528     if (ret == PFD_FUNCT_ERROR) {
1529       mutt_error _("Can't write message to temporary file!");
1530
1531       mutt_sleep (2);
1532       return -1;
1533     }
1534   }
1535
1536   /* Update the header information.  Previously, we only downloaded a
1537    * portion of the headers, those required for the main display.
1538    */
1539   cache->index = h->index;
1540   cache->path = m_strdup(path);
1541   rewind (msg->fp);
1542   uidl = h->data;
1543   envelope_delete(&h->env);
1544   h->env = mutt_read_rfc822_header (msg->fp, h, 0, 0);
1545   h->data = uidl;
1546   h->lines = 0;
1547   fgets (buf, sizeof (buf), msg->fp);
1548   while (!feof (msg->fp)) {
1549     ctx->hdrs[msgno]->lines++;
1550     fgets (buf, sizeof (buf), msg->fp);
1551   }
1552
1553   h->content->length = ftello (msg->fp) - h->content->offset;
1554
1555   /* This needs to be done in case this is a multipart message */
1556   h->security = crypt_query (h->content);
1557
1558   mutt_clear_error ();
1559   rewind (msg->fp);
1560
1561   return 0;
1562 }
1563
1564 /* }}} */