mutt stable branch with some hacks
1/*
2 * Copyright (C) 2000-2002 Vsevolod Volkov <vvv@mutt.org.ua>
3 * Copyright (C) 2006-2007,2009 Rocco Rutte <pdmef@gmx.net>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20#if HAVE_CONFIG_H
21# include "config.h"
22#endif
23
24#include "mutt.h"
25#include "mutt_curses.h"
26#include "mx.h"
27#include "pop.h"
28#include "mutt_crypt.h"
29#include "bcache.h"
30#if USE_HCACHE
31#include "hcache.h"
32#endif
33
34#include <string.h>
35#include <unistd.h>
36#include <errno.h>
37
38#ifdef USE_HCACHE
39#define HC_FNAME "mutt" /* filename for hcache as POP lacks paths */
40#define HC_FEXT "hcache" /* extension for hcache as POP lacks paths */
41#endif
42
43/**
44 * cache_id - Make a message-cache-compatible id
45 * @param id POP message id
46 * @retval ptr Sanitised string
47 *
48 * The POP message id may contain '/' and other awkward characters.
49 *
50 * @note This function returns a pointer to a static buffer.
51 */
52static const char *cache_id(const char *id)
53{
54 static char clean[SHORT_STRING];
55
56 strfcpy (clean, id, sizeof(clean));
57 mutt_sanitize_filename (clean, 1);
58
59 return clean;
60}
61
62/* write line to file */
63static int fetch_message (char *line, void *file)
64{
65 FILE *f = (FILE *) file;
66
67 fputs (line, f);
68 if (fputc ('\n', f) == EOF)
69 return -1;
70
71 return 0;
72}
73
74/*
75 * Read header
76 * returns:
77 * 0 on success
78 * -1 - connection lost,
79 * -2 - invalid command or execution error,
80 * -3 - error writing to tempfile
81 */
82static int pop_read_header (POP_DATA *pop_data, HEADER *h)
83{
84 FILE *f;
85 int ret, index;
86 long length;
87 char buf[LONG_STRING];
88 BUFFER *tempfile;
89
90 tempfile = mutt_buffer_pool_get ();
91 mutt_buffer_mktemp (tempfile);
92 if (!(f = safe_fopen (mutt_b2s (tempfile), "w+")))
93 {
94 mutt_perror (mutt_b2s (tempfile));
95 ret = -3;
96 goto cleanup;
97 }
98
99 snprintf (buf, sizeof (buf), "LIST %d\r\n", h->refno);
100 ret = pop_query (pop_data, buf, sizeof (buf));
101 if (ret == 0)
102 {
103 sscanf (buf, "+OK %d %ld", &index, &length);
104
105 snprintf (buf, sizeof (buf), "TOP %d 0\r\n", h->refno);
106 ret = pop_fetch_data (pop_data, buf, NULL, fetch_message, f);
107
108 if (pop_data->cmd_top == 2)
109 {
110 if (ret == 0)
111 {
112 pop_data->cmd_top = 1;
113
114 dprint (1, (debugfile, "pop_read_header: set TOP capability\n"));
115 }
116
117 if (ret == -2)
118 {
119 pop_data->cmd_top = 0;
120
121 dprint (1, (debugfile, "pop_read_header: unset TOP capability\n"));
122 snprintf (pop_data->err_msg, sizeof (pop_data->err_msg), "%s",
123 _("Command TOP is not supported by server."));
124 }
125 }
126 }
127
128 switch (ret)
129 {
130 case 0:
131 {
132 rewind (f);
133 h->env = mutt_read_rfc822_header (f, h, 0, 0);
134 h->content->length = length - h->content->offset + 1;
135 rewind (f);
136 while (!feof (f))
137 {
138 h->content->length--;
139 fgets (buf, sizeof (buf), f);
140 }
141 break;
142 }
143 case -2:
144 {
145 mutt_error ("%s", pop_data->err_msg);
146 break;
147 }
148 case -3:
149 {
150 mutt_error _("Can't write header to temporary file!");
151 break;
152 }
153 }
154
155 safe_fclose (&f);
156 unlink (mutt_b2s (tempfile));
157
158cleanup:
159 mutt_buffer_pool_release (&tempfile);
160 return ret;
161}
162
163/* parse UIDL */
164static int fetch_uidl (char *line, void *data)
165{
166 int i, index;
167 CONTEXT *ctx = (CONTEXT *)data;
168 POP_DATA *pop_data = (POP_DATA *)ctx->data;
169 char *endp;
170
171 errno = 0;
172 index = strtol(line, &endp, 10);
173 if (errno)
174 return -1;
175 while (*endp == ' ')
176 endp++;
177 memmove(line, endp, strlen(endp) + 1);
178
179 /* uid must be at least be 1 byte */
180 if (strlen(line) == 0)
181 return -1;
182
183 for (i = 0; i < ctx->msgcount; i++)
184 if (!mutt_strcmp (line, ctx->hdrs[i]->data))
185 break;
186
187 if (i == ctx->msgcount)
188 {
189 dprint (1, (debugfile, "pop_fetch_headers: new header %d %s\n", index, line));
190
191 if (i >= ctx->hdrmax)
192 mx_alloc_memory(ctx);
193
194 ctx->msgcount++;
195 ctx->hdrs[i] = mutt_new_header ();
196 ctx->hdrs[i]->data = safe_strdup (line);
197 }
198 else if (ctx->hdrs[i]->index != index - 1)
199 pop_data->clear_cache = 1;
200
201 ctx->hdrs[i]->refno = index;
202 ctx->hdrs[i]->index = index - 1;
203
204 return 0;
205}
206
207static int msg_cache_check (const char *id, body_cache_t *bcache, void *data)
208{
209 CONTEXT *ctx;
210 POP_DATA *pop_data;
211 int i;
212
213 if (!(ctx = (CONTEXT *)data))
214 return -1;
215 if (!(pop_data = (POP_DATA *)ctx->data))
216 return -1;
217
218#ifdef USE_HCACHE
219 /* keep hcache file if hcache == bcache */
220 if (strcmp (HC_FNAME "." HC_FEXT, id) == 0)
221 return 0;
222#endif
223
224 for (i = 0; i < ctx->msgcount; i++)
225 /* if the id we get is known for a header: done (i.e. keep in cache) */
226 if (ctx->hdrs[i]->data && mutt_strcmp (ctx->hdrs[i]->data, id) == 0)
227 return 0;
228
229 /* message not found in context -> remove it from cache
230 * return the result of bcache, so we stop upon its first error
231 */
232 return mutt_bcache_del (bcache, cache_id (id));
233}
234
235#ifdef USE_HCACHE
236static void pop_hcache_namer (const char *path, BUFFER *dest)
237{
238 mutt_buffer_printf (dest, "%s." HC_FEXT, path);
239}
240
241static header_cache_t *pop_hcache_open (POP_DATA *pop_data, const char *path)
242{
243 ciss_url_t url;
244 char p[LONG_STRING];
245
246 if (!pop_data || !pop_data->conn)
247 return mutt_hcache_open (HeaderCache, path, NULL);
248
249 mutt_account_tourl (&pop_data->conn->account, &url);
250 url.path = HC_FNAME;
251 url_ciss_tostring (&url, p, sizeof (p), U_PATH);
252 return mutt_hcache_open (HeaderCache, p, pop_hcache_namer);
253}
254#endif
255
256/*
257 * Read headers
258 * returns:
259 * 0 on success
260 * -1 - connection lost,
261 * -2 - invalid command or execution error,
262 * -3 - error writing to tempfile
263 */
264static int pop_fetch_headers (CONTEXT *ctx)
265{
266 int i, ret, old_count, new_count, deleted;
267 unsigned short hcached = 0, bcached;
268 POP_DATA *pop_data = (POP_DATA *)ctx->data;
269 progress_t progress;
270
271#ifdef USE_HCACHE
272 header_cache_t *hc = NULL;
273 void *data;
274
275 hc = pop_hcache_open (pop_data, ctx->path);
276#endif
277
278 time (&pop_data->check_time);
279 pop_data->clear_cache = 0;
280
281 for (i = 0; i < ctx->msgcount; i++)
282 ctx->hdrs[i]->refno = -1;
283
284 old_count = ctx->msgcount;
285 ret = pop_fetch_data (pop_data, "UIDL\r\n", NULL, fetch_uidl, ctx);
286 new_count = ctx->msgcount;
287 ctx->msgcount = old_count;
288
289 if (pop_data->cmd_uidl == 2)
290 {
291 if (ret == 0)
292 {
293 pop_data->cmd_uidl = 1;
294
295 dprint (1, (debugfile, "pop_fetch_headers: set UIDL capability\n"));
296 }
297
298 if (ret == -2 && pop_data->cmd_uidl == 2)
299 {
300 pop_data->cmd_uidl = 0;
301
302 dprint (1, (debugfile, "pop_fetch_headers: unset UIDL capability\n"));
303 snprintf (pop_data->err_msg, sizeof (pop_data->err_msg), "%s",
304 _("Command UIDL is not supported by server."));
305 }
306 }
307
308 if (!ctx->quiet)
309 mutt_progress_init (&progress, _("Fetching message headers..."),
310 MUTT_PROGRESS_MSG, ReadInc, new_count - old_count);
311
312 if (ret == 0)
313 {
314 for (i = 0, deleted = 0; i < old_count; i++)
315 {
316 if (ctx->hdrs[i]->refno == -1)
317 {
318 ctx->hdrs[i]->deleted = 1;
319 deleted++;
320 }
321 }
322 if (deleted > 0)
323 {
324 mutt_error (_("%d messages have been lost. Try reopening the mailbox."),
325 deleted);
326 mutt_sleep (2);
327 }
328
329 for (i = old_count; i < new_count; i++)
330 {
331 if (!ctx->quiet)
332 mutt_progress_update (&progress, i + 1 - old_count, -1);
333#if USE_HCACHE
334 if ((data = mutt_hcache_fetch (hc, ctx->hdrs[i]->data, strlen)))
335 {
336 char *uidl = safe_strdup (ctx->hdrs[i]->data);
337 int refno = ctx->hdrs[i]->refno;
338 int index = ctx->hdrs[i]->index;
339 /*
340 * - POP dynamically numbers headers and relies on h->refno
341 * to map messages; so restore header and overwrite restored
342 * refno with current refno, same for index
343 * - h->data needs to a separate pointer as it's driver-specific
344 * data freed separately elsewhere
345 * (the old h->data should point inside a malloc'd block from
346 * hcache so there shouldn't be a memleak here)
347 */
348 HEADER *h = mutt_hcache_restore ((unsigned char *) data, NULL);
349 mutt_free_header (&ctx->hdrs[i]);
350 ctx->hdrs[i] = h;
351 ctx->hdrs[i]->refno = refno;
352 ctx->hdrs[i]->index = index;
353 ctx->hdrs[i]->data = uidl;
354 ret = 0;
355 hcached = 1;
356 }
357 else
358#endif
359 if ((ret = pop_read_header (pop_data, ctx->hdrs[i])) < 0)
360 break;
361#if USE_HCACHE
362 else
363 {
364 mutt_hcache_store (hc, ctx->hdrs[i]->data, ctx->hdrs[i], 0, strlen, MUTT_GENERATE_UIDVALIDITY);
365 }
366
367 mutt_hcache_free (&data);
368#endif
369
370 /*
371 * faked support for flags works like this:
372 * - if 'hcached' is 1, we have the message in our hcache:
373 * - if we also have a body: read
374 * - if we don't have a body: old
375 * (if $mark_old is set which is maybe wrong as
376 * $mark_old should be considered for syncing the
377 * folder and not when opening it XXX)
378 * - if 'hcached' is 0, we don't have the message in our hcache:
379 * - if we also have a body: read
380 * - if we don't have a body: new
381 */
382 bcached = mutt_bcache_exists (pop_data->bcache, cache_id (ctx->hdrs[i]->data)) == 0;
383 ctx->hdrs[i]->old = 0;
384 ctx->hdrs[i]->read = 0;
385 if (hcached)
386 {
387 if (bcached)
388 ctx->hdrs[i]->read = 1;
389 else if (option (OPTMARKOLD))
390 ctx->hdrs[i]->old = 1;
391 }
392 else
393 {
394 if (bcached)
395 ctx->hdrs[i]->read = 1;
396 }
397
398 ctx->msgcount++;
399 }
400
401 if (i > old_count)
402 mx_update_context (ctx, i - old_count);
403 }
404
405#if USE_HCACHE
406 mutt_hcache_close (hc);
407#endif
408
409 if (ret < 0)
410 {
411 for (i = ctx->msgcount; i < new_count; i++)
412 mutt_free_header (&ctx->hdrs[i]);
413 return ret;
414 }
415
416 /* after putting the result into our structures,
417 * clean up cache, i.e. wipe messages deleted outside
418 * the availability of our cache
419 */
420 if (option (OPTMESSAGECACHECLEAN))
421 mutt_bcache_list (pop_data->bcache, msg_cache_check, (void*)ctx);
422
423 mutt_clear_error ();
424 return (new_count - old_count);
425}
426
427/* open POP mailbox - fetch only headers */
428static int pop_open_mailbox (CONTEXT *ctx)
429{
430 int ret;
431 char buf[LONG_STRING];
432 CONNECTION *conn;
433 ACCOUNT acct;
434 POP_DATA *pop_data;
435 ciss_url_t url;
436
437 if (pop_parse_path (ctx->path, &acct))
438 {
439 mutt_error (_("%s is an invalid POP path"), ctx->path);
440 mutt_sleep (2);
441 return -1;
442 }
443
444 mutt_account_tourl (&acct, &url);
445 url.path = NULL;
446 url_ciss_tostring (&url, buf, sizeof (buf), 0);
447 conn = mutt_conn_find (NULL, &acct);
448 if (!conn)
449 return -1;
450
451 FREE (&ctx->path);
452 FREE (&ctx->realpath);
453 ctx->path = safe_strdup (buf);
454 ctx->realpath = safe_strdup (ctx->path);
455
456 pop_data = safe_calloc (1, sizeof (POP_DATA));
457 pop_data->conn = conn;
458 ctx->data = pop_data;
459
460 if (pop_open_connection (pop_data) < 0)
461 return -1;
462
463 conn->data = pop_data;
464 pop_data->bcache = mutt_bcache_open (&acct, NULL);
465
466 /* init (hard-coded) ACL rights */
467 memset (ctx->rights, 0, sizeof (ctx->rights));
468 mutt_bit_set (ctx->rights, MUTT_ACL_SEEN);
469 mutt_bit_set (ctx->rights, MUTT_ACL_DELETE);
470#if USE_HCACHE
471 /* flags are managed using header cache, so it only makes sense to
472 * enable them in that case */
473 mutt_bit_set (ctx->rights, MUTT_ACL_WRITE);
474#endif
475
476 FOREVER
477 {
478 if (pop_reconnect (ctx) < 0)
479 return -1;
480
481 ctx->size = pop_data->size;
482
483 mutt_message _("Fetching list of messages...");
484
485 ret = pop_fetch_headers (ctx);
486
487 if (ret >= 0)
488 return 0;
489
490 if (ret < -1)
491 {
492 mutt_sleep (2);
493 return -1;
494 }
495 }
496}
497
498/* delete all cached messages */
499static void pop_clear_cache (POP_DATA *pop_data)
500{
501 int i;
502
503 if (!pop_data->clear_cache)
504 return;
505
506 dprint (1, (debugfile, "pop_clear_cache: delete cached messages\n"));
507
508 for (i = 0; i < POP_CACHE_LEN; i++)
509 {
510 if (pop_data->cache[i].path)
511 {
512 unlink (pop_data->cache[i].path);
513 FREE (&pop_data->cache[i].path);
514 }
515 }
516}
517
518/* close POP mailbox */
519int pop_close_mailbox (CONTEXT *ctx)
520{
521 POP_DATA *pop_data = (POP_DATA *)ctx->data;
522
523 if (!pop_data)
524 return 0;
525
526 pop_logout (ctx);
527
528 if (pop_data->status != POP_NONE)
529 mutt_socket_close (pop_data->conn);
530
531 pop_data->status = POP_NONE;
532
533 pop_data->clear_cache = 1;
534 pop_clear_cache (pop_data);
535
536 if (!pop_data->conn->data)
537 mutt_socket_free (pop_data->conn);
538
539 mutt_bcache_close (&pop_data->bcache);
540
541 return 0;
542}
543
544/* fetch message from POP server */
545static int pop_fetch_message (CONTEXT* ctx, MESSAGE* msg, int msgno)
546{
547 int ret, rc = -1;
548 void *uidl;
549 char buf[LONG_STRING];
550 BUFFER *path = NULL;
551 progress_t progressbar;
552 POP_DATA *pop_data = (POP_DATA *)ctx->data;
553 POP_CACHE *cache;
554 HEADER *h = ctx->hdrs[msgno];
555 unsigned short bcache = 1;
556
557 /* see if we already have the message in body cache */
558 if ((msg->fp = mutt_bcache_get (pop_data->bcache, cache_id (h->data))))
559 return 0;
560
561 /*
562 * see if we already have the message in our cache in
563 * case $message_cachedir is unset
564 */
565 cache = &pop_data->cache[h->index % POP_CACHE_LEN];
566
567 if (cache->path)
568 {
569 if (cache->index == h->index)
570 {
571 /* yes, so just return a pointer to the message */
572 msg->fp = fopen (cache->path, "r");
573 if (msg->fp)
574 return 0;
575
576 mutt_perror (cache->path);
577 mutt_sleep (2);
578 return -1;
579 }
580 else
581 {
582 /* clear the previous entry */
583 unlink (cache->path);
584 FREE (&cache->path);
585 }
586 }
587
588 path = mutt_buffer_pool_get ();
589
590 FOREVER
591 {
592 if (pop_reconnect (ctx) < 0)
593 goto cleanup;
594
595 /* verify that massage index is correct */
596 if (h->refno < 0)
597 {
598 mutt_error _("The message index is incorrect. Try reopening the mailbox.");
599 mutt_sleep (2);
600 goto cleanup;
601 }
602
603 mutt_progress_init (&progressbar, _("Fetching message..."),
604 MUTT_PROGRESS_SIZE, NetInc, h->content->length + h->content->offset - 1);
605
606 /* see if we can put in body cache; use our cache as fallback */
607 if (!(msg->fp = mutt_bcache_put (pop_data->bcache, cache_id (h->data), 1)))
608 {
609 /* no */
610 bcache = 0;
611 mutt_buffer_mktemp (path);
612 if (!(msg->fp = safe_fopen (mutt_b2s (path), "w+")))
613 {
614 mutt_perror (mutt_b2s (path));
615 mutt_sleep (2);
616 goto cleanup;
617 }
618 }
619
620 snprintf (buf, sizeof (buf), "RETR %d\r\n", h->refno);
621
622 ret = pop_fetch_data (pop_data, buf, &progressbar, fetch_message, msg->fp);
623 if (ret == 0)
624 break;
625
626 safe_fclose (&msg->fp);
627
628 /* if RETR failed (e.g. connection closed), be sure to remove either
629 * the file in bcache or from POP's own cache since the next iteration
630 * of the loop will re-attempt to put() the message */
631 if (!bcache)
632 unlink (mutt_b2s (path));
633
634 if (ret == -2)
635 {
636 mutt_error ("%s", pop_data->err_msg);
637 mutt_sleep (2);
638 goto cleanup;
639 }
640
641 if (ret == -3)
642 {
643 mutt_error _("Can't write message to temporary file!");
644 mutt_sleep (2);
645 goto cleanup;
646 }
647 }
648
649 /* Update the header information. Previously, we only downloaded a
650 * portion of the headers, those required for the main display.
651 */
652 if (bcache)
653 mutt_bcache_commit (pop_data->bcache, cache_id (h->data));
654 else
655 {
656 cache->index = h->index;
657 cache->path = safe_strdup (mutt_b2s (path));
658 }
659 rewind (msg->fp);
660 uidl = h->data;
661
662 /* we replace envelop, key in subj_hash has to be updated as well */
663 if (ctx->subj_hash && h->env->real_subj)
664 hash_delete (ctx->subj_hash, h->env->real_subj, h, NULL);
665 mutt_label_hash_remove (ctx, h);
666 mutt_free_envelope (&h->env);
667 h->env = mutt_read_rfc822_header (msg->fp, h, 0, 0);
668 if (ctx->subj_hash && h->env->real_subj)
669 hash_insert (ctx->subj_hash, h->env->real_subj, h);
670 mutt_label_hash_add (ctx, h);
671
672 h->data = uidl;
673 h->lines = 0;
674 fgets (buf, sizeof (buf), msg->fp);
675 while (!feof (msg->fp))
676 {
677 ctx->hdrs[msgno]->lines++;
678 fgets (buf, sizeof (buf), msg->fp);
679 }
680
681 h->content->length = ftello (msg->fp) - h->content->offset;
682
683 /* This needs to be done in case this is a multipart message */
684 if (!WithCrypto)
685 h->security = crypt_query (h->content);
686
687 mutt_clear_error();
688 rewind (msg->fp);
689
690 rc = 0;
691
692cleanup:
693 mutt_buffer_pool_release (&path);
694 return rc;
695}
696
697static int pop_close_message (CONTEXT *ctx, MESSAGE *msg)
698{
699 return safe_fclose (&msg->fp);
700}
701
702/* update POP mailbox - delete messages from server */
703static int pop_sync_mailbox (CONTEXT *ctx, int *index_hint)
704{
705 int i, j, ret = 0;
706 char buf[LONG_STRING];
707 POP_DATA *pop_data = (POP_DATA *)ctx->data;
708 progress_t progress;
709#ifdef USE_HCACHE
710 header_cache_t *hc = NULL;
711#endif
712
713 pop_data->check_time = 0;
714
715 FOREVER
716 {
717 if (pop_reconnect (ctx) < 0)
718 return -1;
719
720 mutt_progress_init (&progress, _("Marking messages deleted..."),
721 MUTT_PROGRESS_MSG, WriteInc, ctx->deleted);
722
723#if USE_HCACHE
724 hc = pop_hcache_open (pop_data, ctx->path);
725#endif
726
727 for (i = 0, j = 0, ret = 0; ret == 0 && i < ctx->msgcount; i++)
728 {
729 if (ctx->hdrs[i]->deleted && ctx->hdrs[i]->refno != -1)
730 {
731 j++;
732 if (!ctx->quiet)
733 mutt_progress_update (&progress, j, -1);
734 snprintf (buf, sizeof (buf), "DELE %d\r\n", ctx->hdrs[i]->refno);
735 if ((ret = pop_query (pop_data, buf, sizeof (buf))) == 0)
736 {
737 mutt_bcache_del (pop_data->bcache, cache_id (ctx->hdrs[i]->data));
738#if USE_HCACHE
739 mutt_hcache_delete (hc, ctx->hdrs[i]->data, strlen);
740#endif
741 }
742 }
743
744#if USE_HCACHE
745 if (ctx->hdrs[i]->changed)
746 {
747 mutt_hcache_store (hc, ctx->hdrs[i]->data, ctx->hdrs[i], 0, strlen, MUTT_GENERATE_UIDVALIDITY);
748 }
749#endif
750
751 }
752
753#if USE_HCACHE
754 mutt_hcache_close (hc);
755#endif
756
757 if (ret == 0)
758 {
759 strfcpy (buf, "QUIT\r\n", sizeof (buf));
760 ret = pop_query (pop_data, buf, sizeof (buf));
761 }
762
763 if (ret == 0)
764 {
765 pop_data->clear_cache = 1;
766 pop_clear_cache (pop_data);
767 pop_data->status = POP_DISCONNECTED;
768 return 0;
769 }
770
771 if (ret == -2)
772 {
773 mutt_error ("%s", pop_data->err_msg);
774 mutt_sleep (2);
775 return -1;
776 }
777 }
778}
779
780/* Check for new messages and fetch headers */
781static int pop_check_mailbox (CONTEXT *ctx, int *index_hint)
782{
783 int ret;
784 POP_DATA *pop_data = (POP_DATA *)ctx->data;
785
786 if ((pop_data->check_time + PopCheckTimeout) > time (NULL))
787 return 0;
788
789 pop_logout (ctx);
790
791 mutt_socket_close (pop_data->conn);
792
793 if (pop_open_connection (pop_data) < 0)
794 return -1;
795
796 ctx->size = pop_data->size;
797
798 mutt_message _("Checking for new messages...");
799
800 ret = pop_fetch_headers (ctx);
801 pop_clear_cache (pop_data);
802
803 if (ret < 0)
804 return -1;
805
806 if (ret > 0)
807 return MUTT_NEW_MAIL;
808
809 return 0;
810}
811
812static int pop_save_to_header_cache (CONTEXT *ctx, HEADER *h)
813{
814 int rc = 0;
815#ifdef USE_HCACHE
816 POP_DATA *pop_data;
817 header_cache_t *hc;
818
819 pop_data = (POP_DATA *)ctx->data;
820 hc = pop_hcache_open (pop_data, ctx->path);
821 rc = mutt_hcache_store (hc, h->data, h, 0, strlen, MUTT_GENERATE_UIDVALIDITY);
822 mutt_hcache_close (hc);
823#endif
824
825 return rc;
826}
827
828/* Fetch messages and save them in $spoolfile */
829void pop_fetch_mail (void)
830{
831 char buffer[LONG_STRING];
832 char msgbuf[SHORT_STRING];
833 char *url, *p;
834 int i, delanswer, last = 0, msgs, bytes, rset = 0, ret;
835 CONNECTION *conn;
836 CONTEXT ctx;
837 MESSAGE *msg = NULL;
838 ACCOUNT acct;
839 POP_DATA *pop_data;
840
841 if (!PopHost)
842 {
843 mutt_error _("POP host is not defined.");
844 return;
845 }
846
847 url = p = safe_calloc (strlen (PopHost) + 7, sizeof (char));
848 if (url_check_scheme (PopHost) == U_UNKNOWN)
849 {
850 strcpy (url, "pop://"); /* __STRCPY_CHECKED__ */
851 p = strchr (url, '\0');
852 }
853 strcpy (p, PopHost); /* __STRCPY_CHECKED__ */
854
855 ret = pop_parse_path (url, &acct);
856 FREE (&url);
857 if (ret)
858 {
859 mutt_error (_("%s is an invalid POP path"), PopHost);
860 return;
861 }
862
863 conn = mutt_conn_find (NULL, &acct);
864 if (!conn)
865 return;
866
867 pop_data = safe_calloc (1, sizeof (POP_DATA));
868 pop_data->conn = conn;
869
870 if (pop_open_connection (pop_data) < 0)
871 {
872 mutt_socket_free (pop_data->conn);
873 FREE (&pop_data);
874 return;
875 }
876
877 conn->data = pop_data;
878
879 mutt_message _("Checking for new messages...");
880
881 /* find out how many messages are in the mailbox. */
882 strfcpy (buffer, "STAT\r\n", sizeof (buffer));
883 ret = pop_query (pop_data, buffer, sizeof (buffer));
884 if (ret == -1)
885 goto fail;
886 if (ret == -2)
887 {
888 mutt_error ("%s", pop_data->err_msg);
889 goto finish;
890 }
891
892 sscanf (buffer, "+OK %d %d", &msgs, &bytes);
893
894 /* only get unread messages */
895 if (msgs > 0 && option (OPTPOPLAST))
896 {
897 strfcpy (buffer, "LAST\r\n", sizeof (buffer));
898 ret = pop_query (pop_data, buffer, sizeof (buffer));
899 if (ret == -1)
900 goto fail;
901 if (ret == 0)
902 sscanf (buffer, "+OK %d", &last);
903 }
904
905 if (msgs <= last)
906 {
907 mutt_message _("No new mail in POP mailbox.");
908 goto finish;
909 }
910
911 if (mx_open_mailbox (NONULL (Spoolfile), MUTT_APPEND, &ctx) == NULL)
912 goto finish;
913
914 delanswer = query_quadoption (OPT_POPDELETE, _("Delete messages from server?"));
915
916 snprintf (msgbuf, sizeof (msgbuf), _("Reading new messages (%d bytes)..."), bytes);
917 mutt_message ("%s", msgbuf);
918
919 for (i = last + 1 ; i <= msgs ; i++)
920 {
921 if ((msg = mx_open_new_message (&ctx, NULL, MUTT_ADD_FROM)) == NULL)
922 ret = -3;
923 else
924 {
925 snprintf (buffer, sizeof (buffer), "RETR %d\r\n", i);
926 ret = pop_fetch_data (pop_data, buffer, NULL, fetch_message, msg->fp);
927 if (ret == -3)
928 rset = 1;
929
930 if (ret == 0 && mx_commit_message (msg, &ctx) != 0)
931 {
932 rset = 1;
933 ret = -3;
934 }
935
936 mx_close_message (&ctx, &msg);
937 }
938
939 if (ret == 0 && delanswer == MUTT_YES)
940 {
941 /* delete the message on the server */
942 snprintf (buffer, sizeof (buffer), "DELE %d\r\n", i);
943 ret = pop_query (pop_data, buffer, sizeof (buffer));
944 }
945
946 if (ret == -1)
947 {
948 mx_close_mailbox (&ctx, NULL);
949 goto fail;
950 }
951 if (ret == -2)
952 {
953 mutt_error ("%s", pop_data->err_msg);
954 break;
955 }
956 if (ret == -3)
957 {
958 mutt_error _("Error while writing mailbox!");
959 break;
960 }
961
962 mutt_message (_("%s [%d of %d messages read]"), msgbuf, i - last, msgs - last);
963 }
964
965 mx_close_mailbox (&ctx, NULL);
966
967 if (rset)
968 {
969 /* make sure no messages get deleted */
970 strfcpy (buffer, "RSET\r\n", sizeof (buffer));
971 if (pop_query (pop_data, buffer, sizeof (buffer)) == -1)
972 goto fail;
973 }
974
975finish:
976 /* exit gracefully */
977 strfcpy (buffer, "QUIT\r\n", sizeof (buffer));
978 if (pop_query (pop_data, buffer, sizeof (buffer)) == -1)
979 goto fail;
980 mutt_socket_close (conn);
981 FREE (&pop_data);
982 return;
983
984fail:
985 mutt_error _("Server closed connection!");
986 mutt_socket_close (conn);
987 FREE (&pop_data);
988}
989
990struct mx_ops mx_pop_ops = {
991 .open = pop_open_mailbox,
992 .open_append = NULL,
993 .close = pop_close_mailbox,
994 .open_msg = pop_fetch_message,
995 .close_msg = pop_close_message,
996 .check = pop_check_mailbox,
997 .commit_msg = NULL,
998 .open_new_msg = NULL,
999 .sync = pop_sync_mailbox,
1000 .save_to_header_cache = pop_save_to_header_cache,
1001};