2 * Copyright (C) 2000-2002 Vsevolod Volkov <vvv@mutt.org.ua>
3 * Copyright (C) 2006-7 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"
38 #define HC_FNAME "mutt" /* filename for hcache as POP lacks paths */
39 #define HC_FEXT "hcache" /* extension for hcache as POP lacks paths */
42 /* write line to file */
43 static int fetch_message (char *line, void *file)
45 FILE *f = (FILE *) file;
48 if (fputc ('\n', f) == EOF)
58 * -1 - conection lost,
59 * -2 - invalid command or execution error,
60 * -3 - error writing to tempfile
62 static int pop_read_header (POP_DATA *pop_data, HEADER *h)
67 char buf[LONG_STRING];
68 char tempfile[_POSIX_PATH_MAX];
70 mutt_mktemp (tempfile);
71 if (!(f = safe_fopen (tempfile, "w+")))
73 mutt_perror (tempfile);
77 snprintf (buf, sizeof (buf), "LIST %d\r\n", h->refno);
78 ret = pop_query (pop_data, buf, sizeof (buf));
81 sscanf (buf, "+OK %d %ld", &index, &length);
83 snprintf (buf, sizeof (buf), "TOP %d 0\r\n", h->refno);
84 ret = pop_fetch_data (pop_data, buf, NULL, fetch_message, f);
86 if (pop_data->cmd_top == 2)
90 pop_data->cmd_top = 1;
92 dprint (1, (debugfile, "pop_read_header: set TOP capability\n"));
97 pop_data->cmd_top = 0;
99 dprint (1, (debugfile, "pop_read_header: unset TOP capability\n"));
100 snprintf (pop_data->err_msg, sizeof (pop_data->err_msg),
101 _("Command TOP is not supported by server."));
111 h->env = mutt_read_rfc822_header (f, h, 0, 0);
112 h->content->length = length - h->content->offset + 1;
116 h->content->length--;
117 fgets (buf, sizeof (buf), f);
123 mutt_error ("%s", pop_data->err_msg);
128 mutt_error _("Can't write header to temporary file!");
139 static int fetch_uidl (char *line, void *data)
142 CONTEXT *ctx = (CONTEXT *)data;
143 POP_DATA *pop_data = (POP_DATA *)ctx->data;
145 sscanf (line, "%d %s", &index, line);
146 for (i = 0; i < ctx->msgcount; i++)
147 if (!mutt_strcmp (line, ctx->hdrs[i]->data))
150 if (i == ctx->msgcount)
152 dprint (1, (debugfile, "pop_fetch_headers: new header %d %s\n", index, line));
154 if (i >= ctx->hdrmax)
155 mx_alloc_memory(ctx);
158 ctx->hdrs[i] = mutt_new_header ();
159 ctx->hdrs[i]->data = safe_strdup (line);
161 else if (ctx->hdrs[i]->index != index - 1)
162 pop_data->clear_cache = 1;
164 ctx->hdrs[i]->refno = index;
165 ctx->hdrs[i]->index = index - 1;
170 static int msg_cache_check (const char *id, body_cache_t *bcache, void *data)
176 if (!(ctx = (CONTEXT *)data))
178 if (!(pop_data = (POP_DATA *)ctx->data))
182 /* keep hcache file if hcache == bcache */
183 if (strcmp (HC_FNAME "." HC_FEXT, id) == 0)
187 for (i = 0; i < ctx->msgcount; i++)
188 /* if the id we get is known for a header: done (i.e. keep in cache) */
189 if (ctx->hdrs[i]->data && mutt_strcmp (ctx->hdrs[i]->data, id) == 0)
192 /* message not found in context -> remove it from cache
193 * return the result of bcache, so we stop upon its first error
195 return mutt_bcache_del (bcache, id);
199 static int pop_hcache_namer (const char *path, char *dest, size_t destlen)
201 return snprintf (dest, destlen, "%s." HC_FEXT, path);
204 static header_cache_t *pop_hcache_open (POP_DATA *pop_data, const char *path)
209 if (!pop_data || !pop_data->conn)
210 return mutt_hcache_open (HeaderCache, path, NULL);
212 mutt_account_tourl (&pop_data->conn->account, &url);
214 url_ciss_tostring (&url, p, sizeof (p), U_PATH);
215 return mutt_hcache_open (HeaderCache, p, pop_hcache_namer);
223 * -1 - conection lost,
224 * -2 - invalid command or execution error,
225 * -3 - error writing to tempfile
227 static int pop_fetch_headers (CONTEXT *ctx)
229 int i, ret, old_count, new_count;
230 unsigned short hcached = 0, bcached;
231 POP_DATA *pop_data = (POP_DATA *)ctx->data;
235 header_cache_t *hc = NULL;
238 hc = pop_hcache_open (pop_data, ctx->path);
241 time (&pop_data->check_time);
242 pop_data->clear_cache = 0;
244 for (i = 0; i < ctx->msgcount; i++)
245 ctx->hdrs[i]->refno = -1;
247 old_count = ctx->msgcount;
248 ret = pop_fetch_data (pop_data, "UIDL\r\n", NULL, fetch_uidl, ctx);
249 new_count = ctx->msgcount;
250 ctx->msgcount = old_count;
252 if (pop_data->cmd_uidl == 2)
256 pop_data->cmd_uidl = 1;
258 dprint (1, (debugfile, "pop_fetch_headers: set UIDL capability\n"));
261 if (ret == -2 && pop_data->cmd_uidl == 2)
263 pop_data->cmd_uidl = 0;
265 dprint (1, (debugfile, "pop_fetch_headers: unset UIDL capability\n"));
266 snprintf (pop_data->err_msg, sizeof (pop_data->err_msg),
267 _("Command UIDL is not supported by server."));
272 mutt_progress_init (&progress, _("Fetching message headers..."),
273 M_PROGRESS_MSG, ReadInc, new_count - old_count);
277 for (i = 0; i < old_count; i++)
278 if (ctx->hdrs[i]->refno == -1)
279 ctx->hdrs[i]->deleted = 1;
281 for (i = old_count; i < new_count; i++)
284 mutt_progress_update (&progress, i + 1 - old_count, -1);
286 if ((data = mutt_hcache_fetch (hc, ctx->hdrs[i]->data, strlen)))
288 char *uidl = safe_strdup (ctx->hdrs[i]->data);
289 int refno = ctx->hdrs[i]->refno;
290 int index = ctx->hdrs[i]->index;
292 * - POP dynamically numbers headers and relies on h->refno
293 * to map messages; so restore header and overwrite restored
294 * refno with current refno, same for index
295 * - h->data needs to a separate pointer as it's driver-specific
296 * data freed separately elsewhere
297 * (the old h->data should point inside a malloc'd block from
298 * hcache so there shouldn't be a memleak here)
300 HEADER *h = mutt_hcache_restore ((unsigned char *) data, NULL);
301 mutt_free_header (&ctx->hdrs[i]);
303 ctx->hdrs[i]->refno = refno;
304 ctx->hdrs[i]->index = index;
305 ctx->hdrs[i]->data = uidl;
311 if ((ret = pop_read_header (pop_data, ctx->hdrs[i])) < 0)
316 mutt_hcache_store (hc, ctx->hdrs[i]->data, ctx->hdrs[i], 0, strlen);
323 * faked support for flags works like this:
324 * - if 'hcached' is 1, we have the message in our hcache:
325 * - if we also have a body: read
326 * - if we don't have a body: old
327 * (if $mark_old is set which is maybe wrong as
328 * $mark_old should be considered for syncing the
329 * folder and not when opening it XXX)
330 * - if 'hcached' is 0, we don't have the message in our hcache:
331 * - if we also have a body: read
332 * - if we don't have a body: new
334 bcached = mutt_bcache_exists (pop_data->bcache, ctx->hdrs[i]->data) == 0;
335 ctx->hdrs[i]->old = 0;
336 ctx->hdrs[i]->read = 0;
340 ctx->hdrs[i]->read = 1;
341 else if (option (OPTMARKOLD))
342 ctx->hdrs[i]->old = 1;
347 ctx->hdrs[i]->read = 1;
354 mx_update_context (ctx, i - old_count);
358 mutt_hcache_close (hc);
363 for (i = ctx->msgcount; i < new_count; i++)
364 mutt_free_header (&ctx->hdrs[i]);
368 /* after putting the result into our structures,
369 * clean up cache, i.e. wipe messages deleted outside
370 * the availability of our cache
372 if (option (OPTMESSAGECACHECLEAN))
373 mutt_bcache_list (pop_data->bcache, msg_cache_check, (void*)ctx);
376 return (new_count - old_count);
379 /* open POP mailbox - fetch only headers */
380 int pop_open_mailbox (CONTEXT *ctx)
383 char buf[LONG_STRING];
389 if (pop_parse_path (ctx->path, &acct))
391 mutt_error (_("%s is an invalid POP path"), ctx->path);
396 mutt_account_tourl (&acct, &url);
398 url_ciss_tostring (&url, buf, sizeof (buf), 0);
399 conn = mutt_conn_find (NULL, &acct);
404 ctx->path = safe_strdup (buf);
406 pop_data = safe_calloc (1, sizeof (POP_DATA));
407 pop_data->conn = conn;
408 ctx->data = pop_data;
409 ctx->mx_close = pop_close_mailbox;
411 if (pop_open_connection (pop_data) < 0)
414 conn->data = pop_data;
415 pop_data->bcache = mutt_bcache_open (&acct, NULL);
417 /* init (hard-coded) ACL rights */
418 memset (ctx->rights, 0, sizeof (ctx->rights));
419 mutt_bit_set (ctx->rights, M_ACL_SEEN);
420 mutt_bit_set (ctx->rights, M_ACL_DELETE);
424 if (pop_reconnect (ctx) < 0)
427 ctx->size = pop_data->size;
429 mutt_message _("Fetching list of messages...");
431 ret = pop_fetch_headers (ctx);
444 /* delete all cached messages */
445 static void pop_clear_cache (POP_DATA *pop_data)
449 if (!pop_data->clear_cache)
452 dprint (1, (debugfile, "pop_clear_cache: delete cached messages\n"));
454 for (i = 0; i < POP_CACHE_LEN; i++)
456 if (pop_data->cache[i].path)
458 unlink (pop_data->cache[i].path);
459 FREE (&pop_data->cache[i].path);
464 /* close POP mailbox */
465 int pop_close_mailbox (CONTEXT *ctx)
467 POP_DATA *pop_data = (POP_DATA *)ctx->data;
474 if (pop_data->status != POP_NONE)
475 mutt_socket_close (pop_data->conn);
477 pop_data->status = POP_NONE;
479 pop_data->clear_cache = 1;
480 pop_clear_cache (pop_data);
482 if (!pop_data->conn->data)
483 mutt_socket_free (pop_data->conn);
485 mutt_bcache_close (&pop_data->bcache);
490 /* fetch message from POP server */
491 int pop_fetch_message (MESSAGE* msg, CONTEXT* ctx, int msgno)
495 char buf[LONG_STRING];
496 char path[_POSIX_PATH_MAX];
497 progress_t progressbar;
498 POP_DATA *pop_data = (POP_DATA *)ctx->data;
500 HEADER *h = ctx->hdrs[msgno];
501 unsigned short bcache = 1;
503 /* see if we already have the message in body cache */
504 if ((msg->fp = mutt_bcache_get (pop_data->bcache, h->data)))
508 * see if we already have the message in our cache in
509 * case $message_cachedir is unset
511 cache = &pop_data->cache[h->index % POP_CACHE_LEN];
515 if (cache->index == h->index)
517 /* yes, so just return a pointer to the message */
518 msg->fp = fopen (cache->path, "r");
522 mutt_perror (cache->path);
528 /* clear the previous entry */
529 unlink (cache->path);
536 if (pop_reconnect (ctx) < 0)
539 /* verify that massage index is correct */
542 mutt_error _("The message index is incorrect. Try reopening the mailbox.");
547 mutt_progress_init (&progressbar, _("Fetching message..."),
548 M_PROGRESS_SIZE, NetInc, h->content->length + h->content->offset - 1);
550 /* see if we can put in body cache; use our cache as fallback */
551 if (!(msg->fp = mutt_bcache_put (pop_data->bcache, h->data, 1)))
556 if (!(msg->fp = safe_fopen (path, "w+")))
564 snprintf (buf, sizeof (buf), "RETR %d\r\n", h->refno);
566 ret = pop_fetch_data (pop_data, buf, &progressbar, fetch_message, msg->fp);
570 safe_fclose (&msg->fp);
572 /* if RETR failed (e.g. connection closed), be sure to remove either
573 * the file in bcache or from POP's own cache since the next iteration
574 * of the loop will re-attempt to put() the message */
580 mutt_error ("%s", pop_data->err_msg);
587 mutt_error _("Can't write message to temporary file!");
593 /* Update the header information. Previously, we only downloaded a
594 * portion of the headers, those required for the main display.
597 mutt_bcache_commit (pop_data->bcache, h->data);
600 cache->index = h->index;
601 cache->path = safe_strdup (path);
605 mutt_free_envelope (&h->env);
606 h->env = mutt_read_rfc822_header (msg->fp, h, 0, 0);
609 fgets (buf, sizeof (buf), msg->fp);
610 while (!feof (msg->fp))
612 ctx->hdrs[msgno]->lines++;
613 fgets (buf, sizeof (buf), msg->fp);
616 h->content->length = ftello (msg->fp) - h->content->offset;
618 /* This needs to be done in case this is a multipart message */
620 h->security = crypt_query (h->content);
628 /* update POP mailbox - delete messages from server */
629 int pop_sync_mailbox (CONTEXT *ctx, int *index_hint)
632 char buf[LONG_STRING];
633 POP_DATA *pop_data = (POP_DATA *)ctx->data;
636 header_cache_t *hc = NULL;
639 pop_data->check_time = 0;
643 if (pop_reconnect (ctx) < 0)
646 mutt_progress_init (&progress, _("Marking messages deleted..."),
647 M_PROGRESS_MSG, WriteInc, ctx->deleted);
650 hc = pop_hcache_open (pop_data, ctx->path);
653 for (i = 0, j = 0, ret = 0; ret == 0 && i < ctx->msgcount; i++)
655 if (ctx->hdrs[i]->deleted)
659 mutt_progress_update (&progress, j, -1);
660 snprintf (buf, sizeof (buf), "DELE %d\r\n", ctx->hdrs[i]->refno);
661 if ((ret = pop_query (pop_data, buf, sizeof (buf))) == 0)
663 mutt_bcache_del (pop_data->bcache, ctx->hdrs[i]->data);
665 mutt_hcache_delete (hc, ctx->hdrs[i]->data, strlen);
672 mutt_hcache_close (hc);
677 strfcpy (buf, "QUIT\r\n", sizeof (buf));
678 ret = pop_query (pop_data, buf, sizeof (buf));
683 pop_data->clear_cache = 1;
684 pop_clear_cache (pop_data);
685 pop_data->status = POP_DISCONNECTED;
691 mutt_error ("%s", pop_data->err_msg);
698 /* Check for new messages and fetch headers */
699 int pop_check_mailbox (CONTEXT *ctx, int *index_hint)
702 POP_DATA *pop_data = (POP_DATA *)ctx->data;
704 if ((pop_data->check_time + PopCheckTimeout) > time (NULL))
709 mutt_socket_close (pop_data->conn);
711 if (pop_open_connection (pop_data) < 0)
714 ctx->size = pop_data->size;
716 mutt_message _("Checking for new messages...");
718 ret = pop_fetch_headers (ctx);
719 pop_clear_cache (pop_data);
730 /* Fetch messages and save them in $spoolfile */
731 void pop_fetch_mail (void)
733 char buffer[LONG_STRING];
734 char msgbuf[SHORT_STRING];
736 int i, delanswer, last = 0, msgs, bytes, rset = 0, ret;
745 mutt_error _("POP host is not defined.");
749 url = p = safe_calloc (strlen (PopHost) + 7, sizeof (char));
750 if (url_check_scheme (PopHost) == U_UNKNOWN)
752 strcpy (url, "pop://"); /* __STRCPY_CHECKED__ */
753 p = strchr (url, '\0');
755 strcpy (p, PopHost); /* __STRCPY_CHECKED__ */
757 ret = pop_parse_path (url, &acct);
761 mutt_error (_("%s is an invalid POP path"), PopHost);
765 conn = mutt_conn_find (NULL, &acct);
769 pop_data = safe_calloc (1, sizeof (POP_DATA));
770 pop_data->conn = conn;
772 if (pop_open_connection (pop_data) < 0)
774 mutt_socket_free (pop_data->conn);
779 conn->data = pop_data;
781 mutt_message _("Checking for new messages...");
783 /* find out how many messages are in the mailbox. */
784 strfcpy (buffer, "STAT\r\n", sizeof (buffer));
785 ret = pop_query (pop_data, buffer, sizeof (buffer));
790 mutt_error ("%s", pop_data->err_msg);
794 sscanf (buffer, "+OK %d %d", &msgs, &bytes);
796 /* only get unread messages */
797 if (msgs > 0 && option (OPTPOPLAST))
799 strfcpy (buffer, "LAST\r\n", sizeof (buffer));
800 ret = pop_query (pop_data, buffer, sizeof (buffer));
804 sscanf (buffer, "+OK %d", &last);
809 mutt_message _("No new mail in POP mailbox.");
813 if (mx_open_mailbox (NONULL (Spoolfile), M_APPEND, &ctx) == NULL)
816 delanswer = query_quadoption (OPT_POPDELETE, _("Delete messages from server?"));
818 snprintf (msgbuf, sizeof (msgbuf), _("Reading new messages (%d bytes)..."), bytes);
819 mutt_message ("%s", msgbuf);
821 for (i = last + 1 ; i <= msgs ; i++)
823 if ((msg = mx_open_new_message (&ctx, NULL, M_ADD_FROM)) == NULL)
827 snprintf (buffer, sizeof (buffer), "RETR %d\r\n", i);
828 ret = pop_fetch_data (pop_data, buffer, NULL, fetch_message, msg->fp);
832 if (ret == 0 && mx_commit_message (msg, &ctx) != 0)
838 mx_close_message (&msg);
841 if (ret == 0 && delanswer == M_YES)
843 /* delete the message on the server */
844 snprintf (buffer, sizeof (buffer), "DELE %d\r\n", i);
845 ret = pop_query (pop_data, buffer, sizeof (buffer));
850 mx_close_mailbox (&ctx, NULL);
855 mutt_error ("%s", pop_data->err_msg);
860 mutt_error _("Error while writing mailbox!");
864 mutt_message (_("%s [%d of %d messages read]"), msgbuf, i - last, msgs - last);
867 mx_close_mailbox (&ctx, NULL);
871 /* make sure no messages get deleted */
872 strfcpy (buffer, "RSET\r\n", sizeof (buffer));
873 if (pop_query (pop_data, buffer, sizeof (buffer)) == -1)
878 /* exit gracefully */
879 strfcpy (buffer, "QUIT\r\n", sizeof (buffer));
880 if (pop_query (pop_data, buffer, sizeof (buffer)) == -1)
882 mutt_socket_close (conn);
887 mutt_error _("Server closed connection!");
888 mutt_socket_close (conn);