Andreas Krennmair:
[apps/madmutt.git] / pop.c
1 /*
2  * Copyright (C) 2000-2002 Vsevolod Volkov <vvv@mutt.org.ua>
3  * 
4  *     This program is free software; you can redistribute it and/or modify
5  *     it under the terms of the GNU General Public License as published by
6  *     the Free Software Foundation; either version 2 of the License, or
7  *     (at your option) any later version.
8  * 
9  *     This program is distributed in the hope that it will be useful,
10  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *     GNU General Public License for more details.
13  * 
14  *     You should have received a copy of the GNU General Public License
15  *     along with this program; if not, write to the Free Software
16  *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
17  */ 
18
19 #include "mutt.h"
20 #include "mx.h"
21 #include "pop.h"
22 #include "mutt_crypt.h"
23
24 #include <string.h>
25 #include <unistd.h>
26
27 /* write line to file */
28 static int fetch_message (char *line, void *file)
29 {
30   FILE *f = (FILE *) file;
31
32   fputs (line, f);
33   if (fputc ('\n', f) == EOF)
34     return -1;
35
36   return 0;
37 }
38
39 /*
40  * Read header
41  * returns:
42  *  0 on success
43  * -1 - conection lost,
44  * -2 - invalid command or execution error,
45  * -3 - error writing to tempfile
46  */
47 static int pop_read_header (POP_DATA *pop_data, HEADER *h)
48 {
49   FILE *f;
50   int ret, index;
51   long length;
52   char buf[LONG_STRING];
53   char tempfile[_POSIX_PATH_MAX];
54
55   mutt_mktemp (tempfile);
56   if (!(f = safe_fopen (tempfile, "w+")))
57   {
58     mutt_perror (tempfile);
59     return -3;
60   }
61
62   snprintf (buf, sizeof (buf), "LIST %d\r\n", h->refno);
63   ret = pop_query (pop_data, buf, sizeof (buf));
64   if (ret == 0)
65   {
66     sscanf (buf, "+OK %d %ld", &index, &length);
67
68     snprintf (buf, sizeof (buf), "TOP %d 0\r\n", h->refno);
69     ret = pop_fetch_data (pop_data, buf, NULL, fetch_message, f);
70
71     if (pop_data->cmd_top == 2)
72     {
73       if (ret == 0)
74       {
75         pop_data->cmd_top = 1;
76
77         dprint (1, (debugfile, "pop_read_header: set TOP capability\n"));
78       }
79
80       if (ret == -2)
81       {
82         pop_data->cmd_top = 0;
83
84         dprint (1, (debugfile, "pop_read_header: unset TOP capability\n"));
85         snprintf (pop_data->err_msg, sizeof (pop_data->err_msg),
86                 _("Command TOP is not supported by server."));
87       }
88     }
89   }
90
91   switch (ret)
92   {
93     case 0:
94     {
95       rewind (f);
96       h->env = mutt_read_rfc822_header (f, h, 0, 0);
97       h->content->length = length - h->content->offset + 1;
98       rewind (f);
99       while (!feof (f))
100       {
101         h->content->length--;
102         fgets (buf, sizeof (buf), f);
103       }
104       break;
105     }
106     case -2:
107     {
108       mutt_error ("%s", pop_data->err_msg);
109       break;
110     }
111     case -3:
112     {
113       mutt_error _("Can't write header to temporary file!");
114       break;
115     }
116   }
117
118   fclose (f);
119   unlink (tempfile);
120   return ret;
121 }
122
123 /* parse UIDL */
124 static int fetch_uidl (char *line, void *data)
125 {
126   int i, index;
127   CONTEXT *ctx = (CONTEXT *)data;
128   POP_DATA *pop_data = (POP_DATA *)ctx->data;
129
130   sscanf (line, "%d %s", &index, line);
131   for (i = 0; i < ctx->msgcount; i++)
132     if (!mutt_strcmp (line, ctx->hdrs[i]->data))
133       break;
134
135   if (i == ctx->msgcount)
136   {
137     dprint (1, (debugfile, "pop_fetch_headers: new header %d %s\n", index, line));
138
139     if (i >= ctx->hdrmax)
140       mx_alloc_memory(ctx);
141
142     ctx->msgcount++;
143     ctx->hdrs[i] = mutt_new_header ();
144     ctx->hdrs[i]->data = safe_strdup (line);
145   }
146   else if (ctx->hdrs[i]->index != index - 1)
147     pop_data->clear_cache = 1;
148
149   ctx->hdrs[i]->refno = index;
150   ctx->hdrs[i]->index = index - 1;
151
152   return 0;
153 }
154
155 /*
156  * Read headers
157  * returns:
158  *  0 on success
159  * -1 - conection lost,
160  * -2 - invalid command or execution error,
161  * -3 - error writing to tempfile
162  */
163 static int pop_fetch_headers (CONTEXT *ctx)
164 {
165   int i, ret, old_count, new_count;
166   POP_DATA *pop_data = (POP_DATA *)ctx->data;
167
168   time (&pop_data->check_time);
169   pop_data->clear_cache = 0;
170
171   for (i = 0; i < ctx->msgcount; i++)
172     ctx->hdrs[i]->refno = -1;
173
174   old_count = ctx->msgcount;
175   ret = pop_fetch_data (pop_data, "UIDL\r\n", NULL, fetch_uidl, ctx);
176   new_count = ctx->msgcount;
177   ctx->msgcount = old_count;
178
179   if (pop_data->cmd_uidl == 2)
180   {
181     if (ret == 0)
182     {
183       pop_data->cmd_uidl = 1;
184
185       dprint (1, (debugfile, "pop_fetch_headers: set UIDL capability\n"));
186     }
187
188     if (ret == -2 && pop_data->cmd_uidl == 2)
189     {
190       pop_data->cmd_uidl = 0;
191
192       dprint (1, (debugfile, "pop_fetch_headers: unset UIDL capability\n"));
193       snprintf (pop_data->err_msg, sizeof (pop_data->err_msg),
194               _("Command UIDL is not supported by server."));
195     }
196   }
197
198   if (ret == 0)
199   {
200     for (i = 0; i < old_count; i++)
201       if (ctx->hdrs[i]->refno == -1)
202         ctx->hdrs[i]->deleted = 1;
203
204     for (i = old_count; i < new_count; i++)
205     {
206       mutt_message (_("Fetching message headers... [%d/%d]"),
207                     i + 1 - old_count, new_count - old_count);
208
209       ret = pop_read_header (pop_data, ctx->hdrs[i]);
210       if (ret < 0)
211         break;
212
213       ctx->msgcount++;
214     }
215
216     if (i > old_count)
217       mx_update_context (ctx, i - old_count);
218   }
219
220   if (ret < 0)
221   {
222     for (i = ctx->msgcount; i < new_count; i++)
223       mutt_free_header (&ctx->hdrs[i]);
224     return ret;
225   }
226
227   mutt_clear_error ();
228   return (new_count - old_count);
229 }
230
231 /* open POP mailbox - fetch only headers */
232 int pop_open_mailbox (CONTEXT *ctx)
233 {
234   int ret;
235   char buf[LONG_STRING];
236   CONNECTION *conn;
237   ACCOUNT acct;
238   POP_DATA *pop_data;
239   ciss_url_t url;
240
241   if (pop_parse_path (ctx->path, &acct))
242   {
243     mutt_error (_("%s is an invalid POP path"), ctx->path);
244     mutt_sleep (2);
245     return -1;
246   }
247
248   mutt_account_tourl (&acct, &url);
249   url.path = NULL;
250   url_ciss_tostring (&url, buf, sizeof (buf), 0);
251   conn = mutt_conn_find (NULL, &acct);
252   if (!conn)
253     return -1;
254
255   FREE (&ctx->path);
256   ctx->path = safe_strdup (buf);
257
258   pop_data = safe_calloc (1, sizeof (POP_DATA));
259   pop_data->conn = conn;
260   ctx->data = pop_data;
261
262   if (pop_open_connection (pop_data) < 0)
263     return -1;
264
265   conn->data = pop_data;
266
267   FOREVER
268   {
269     if (pop_reconnect (ctx) < 0)
270       return -1;
271
272     ctx->size = pop_data->size;
273
274     mutt_message _("Fetching list of messages...");
275
276     ret = pop_fetch_headers (ctx);
277
278     if (ret >= 0)
279       return 0;
280
281     if (ret < -1)
282     {
283       mutt_sleep (2);
284       return -1;
285     }
286   }
287 }
288
289 /* delete all cached messages */
290 static void pop_clear_cache (POP_DATA *pop_data)
291 {
292   int i;
293
294   if (!pop_data->clear_cache)
295     return;
296
297   dprint (1, (debugfile, "pop_clear_cache: delete cached messages\n"));
298
299   for (i = 0; i < POP_CACHE_LEN; i++)
300   {
301     if (pop_data->cache[i].path)
302     {
303       unlink (pop_data->cache[i].path);
304       FREE (&pop_data->cache[i].path);
305     }
306   }
307 }
308
309 /* close POP mailbox */
310 void pop_close_mailbox (CONTEXT *ctx)
311 {
312   POP_DATA *pop_data = (POP_DATA *)ctx->data;
313
314   if (!pop_data)
315     return;
316
317   pop_logout (ctx);
318
319   if (pop_data->status != POP_NONE)
320     mutt_socket_close (pop_data->conn);
321
322   pop_data->status = POP_NONE;
323
324   pop_data->clear_cache = 1;
325   pop_clear_cache (pop_data);
326
327   if (!pop_data->conn->data)
328     mutt_socket_free (pop_data->conn);
329
330   return;
331 }
332
333 /* fetch message from POP server */
334 int pop_fetch_message (MESSAGE* msg, CONTEXT* ctx, int msgno)
335 {
336   int ret;
337   void *uidl;
338   char buf[LONG_STRING];
339   char path[_POSIX_PATH_MAX];
340   char *m = _("Fetching message...");
341   POP_DATA *pop_data = (POP_DATA *)ctx->data;
342   POP_CACHE *cache;
343   HEADER *h = ctx->hdrs[msgno];
344
345   /* see if we already have the message in our cache */
346   cache = &pop_data->cache[h->index % POP_CACHE_LEN];
347
348   if (cache->path)
349   {
350     if (cache->index == h->index)
351     {
352       /* yes, so just return a pointer to the message */
353       msg->fp = fopen (cache->path, "r");
354       if (msg->fp)
355         return 0;
356
357       mutt_perror (cache->path);
358       mutt_sleep (2);
359       return -1;
360     }
361     else
362     {
363       /* clear the previous entry */
364       unlink (cache->path);
365       FREE (&cache->path);
366     }
367   }
368
369   FOREVER
370   {
371     if (pop_reconnect (ctx) < 0)
372       return -1;
373
374     /* verify that massage index is correct */
375     if (h->refno < 0)
376     {
377       mutt_error _("The message index is incorrect. Try reopening the mailbox.");
378       mutt_sleep (2);
379       return -1;
380     }
381
382     mutt_message (m);
383
384     mutt_mktemp (path);
385     msg->fp = safe_fopen (path, "w+");
386     if (!msg->fp)
387     {
388       mutt_perror (path);
389       mutt_sleep (2);
390       return -1;
391     }
392
393     snprintf (buf, sizeof (buf), "RETR %d\r\n", h->refno);
394
395     ret = pop_fetch_data (pop_data, buf, m, fetch_message, msg->fp);
396     if (ret == 0)
397       break;
398
399     safe_fclose (&msg->fp);
400     unlink (path);
401
402     if (ret == -2)
403     {
404       mutt_error ("%s", pop_data->err_msg);
405       mutt_sleep (2);
406       return -1;
407     }
408
409     if (ret == -3)
410     {
411       mutt_error _("Can't write message to temporary file!");
412       mutt_sleep (2);
413       return -1;
414     }
415   }
416
417   /* Update the header information.  Previously, we only downloaded a
418    * portion of the headers, those required for the main display.
419    */
420   cache->index = h->index;
421   cache->path = safe_strdup (path);
422   rewind (msg->fp);
423   uidl = h->data;
424   mutt_free_envelope (&h->env);
425   h->env = mutt_read_rfc822_header (msg->fp, h, 0, 0);
426   h->data = uidl;
427   h->lines = 0;
428   fgets (buf, sizeof (buf), msg->fp);
429   while (!feof (msg->fp))
430   {
431     ctx->hdrs[msgno]->lines++;
432     fgets (buf, sizeof (buf), msg->fp);
433   }
434
435   h->content->length = ftell (msg->fp) - h->content->offset;
436
437   /* This needs to be done in case this is a multipart message */
438   if (!WithCrypto)
439     h->security = crypt_query (h->content);
440
441   mutt_clear_error();
442   rewind (msg->fp);
443
444   return 0;
445 }
446
447 /* update POP mailbox - delete messages from server */
448 int pop_sync_mailbox (CONTEXT *ctx, int *index_hint)
449 {
450   int i, ret;
451   char buf[LONG_STRING];
452   POP_DATA *pop_data = (POP_DATA *)ctx->data;
453
454   pop_data->check_time = 0;
455
456   FOREVER
457   {
458     if (pop_reconnect (ctx) < 0)
459       return -1;
460
461     mutt_message (_("Marking %d messages deleted..."), ctx->deleted);
462
463     for (i = 0, ret = 0; ret == 0 && i < ctx->msgcount; i++)
464     {
465       if (ctx->hdrs[i]->deleted)
466       {
467         snprintf (buf, sizeof (buf), "DELE %d\r\n", ctx->hdrs[i]->refno);
468         ret = pop_query (pop_data, buf, sizeof (buf));
469       }
470     }
471
472     if (ret == 0)
473     {
474       strfcpy (buf, "QUIT\r\n", sizeof (buf));
475       ret = pop_query (pop_data, buf, sizeof (buf));
476     }
477
478     if (ret == 0)
479     {
480       pop_data->clear_cache = 1;
481       pop_clear_cache (pop_data);
482       pop_data->status = POP_DISCONNECTED;
483       return 0;
484     }
485
486     if (ret == -2)
487     {
488       mutt_error ("%s", pop_data->err_msg);
489       mutt_sleep (2);
490       return -1;
491     }
492   }
493 }
494
495 /* Check for new messages and fetch headers */
496 int pop_check_mailbox (CONTEXT *ctx, int *index_hint)
497 {
498   int ret;
499   POP_DATA *pop_data = (POP_DATA *)ctx->data;
500
501   if ((pop_data->check_time + PopCheckTimeout) > time (NULL))
502     return 0;
503
504   pop_logout (ctx);
505
506   mutt_socket_close (pop_data->conn);
507
508   if (pop_open_connection (pop_data) < 0)
509     return -1;
510
511   ctx->size = pop_data->size;
512
513   mutt_message _("Checking for new messages...");
514
515   ret = pop_fetch_headers (ctx);
516   pop_clear_cache (pop_data);
517
518   if (ret < 0)
519     return -1;
520
521   if (ret > 0)
522     return M_NEW_MAIL;
523
524   return 0;
525 }
526
527 /* Fetch messages and save them in $spoolfile */
528 void pop_fetch_mail (void)
529 {
530   char buffer[LONG_STRING];
531   char msgbuf[SHORT_STRING];
532   char *url, *p;
533   int i, delanswer, last = 0, msgs, bytes, rset = 0, ret;
534   CONNECTION *conn;
535   CONTEXT ctx;
536   MESSAGE *msg = NULL;
537   ACCOUNT acct;
538   POP_DATA *pop_data;
539
540   if (!PopHost)
541   {
542     mutt_error _("POP host is not defined.");
543     return;
544   }
545
546   url = p = safe_calloc (strlen (PopHost) + 7, sizeof (char));
547   if (url_check_scheme (PopHost) == U_UNKNOWN)
548   {
549     strcpy (url, "pop://");     /* __STRCPY_CHECKED__ */
550     p = strchr (url, '\0');
551   }
552   strcpy (p, PopHost);          /* __STRCPY_CHECKED__ */
553
554   ret = pop_parse_path (url, &acct);
555   FREE (&url);
556   if (ret)
557   {
558     mutt_error (_("%s is an invalid POP path"), PopHost);
559     return;
560   }
561
562   conn = mutt_conn_find (NULL, &acct);
563   if (!conn)
564     return;
565
566   pop_data = safe_calloc (1, sizeof (POP_DATA));
567   pop_data->conn = conn;
568
569   if (pop_open_connection (pop_data) < 0)
570   {
571     mutt_socket_free (pop_data->conn);
572     FREE (&pop_data);
573     return;
574   }
575
576   conn->data = pop_data;
577
578   mutt_message _("Checking for new messages...");
579
580   /* find out how many messages are in the mailbox. */
581   strfcpy (buffer, "STAT\r\n", sizeof (buffer));
582   ret = pop_query (pop_data, buffer, sizeof (buffer));
583   if (ret == -1)
584     goto fail;
585   if (ret == -2)
586   {
587     mutt_error ("%s", pop_data->err_msg);
588     goto finish;
589   }
590
591   sscanf (buffer, "+OK %d %d", &msgs, &bytes);
592
593   /* only get unread messages */
594   if (msgs > 0 && option (OPTPOPLAST))
595   {
596     strfcpy (buffer, "LAST\r\n", sizeof (buffer));
597     ret = pop_query (pop_data, buffer, sizeof (buffer));
598     if (ret == -1)
599       goto fail;
600     if (ret == 0)
601       sscanf (buffer, "+OK %d", &last);
602   }
603
604   if (msgs <= last)
605   {
606     mutt_message _("No new mail in POP mailbox.");
607     goto finish;
608   }
609
610   if (mx_open_mailbox (NONULL (Spoolfile), M_APPEND, &ctx) == NULL)
611     goto finish;
612
613   delanswer = query_quadoption (OPT_POPDELETE, _("Delete messages from server?"));
614
615   snprintf (msgbuf, sizeof (msgbuf), _("Reading new messages (%d bytes)..."), bytes);
616   mutt_message ("%s", msgbuf);
617
618   for (i = last + 1 ; i <= msgs ; i++)
619   {
620     if ((msg = mx_open_new_message (&ctx, NULL, M_ADD_FROM)) == NULL)
621       ret = -3;
622     else
623     {
624       snprintf (buffer, sizeof (buffer), "RETR %d\r\n", i);
625       ret = pop_fetch_data (pop_data, buffer, NULL, fetch_message, msg->fp);
626       if (ret == -3)
627         rset = 1;
628
629       if (ret == 0 && mx_commit_message (msg, &ctx) != 0)
630       {
631         rset = 1;
632         ret = -3;
633       }
634
635       mx_close_message (&msg);
636     }
637
638     if (ret == 0 && delanswer == M_YES)
639     {
640       /* delete the message on the server */
641       snprintf (buffer, sizeof (buffer), "DELE %d\r\n", i);
642       ret = pop_query (pop_data, buffer, sizeof (buffer));
643     }
644
645     if (ret == -1)
646     {
647       mx_close_mailbox (&ctx, NULL);
648       goto fail;
649     }
650     if (ret == -2)
651     {
652       mutt_error ("%s", pop_data->err_msg);
653       break;
654     }
655     if (ret == -3)
656     {
657       mutt_error _("Error while writing mailbox!");
658       break;
659     }
660
661     mutt_message (_("%s [%d of %d messages read]"), msgbuf, i - last, msgs - last);
662   }
663
664   mx_close_mailbox (&ctx, NULL);
665
666   if (rset)
667   {
668     /* make sure no messages get deleted */
669     strfcpy (buffer, "RSET\r\n", sizeof (buffer));
670     if (pop_query (pop_data, buffer, sizeof (buffer)) == -1)
671       goto fail;
672   }
673
674 finish:
675   /* exit gracefully */
676   strfcpy (buffer, "QUIT\r\n", sizeof (buffer));
677   if (pop_query (pop_data, buffer, sizeof (buffer)) == -1)
678     goto fail;
679   mutt_socket_close (conn);
680   FREE (&pop_data);
681   return;
682
683 fail:
684   mutt_error _("Server closed connection!");
685   mutt_socket_close (conn);
686   FREE (&pop_data);
687 }