added mail_receive_hook
[claws.git] / src / pop.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2003 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 "md5.h"
36 #include "prefs_account.h"
37 #include "utils.h"
38 #include "inc.h"
39 #include "recv.h"
40
41 #include "log.h"
42 #include "hooks.h"
43
44 static gint pop3_greeting_recv          (Pop3Session *session,
45                                          const gchar *msg);
46 static gint pop3_getauth_user_send      (Pop3Session *session);
47 static gint pop3_getauth_pass_send      (Pop3Session *session);
48 static gint pop3_getauth_apop_send      (Pop3Session *session);
49 #if USE_OPENSSL
50 static gint pop3_stls_send              (Pop3Session *session);
51 static gint pop3_stls_recv              (Pop3Session *session);
52 #endif
53 static gint pop3_getrange_stat_send     (Pop3Session *session);
54 static gint pop3_getrange_stat_recv     (Pop3Session *session,
55                                          const gchar *msg);
56 static gint pop3_getrange_last_send     (Pop3Session *session);
57 static gint pop3_getrange_last_recv     (Pop3Session *session,
58                                          const gchar *msg);
59 static gint pop3_getrange_uidl_send     (Pop3Session *session);
60 static gint pop3_getrange_uidl_recv     (Pop3Session *session,
61                                          const gchar *msg);
62 static gint pop3_getsize_list_send      (Pop3Session *session);
63 static gint pop3_getsize_list_recv      (Pop3Session *session,
64                                          const gchar *msg);
65 static gint pop3_retr_send              (Pop3Session *session);
66 static gint pop3_retr_recv              (Pop3Session *session,
67                                          const gchar *data,
68                                          guint        len);
69 static gint pop3_delete_send            (Pop3Session *session);
70 static gint pop3_delete_recv            (Pop3Session *session);
71 static gint pop3_logout_send            (Pop3Session *session);
72
73 static void pop3_gen_send               (Pop3Session    *session,
74                                          const gchar    *format, ...);
75
76 static void pop3_session_destroy        (Session        *session);
77
78 static gint pop3_write_msg_to_file      (const gchar    *file,
79                                          const gchar    *data,
80                                          guint           len);
81
82 static Pop3State pop3_lookup_next       (Pop3Session    *session);
83 static Pop3ErrorValue pop3_ok           (Pop3Session    *session,
84                                          const gchar    *msg);
85
86 static gint pop3_session_recv_msg               (Session        *session,
87                                                  const gchar    *msg);
88 static gint pop3_session_recv_data_finished     (Session        *session,
89                                                  guchar         *data,
90                                                  guint           len);
91
92
93 static gint pop3_greeting_recv(Pop3Session *session, const gchar *msg)
94 {
95         session->state = POP3_GREETING;
96
97         session->greeting = g_strdup(msg);
98         return PS_SUCCESS;
99 }
100
101 #if USE_OPENSSL
102 static gint pop3_stls_send(Pop3Session *session)
103 {
104         session->state = POP3_STLS;
105         pop3_gen_send(session, "STLS");
106         return PS_SUCCESS;
107 }
108
109 static gint pop3_stls_recv(Pop3Session *session)
110 {
111         if (session_start_tls(SESSION(session)) < 0) {
112                 session->error_val = PS_SOCKET;
113                 return -1;
114         }
115         return PS_SUCCESS;
116 }
117 #endif /* USE_OPENSSL */
118
119 static gint pop3_getauth_user_send(Pop3Session *session)
120 {
121         g_return_val_if_fail(session->user != NULL, -1);
122
123         session->state = POP3_GETAUTH_USER;
124         pop3_gen_send(session, "USER %s", session->user);
125         return PS_SUCCESS;
126 }
127
128 static gint pop3_getauth_pass_send(Pop3Session *session)
129 {
130         g_return_val_if_fail(session->pass != NULL, -1);
131
132         session->state = POP3_GETAUTH_PASS;
133         pop3_gen_send(session, "PASS %s", session->pass);
134         return PS_SUCCESS;
135 }
136
137 static gint pop3_getauth_apop_send(Pop3Session *session)
138 {
139         gchar *start, *end;
140         gchar *apop_str;
141         gchar md5sum[33];
142
143         g_return_val_if_fail(session->user != NULL, -1);
144         g_return_val_if_fail(session->pass != NULL, -1);
145
146         session->state = POP3_GETAUTH_APOP;
147
148         if ((start = strchr(session->greeting, '<')) == NULL) {
149                 log_warning(_("Required APOP timestamp not found "
150                               "in greeting\n"));
151                 session->error_val = PS_PROTOCOL;
152                 return -1;
153         }
154
155         if ((end = strchr(start, '>')) == NULL || end == start + 1) {
156                 log_warning(_("Timestamp syntax error in greeting\n"));
157                 session->error_val = PS_PROTOCOL;
158                 return -1;
159         }
160
161         *(end + 1) = '\0';
162
163         apop_str = g_strconcat(start, session->pass, NULL);
164         md5_hex_digest(md5sum, apop_str);
165         g_free(apop_str);
166
167         pop3_gen_send(session, "APOP %s %s", session->user, md5sum);
168
169         return PS_SUCCESS;
170 }
171
172 static gint pop3_getrange_stat_send(Pop3Session *session)
173 {
174         session->state = POP3_GETRANGE_STAT;
175         pop3_gen_send(session, "STAT");
176         return PS_SUCCESS;
177 }
178
179 static gint pop3_getrange_stat_recv(Pop3Session *session, const gchar *msg)
180 {
181         if (sscanf(msg, "%d %d", &session->count, &session->total_bytes) != 2) {
182                 log_warning(_("POP3 protocol error\n"));
183                 session->error_val = PS_PROTOCOL;
184                 return -1;
185         } else {
186                 if (session->count == 0) {
187                         session->uidl_is_valid = TRUE;
188                 } else {
189                         session->msg = g_new0(Pop3MsgInfo, session->count + 1);
190                         session->cur_msg = 1;
191                 }
192         }
193
194         return PS_SUCCESS;
195 }
196
197 static gint pop3_getrange_last_send(Pop3Session *session)
198 {
199         session->state = POP3_GETRANGE_LAST;
200         pop3_gen_send(session, "LAST");
201         return PS_SUCCESS;
202 }
203
204 static gint pop3_getrange_last_recv(Pop3Session *session, const gchar *msg)
205 {
206         gint last;
207
208         if (sscanf(msg, "%d", &last) == 0) {
209                 log_warning(_("POP3 protocol error\n"));
210                 session->error_val = PS_PROTOCOL;
211                 return -1;
212         } else {
213                 if (session->count > last)
214                         session->cur_msg = last + 1;
215                 else
216                         session->cur_msg = 0;
217         }
218
219         return PS_SUCCESS;
220 }
221
222 static gint pop3_getrange_uidl_send(Pop3Session *session)
223 {
224         session->state = POP3_GETRANGE_UIDL;
225         pop3_gen_send(session, "UIDL");
226         return PS_SUCCESS;
227 }
228
229 static gint pop3_getrange_uidl_recv(Pop3Session *session, const gchar *msg)
230 {
231         gchar id[IDLEN + 1];
232         gint num;
233         time_t recv_time;
234
235         if (msg[0] == '.') {
236                 session->uidl_is_valid = TRUE;
237                 return PS_SUCCESS;
238         }
239
240         if (sscanf(msg, "%d %" Xstr(IDLEN) "s", &num, id) != 2)
241                 return -1;
242         if (num <= 0 || num > session->count)
243                 return -1;
244
245         session->msg[num].uidl = g_strdup(id);
246
247         if (!session->uidl_table)
248                 return PS_CONTINUE;
249
250         recv_time = (time_t)g_hash_table_lookup(session->uidl_table, id);
251         session->msg[num].recv_time = recv_time;
252
253         if (!session->ac_prefs->getall && recv_time != RECV_TIME_NONE)
254                 session->msg[num].received = TRUE;
255
256         if (!session->new_msg_exist &&
257             (session->ac_prefs->getall || recv_time == RECV_TIME_NONE ||
258              session->ac_prefs->rmmail)) {
259                 session->cur_msg = num;
260                 session->new_msg_exist = TRUE;
261         }
262
263         return PS_CONTINUE;
264 }
265
266 static gint pop3_getsize_list_send(Pop3Session *session)
267 {
268         session->state = POP3_GETSIZE_LIST;
269         pop3_gen_send(session, "LIST");
270         return PS_SUCCESS;
271 }
272
273 static gint pop3_getsize_list_recv(Pop3Session *session, const gchar *msg)
274 {
275         guint num, size;
276
277         if (msg[0] == '.')
278                 return PS_SUCCESS;
279
280         if (sscanf(msg, "%u %u", &num, &size) != 2) {
281                 session->error_val = PS_PROTOCOL;
282                 return -1;
283         }
284
285         if (num > 0 && num <= session->count)
286                 session->msg[num].size = size;
287         if (num > 0 && num < session->cur_msg)
288                 session->cur_total_bytes += size;
289
290         return PS_CONTINUE;
291 }
292
293 static gint pop3_retr_send(Pop3Session *session)
294 {
295         session->state = POP3_RETR;
296         pop3_gen_send(session, "RETR %d", session->cur_msg);
297         return PS_SUCCESS;
298 }
299
300 static gint pop3_retr_recv(Pop3Session *session, const gchar *data, guint len)
301 {
302         gchar *file;
303         gint drop_ok;
304         MailReceiveData mail_receive_data;
305
306         mail_receive_data.session = session;
307         mail_receive_data.data = g_strndup(data, len);
308         hooks_invoke(MAIL_RECEIVE_HOOKLIST, &mail_receive_data);
309
310         file = get_tmp_file();
311         if (pop3_write_msg_to_file(file, mail_receive_data.data,
312                 strlen(mail_receive_data.data)) < 0) {
313                 g_free(file);
314                 g_free(mail_receive_data.data);
315                 session->error_val = PS_IOERR;
316                 return -1;
317         }
318         g_free(mail_receive_data.data);
319
320         /* drop_ok: 0: success 1: don't receive -1: error */
321         drop_ok = inc_drop_message(file, session);
322         g_free(file);
323         if (drop_ok < 0) {
324                 session->error_val = PS_IOERR;
325                 return -1;
326         }
327
328         session->cur_total_bytes += session->msg[session->cur_msg].size;
329         session->cur_total_recv_bytes += session->msg[session->cur_msg].size;
330         session->cur_total_num++;
331
332         session->msg[session->cur_msg].received = TRUE;
333         session->msg[session->cur_msg].recv_time =
334                 drop_ok == 1 ? RECV_TIME_KEEP : session->current_time;
335
336         return PS_SUCCESS;
337 }
338
339 static gint pop3_delete_send(Pop3Session *session)
340 {
341         session->state = POP3_DELETE;
342         pop3_gen_send(session, "DELE %d", session->cur_msg);
343         return PS_SUCCESS;
344 }
345
346 static gint pop3_delete_recv(Pop3Session *session)
347 {
348         session->msg[session->cur_msg].deleted = TRUE;
349         return PS_SUCCESS;
350 }
351
352 static gint pop3_logout_send(Pop3Session *session)
353 {
354         session->state = POP3_LOGOUT;
355         pop3_gen_send(session, "QUIT");
356         return PS_SUCCESS;
357 }
358
359 static void pop3_gen_send(Pop3Session *session, const gchar *format, ...)
360 {
361         gchar buf[POPBUFSIZE + 1];
362         va_list args;
363
364         va_start(args, format);
365         g_vsnprintf(buf, sizeof(buf) - 2, format, args);
366         va_end(args);
367
368         if (!strncasecmp(buf, "PASS ", 5))
369                 log_print("POP3> PASS ********\n");
370         else
371                 log_print("POP3> %s\n", buf);
372
373         session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
374 }
375
376 Session *pop3_session_new(PrefsAccount *account)
377 {
378         Pop3Session *session;
379
380         g_return_val_if_fail(account != NULL, NULL);
381
382         session = g_new0(Pop3Session, 1);
383
384         SESSION(session)->type = SESSION_POP3;
385         SESSION(session)->server = NULL;
386         SESSION(session)->port = 0;
387         SESSION(session)->sock = NULL;
388         SESSION(session)->state = SESSION_READY;
389         SESSION(session)->data = NULL;
390
391         SESSION(session)->recv_msg = pop3_session_recv_msg;
392         SESSION(session)->recv_data_finished = pop3_session_recv_data_finished;
393         SESSION(session)->send_data_finished = NULL;
394
395         SESSION(session)->destroy = pop3_session_destroy;
396
397         session->state = POP3_READY;
398         session->ac_prefs = account;
399         session->uidl_table = pop3_get_uidl_table(account);
400         session->current_time = time(NULL);
401         session->error_val = PS_SUCCESS;
402         session->error_msg = NULL;
403
404         return SESSION(session);
405 }
406
407 static void pop3_session_destroy(Session *session)
408 {
409         Pop3Session *pop3_session = POP3_SESSION(session);
410         gint n;
411
412         g_return_if_fail(session != NULL);
413
414         for (n = 1; n <= pop3_session->count; n++)
415                 g_free(pop3_session->msg[n].uidl);
416         g_free(pop3_session->msg);
417
418         if (pop3_session->uidl_table) {
419                 hash_free_strings(pop3_session->uidl_table);
420                 g_hash_table_destroy(pop3_session->uidl_table);
421         }
422
423         g_free(pop3_session->greeting);
424         g_free(pop3_session->user);
425         g_free(pop3_session->pass);
426         g_free(pop3_session->error_msg);
427 }
428
429 GHashTable *pop3_get_uidl_table(PrefsAccount *ac_prefs)
430 {
431         GHashTable *table;
432         gchar *path;
433         FILE *fp;
434         gchar buf[POPBUFSIZE];
435         gchar uidl[POPBUFSIZE];
436         time_t recv_time;
437         time_t now;
438
439         table = g_hash_table_new(g_str_hash, g_str_equal);
440
441         path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
442                            "uidl", G_DIR_SEPARATOR_S, ac_prefs->recv_server,
443                            "-", ac_prefs->userid, NULL);
444         if ((fp = fopen(path, "rb")) == NULL) {
445                 if (ENOENT != errno) FILE_OP_ERROR(path, "fopen");
446                 g_free(path);
447                 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
448                                    "uidl-", ac_prefs->recv_server,
449                                    "-", ac_prefs->userid, NULL);
450                 if ((fp = fopen(path, "rb")) == NULL) {
451                         if (ENOENT != errno) FILE_OP_ERROR(path, "fopen");
452                         g_free(path);
453                         return table;
454                 }
455         }
456         g_free(path);
457
458         now = time(NULL);
459
460         while (fgets(buf, sizeof(buf), fp) != NULL) {
461                 strretchomp(buf);
462                 recv_time = RECV_TIME_NONE;
463                 if (sscanf(buf, "%s\t%ld", uidl, &recv_time) != 2) {
464                         if (sscanf(buf, "%s", uidl) != 1)
465                                 continue;
466                         else
467                                 recv_time = now;
468                 }
469                 if (recv_time == RECV_TIME_NONE)
470                         recv_time = RECV_TIME_RECEIVED;
471                 g_hash_table_insert(table, g_strdup(uidl),
472                                     GINT_TO_POINTER(recv_time));
473         }
474
475         fclose(fp);
476         return table;
477 }
478
479 gint pop3_write_uidl_list(Pop3Session *session)
480 {
481         gchar *path;
482         FILE *fp;
483         Pop3MsgInfo *msg;
484         gint n;
485
486         if (!session->uidl_is_valid) return 0;
487
488         path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
489                            "uidl", G_DIR_SEPARATOR_S,
490                            session->ac_prefs->recv_server,
491                            "-", session->ac_prefs->userid, NULL);
492         if ((fp = fopen(path, "wb")) == NULL) {
493                 FILE_OP_ERROR(path, "fopen");
494                 g_free(path);
495                 return -1;
496         }
497
498         for (n = 1; n <= session->count; n++) {
499                 msg = &session->msg[n];
500                 if (msg->uidl && msg->received && !msg->deleted)
501                         fprintf(fp, "%s\t%ld\n", msg->uidl, msg->recv_time);
502         }
503
504         if (fclose(fp) == EOF) FILE_OP_ERROR(path, "fclose");
505         g_free(path);
506
507         return 0;
508 }
509
510 static gint pop3_write_msg_to_file(const gchar *file, const gchar *data,
511                                    guint len)
512 {
513         FILE *fp;
514         const gchar *prev, *cur;
515
516         g_return_val_if_fail(file != NULL, -1);
517
518         if ((fp = fopen(file, "wb")) == NULL) {
519                 FILE_OP_ERROR(file, "fopen");
520                 return -1;
521         }
522
523         if (change_file_mode_rw(fp, file) < 0)
524                 FILE_OP_ERROR(file, "chmod");
525
526         /* +------------------+----------------+--------------------------+ *
527          * ^data              ^prev            ^cur             data+len-1^ */
528
529         prev = data;
530         while ((cur = memchr(prev, '\r', len - (prev - data))) != NULL) {
531                 if ((cur > prev && fwrite(prev, cur - prev, 1, fp) < 1) ||
532                     fputc('\n', fp) == EOF) {
533                         FILE_OP_ERROR(file, "fwrite");
534                         g_warning("can't write to file: %s\n", file);
535                         fclose(fp);
536                         unlink(file);
537                         return -1;
538                 }
539
540                 if (cur == data + len - 1) {
541                         prev = cur + 1;
542                         break;
543                 }
544
545                 if (*(cur + 1) == '\n')
546                         prev = cur + 2;
547                 else
548                         prev = cur + 1;
549
550                 if (prev - data >= len)
551                         break;
552         }
553
554         if (prev - data < len &&
555             fwrite(prev, len - (prev - data), 1, fp) < 1) {
556                 FILE_OP_ERROR(file, "fwrite");
557                 g_warning("can't write to file: %s\n", file);
558                 fclose(fp);
559                 unlink(file);
560                 return -1;
561         }
562         if (data[len - 1] != '\r' && data[len - 1] != '\n') {
563                 if (fputc('\n', fp) == EOF) {
564                         FILE_OP_ERROR(file, "fputc");
565                         g_warning("can't write to file: %s\n", file);
566                         fclose(fp);
567                         unlink(file);
568                         return -1;
569                 }
570         }
571
572         if (fclose(fp) == EOF) {
573                 FILE_OP_ERROR(file, "fclose");
574                 unlink(file);
575                 return -1;
576         }
577
578         return 0;
579 }
580
581 static Pop3State pop3_lookup_next(Pop3Session *session)
582 {
583         Pop3MsgInfo *msg;
584         PrefsAccount *ac = session->ac_prefs;
585         gint size;
586         gboolean size_limit_over;
587
588         for (;;) {
589                 msg = &session->msg[session->cur_msg];
590                 size = msg->size;
591                 size_limit_over =
592                     (ac->enable_size_limit &&
593                      ac->size_limit > 0 &&
594                      size > ac->size_limit * 1024);
595
596                 if (ac->rmmail &&
597                     msg->recv_time != RECV_TIME_NONE &&
598                     msg->recv_time != RECV_TIME_KEEP &&
599                     session->current_time - msg->recv_time >=
600                     ac->msg_leave_time * 24 * 60 * 60) {
601                         log_print(_("POP3: Deleting expired message %d\n"),
602                                   session->cur_msg);
603                         pop3_delete_send(session);
604                         return POP3_DELETE;
605                 }
606
607                 if (size_limit_over)
608                         log_print
609                                 (_("POP3: Skipping message %d (%d bytes)\n"),
610                                   session->cur_msg, size);
611
612                 if (size == 0 || msg->received || size_limit_over) {
613                         session->cur_total_bytes += size;
614                         if (session->cur_msg == session->count) {
615                                 pop3_logout_send(session);
616                                 return POP3_LOGOUT;
617                         } else
618                                 session->cur_msg++;
619                 } else
620                         break;
621         }
622
623         pop3_retr_send(session);
624         return POP3_RETR;
625 }
626
627 static Pop3ErrorValue pop3_ok(Pop3Session *session, const gchar *msg)
628 {
629         Pop3ErrorValue ok;
630
631         log_print("POP3< %s\n", msg);
632
633         if (!strncmp(msg, "+OK", 3))
634                 ok = PS_SUCCESS;
635         else if (!strncmp(msg, "-ERR", 4)) {
636                 if (strstr(msg + 4, "lock") ||
637                     strstr(msg + 4, "Lock") ||
638                     strstr(msg + 4, "LOCK") ||
639                     strstr(msg + 4, "wait")) {
640                         log_warning(_("mailbox is locked\n"));
641                         ok = PS_LOCKBUSY;
642                 } else if (strcasestr(msg + 4, "timeout")) {
643                         log_warning(_("session timeout\n"));
644                         ok = PS_ERROR;
645                 } else {
646                         switch (session->state) {
647 #if USE_OPENSSL
648                         case POP3_STLS:
649                                 log_warning(_("can't start TLS session\n"));
650                                 ok = PS_ERROR;
651                                 break;
652 #endif
653                         case POP3_GETAUTH_USER:
654                         case POP3_GETAUTH_PASS:
655                         case POP3_GETAUTH_APOP:
656                                 log_warning(_("error occurred on authentication\n"));
657                                 ok = PS_AUTHFAIL;
658                                 break;
659                         case POP3_GETRANGE_LAST:
660                         case POP3_GETRANGE_UIDL:
661                                 log_warning(_("command not supported\n"));
662                                 ok = PS_NOTSUPPORTED;
663                                 break;
664                         default:
665                                 log_warning(_("error occurred on POP3 session\n"));
666                                 ok = PS_ERROR;
667                         }
668                 }
669
670                 g_free(session->error_msg);
671                 session->error_msg = g_strdup(msg);
672                 fprintf(stderr, "POP3: %s\n", msg);
673         } else
674                 ok = PS_PROTOCOL;
675
676         session->error_val = ok;
677         return ok;
678 }
679
680 static gint pop3_session_recv_msg(Session *session, const gchar *msg)
681 {
682         Pop3Session *pop3_session = POP3_SESSION(session);
683         Pop3ErrorValue val = PS_SUCCESS;
684         const gchar *body;
685
686         body = msg;
687         if (pop3_session->state != POP3_GETRANGE_UIDL_RECV &&
688             pop3_session->state != POP3_GETSIZE_LIST_RECV) {
689                 val = pop3_ok(pop3_session, msg);
690                 if (val != PS_SUCCESS) {
691                         if (val != PS_NOTSUPPORTED) {
692                                 pop3_session->state = POP3_ERROR;
693                                 return -1;
694                         }
695                 }
696
697                 if (*body == '+' || *body == '-')
698                         body++;
699                 while (isalpha(*body))
700                         body++;
701                 while (isspace(*body))
702                         body++;
703         }
704
705         switch (pop3_session->state) {
706         case POP3_READY:
707         case POP3_GREETING:
708                 pop3_greeting_recv(pop3_session, body);
709 #if USE_OPENSSL
710                 if (pop3_session->ac_prefs->ssl_pop == SSL_STARTTLS)
711                         pop3_stls_send(pop3_session);
712                 else
713 #endif
714                 if (pop3_session->ac_prefs->protocol == A_APOP)
715                         pop3_getauth_apop_send(pop3_session);
716                 else
717                         pop3_getauth_user_send(pop3_session);
718                 break;
719 #if USE_OPENSSL
720         case POP3_STLS:
721                 if (pop3_stls_recv(pop3_session) != PS_SUCCESS)
722                         return -1;
723                 if (pop3_session->ac_prefs->protocol == A_APOP)
724                         pop3_getauth_apop_send(pop3_session);
725                 else
726                         pop3_getauth_user_send(pop3_session);
727                 break;
728 #endif
729         case POP3_GETAUTH_USER:
730                 pop3_getauth_pass_send(pop3_session);
731                 break;
732         case POP3_GETAUTH_PASS:
733         case POP3_GETAUTH_APOP:
734                 pop3_getrange_stat_send(pop3_session);
735                 break;
736         case POP3_GETRANGE_STAT:
737                 if (pop3_getrange_stat_recv(pop3_session, body) < 0)
738                         return -1;
739                 if (pop3_session->count > 0)
740                         pop3_getrange_uidl_send(pop3_session);
741                 else
742                         pop3_logout_send(pop3_session);
743                 break;
744         case POP3_GETRANGE_LAST:
745                 if (val == PS_NOTSUPPORTED)
746                         pop3_session->error_val = PS_SUCCESS;
747                 else if (pop3_getrange_last_recv(pop3_session, body) < 0)
748                         return -1;
749                 if (pop3_session->cur_msg > 0)
750                         pop3_getsize_list_send(pop3_session);
751                 else
752                         pop3_logout_send(pop3_session);
753                 break;
754         case POP3_GETRANGE_UIDL:
755                 if (val == PS_NOTSUPPORTED) {
756                         pop3_session->error_val = PS_SUCCESS;
757                         pop3_getrange_last_send(pop3_session);
758                 } else {
759                         pop3_session->state = POP3_GETRANGE_UIDL_RECV;
760                         return 1;
761                 }
762                 break;
763         case POP3_GETRANGE_UIDL_RECV:
764                 val = pop3_getrange_uidl_recv(pop3_session, body);
765                 if (val == PS_CONTINUE)
766                         return 1;
767                 else if (val == PS_SUCCESS) {
768                         if (pop3_session->new_msg_exist)
769                                 pop3_getsize_list_send(pop3_session);
770                         else
771                                 pop3_logout_send(pop3_session);
772                 } else
773                         return -1;
774                 break;
775         case POP3_GETSIZE_LIST:
776                 pop3_session->state = POP3_GETSIZE_LIST_RECV;
777                 return 1;
778         case POP3_GETSIZE_LIST_RECV:
779                 val = pop3_getsize_list_recv(pop3_session, body);
780                 if (val == PS_CONTINUE)
781                         return 1;
782                 else if (val == PS_SUCCESS) {
783                         if (pop3_lookup_next(pop3_session) == POP3_ERROR)
784                                 return -1;
785                 } else
786                         return -1;
787                 break;
788         case POP3_RETR:
789                 pop3_session->state = POP3_RETR_RECV;
790                 session_recv_data
791                         (session,
792                          pop3_session->msg[pop3_session->cur_msg].size, TRUE);
793                 break;
794         case POP3_DELETE:
795                 pop3_delete_recv(pop3_session);
796                 if (pop3_session->cur_msg == pop3_session->count)
797                         pop3_logout_send(pop3_session);
798                 else {
799                         pop3_session->cur_msg++;
800                         if (pop3_lookup_next(pop3_session) == POP3_ERROR)
801                                 return -1;
802                 }
803                 break;
804         case POP3_LOGOUT:
805                 session_disconnect(session);
806                 break;
807         case POP3_ERROR:
808         default:
809                 return -1;
810         }
811
812         return 0;
813 }
814
815 static gint pop3_session_recv_data_finished(Session *session, guchar *data,
816                                             guint len)
817 {
818         Pop3Session *pop3_session = POP3_SESSION(session);
819
820         if (len == 0)
821                 return -1;
822
823         if (pop3_retr_recv(pop3_session, data, len) < 0)
824                 return -1;
825
826         if (pop3_session->ac_prefs->rmmail &&
827             pop3_session->ac_prefs->msg_leave_time == 0 &&
828             pop3_session->msg[pop3_session->cur_msg].recv_time
829             != RECV_TIME_KEEP)
830                 pop3_delete_send(pop3_session);
831         else if (pop3_session->cur_msg == pop3_session->count)
832                 pop3_logout_send(pop3_session);
833         else {
834                 pop3_session->cur_msg++;
835                 if (pop3_lookup_next(pop3_session) == POP3_ERROR)
836                         return -1;
837         }
838
839         return 0;
840 }