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