_Really_ clean it
[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                 g_free(path);
479                 g_free(filename);
480                 return -1;
481         }
482         
483         g_free(path);
484         g_free(filename);
485         
486         pop3_sd_state(state, POP3_TOP_RECV, &next_state);
487         
488         if (state->cur_msg < state->count) {
489                 state->cur_msg++;
490                 return POP3_TOP_SEND;
491         } else
492                 return POP3_LOGOUT_SEND;
493 }
494
495 gint pop3_retr_send(SockInfo *sock, gpointer data)
496 {
497         Pop3State *state = (Pop3State *)data;
498
499         pop3_gen_send(sock, "RETR %d", state->cur_msg);
500
501         return POP3_RETR_RECV;
502 }
503
504 gint pop3_retr_recv(SockInfo *sock, gpointer data)
505 {
506         Pop3State *state = (Pop3State *)data;
507         gchar *file;
508         gint ok, drop_ok;
509         gint next_state;
510         gint write_val;
511         if ((ok = pop3_ok(sock, NULL)) == PS_SUCCESS) {
512                 file = get_tmp_file();
513                 if ((write_val = recv_write_to_file(sock, file)) < 0) {
514                         g_free(file);
515                         if (!state->cancelled)
516                                 state->error_val = 
517                                         (write_val == -1 ? PS_IOERR : PS_SOCKET);
518                         return -1;
519                 }
520
521                 /* drop_ok: 0: success 1: don't receive -1: error */
522                 drop_ok = inc_drop_message(file, state);
523                 g_free(file);
524                 if (drop_ok < 0) {
525                         state->error_val = PS_ERROR;
526                         return -1;
527                 }
528
529                 if (pop3_sd_state(state, POP3_RETR_RECV, &next_state))
530                         return next_state;
531         
532                 state->cur_total_bytes += state->msg[state->cur_msg].size;
533                 state->cur_total_recv_bytes += state->msg[state->cur_msg].size;
534                 state->cur_total_num++;
535
536                 state->msg[state->cur_msg].received = TRUE;
537                 state->msg[state->cur_msg].recv_time =
538                         drop_ok == 1 ? RECV_TIME_KEEP : state->current_time;
539
540                 if (drop_ok == 0 && state->ac_prefs->rmmail &&
541                     state->ac_prefs->msg_leave_time == 0)
542                         return POP3_DELETE_SEND;
543
544                 if (state->cur_msg < state->count) {
545                         state->cur_msg++;
546                         LOOKUP_NEXT_MSG();
547                         return POP3_RETR_SEND;
548                 } else
549                         return POP3_LOGOUT_SEND;
550         } else if (ok == PS_PROTOCOL) {
551                 state->error_val = ok;
552                 return POP3_LOGOUT_SEND;
553         } else {
554                 state->error_val = ok;
555                 return -1;
556         }
557 }
558
559 gint pop3_delete_send(SockInfo *sock, gpointer data)
560 {
561         Pop3State *state = (Pop3State *)data;
562
563         pop3_gen_send(sock, "DELE %d", state->cur_msg);
564
565         return POP3_DELETE_RECV;
566 }
567
568 gint pop3_delete_recv(SockInfo *sock, gpointer data)
569 {
570         Pop3State *state = (Pop3State *)data;
571         gint next_state;
572         gint ok;
573
574         if ((ok = pop3_ok(sock, NULL)) == PS_SUCCESS) {
575                 state->msg[state->cur_msg].deleted = TRUE;
576                 
577                 if (pop3_sd_state(state, POP3_DELETE_RECV, &next_state))
578                         return next_state;      
579
580                 if (state->cur_msg < state->count) {
581                         state->cur_msg++;
582                         LOOKUP_NEXT_MSG();
583                         return POP3_RETR_SEND;
584                 } else
585                         return POP3_LOGOUT_SEND;
586         } else if (ok == PS_PROTOCOL) {
587                 state->error_val = ok;
588                 return POP3_LOGOUT_SEND;
589         } else {
590                 state->error_val = ok;
591                 return -1;
592         }
593 }
594
595 gint pop3_logout_send(SockInfo *sock, gpointer data)
596 {
597         pop3_gen_send(sock, "QUIT");
598
599         return POP3_LOGOUT_RECV;
600 }
601
602 gint pop3_logout_recv(SockInfo *sock, gpointer data)
603 {
604         Pop3State *state = (Pop3State *)data;
605         gint ok;
606
607         if ((ok = pop3_ok(sock, NULL)) != PS_SUCCESS)
608                 state->error_val = ok;
609
610         return -1;
611 }
612
613 static gint pop3_ok(SockInfo *sock, gchar *argbuf)
614 {
615         gint ok;
616         gchar buf[POPBUFSIZE + 1];
617         gchar *bufp;
618
619         if ((ok = pop3_gen_recv(sock, buf, sizeof(buf))) == PS_SUCCESS) {
620                 bufp = buf;
621                 if (*bufp == '+' || *bufp == '-')
622                         bufp++;
623                 else
624                         return PS_PROTOCOL;
625
626                 while (isalpha(*bufp))
627                         bufp++;
628
629                 if (*bufp)
630                         *(bufp++) = '\0';
631
632                 if (!strcmp(buf, "+OK"))
633                         ok = PS_SUCCESS;
634                 else if (!strncmp(buf, "-ERR", 4)) {
635                         if (strstr(bufp, "lock") ||
636                                  strstr(bufp, "Lock") ||
637                                  strstr(bufp, "LOCK") ||
638                                  strstr(bufp, "wait"))
639                                 ok = PS_LOCKBUSY;
640                         else
641                                 ok = PS_PROTOCOL;
642
643                         if (*bufp)
644                                 fprintf(stderr, "POP3: %s\n", bufp);
645                 } else
646                         ok = PS_PROTOCOL;
647
648                 if (argbuf)
649                         strcpy(argbuf, bufp);
650         }
651
652         return ok;
653 }
654
655 static void pop3_gen_send(SockInfo *sock, const gchar *format, ...)
656 {
657         gchar buf[POPBUFSIZE + 1];
658         va_list args;
659
660         va_start(args, format);
661         g_vsnprintf(buf, sizeof(buf) - 2, format, args);
662         va_end(args);
663
664         if (!strncasecmp(buf, "PASS ", 5))
665                 log_print("POP3> PASS ********\n");
666         else
667                 log_print("POP3> %s\n", buf);
668
669         strcat(buf, "\r\n");
670         sock_write(sock, buf, strlen(buf));
671 }
672
673 static gint pop3_gen_recv(SockInfo *sock, gchar *buf, gint size)
674 {
675         if (sock_gets(sock, buf, size) < 0) {
676                 return PS_SOCKET;
677         } else {
678                 strretchomp(buf);
679                 log_print("POP3< %s\n", buf);
680
681                 return PS_SUCCESS;
682         }
683 }
684
685 static void pop3_sd_new_header(Pop3State *state)
686 {
687         HeaderItems *new_msg;
688         if (state->cur_msg <= state->count) {
689                 new_msg = g_new0(HeaderItems, 1); 
690                 
691                 new_msg->index              = state->cur_msg;
692                 new_msg->state              = SD_UNCHECKED;
693                 new_msg->size               = state->msg[state->cur_msg].size; 
694                 new_msg->received           = state->msg[state->cur_msg].received;
695                 new_msg->del_by_old_session = FALSE;
696                 
697                 state->ac_prefs->msg_list = g_slist_append(state->ac_prefs->msg_list, 
698                                                            new_msg);
699                 debug_print("received ?: msg %i, received: %i\n",new_msg->index, new_msg->received); 
700         }
701 }
702
703 gboolean pop3_sd_state(Pop3State *state, gint cur_state, guint *next_state) 
704 {
705         gint session = state->ac_prefs->session;
706         guint goto_state = -1;
707
708         switch (cur_state) { 
709         case POP3_GETRANGE_UIDL_RECV:
710                 switch (session) {
711                 case STYPE_POP_BEFORE_SMTP:
712                         goto_state = POP3_LOGOUT_SEND;
713                         break;
714                 case STYPE_DOWNLOAD:
715                 case STYPE_DELETE:
716                 case STYPE_PREVIEW_ALL:
717                         goto_state = POP3_GETSIZE_LIST_SEND;
718                 default:
719                         break;
720                 }
721                 break;
722         case POP3_GETSIZE_LIST_RECV:
723                 switch (session) {
724                 case STYPE_PREVIEW_ALL:
725                         state->cur_msg = 1;
726                 case STYPE_PREVIEW_NEW:
727                         goto_state = POP3_TOP_SEND;
728                         break;
729                 case STYPE_DELETE:
730                         if (pop3_sd_get_next(state))
731                                 goto_state = POP3_DELETE_SEND;          
732                         else
733                                 goto_state = POP3_LOGOUT_SEND;
734                         break;
735                 case STYPE_DOWNLOAD:
736                         if (pop3_sd_get_next(state))
737                                 goto_state = POP3_RETR_SEND;
738                         else
739                                 goto_state = POP3_LOGOUT_SEND;
740                 default:
741                         break;
742                 }
743                 break;
744         case POP3_TOP_RECV: 
745                 switch (session) { 
746                 case STYPE_PREVIEW_ALL:
747                 case STYPE_PREVIEW_NEW:
748                         pop3_sd_new_header(state);
749                 default:
750                         break;
751                 }
752                 break;
753         case POP3_RETR_RECV:
754                 switch (session) {
755                 case STYPE_DOWNLOAD:
756                         if (state->ac_prefs->sd_rmmail_on_download) 
757                                 goto_state = POP3_DELETE_SEND;
758                         else {
759                                 if (pop3_sd_get_next(state)) 
760                                         goto_state = POP3_RETR_SEND;
761                                 else
762                                         goto_state = POP3_LOGOUT_SEND;
763                         }
764                 default:        
765                         break;
766                 }
767                 break;
768         case POP3_DELETE_RECV:
769                 switch (session) {
770                 case STYPE_DELETE:
771                         if (pop3_sd_get_next(state)) 
772                                 goto_state = POP3_DELETE_SEND;
773                         else
774                                 goto_state =  POP3_LOGOUT_SEND;
775                         break;
776                 case STYPE_DOWNLOAD:
777                         if (pop3_sd_get_next(state)) 
778                                 goto_state = POP3_RETR_SEND;
779                         else
780                                 goto_state = POP3_LOGOUT_SEND;
781                 default:
782                         break;
783                 }
784         default:
785                 break;
786                 
787         }                 
788
789         *next_state = goto_state;
790         if (goto_state != -1)
791                 return TRUE;
792         else 
793                 return FALSE;
794 }
795
796 gboolean pop3_sd_get_next(Pop3State *state)
797 {
798         GSList *cur;
799         gint deleted_msgs = 0;
800         
801         switch (state->ac_prefs->session) {
802         case STYPE_DOWNLOAD:
803         case STYPE_DELETE:      
804                 for (cur = state->ac_prefs->msg_list; cur != NULL; cur = cur->next) {
805                         HeaderItems *items = (HeaderItems*)cur->data;
806
807                         if (items->del_by_old_session)
808                                 deleted_msgs++;
809
810                         switch (items->state) {
811                         case SD_REMOVE:
812                                 items->state = SD_REMOVED;
813                                 break;
814                         case SD_DOWNLOAD:
815                                 items->state = SD_DOWNLOADED;
816                                 break;
817                         case SD_CHECKED:
818                                 state->cur_msg = items->index - deleted_msgs;
819                                 if (state->ac_prefs->session == STYPE_DELETE)
820                                         items->state = SD_REMOVE;
821                                 else
822                                         items->state = SD_DOWNLOAD;
823                                 return TRUE;
824                         default:
825                                 break;
826                         }
827                 }
828                 return FALSE;
829         default:
830                 return FALSE;
831         }
832 }
833
834 Pop3State *pop3_state_new(PrefsAccount *account)
835 {
836         Pop3State *state;
837
838         g_return_val_if_fail(account != NULL, NULL);
839
840         state = g_new0(Pop3State, 1);
841
842         state->ac_prefs = account;
843         state->uidl_table = pop3_get_uidl_table(account);
844         state->current_time = time(NULL);
845         state->error_val = PS_SUCCESS;
846
847         return state;
848 }
849
850 void pop3_state_destroy(Pop3State *state)
851 {
852         gint n;
853
854         g_return_if_fail(state != NULL);
855
856         for (n = 1; n <= state->count; n++)
857                 g_free(state->msg[n].uidl);
858         g_free(state->msg);
859
860         if (state->uidl_table) {
861                 hash_free_strings(state->uidl_table);
862                 g_hash_table_destroy(state->uidl_table);
863         }
864
865         g_free(state->greeting);
866         g_free(state->user);
867         g_free(state->pass);
868         g_free(state->prev_folder);
869
870         g_free(state);
871 }
872
873 GHashTable *pop3_get_uidl_table(PrefsAccount *ac_prefs)
874 {
875         GHashTable *table;
876         gchar *path;
877         FILE *fp;
878         gchar buf[POPBUFSIZE];
879         gchar uidl[POPBUFSIZE];
880         time_t recv_time;
881         time_t now;
882
883         path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
884                            "uidl", G_DIR_SEPARATOR_S, 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                 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
890                                    "uidl-", ac_prefs->recv_server,
891                                    "-", ac_prefs->userid, NULL);
892                 if ((fp = fopen(path, "rb")) == NULL) {
893                         if (ENOENT != errno) FILE_OP_ERROR(path, "fopen");
894                         g_free(path);
895                         return NULL;
896                 }
897         }
898         g_free(path);
899
900         table = g_hash_table_new(g_str_hash, g_str_equal);
901
902         now = time(NULL);
903
904         while (fgets(buf, sizeof(buf), fp) != NULL) {
905                 strretchomp(buf);
906                 recv_time = RECV_TIME_NONE;
907                 if (sscanf(buf, "%s\t%ld", uidl, &recv_time) != 2) {
908                         if (sscanf(buf, "%s", uidl) != 1)
909                                 continue;
910                         else
911                                 recv_time = now;
912                 }
913                 if (recv_time == RECV_TIME_NONE)
914                         recv_time = RECV_TIME_RECEIVED;
915                 g_hash_table_insert(table, g_strdup(uidl),
916                                     GINT_TO_POINTER(recv_time));
917         }
918
919         fclose(fp);
920         return table;
921 }
922
923 gint pop3_write_uidl_list(Pop3State *state)
924 {
925         gchar *path;
926         FILE *fp;
927         Pop3MsgInfo *msg;
928         gint n;
929
930         if (!state->uidl_is_valid) return 0;
931
932         path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
933                            "uidl", G_DIR_SEPARATOR_S,
934                            state->ac_prefs->recv_server,
935                            "-", state->ac_prefs->userid, NULL);
936         if ((fp = fopen(path, "wb")) == NULL) {
937                 FILE_OP_ERROR(path, "fopen");
938                 g_free(path);
939                 return -1;
940         }
941
942         for (n = 1; n <= state->count; n++) {
943                 msg = &state->msg[n];
944                 if (msg->uidl && msg->received && !msg->deleted)
945                         fprintf(fp, "%s\t%ld\n", msg->uidl, msg->recv_time);
946         }
947
948         if (fclose(fp) == EOF) FILE_OP_ERROR(path, "fclose");
949         g_free(path);
950
951         return 0;
952 }