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