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