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