2 * Copyright (C) 2000-2002 Vsevolod Volkov <vvv@mutt.org.ua>
3 * Copyright (C) 2006-2007,2009 Rocco Rutte <pdmef@gmx.net>
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.
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.
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.
25 #include "mutt_curses.h"
28 #include "mutt_crypt.h"
39 #define HC_FNAME "mutt" /* filename for hcache as POP lacks paths */
40 #define HC_FEXT "hcache" /* extension for hcache as POP lacks paths */
44 * cache_id - Make a message-cache-compatible id
45 * @param id POP message id
46 * @retval ptr Sanitised string
48 * The POP message id may contain '/' and other awkward characters.
50 * @note This function returns a pointer to a static buffer.
52 static const char *cache_id(const char *id)
54 static char clean[SHORT_STRING];
56 strfcpy (clean, id, sizeof(clean));
57 mutt_sanitize_filename (clean, 1);
62 /* write line to file */
63 static int fetch_message (char *line, void *file)
65 FILE *f = (FILE *) file;
68 if (fputc ('\n', f) == EOF)
78 * -1 - connection lost,
79 * -2 - invalid command or execution error,
80 * -3 - error writing to tempfile
82 static int pop_read_header (POP_DATA *pop_data, HEADER *h)
87 char buf[LONG_STRING];
88 char tempfile[_POSIX_PATH_MAX];
90 mutt_mktemp (tempfile, sizeof (tempfile));
91 if (!(f = safe_fopen (tempfile, "w+")))
93 mutt_perror (tempfile);
97 snprintf (buf, sizeof (buf), "LIST %d\r\n", h->refno);
98 ret = pop_query (pop_data, buf, sizeof (buf));
101 sscanf (buf, "+OK %d %ld", &index, &length);
103 snprintf (buf, sizeof (buf), "TOP %d 0\r\n", h->refno);
104 ret = pop_fetch_data (pop_data, buf, NULL, fetch_message, f);
106 if (pop_data->cmd_top == 2)
110 pop_data->cmd_top = 1;
112 dprint (1, (debugfile, "pop_read_header: set TOP capability\n"));
117 pop_data->cmd_top = 0;
119 dprint (1, (debugfile, "pop_read_header: unset TOP capability\n"));
120 snprintf (pop_data->err_msg, sizeof (pop_data->err_msg), "%s",
121 _("Command TOP is not supported by server."));
131 h->env = mutt_read_rfc822_header (f, h, 0, 0);
132 h->content->length = length - h->content->offset + 1;
136 h->content->length--;
137 fgets (buf, sizeof (buf), f);
143 mutt_error ("%s", pop_data->err_msg);
148 mutt_error _("Can't write header to temporary file!");
159 static int fetch_uidl (char *line, void *data)
162 CONTEXT *ctx = (CONTEXT *)data;
163 POP_DATA *pop_data = (POP_DATA *)ctx->data;
167 index = strtol(line, &endp, 10);
172 memmove(line, endp, strlen(endp) + 1);
174 /* uid must be at least be 1 byte */
175 if (strlen(line) == 0)
178 for (i = 0; i < ctx->msgcount; i++)
179 if (!mutt_strcmp (line, ctx->hdrs[i]->data))
182 if (i == ctx->msgcount)
184 dprint (1, (debugfile, "pop_fetch_headers: new header %d %s\n", index, line));
186 if (i >= ctx->hdrmax)
187 mx_alloc_memory(ctx);
190 ctx->hdrs[i] = mutt_new_header ();
191 ctx->hdrs[i]->data = safe_strdup (line);
193 else if (ctx->hdrs[i]->index != index - 1)
194 pop_data->clear_cache = 1;
196 ctx->hdrs[i]->refno = index;
197 ctx->hdrs[i]->index = index - 1;
202 static int msg_cache_check (const char *id, body_cache_t *bcache, void *data)
208 if (!(ctx = (CONTEXT *)data))
210 if (!(pop_data = (POP_DATA *)ctx->data))
214 /* keep hcache file if hcache == bcache */
215 if (strcmp (HC_FNAME "." HC_FEXT, id) == 0)
219 for (i = 0; i < ctx->msgcount; i++)
220 /* if the id we get is known for a header: done (i.e. keep in cache) */
221 if (ctx->hdrs[i]->data && mutt_strcmp (ctx->hdrs[i]->data, id) == 0)
224 /* message not found in context -> remove it from cache
225 * return the result of bcache, so we stop upon its first error
227 return mutt_bcache_del (bcache, cache_id (id));
231 static void pop_hcache_namer (const char *path, BUFFER *dest)
233 mutt_buffer_printf (dest, "%s." HC_FEXT, path);
236 static header_cache_t *pop_hcache_open (POP_DATA *pop_data, const char *path)
241 if (!pop_data || !pop_data->conn)
242 return mutt_hcache_open (HeaderCache, path, NULL);
244 mutt_account_tourl (&pop_data->conn->account, &url);
246 url_ciss_tostring (&url, p, sizeof (p), U_PATH);
247 return mutt_hcache_open (HeaderCache, p, pop_hcache_namer);
255 * -1 - connection lost,
256 * -2 - invalid command or execution error,
257 * -3 - error writing to tempfile
259 static int pop_fetch_headers (CONTEXT *ctx)
261 int i, ret, old_count, new_count, deleted;
262 unsigned short hcached = 0, bcached;
263 POP_DATA *pop_data = (POP_DATA *)ctx->data;
267 header_cache_t *hc = NULL;
270 hc = pop_hcache_open (pop_data, ctx->path);
273 time (&pop_data->check_time);
274 pop_data->clear_cache = 0;
276 for (i = 0; i < ctx->msgcount; i++)
277 ctx->hdrs[i]->refno = -1;
279 old_count = ctx->msgcount;
280 ret = pop_fetch_data (pop_data, "UIDL\r\n", NULL, fetch_uidl, ctx);
281 new_count = ctx->msgcount;
282 ctx->msgcount = old_count;
284 if (pop_data->cmd_uidl == 2)
288 pop_data->cmd_uidl = 1;
290 dprint (1, (debugfile, "pop_fetch_headers: set UIDL capability\n"));
293 if (ret == -2 && pop_data->cmd_uidl == 2)
295 pop_data->cmd_uidl = 0;
297 dprint (1, (debugfile, "pop_fetch_headers: unset UIDL capability\n"));
298 snprintf (pop_data->err_msg, sizeof (pop_data->err_msg), "%s",
299 _("Command UIDL is not supported by server."));
304 mutt_progress_init (&progress, _("Fetching message headers..."),
305 MUTT_PROGRESS_MSG, ReadInc, new_count - old_count);
309 for (i = 0, deleted = 0; i < old_count; i++)
311 if (ctx->hdrs[i]->refno == -1)
313 ctx->hdrs[i]->deleted = 1;
319 mutt_error (_("%d messages have been lost. Try reopening the mailbox."),
324 for (i = old_count; i < new_count; i++)
327 mutt_progress_update (&progress, i + 1 - old_count, -1);
329 if ((data = mutt_hcache_fetch (hc, ctx->hdrs[i]->data, strlen)))
331 char *uidl = safe_strdup (ctx->hdrs[i]->data);
332 int refno = ctx->hdrs[i]->refno;
333 int index = ctx->hdrs[i]->index;
335 * - POP dynamically numbers headers and relies on h->refno
336 * to map messages; so restore header and overwrite restored
337 * refno with current refno, same for index
338 * - h->data needs to a separate pointer as it's driver-specific
339 * data freed separately elsewhere
340 * (the old h->data should point inside a malloc'd block from
341 * hcache so there shouldn't be a memleak here)
343 HEADER *h = mutt_hcache_restore ((unsigned char *) data, NULL);
344 mutt_free_header (&ctx->hdrs[i]);
346 ctx->hdrs[i]->refno = refno;
347 ctx->hdrs[i]->index = index;
348 ctx->hdrs[i]->data = uidl;
354 if ((ret = pop_read_header (pop_data, ctx->hdrs[i])) < 0)
359 mutt_hcache_store (hc, ctx->hdrs[i]->data, ctx->hdrs[i], 0, strlen, MUTT_GENERATE_UIDVALIDITY);
362 mutt_hcache_free (&data);
366 * faked support for flags works like this:
367 * - if 'hcached' is 1, we have the message in our hcache:
368 * - if we also have a body: read
369 * - if we don't have a body: old
370 * (if $mark_old is set which is maybe wrong as
371 * $mark_old should be considered for syncing the
372 * folder and not when opening it XXX)
373 * - if 'hcached' is 0, we don't have the message in our hcache:
374 * - if we also have a body: read
375 * - if we don't have a body: new
377 bcached = mutt_bcache_exists (pop_data->bcache, cache_id (ctx->hdrs[i]->data)) == 0;
378 ctx->hdrs[i]->old = 0;
379 ctx->hdrs[i]->read = 0;
383 ctx->hdrs[i]->read = 1;
384 else if (option (OPTMARKOLD))
385 ctx->hdrs[i]->old = 1;
390 ctx->hdrs[i]->read = 1;
397 mx_update_context (ctx, i - old_count);
401 mutt_hcache_close (hc);
406 for (i = ctx->msgcount; i < new_count; i++)
407 mutt_free_header (&ctx->hdrs[i]);
411 /* after putting the result into our structures,
412 * clean up cache, i.e. wipe messages deleted outside
413 * the availability of our cache
415 if (option (OPTMESSAGECACHECLEAN))
416 mutt_bcache_list (pop_data->bcache, msg_cache_check, (void*)ctx);
419 return (new_count - old_count);
422 /* open POP mailbox - fetch only headers */
423 static int pop_open_mailbox (CONTEXT *ctx)
426 char buf[LONG_STRING];
432 if (pop_parse_path (ctx->path, &acct))
434 mutt_error (_("%s is an invalid POP path"), ctx->path);
439 mutt_account_tourl (&acct, &url);
441 url_ciss_tostring (&url, buf, sizeof (buf), 0);
442 conn = mutt_conn_find (NULL, &acct);
447 FREE (&ctx->realpath);
448 ctx->path = safe_strdup (buf);
449 ctx->realpath = safe_strdup (ctx->path);
451 pop_data = safe_calloc (1, sizeof (POP_DATA));
452 pop_data->conn = conn;
453 ctx->data = pop_data;
455 if (pop_open_connection (pop_data) < 0)
458 conn->data = pop_data;
459 pop_data->bcache = mutt_bcache_open (&acct, NULL);
461 /* init (hard-coded) ACL rights */
462 memset (ctx->rights, 0, sizeof (ctx->rights));
463 mutt_bit_set (ctx->rights, MUTT_ACL_SEEN);
464 mutt_bit_set (ctx->rights, MUTT_ACL_DELETE);
466 /* flags are managed using header cache, so it only makes sense to
467 * enable them in that case */
468 mutt_bit_set (ctx->rights, MUTT_ACL_WRITE);
473 if (pop_reconnect (ctx) < 0)
476 ctx->size = pop_data->size;
478 mutt_message _("Fetching list of messages...");
480 ret = pop_fetch_headers (ctx);
493 /* delete all cached messages */
494 static void pop_clear_cache (POP_DATA *pop_data)
498 if (!pop_data->clear_cache)
501 dprint (1, (debugfile, "pop_clear_cache: delete cached messages\n"));
503 for (i = 0; i < POP_CACHE_LEN; i++)
505 if (pop_data->cache[i].path)
507 unlink (pop_data->cache[i].path);
508 FREE (&pop_data->cache[i].path);
513 /* close POP mailbox */
514 int pop_close_mailbox (CONTEXT *ctx)
516 POP_DATA *pop_data = (POP_DATA *)ctx->data;
523 if (pop_data->status != POP_NONE)
524 mutt_socket_close (pop_data->conn);
526 pop_data->status = POP_NONE;
528 pop_data->clear_cache = 1;
529 pop_clear_cache (pop_data);
531 if (!pop_data->conn->data)
532 mutt_socket_free (pop_data->conn);
534 mutt_bcache_close (&pop_data->bcache);
539 /* fetch message from POP server */
540 static int pop_fetch_message (CONTEXT* ctx, MESSAGE* msg, int msgno)
544 char buf[LONG_STRING];
545 char path[_POSIX_PATH_MAX];
546 progress_t progressbar;
547 POP_DATA *pop_data = (POP_DATA *)ctx->data;
549 HEADER *h = ctx->hdrs[msgno];
550 unsigned short bcache = 1;
552 /* see if we already have the message in body cache */
553 if ((msg->fp = mutt_bcache_get (pop_data->bcache, cache_id (h->data))))
557 * see if we already have the message in our cache in
558 * case $message_cachedir is unset
560 cache = &pop_data->cache[h->index % POP_CACHE_LEN];
564 if (cache->index == h->index)
566 /* yes, so just return a pointer to the message */
567 msg->fp = fopen (cache->path, "r");
571 mutt_perror (cache->path);
577 /* clear the previous entry */
578 unlink (cache->path);
585 if (pop_reconnect (ctx) < 0)
588 /* verify that massage index is correct */
591 mutt_error _("The message index is incorrect. Try reopening the mailbox.");
596 mutt_progress_init (&progressbar, _("Fetching message..."),
597 MUTT_PROGRESS_SIZE, NetInc, h->content->length + h->content->offset - 1);
599 /* see if we can put in body cache; use our cache as fallback */
600 if (!(msg->fp = mutt_bcache_put (pop_data->bcache, cache_id (h->data), 1)))
604 mutt_mktemp (path, sizeof (path));
605 if (!(msg->fp = safe_fopen (path, "w+")))
613 snprintf (buf, sizeof (buf), "RETR %d\r\n", h->refno);
615 ret = pop_fetch_data (pop_data, buf, &progressbar, fetch_message, msg->fp);
619 safe_fclose (&msg->fp);
621 /* if RETR failed (e.g. connection closed), be sure to remove either
622 * the file in bcache or from POP's own cache since the next iteration
623 * of the loop will re-attempt to put() the message */
629 mutt_error ("%s", pop_data->err_msg);
636 mutt_error _("Can't write message to temporary file!");
642 /* Update the header information. Previously, we only downloaded a
643 * portion of the headers, those required for the main display.
646 mutt_bcache_commit (pop_data->bcache, cache_id (h->data));
649 cache->index = h->index;
650 cache->path = safe_strdup (path);
655 /* we replace envelop, key in subj_hash has to be updated as well */
656 if (ctx->subj_hash && h->env->real_subj)
657 hash_delete (ctx->subj_hash, h->env->real_subj, h, NULL);
658 mutt_label_hash_remove (ctx, h);
659 mutt_free_envelope (&h->env);
660 h->env = mutt_read_rfc822_header (msg->fp, h, 0, 0);
661 if (ctx->subj_hash && h->env->real_subj)
662 hash_insert (ctx->subj_hash, h->env->real_subj, h);
663 mutt_label_hash_add (ctx, h);
667 fgets (buf, sizeof (buf), msg->fp);
668 while (!feof (msg->fp))
670 ctx->hdrs[msgno]->lines++;
671 fgets (buf, sizeof (buf), msg->fp);
674 h->content->length = ftello (msg->fp) - h->content->offset;
676 /* This needs to be done in case this is a multipart message */
678 h->security = crypt_query (h->content);
686 static int pop_close_message (CONTEXT *ctx, MESSAGE *msg)
688 return safe_fclose (&msg->fp);
691 /* update POP mailbox - delete messages from server */
692 static int pop_sync_mailbox (CONTEXT *ctx, int *index_hint)
695 char buf[LONG_STRING];
696 POP_DATA *pop_data = (POP_DATA *)ctx->data;
699 header_cache_t *hc = NULL;
702 pop_data->check_time = 0;
706 if (pop_reconnect (ctx) < 0)
709 mutt_progress_init (&progress, _("Marking messages deleted..."),
710 MUTT_PROGRESS_MSG, WriteInc, ctx->deleted);
713 hc = pop_hcache_open (pop_data, ctx->path);
716 for (i = 0, j = 0, ret = 0; ret == 0 && i < ctx->msgcount; i++)
718 if (ctx->hdrs[i]->deleted && ctx->hdrs[i]->refno != -1)
722 mutt_progress_update (&progress, j, -1);
723 snprintf (buf, sizeof (buf), "DELE %d\r\n", ctx->hdrs[i]->refno);
724 if ((ret = pop_query (pop_data, buf, sizeof (buf))) == 0)
726 mutt_bcache_del (pop_data->bcache, cache_id (ctx->hdrs[i]->data));
728 mutt_hcache_delete (hc, ctx->hdrs[i]->data, strlen);
734 if (ctx->hdrs[i]->changed)
736 mutt_hcache_store (hc, ctx->hdrs[i]->data, ctx->hdrs[i], 0, strlen, MUTT_GENERATE_UIDVALIDITY);
743 mutt_hcache_close (hc);
748 strfcpy (buf, "QUIT\r\n", sizeof (buf));
749 ret = pop_query (pop_data, buf, sizeof (buf));
754 pop_data->clear_cache = 1;
755 pop_clear_cache (pop_data);
756 pop_data->status = POP_DISCONNECTED;
762 mutt_error ("%s", pop_data->err_msg);
769 /* Check for new messages and fetch headers */
770 static int pop_check_mailbox (CONTEXT *ctx, int *index_hint)
773 POP_DATA *pop_data = (POP_DATA *)ctx->data;
775 if ((pop_data->check_time + PopCheckTimeout) > time (NULL))
780 mutt_socket_close (pop_data->conn);
782 if (pop_open_connection (pop_data) < 0)
785 ctx->size = pop_data->size;
787 mutt_message _("Checking for new messages...");
789 ret = pop_fetch_headers (ctx);
790 pop_clear_cache (pop_data);
796 return MUTT_NEW_MAIL;
801 static int pop_save_to_header_cache (CONTEXT *ctx, HEADER *h)
808 pop_data = (POP_DATA *)ctx->data;
809 hc = pop_hcache_open (pop_data, ctx->path);
810 rc = mutt_hcache_store (hc, h->data, h, 0, strlen, MUTT_GENERATE_UIDVALIDITY);
811 mutt_hcache_close (hc);
817 /* Fetch messages and save them in $spoolfile */
818 void pop_fetch_mail (void)
820 char buffer[LONG_STRING];
821 char msgbuf[SHORT_STRING];
823 int i, delanswer, last = 0, msgs, bytes, rset = 0, ret;
832 mutt_error _("POP host is not defined.");
836 url = p = safe_calloc (strlen (PopHost) + 7, sizeof (char));
837 if (url_check_scheme (PopHost) == U_UNKNOWN)
839 strcpy (url, "pop://"); /* __STRCPY_CHECKED__ */
840 p = strchr (url, '\0');
842 strcpy (p, PopHost); /* __STRCPY_CHECKED__ */
844 ret = pop_parse_path (url, &acct);
848 mutt_error (_("%s is an invalid POP path"), PopHost);
852 conn = mutt_conn_find (NULL, &acct);
856 pop_data = safe_calloc (1, sizeof (POP_DATA));
857 pop_data->conn = conn;
859 if (pop_open_connection (pop_data) < 0)
861 mutt_socket_free (pop_data->conn);
866 conn->data = pop_data;
868 mutt_message _("Checking for new messages...");
870 /* find out how many messages are in the mailbox. */
871 strfcpy (buffer, "STAT\r\n", sizeof (buffer));
872 ret = pop_query (pop_data, buffer, sizeof (buffer));
877 mutt_error ("%s", pop_data->err_msg);
881 sscanf (buffer, "+OK %d %d", &msgs, &bytes);
883 /* only get unread messages */
884 if (msgs > 0 && option (OPTPOPLAST))
886 strfcpy (buffer, "LAST\r\n", sizeof (buffer));
887 ret = pop_query (pop_data, buffer, sizeof (buffer));
891 sscanf (buffer, "+OK %d", &last);
896 mutt_message _("No new mail in POP mailbox.");
900 if (mx_open_mailbox (NONULL (Spoolfile), MUTT_APPEND, &ctx) == NULL)
903 delanswer = query_quadoption (OPT_POPDELETE, _("Delete messages from server?"));
905 snprintf (msgbuf, sizeof (msgbuf), _("Reading new messages (%d bytes)..."), bytes);
906 mutt_message ("%s", msgbuf);
908 for (i = last + 1 ; i <= msgs ; i++)
910 if ((msg = mx_open_new_message (&ctx, NULL, MUTT_ADD_FROM)) == NULL)
914 snprintf (buffer, sizeof (buffer), "RETR %d\r\n", i);
915 ret = pop_fetch_data (pop_data, buffer, NULL, fetch_message, msg->fp);
919 if (ret == 0 && mx_commit_message (msg, &ctx) != 0)
925 mx_close_message (&ctx, &msg);
928 if (ret == 0 && delanswer == MUTT_YES)
930 /* delete the message on the server */
931 snprintf (buffer, sizeof (buffer), "DELE %d\r\n", i);
932 ret = pop_query (pop_data, buffer, sizeof (buffer));
937 mx_close_mailbox (&ctx, NULL);
942 mutt_error ("%s", pop_data->err_msg);
947 mutt_error _("Error while writing mailbox!");
951 mutt_message (_("%s [%d of %d messages read]"), msgbuf, i - last, msgs - last);
954 mx_close_mailbox (&ctx, NULL);
958 /* make sure no messages get deleted */
959 strfcpy (buffer, "RSET\r\n", sizeof (buffer));
960 if (pop_query (pop_data, buffer, sizeof (buffer)) == -1)
965 /* exit gracefully */
966 strfcpy (buffer, "QUIT\r\n", sizeof (buffer));
967 if (pop_query (pop_data, buffer, sizeof (buffer)) == -1)
969 mutt_socket_close (conn);
974 mutt_error _("Server closed connection!");
975 mutt_socket_close (conn);
979 struct mx_ops mx_pop_ops = {
980 .open = pop_open_mailbox,
982 .close = pop_close_mailbox,
983 .open_msg = pop_fetch_message,
984 .close_msg = pop_close_message,
985 .check = pop_check_mailbox,
987 .open_new_msg = NULL,
988 .sync = pop_sync_mailbox,
989 .save_to_header_cache = pop_save_to_header_cache,