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