* src/filter.c
[claws.git] / src / pop.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2002 Hiroyuki Yamamoto
4  *
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.
9  *
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.
14  *
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include <glib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdarg.h>
28 #include <ctype.h>
29 #include <unistd.h>
30 #include <time.h>
31 #include <errno.h>
32
33 #include "intl.h"
34 #include "pop.h"
35 #include "socket.h"
36 #include "md5.h"
37 #include "prefs_account.h"
38 #include "utils.h"
39 #include "inc.h"
40 #include "recv.h"
41 #include "selective_download.h"
42 #if USE_SSL
43 #  include "ssl.h"
44 #endif
45
46 #define LOOKUP_NEXT_MSG()                                                       \
47 {                                                                               \
48         Pop3MsgInfo *msg;                                                       \
49         PrefsAccount *ac = state->ac_prefs;                                     \
50         gint size;                                                              \
51         gboolean size_limit_over;                                               \
52                                                                                 \
53         for (;;) {                                                              \
54                 msg = &state->msg[state->cur_msg];                              \
55                 size = msg->size;                                               \
56                 size_limit_over =                                               \
57                     (ac->enable_size_limit &&                                   \
58                      ac->size_limit > 0 &&                                      \
59                      size > ac->size_limit * 1024);                             \
60                                                                                 \
61                 if (ac->rmmail &&                                               \
62                     msg->recv_time != 0 &&                                      \
63                     state->current_time - msg->recv_time >=                     \
64                     ac->msg_leave_time * 24 * 60 * 60) {                        \
65                         log_print(_("POP3: Deleting expired message %d\n"),     \
66                                   state->cur_msg);                              \
67                         return POP3_DELETE_SEND;                                \
68                 }                                                               \
69                                                                                 \
70                 if (size_limit_over)                                            \
71                         log_print(_("POP3: Skipping message %d (%d bytes)\n"),  \
72                                   state->cur_msg, size);                        \
73                                                                                 \
74                 if (size == 0 || msg->received || size_limit_over) {            \
75                         state->cur_total_bytes += size;                         \
76                         if (state->cur_msg == state->count)                     \
77                                 return POP3_LOGOUT_SEND;                        \
78                         else                                                    \
79                                 state->cur_msg++;                               \
80                 } else                                                          \
81                         break;                                                  \
82         }                                                                       \
83 }
84
85 static gint pop3_ok(SockInfo *sock, gchar *argbuf);
86 static void pop3_gen_send(SockInfo *sock, const gchar *format, ...);
87 static gint pop3_gen_recv(SockInfo *sock, gchar *buf, gint size);
88 static gboolean pop3_sd_get_next (Pop3State *state);
89 static void pop3_sd_new_header(Pop3State *state);
90 gboolean pop3_sd_state(Pop3State *state, gint cur_state, guint *next_state);
91
92 gint pop3_greeting_recv(SockInfo *sock, gpointer data)
93 {
94         Pop3State *state = (Pop3State *)data;
95         gchar buf[POPBUFSIZE];
96         gint ok;
97
98         if ((ok = pop3_ok(sock, buf)) == PS_SUCCESS) {
99                 state->greeting = g_strdup(buf);
100 #if USE_SSL
101                 if (state->ac_prefs->ssl_pop == SSL_STARTTLS)
102                         return POP3_STLS_SEND;
103 #endif
104                 if (state->ac_prefs->protocol == A_APOP)
105                         return POP3_GETAUTH_APOP_SEND;
106                 else
107                         return POP3_GETAUTH_USER_SEND;
108         } else {
109                 state->error_val = ok;
110                 return -1;
111         }
112 }
113
114 #if USE_SSL
115 gint pop3_stls_send(SockInfo *sock, gpointer data)
116 {
117         pop3_gen_send(sock, "STLS");
118
119         return POP3_STLS_RECV;
120 }
121
122 gint pop3_stls_recv(SockInfo *sock, gpointer data)
123 {
124         Pop3State *state = (Pop3State *)data;
125         gint ok;
126
127         if ((ok = pop3_ok(sock, NULL)) == PS_SUCCESS) {
128                 if (!ssl_init_socket_with_method(sock, SSL_METHOD_TLSv1)) {
129                         state->error_val = PS_SOCKET;
130                         return -1;
131                 }
132                 if (state->ac_prefs->protocol == A_APOP)
133                         return POP3_GETAUTH_APOP_SEND;
134                 else
135                         return POP3_GETAUTH_USER_SEND;
136         } else if (ok == PS_PROTOCOL) {
137                 log_warning(_("can't start TLS session\n"));
138                 state->error_val = ok;
139                 return POP3_LOGOUT_SEND;
140         } else {
141                 state->error_val = ok;
142                 return -1;
143         }
144 }
145 #endif /* USE_SSL */
146
147 gint pop3_getauth_user_send(SockInfo *sock, gpointer data)
148 {
149         Pop3State *state = (Pop3State *)data;
150
151         g_return_val_if_fail(state->user != NULL, -1);
152
153         pop3_gen_send(sock, "USER %s", state->user);
154
155         return POP3_GETAUTH_USER_RECV;
156 }
157
158 gint pop3_getauth_user_recv(SockInfo *sock, gpointer data)
159 {
160         Pop3State *state = (Pop3State *)data;
161
162         if (pop3_ok(sock, NULL) == PS_SUCCESS)
163                 return POP3_GETAUTH_PASS_SEND;
164         else {
165                 log_warning(_("error occurred on authentication\n"));
166                 state->error_val = PS_AUTHFAIL;
167                 return -1;
168         }
169 }
170
171 gint pop3_getauth_pass_send(SockInfo *sock, gpointer data)
172 {
173         Pop3State *state = (Pop3State *)data;
174
175         g_return_val_if_fail(state->pass != NULL, -1);
176
177         pop3_gen_send(sock, "PASS %s", state->pass);
178
179         return POP3_GETAUTH_PASS_RECV;
180 }
181
182 gint pop3_getauth_pass_recv(SockInfo *sock, gpointer data)
183 {
184         Pop3State *state = (Pop3State *)data;
185         gint ok;
186
187         if ((ok = pop3_ok(sock, NULL)) == PS_SUCCESS)
188                 return POP3_GETRANGE_STAT_SEND;
189         else if (ok == PS_LOCKBUSY) {
190                 log_warning(_("mailbox is locked\n"));
191                 state->error_val = ok;
192                 return -1;
193         } else {
194                 log_warning(_("error occurred on authentication\n"));
195                 state->error_val = PS_AUTHFAIL;
196                 return -1;
197         }
198 }
199
200 gint pop3_getauth_apop_send(SockInfo *sock, gpointer data)
201 {
202         Pop3State *state = (Pop3State *)data;
203         gchar *start, *end;
204         gchar *apop_str;
205         gchar md5sum[33];
206
207         g_return_val_if_fail(state->user != NULL, -1);
208         g_return_val_if_fail(state->pass != NULL, -1);
209
210         if ((start = strchr(state->greeting, '<')) == NULL) {
211                 log_warning(_("Required APOP timestamp not found "
212                               "in greeting\n"));
213                 state->error_val = PS_PROTOCOL;
214                 return -1;
215         }
216
217         if ((end = strchr(start, '>')) == NULL || end == start + 1) {
218                 log_warning(_("Timestamp syntax error in greeting\n"));
219                 state->error_val = PS_PROTOCOL;
220                 return -1;
221         }
222
223         *(end + 1) = '\0';
224
225         apop_str = g_strconcat(start, state->pass, NULL);
226         md5_hex_digest(md5sum, apop_str);
227         g_free(apop_str);
228
229         pop3_gen_send(sock, "APOP %s %s", state->user, md5sum);
230
231         return POP3_GETAUTH_APOP_RECV;
232 }
233
234 gint pop3_getauth_apop_recv(SockInfo *sock, gpointer data)
235 {
236         Pop3State *state = (Pop3State *)data;
237         gint ok;
238
239         if ((ok = pop3_ok(sock, NULL)) == PS_SUCCESS)
240                 return POP3_GETRANGE_STAT_SEND;
241         else if (ok == PS_LOCKBUSY) {
242                 log_warning(_("mailbox is locked\n"));
243                 state->error_val = ok;
244                 return -1;
245         } else {
246                 log_warning(_("error occurred on authentication\n"));
247                 state->error_val = PS_AUTHFAIL;
248                 return -1;
249         }
250 }
251
252 gint pop3_getrange_stat_send(SockInfo *sock, gpointer data)
253 {
254         pop3_gen_send(sock, "STAT");
255
256         return POP3_GETRANGE_STAT_RECV;
257 }
258
259 gint pop3_getrange_stat_recv(SockInfo *sock, gpointer data)
260 {
261         Pop3State *state = (Pop3State *)data;
262         gchar buf[POPBUFSIZE + 1];
263         gint ok;
264
265         if ((ok = pop3_ok(sock, buf)) == PS_SUCCESS) {
266                 if (sscanf(buf, "%d %d", &state->count, &state->total_bytes)
267                     != 2) {
268                         log_warning(_("POP3 protocol error\n"));
269                         state->error_val = PS_PROTOCOL;
270                         return -1;
271                 } else {
272                         if (state->count == 0) {
273                                 state->uidl_is_valid = TRUE;
274                                 return POP3_LOGOUT_SEND;
275                         } else {
276                                 state->msg = g_new0
277                                         (Pop3MsgInfo, state->count + 1);
278                                 state->cur_msg = 1;
279                                 return POP3_GETRANGE_UIDL_SEND;
280                         }
281                 }
282         } else if (ok == PS_PROTOCOL) {
283                 state->error_val = ok;
284                 return POP3_LOGOUT_SEND;
285         } else {
286                 state->error_val = ok;
287                 return -1;
288         }
289 }
290
291 gint pop3_getrange_last_send(SockInfo *sock, gpointer data)
292 {
293         pop3_gen_send(sock, "LAST");
294
295         return POP3_GETRANGE_LAST_RECV;
296 }
297
298 gint pop3_getrange_last_recv(SockInfo *sock, gpointer data)
299 {
300         Pop3State *state = (Pop3State *)data;
301         gchar buf[POPBUFSIZE + 1];
302
303         if (pop3_ok(sock, buf) == PS_SUCCESS) {
304                 gint last;
305
306                 if (sscanf(buf, "%d", &last) == 0) {
307                         log_warning(_("POP3 protocol error\n"));
308                         state->error_val = PS_PROTOCOL;
309                         return -1;
310                 } else {
311                         if (state->count == last)
312                                 return POP3_LOGOUT_SEND;
313                         else {
314                                 state->cur_msg = last + 1;
315                                 return POP3_GETSIZE_LIST_SEND;
316                         }
317                 }
318         } else
319                 return POP3_GETSIZE_LIST_SEND;
320 }
321
322 gint pop3_getrange_uidl_send(SockInfo *sock, gpointer data)
323 {
324         pop3_gen_send(sock, "UIDL");
325
326         return POP3_GETRANGE_UIDL_RECV;
327 }
328
329 gint pop3_getrange_uidl_recv(SockInfo *sock, gpointer data)
330 {
331         Pop3State *state = (Pop3State *)data;
332         gboolean new = FALSE;
333         gboolean get_all = FALSE;
334         gchar buf[POPBUFSIZE];
335         gchar id[IDLEN + 1];
336         gint len;
337         gint next_state;
338
339         if (!state->uidl_table) new = TRUE;
340         if (state->ac_prefs->getall ||
341             (state->ac_prefs->rmmail && state->ac_prefs->msg_leave_time == 0))
342                 get_all = TRUE;
343
344         if (pop3_ok(sock, NULL) != PS_SUCCESS) {
345                 /* UIDL is not supported */
346                 if (pop3_sd_state(state, POP3_GETRANGE_UIDL_RECV, &next_state))
347                         return next_state;
348
349                 if (!get_all)
350                         return POP3_GETRANGE_LAST_SEND;
351                 else
352                         return POP3_GETSIZE_LIST_SEND;
353         }
354
355         while ((len = sock_gets(sock, buf, sizeof(buf))) > 0) {
356                 gint num;
357                 time_t recv_time;
358
359                 if (buf[0] == '.') break;
360                 if (sscanf(buf, "%d %" Xstr(IDLEN) "s", &num, id) != 2)
361                         continue;
362                 if (num <= 0 || num > state->count) continue;
363
364                 state->msg[num].uidl = g_strdup(id);
365
366                 if (!state->uidl_table) continue;
367
368                 recv_time = (time_t)g_hash_table_lookup(state->uidl_table, id);
369                 state->msg[num].recv_time = recv_time;
370
371                 if (!get_all && recv_time != 0)
372                         state->msg[num].received = TRUE;
373
374                 if (new == FALSE &&
375                     (get_all || recv_time == 0 || state->ac_prefs->rmmail)) {
376                         state->cur_msg = num;
377                         new = TRUE;
378                 }
379         }
380
381         if (len < 0) {
382                 log_error(_("Socket error\n"));
383                 state->error_val = PS_SOCKET;
384                 return -1;
385         }
386
387         state->uidl_is_valid = TRUE;
388         if (pop3_sd_state(state, POP3_GETRANGE_UIDL_RECV, &next_state))
389                 return next_state;
390
391         if (new == TRUE)
392                 return POP3_GETSIZE_LIST_SEND;
393         else
394                 return POP3_LOGOUT_SEND;
395 }
396
397 gint pop3_getsize_list_send(SockInfo *sock, gpointer data)
398 {
399         pop3_gen_send(sock, "LIST");
400
401         return POP3_GETSIZE_LIST_RECV;
402 }
403
404 gint pop3_getsize_list_recv(SockInfo *sock, gpointer data)
405 {
406         Pop3State *state = (Pop3State *)data;
407         gchar buf[POPBUFSIZE];
408         gint len;
409         gint next_state;
410
411         if (pop3_ok(sock, NULL) != PS_SUCCESS) return POP3_LOGOUT_SEND;
412
413         state->cur_total_bytes = 0;
414         state->cur_total_recv_bytes = 0;
415
416         while ((len = sock_gets(sock, buf, sizeof(buf))) > 0) {
417                 guint num, size;
418
419                 if (buf[0] == '.') break;
420                 if (sscanf(buf, "%u %u", &num, &size) != 2) {
421                         state->error_val = PS_PROTOCOL;
422                         return -1;
423                 }
424
425                 if (num > 0 && num <= state->count)
426                         state->msg[num].size = size;
427                 if (num > 0 && num < state->cur_msg)
428                         state->cur_total_bytes += size;
429         }
430
431         if (len < 0) {
432                 log_error(_("Socket error\n"));
433                 state->error_val = PS_SOCKET;
434                 return -1;
435         }
436
437         if (pop3_sd_state(state, POP3_GETSIZE_LIST_RECV, &next_state))
438                 return next_state;
439
440         LOOKUP_NEXT_MSG();
441         return POP3_RETR_SEND;
442 }
443  
444 gint pop3_top_send(SockInfo *sock, gpointer data)
445 {
446         Pop3State *state = (Pop3State *)data;
447
448         inc_progress_update(state, POP3_TOP_SEND); 
449
450         pop3_gen_send(sock, "TOP %i 0", state->cur_msg );
451
452         return POP3_TOP_RECV;
453 }
454
455 gint pop3_top_recv(SockInfo *sock, gpointer data)
456 {
457         Pop3State *state = (Pop3State *)data;
458         gchar *filename, *path;
459         gint next_state;
460         
461         if (pop3_ok(sock, NULL) != PS_SUCCESS) 
462                 return POP3_LOGOUT_SEND;
463
464         path = g_strconcat(get_header_cache_dir(), G_DIR_SEPARATOR_S, NULL);
465
466         if ( !is_dir_exist(path) )
467                 make_dir_hier(path);
468         
469         filename = g_strdup_printf("%s%i", path, state->cur_msg);
470                                    
471         if (recv_write_to_file(sock, filename) < 0) {
472                 state->error_val = PS_IOERR;
473                 return -1;
474         }
475
476         pop3_sd_state(state, POP3_TOP_RECV, &next_state);
477         
478         if (state->cur_msg < state->count) {
479                 state->cur_msg++;
480                 return POP3_TOP_SEND;
481         } else
482                 return POP3_LOGOUT_SEND;
483 }
484
485 gint pop3_retr_send(SockInfo *sock, gpointer data)
486 {
487         Pop3State *state = (Pop3State *)data;
488
489         pop3_gen_send(sock, "RETR %d", state->cur_msg);
490
491         return POP3_RETR_RECV;
492 }
493
494 gint pop3_retr_recv(SockInfo *sock, gpointer data)
495 {
496         Pop3State *state = (Pop3State *)data;
497         gchar *file;
498         gint ok, drop_ok;
499         gint next_state;
500         if ((ok = pop3_ok(sock, NULL)) == PS_SUCCESS) {
501                 file = get_tmp_file();
502                 if (recv_write_to_file(sock, file) < 0) {
503                         g_free(file);
504                         if (!state->cancelled)
505                                 state->error_val = PS_IOERR;
506                         return -1;
507                 }
508
509                 drop_ok = inc_drop_message(file, state);
510                 g_free(file);
511                 if (drop_ok < 0) {
512                         state->error_val = PS_ERROR;
513                         return -1;
514                 }
515
516                 if (pop3_sd_state(state, POP3_RETR_RECV, &next_state))
517                         return next_state;
518         
519                 state->cur_total_bytes += state->msg[state->cur_msg].size;
520                 state->cur_total_recv_bytes += state->msg[state->cur_msg].size;
521                 state->cur_total_num++;
522
523                 state->msg[state->cur_msg].received = TRUE;
524                 state->msg[state->cur_msg].recv_time = state->current_time;
525
526                 if (state->ac_prefs->rmmail &&
527                     state->ac_prefs->msg_leave_time == 0)
528                         return POP3_DELETE_SEND;
529
530                 if (state->cur_msg < state->count) {
531                         state->cur_msg++;
532                         LOOKUP_NEXT_MSG();
533                         return POP3_RETR_SEND;
534                 } else
535                         return POP3_LOGOUT_SEND;
536         } else if (ok == PS_PROTOCOL) {
537                 state->error_val = ok;
538                 return POP3_LOGOUT_SEND;
539         } else {
540                 state->error_val = ok;
541                 return -1;
542         }
543 }
544
545 gint pop3_delete_send(SockInfo *sock, gpointer data)
546 {
547         Pop3State *state = (Pop3State *)data;
548
549         pop3_gen_send(sock, "DELE %d", state->cur_msg);
550
551         return POP3_DELETE_RECV;
552 }
553
554 gint pop3_delete_recv(SockInfo *sock, gpointer data)
555 {
556         Pop3State *state = (Pop3State *)data;
557         gint next_state;
558         gint ok;
559
560         if ((ok = pop3_ok(sock, NULL)) == PS_SUCCESS) {
561                 state->msg[state->cur_msg].deleted = TRUE;
562                 
563                 if (pop3_sd_state(state, POP3_DELETE_RECV, &next_state))
564                         return next_state;      
565
566                 if (state->cur_msg < state->count) {
567                         state->cur_msg++;
568                         LOOKUP_NEXT_MSG();
569                         return POP3_RETR_SEND;
570                 } else
571                         return POP3_LOGOUT_SEND;
572         } else if (ok == PS_PROTOCOL) {
573                 state->error_val = ok;
574                 return POP3_LOGOUT_SEND;
575         } else {
576                 state->error_val = ok;
577                 return -1;
578         }
579 }
580
581 gint pop3_logout_send(SockInfo *sock, gpointer data)
582 {
583         pop3_gen_send(sock, "QUIT");
584
585         return POP3_LOGOUT_RECV;
586 }
587
588 gint pop3_logout_recv(SockInfo *sock, gpointer data)
589 {
590         Pop3State *state = (Pop3State *)data;
591         gint ok;
592
593         if ((ok = pop3_ok(sock, NULL)) != PS_SUCCESS)
594                 state->error_val = ok;
595
596         return -1;
597 }
598
599 static gint pop3_ok(SockInfo *sock, gchar *argbuf)
600 {
601         gint ok;
602         gchar buf[POPBUFSIZE + 1];
603         gchar *bufp;
604
605         if ((ok = pop3_gen_recv(sock, buf, sizeof(buf))) == PS_SUCCESS) {
606                 bufp = buf;
607                 if (*bufp == '+' || *bufp == '-')
608                         bufp++;
609                 else
610                         return PS_PROTOCOL;
611
612                 while (isalpha(*bufp))
613                         bufp++;
614
615                 if (*bufp)
616                         *(bufp++) = '\0';
617
618                 if (!strcmp(buf, "+OK"))
619                         ok = PS_SUCCESS;
620                 else if (!strncmp(buf, "-ERR", 4)) {
621                         if (strstr(bufp, "lock") ||
622                                  strstr(bufp, "Lock") ||
623                                  strstr(bufp, "LOCK") ||
624                                  strstr(bufp, "wait"))
625                                 ok = PS_LOCKBUSY;
626                         else
627                                 ok = PS_PROTOCOL;
628
629                         if (*bufp)
630                                 fprintf(stderr, "POP3: %s\n", bufp);
631                 } else
632                         ok = PS_PROTOCOL;
633
634                 if (argbuf)
635                         strcpy(argbuf, bufp);
636         }
637
638         return ok;
639 }
640
641 static void pop3_gen_send(SockInfo *sock, const gchar *format, ...)
642 {
643         gchar buf[POPBUFSIZE + 1];
644         va_list args;
645
646         va_start(args, format);
647         g_vsnprintf(buf, sizeof(buf) - 2, format, args);
648         va_end(args);
649
650         if (!strncasecmp(buf, "PASS ", 5))
651                 log_print("POP3> PASS ********\n");
652         else
653                 log_print("POP3> %s\n", buf);
654
655         strcat(buf, "\r\n");
656         sock_write(sock, buf, strlen(buf));
657 }
658
659 static gint pop3_gen_recv(SockInfo *sock, gchar *buf, gint size)
660 {
661         if (sock_gets(sock, buf, size) < 0) {
662                 return PS_SOCKET;
663         } else {
664                 strretchomp(buf);
665                 log_print("POP3< %s\n", buf);
666
667                 return PS_SUCCESS;
668         }
669 }
670
671 static void pop3_sd_new_header(Pop3State *state)
672 {
673         HeaderItems *new_msg;
674         if (state->cur_msg <= state->count) {
675                 new_msg = g_new0(HeaderItems, 1); 
676                 
677                 new_msg->index              = state->cur_msg;
678                 new_msg->state              = SD_UNCHECKED;
679                 new_msg->size               = state->msg[state->cur_msg].size; 
680                 new_msg->received           = state->msg[state->cur_msg].received;
681                 new_msg->del_by_old_session = FALSE;
682                 
683                 state->ac_prefs->msg_list = g_slist_append(state->ac_prefs->msg_list, 
684                                                            new_msg);
685         }
686 }
687
688 gboolean pop3_sd_state(Pop3State *state, gint cur_state, guint *next_state) 
689 {
690         gint session = state->ac_prefs->session;
691         guint goto_state = -1;
692
693         switch (cur_state) { 
694         case POP3_GETRANGE_UIDL_RECV:
695                 switch (session) {
696                 case STYPE_DOWNLOAD:
697                 case STYPE_DELETE:
698                 case STYPE_PREVIEW_ALL:
699                         goto_state = POP3_GETSIZE_LIST_SEND;
700                 default:
701                         break;
702                 }
703                 break;
704         case POP3_GETSIZE_LIST_RECV:
705                 switch (session) {
706                 case STYPE_PREVIEW_ALL:
707                         state->cur_msg = 1;
708                 case STYPE_PREVIEW_NEW:
709                         goto_state = POP3_TOP_SEND;
710                         break;
711                 case STYPE_DELETE:
712                         if (pop3_sd_get_next(state))
713                                 goto_state = POP3_DELETE_SEND;          
714                         else
715                                 goto_state = POP3_LOGOUT_SEND;
716                         break;
717                 case STYPE_DOWNLOAD:
718                         if (pop3_sd_get_next(state))
719                                 goto_state = POP3_RETR_SEND;
720                         else
721                                 goto_state = POP3_LOGOUT_SEND;
722                 default:
723                         break;
724                 }
725                 break;
726         case POP3_TOP_RECV: 
727                 switch (session) { 
728                 case STYPE_PREVIEW_ALL:
729                 case STYPE_PREVIEW_NEW:
730                         pop3_sd_new_header(state);
731                 default:
732                         break;
733                 }
734                 break;
735         case POP3_RETR_RECV:
736                 switch (session) {
737                 case STYPE_DOWNLOAD:
738                         if (state->ac_prefs->sd_rmmail_on_download) 
739                                 goto_state = POP3_DELETE_SEND;
740                         else {
741                                 if (pop3_sd_get_next(state)) 
742                                         goto_state = POP3_RETR_SEND;
743                                 else
744                                         goto_state = POP3_LOGOUT_SEND;
745                         }
746                 default:        
747                         break;
748                 }
749                 break;
750         case POP3_DELETE_RECV:
751                 switch (session) {
752                 case STYPE_DELETE:
753                         if (pop3_sd_get_next(state)) 
754                                 goto_state = POP3_DELETE_SEND;
755                         else
756                                 goto_state =  POP3_LOGOUT_SEND;
757                         break;
758                 case STYPE_DOWNLOAD:
759                         if (pop3_sd_get_next(state)) 
760                                 goto_state = POP3_RETR_SEND;
761                         else
762                                 goto_state = POP3_LOGOUT_SEND;
763                 default:
764                         break;
765                 }
766         default:
767                 break;
768                 
769         }                 
770
771         *next_state = goto_state;
772         if (goto_state != -1)
773                 return TRUE;
774         else 
775                 return FALSE;
776 }
777
778 gboolean pop3_sd_get_next(Pop3State *state)
779 {
780         GSList *cur;
781         gint deleted_msgs = 0;
782         
783         switch (state->ac_prefs->session) {
784         case STYPE_DOWNLOAD:
785         case STYPE_DELETE:      
786                 for (cur = state->ac_prefs->msg_list; cur != NULL; cur = cur->next) {
787                         HeaderItems *items = (HeaderItems*)cur->data;
788
789                         if (items->del_by_old_session)
790                                 deleted_msgs++;
791
792                         switch (items->state) {
793                         case SD_REMOVE:
794                                 items->state = SD_REMOVED;
795                                 break;
796                         case SD_DOWNLOAD:
797                                 items->state = SD_DOWNLOADED;
798                                 break;
799                         case SD_CHECKED:
800                                 state->cur_msg = items->index - deleted_msgs;
801                                 if (state->ac_prefs->session == STYPE_DELETE)
802                                         items->state = SD_REMOVE;
803                                 else
804                                         items->state = SD_DOWNLOAD;
805                                 return TRUE;
806                         default:
807                                 break;
808                         }
809                 }
810                 return FALSE;
811         default:
812                 return FALSE;
813         }
814 }
815
816 Pop3State *pop3_state_new(PrefsAccount *account)
817 {
818         Pop3State *state;
819
820         g_return_val_if_fail(account != NULL, NULL);
821
822         state = g_new0(Pop3State, 1);
823
824         state->ac_prefs = account;
825         state->uidl_table = pop3_get_uidl_table(account);
826         state->current_time = time(NULL);
827         state->error_val = PS_SUCCESS;
828
829         return state;
830 }
831
832 void pop3_state_destroy(Pop3State *state)
833 {
834         gint n;
835
836         g_return_if_fail(state != NULL);
837
838         for (n = 1; n <= state->count; n++)
839                 g_free(state->msg[n].uidl);
840         g_free(state->msg);
841
842         if (state->uidl_table) {
843                 hash_free_strings(state->uidl_table);
844                 g_hash_table_destroy(state->uidl_table);
845         }
846
847         g_free(state->greeting);
848         g_free(state->user);
849         g_free(state->pass);
850         g_free(state->prev_folder);
851
852         g_free(state);
853 }
854
855 GHashTable *pop3_get_uidl_table(PrefsAccount *ac_prefs)
856 {
857         GHashTable *table;
858         gchar *path;
859         FILE *fp;
860         gchar buf[IDLEN + 3];
861         gchar uidl[IDLEN + 3];
862         time_t recv_time;
863         time_t now;
864
865         path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
866                            "uidl", G_DIR_SEPARATOR_S, ac_prefs->recv_server,
867                            "-", ac_prefs->userid, NULL);
868         if ((fp = fopen(path, "rb")) == NULL) {
869                 if (ENOENT != errno) FILE_OP_ERROR(path, "fopen");
870                 g_free(path);
871                 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
872                                    "uidl-", ac_prefs->recv_server,
873                                    "-", ac_prefs->userid, NULL);
874                 if ((fp = fopen(path, "rb")) == NULL) {
875                         if (ENOENT != errno) FILE_OP_ERROR(path, "fopen");
876                         g_free(path);
877                         return NULL;
878                 }
879         }
880         g_free(path);
881
882         table = g_hash_table_new(g_str_hash, g_str_equal);
883
884         now = time(NULL);
885
886         while (fgets(buf, sizeof(buf), fp) != NULL) {
887                 strretchomp(buf);
888                 recv_time = 0;
889                 if (sscanf(buf, "%s\t%ld", uidl, &recv_time) != 2) {
890                         if (sscanf(buf, "%s", uidl) != 1)
891                                 continue;
892                         else
893                                 recv_time = now;
894                 }
895                 if (recv_time == 0)
896                         recv_time = 1;
897                 g_hash_table_insert(table, g_strdup(uidl),
898                                     GINT_TO_POINTER(recv_time));
899         }
900
901         fclose(fp);
902         return table;
903 }
904
905 gint pop3_write_uidl_list(Pop3State *state)
906 {
907         gchar *path;
908         FILE *fp;
909         Pop3MsgInfo *msg;
910         gint n;
911
912         if (!state->uidl_is_valid) return 0;
913
914         path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
915                            "uidl", G_DIR_SEPARATOR_S,
916                            state->ac_prefs->recv_server,
917                            "-", state->ac_prefs->userid, NULL);
918         if ((fp = fopen(path, "wb")) == NULL) {
919                 FILE_OP_ERROR(path, "fopen");
920                 g_free(path);
921                 return -1;
922         }
923
924         for (n = 1; n <= state->count; n++) {
925                 msg = &state->msg[n];
926                 if (msg->uidl && msg->received && !msg->deleted)
927                         fprintf(fp, "%s\t%ld\n", msg->uidl, msg->recv_time);
928         }
929
930         if (fclose(fp) == EOF) FILE_OP_ERROR(path, "fclose");
931         g_free(path);
932
933         return 0;
934 }