Add pop before smtp
[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_POP_BEFORE_SMTP:
697                         goto_state = POP3_LOGOUT_SEND;
698                         break;
699                 case STYPE_DOWNLOAD:
700                 case STYPE_DELETE:
701                 case STYPE_PREVIEW_ALL:
702                         goto_state = POP3_GETSIZE_LIST_SEND;
703                 default:
704                         break;
705                 }
706                 break;
707         case POP3_GETSIZE_LIST_RECV:
708                 switch (session) {
709                 case STYPE_PREVIEW_ALL:
710                         state->cur_msg = 1;
711                 case STYPE_PREVIEW_NEW:
712                         goto_state = POP3_TOP_SEND;
713                         break;
714                 case STYPE_DELETE:
715                         if (pop3_sd_get_next(state))
716                                 goto_state = POP3_DELETE_SEND;          
717                         else
718                                 goto_state = POP3_LOGOUT_SEND;
719                         break;
720                 case STYPE_DOWNLOAD:
721                         if (pop3_sd_get_next(state))
722                                 goto_state = POP3_RETR_SEND;
723                         else
724                                 goto_state = POP3_LOGOUT_SEND;
725                 default:
726                         break;
727                 }
728                 break;
729         case POP3_TOP_RECV: 
730                 switch (session) { 
731                 case STYPE_PREVIEW_ALL:
732                 case STYPE_PREVIEW_NEW:
733                         pop3_sd_new_header(state);
734                 default:
735                         break;
736                 }
737                 break;
738         case POP3_RETR_RECV:
739                 switch (session) {
740                 case STYPE_DOWNLOAD:
741                         if (state->ac_prefs->sd_rmmail_on_download) 
742                                 goto_state = POP3_DELETE_SEND;
743                         else {
744                                 if (pop3_sd_get_next(state)) 
745                                         goto_state = POP3_RETR_SEND;
746                                 else
747                                         goto_state = POP3_LOGOUT_SEND;
748                         }
749                 default:        
750                         break;
751                 }
752                 break;
753         case POP3_DELETE_RECV:
754                 switch (session) {
755                 case STYPE_DELETE:
756                         if (pop3_sd_get_next(state)) 
757                                 goto_state = POP3_DELETE_SEND;
758                         else
759                                 goto_state =  POP3_LOGOUT_SEND;
760                         break;
761                 case STYPE_DOWNLOAD:
762                         if (pop3_sd_get_next(state)) 
763                                 goto_state = POP3_RETR_SEND;
764                         else
765                                 goto_state = POP3_LOGOUT_SEND;
766                 default:
767                         break;
768                 }
769         default:
770                 break;
771                 
772         }                 
773
774         *next_state = goto_state;
775         if (goto_state != -1)
776                 return TRUE;
777         else 
778                 return FALSE;
779 }
780
781 gboolean pop3_sd_get_next(Pop3State *state)
782 {
783         GSList *cur;
784         gint deleted_msgs = 0;
785         
786         switch (state->ac_prefs->session) {
787         case STYPE_DOWNLOAD:
788         case STYPE_DELETE:      
789                 for (cur = state->ac_prefs->msg_list; cur != NULL; cur = cur->next) {
790                         HeaderItems *items = (HeaderItems*)cur->data;
791
792                         if (items->del_by_old_session)
793                                 deleted_msgs++;
794
795                         switch (items->state) {
796                         case SD_REMOVE:
797                                 items->state = SD_REMOVED;
798                                 break;
799                         case SD_DOWNLOAD:
800                                 items->state = SD_DOWNLOADED;
801                                 break;
802                         case SD_CHECKED:
803                                 state->cur_msg = items->index - deleted_msgs;
804                                 if (state->ac_prefs->session == STYPE_DELETE)
805                                         items->state = SD_REMOVE;
806                                 else
807                                         items->state = SD_DOWNLOAD;
808                                 return TRUE;
809                         default:
810                                 break;
811                         }
812                 }
813                 return FALSE;
814         default:
815                 return FALSE;
816         }
817 }
818
819 Pop3State *pop3_state_new(PrefsAccount *account)
820 {
821         Pop3State *state;
822
823         g_return_val_if_fail(account != NULL, NULL);
824
825         state = g_new0(Pop3State, 1);
826
827         state->ac_prefs = account;
828         state->uidl_table = pop3_get_uidl_table(account);
829         state->current_time = time(NULL);
830         state->error_val = PS_SUCCESS;
831
832         return state;
833 }
834
835 void pop3_state_destroy(Pop3State *state)
836 {
837         gint n;
838
839         g_return_if_fail(state != NULL);
840
841         for (n = 1; n <= state->count; n++)
842                 g_free(state->msg[n].uidl);
843         g_free(state->msg);
844
845         if (state->uidl_table) {
846                 hash_free_strings(state->uidl_table);
847                 g_hash_table_destroy(state->uidl_table);
848         }
849
850         g_free(state->greeting);
851         g_free(state->user);
852         g_free(state->pass);
853         g_free(state->prev_folder);
854
855         g_free(state);
856 }
857
858 GHashTable *pop3_get_uidl_table(PrefsAccount *ac_prefs)
859 {
860         GHashTable *table;
861         gchar *path;
862         FILE *fp;
863         gchar buf[IDLEN + 3];
864         gchar uidl[IDLEN + 3];
865         time_t recv_time;
866         time_t now;
867
868         path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
869                            "uidl", G_DIR_SEPARATOR_S, ac_prefs->recv_server,
870                            "-", ac_prefs->userid, NULL);
871         if ((fp = fopen(path, "rb")) == NULL) {
872                 if (ENOENT != errno) FILE_OP_ERROR(path, "fopen");
873                 g_free(path);
874                 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
875                                    "uidl-", ac_prefs->recv_server,
876                                    "-", ac_prefs->userid, NULL);
877                 if ((fp = fopen(path, "rb")) == NULL) {
878                         if (ENOENT != errno) FILE_OP_ERROR(path, "fopen");
879                         g_free(path);
880                         return NULL;
881                 }
882         }
883         g_free(path);
884
885         table = g_hash_table_new(g_str_hash, g_str_equal);
886
887         now = time(NULL);
888
889         while (fgets(buf, sizeof(buf), fp) != NULL) {
890                 strretchomp(buf);
891                 recv_time = 0;
892                 if (sscanf(buf, "%s\t%ld", uidl, &recv_time) != 2) {
893                         if (sscanf(buf, "%s", uidl) != 1)
894                                 continue;
895                         else
896                                 recv_time = now;
897                 }
898                 if (recv_time == 0)
899                         recv_time = 1;
900                 g_hash_table_insert(table, g_strdup(uidl),
901                                     GINT_TO_POINTER(recv_time));
902         }
903
904         fclose(fp);
905         return table;
906 }
907
908 gint pop3_write_uidl_list(Pop3State *state)
909 {
910         gchar *path;
911         FILE *fp;
912         Pop3MsgInfo *msg;
913         gint n;
914
915         if (!state->uidl_is_valid) return 0;
916
917         path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
918                            "uidl", G_DIR_SEPARATOR_S,
919                            state->ac_prefs->recv_server,
920                            "-", state->ac_prefs->userid, NULL);
921         if ((fp = fopen(path, "wb")) == NULL) {
922                 FILE_OP_ERROR(path, "fopen");
923                 g_free(path);
924                 return -1;
925         }
926
927         for (n = 1; n <= state->count; n++) {
928                 msg = &state->msg[n];
929                 if (msg->uidl && msg->received && !msg->deleted)
930                         fprintf(fp, "%s\t%ld\n", msg->uidl, msg->recv_time);
931         }
932
933         if (fclose(fp) == EOF) FILE_OP_ERROR(path, "fclose");
934         g_free(path);
935
936         return 0;
937 }