summaryrefslogtreecommitdiff
path: root/libsylph/pop.c
diff options
context:
space:
mode:
Diffstat (limited to 'libsylph/pop.c')
-rw-r--r--libsylph/pop.c927
1 files changed, 927 insertions, 0 deletions
diff --git a/libsylph/pop.c b/libsylph/pop.c
new file mode 100644
index 0000000..8cb7f5c
--- /dev/null
+++ b/libsylph/pop.c
@@ -0,0 +1,927 @@
1/*
2 * LibSylph -- E-Mail client library
3 * Copyright (C) 1999-2014 Hiroyuki Yamamoto
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library 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 GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifdef HAVE_CONFIG_H
21# include "config.h"
22#endif
23
24#include "defs.h"
25
26#include <glib.h>
27#include <glib/gi18n.h>
28#include <stdio.h>
29#include <string.h>
30#include <stdarg.h>
31#include <ctype.h>
32#include <unistd.h>
33#include <time.h>
34#include <errno.h>
35
36#include "pop.h"
37#include "md5.h"
38#include "prefs.h"
39#include "prefs_account.h"
40#include "utils.h"
41#include "recv.h"
42
43gint pop3_greeting_recv (Pop3Session *session,
44 const gchar *msg);
45gint pop3_getauth_user_send (Pop3Session *session);
46gint pop3_getauth_pass_send (Pop3Session *session);
47gint pop3_getauth_apop_send (Pop3Session *session);
48#if USE_SSL
49gint pop3_stls_send (Pop3Session *session);
50gint pop3_stls_recv (Pop3Session *session);
51#endif
52gint pop3_getrange_stat_send (Pop3Session *session);
53gint pop3_getrange_stat_recv (Pop3Session *session,
54 const gchar *msg);
55gint pop3_getrange_last_send (Pop3Session *session);
56gint pop3_getrange_last_recv (Pop3Session *session,
57 const gchar *msg);
58gint pop3_getrange_uidl_send (Pop3Session *session);
59gint pop3_getrange_uidl_recv (Pop3Session *session,
60 const gchar *data,
61 guint len);
62gint pop3_getsize_list_send (Pop3Session *session);
63gint pop3_getsize_list_recv (Pop3Session *session,
64 const gchar *data,
65 guint len);
66gint pop3_retr_send (Pop3Session *session);
67gint pop3_retr_recv (Pop3Session *session,
68 FILE *fp,
69 guint len);
70gint pop3_delete_send (Pop3Session *session);
71gint pop3_delete_recv (Pop3Session *session);
72gint pop3_logout_send (Pop3Session *session);
73
74void pop3_gen_send (Pop3Session *session,
75 const gchar *format, ...);
76
77static void pop3_session_destroy (Session *session);
78
79gint pop3_write_msg_to_file (const gchar *file,
80 FILE *src_fp,
81 guint len);
82
83static Pop3State pop3_lookup_next (Pop3Session *session);
84
85Pop3ErrorValue pop3_ok (Pop3Session *session,
86 const gchar *msg);
87
88static gint pop3_session_recv_msg (Session *session,
89 const gchar *msg);
90static gint pop3_session_recv_data_finished (Session *session,
91 guchar *data,
92 guint len);
93static gint pop3_session_recv_data_as_file_finished
94 (Session *session,
95 FILE *fp,
96 guint len);
97
98
99gint pop3_greeting_recv(Pop3Session *session, const gchar *msg)
100{
101 session->state = POP3_GREETING;
102
103 session->greeting = g_strdup(msg);
104 return PS_SUCCESS;
105}
106
107#if USE_SSL
108gint pop3_stls_send(Pop3Session *session)
109{
110 session->state = POP3_STLS;
111 pop3_gen_send(session, "STLS");
112 return PS_SUCCESS;
113}
114
115gint pop3_stls_recv(Pop3Session *session)
116{
117 if (session_start_tls(SESSION(session)) < 0) {
118 session->error_val = PS_SOCKET;
119 return PS_SOCKET;
120 }
121 return PS_SUCCESS;
122}
123#endif /* USE_SSL */
124
125gint pop3_getauth_user_send(Pop3Session *session)
126{
127 g_return_val_if_fail(session->user != NULL, -1);
128
129 session->state = POP3_GETAUTH_USER;
130 pop3_gen_send(session, "USER %s", session->user);
131 return PS_SUCCESS;
132}
133
134gint pop3_getauth_pass_send(Pop3Session *session)
135{
136 g_return_val_if_fail(session->pass != NULL, -1);
137
138 session->state = POP3_GETAUTH_PASS;
139 pop3_gen_send(session, "PASS %s", session->pass);
140 return PS_SUCCESS;
141}
142
143gint pop3_getauth_apop_send(Pop3Session *session)
144{
145 gchar *start, *end;
146 gchar *apop_str;
147 SMD5 *md5;
148 gchar *md5sum;
149
150 g_return_val_if_fail(session->user != NULL, -1);
151 g_return_val_if_fail(session->pass != NULL, -1);
152
153 session->state = POP3_GETAUTH_APOP;
154
155 if ((start = strchr(session->greeting, '<')) == NULL) {
156 log_warning(_("Required APOP timestamp not found "
157 "in greeting\n"));
158 session->error_val = PS_PROTOCOL;
159 return PS_PROTOCOL;
160 }
161
162 if ((end = strchr(start, '>')) == NULL || end == start + 1) {
163 log_warning(_("Timestamp syntax error in greeting\n"));
164 session->error_val = PS_PROTOCOL;
165 return PS_PROTOCOL;
166 }
167
168 *(end + 1) = '\0';
169
170 if (!is_ascii_str(start) || strchr(start, '@') == NULL) {
171 log_warning(_("Invalid timestamp in greeting\n"));
172 session->error_val = PS_PROTOCOL;
173 return PS_PROTOCOL;
174 }
175
176 apop_str = g_strconcat(start, session->pass, NULL);
177 md5 = s_gnet_md5_new((guchar *)apop_str, strlen(apop_str));
178 md5sum = s_gnet_md5_get_string(md5);
179
180 pop3_gen_send(session, "APOP %s %s", session->user, md5sum);
181
182 g_free(md5sum);
183 s_gnet_md5_delete(md5);
184 g_free(apop_str);
185
186 return PS_SUCCESS;
187}
188
189gint pop3_getrange_stat_send(Pop3Session *session)
190{
191 session->state = POP3_GETRANGE_STAT;
192 pop3_gen_send(session, "STAT");
193 return PS_SUCCESS;
194}
195
196gint pop3_getrange_stat_recv(Pop3Session *session, const gchar *msg)
197{
198 if (sscanf(msg, "%d %lld", &session->count, &session->total_bytes) != 2) {
199 log_warning(_("POP3 protocol error\n"));
200 session->error_val = PS_PROTOCOL;
201 return PS_PROTOCOL;
202 } else {
203 if (session->count == 0) {
204 session->uidl_is_valid = TRUE;
205 } else {
206 session->msg = g_new0(Pop3MsgInfo, session->count + 1);
207 session->cur_msg = 1;
208 }
209 }
210
211 return PS_SUCCESS;
212}
213
214gint pop3_getrange_last_send(Pop3Session *session)
215{
216 session->state = POP3_GETRANGE_LAST;
217 pop3_gen_send(session, "LAST");
218 return PS_SUCCESS;
219}
220
221gint pop3_getrange_last_recv(Pop3Session *session, const gchar *msg)
222{
223 gint last;
224
225 if (sscanf(msg, "%d", &last) == 0) {
226 log_warning(_("POP3 protocol error\n"));
227 session->error_val = PS_PROTOCOL;
228 return PS_PROTOCOL;
229 } else {
230 if (session->count > last) {
231 session->new_msg_exist = TRUE;
232 session->cur_msg = last + 1;
233 } else
234 session->cur_msg = 0;
235 }
236
237 return PS_SUCCESS;
238}
239
240gint pop3_getrange_uidl_send(Pop3Session *session)
241{
242 session->state = POP3_GETRANGE_UIDL;
243 pop3_gen_send(session, "UIDL");
244 return PS_SUCCESS;
245}
246
247gint pop3_getrange_uidl_recv(Pop3Session *session, const gchar *data, guint len)
248{
249 gchar id[IDLEN + 1];
250 gchar buf[POPBUFSIZE];
251 gint buf_len;
252 gint num;
253 time_t recv_time;
254 const gchar *p = data;
255 const gchar *lastp = data + len;
256 const gchar *newline;
257
258 while (p < lastp) {
259 if ((newline = memchr(p, '\r', lastp - p)) == NULL)
260 return PS_PROTOCOL;
261 buf_len = MIN(newline - p, sizeof(buf) - 1);
262 memcpy(buf, p, buf_len);
263 buf[buf_len] = '\0';
264
265 p = newline + 1;
266 if (p < lastp && *p == '\n') p++;
267
268 if (sscanf(buf, "%d %" Xstr(IDLEN) "s", &num, id) != 2 ||
269 num <= 0 || num > session->count) {
270 log_warning(_("invalid UIDL response: %s\n"), buf);
271 continue;
272 }
273
274 session->msg[num].uidl = g_strdup(id);
275
276 recv_time = GPOINTER_TO_INT(g_hash_table_lookup(session->uidl_table, id));
277 session->msg[num].recv_time = recv_time;
278
279 if (!session->ac_prefs->getall && recv_time != RECV_TIME_NONE)
280 session->msg[num].received = TRUE;
281
282 if (!session->new_msg_exist &&
283 (session->ac_prefs->getall || recv_time == RECV_TIME_NONE ||
284 session->ac_prefs->rmmail)) {
285 session->cur_msg = num;
286 session->new_msg_exist = TRUE;
287 }
288 }
289
290 session->uidl_is_valid = TRUE;
291 return PS_SUCCESS;
292}
293
294gint pop3_getsize_list_send(Pop3Session *session)
295{
296 session->state = POP3_GETSIZE_LIST;
297 pop3_gen_send(session, "LIST");
298 return PS_SUCCESS;
299}
300
301gint pop3_getsize_list_recv(Pop3Session *session, const gchar *data, guint len)
302{
303 gchar buf[POPBUFSIZE];
304 gint buf_len;
305 guint num, size;
306 const gchar *p = data;
307 const gchar *lastp = data + len;
308 const gchar *newline;
309
310 while (p < lastp) {
311 if ((newline = memchr(p, '\r', lastp - p)) == NULL)
312 return PS_PROTOCOL;
313 buf_len = MIN(newline - p, sizeof(buf) - 1);
314 memcpy(buf, p, buf_len);
315 buf[buf_len] = '\0';
316
317 p = newline + 1;
318 if (p < lastp && *p == '\n') p++;
319
320 if (sscanf(buf, "%u %u", &num, &size) != 2) {
321 session->error_val = PS_PROTOCOL;
322 return PS_PROTOCOL;
323 }
324
325 if (num > 0 && num <= session->count)
326 session->msg[num].size = size;
327 if (num > 0 && num < session->cur_msg)
328 session->cur_total_bytes += size;
329 }
330
331 return PS_SUCCESS;
332}
333
334gint pop3_retr_send(Pop3Session *session)
335{
336 session->state = POP3_RETR;
337 pop3_gen_send(session, "RETR %d", session->cur_msg);
338 return PS_SUCCESS;
339}
340
341gint pop3_retr_recv(Pop3Session *session, FILE *fp, guint len)
342{
343 gchar *file;
344 gint drop_ok;
345
346 file = get_tmp_file();
347 if (pop3_write_msg_to_file(file, fp, len) < 0) {
348 g_free(file);
349 session->error_val = PS_IOERR;
350 return PS_IOERR;
351 }
352
353 drop_ok = session->drop_message(session, file);
354 g_unlink(file);
355 g_free(file);
356 if (drop_ok < 0) {
357 session->error_val = PS_IOERR;
358 return PS_IOERR;
359 }
360
361 session->cur_total_bytes += session->msg[session->cur_msg].size;
362 session->cur_total_recv_bytes += session->msg[session->cur_msg].size;
363 session->cur_total_num++;
364
365 session->msg[session->cur_msg].received = TRUE;
366 session->msg[session->cur_msg].recv_time =
367 drop_ok == DROP_DONT_RECEIVE ? RECV_TIME_KEEP
368 : drop_ok == DROP_DELETE ? RECV_TIME_DELETE
369 : session->current_time;
370
371 return PS_SUCCESS;
372}
373
374gint pop3_delete_send(Pop3Session *session)
375{
376 session->state = POP3_DELETE;
377 pop3_gen_send(session, "DELE %d", session->cur_msg);
378 return PS_SUCCESS;
379}
380
381gint pop3_delete_recv(Pop3Session *session)
382{
383 session->msg[session->cur_msg].recv_time = RECV_TIME_DELETE;
384 session->msg[session->cur_msg].deleted = TRUE;
385 return PS_SUCCESS;
386}
387
388gint pop3_logout_send(Pop3Session *session)
389{
390 session->state = POP3_LOGOUT;
391 pop3_gen_send(session, "QUIT");
392 return PS_SUCCESS;
393}
394
395void pop3_gen_send(Pop3Session *session, const gchar *format, ...)
396{
397 gchar buf[POPBUFSIZE + 1];
398 va_list args;
399
400 va_start(args, format);
401 g_vsnprintf(buf, sizeof(buf) - 2, format, args);
402 va_end(args);
403
404 if (!g_ascii_strncasecmp(buf, "PASS ", 5))
405 log_print("POP3> PASS ********\n");
406 else
407 log_print("POP3> %s\n", buf);
408
409 session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
410}
411
412Session *pop3_session_new(PrefsAccount *account)
413{
414 Pop3Session *session;
415
416 g_return_val_if_fail(account != NULL, NULL);
417
418 session = g_new0(Pop3Session, 1);
419
420 session_init(SESSION(session));
421
422 SESSION(session)->type = SESSION_POP3;
423
424 SESSION(session)->recv_msg = pop3_session_recv_msg;
425 SESSION(session)->send_data_finished = NULL;
426 SESSION(session)->recv_data_finished = pop3_session_recv_data_finished;
427 SESSION(session)->recv_data_as_file_finished =
428 pop3_session_recv_data_as_file_finished;
429
430 SESSION(session)->destroy = pop3_session_destroy;
431
432 session->state = POP3_READY;
433 session->ac_prefs = account;
434 session->uidl_table = pop3_get_uidl_table(account);
435 session->current_time = time(NULL);
436 session->error_val = PS_SUCCESS;
437 session->error_msg = NULL;
438
439 session->user = g_strdup(account->userid);
440 session->pass = account->passwd ? g_strdup(account->passwd) :
441 account->tmp_pass ? g_strdup(account->tmp_pass) : NULL;
442
443 SESSION(session)->server = g_strdup(account->recv_server);
444
445#if USE_SSL
446 SESSION(session)->port = account->set_popport ?
447 account->popport : account->ssl_pop == SSL_TUNNEL ? 995 : 110;
448 SESSION(session)->ssl_type = account->ssl_pop;
449 if (account->ssl_pop != SSL_NONE)
450 SESSION(session)->nonblocking = account->use_nonblocking_ssl;
451#else
452 SESSION(session)->port = account->set_popport ? account->popport : 110;
453#endif
454
455 return SESSION(session);
456}
457
458static void pop3_session_destroy(Session *session)
459{
460 Pop3Session *pop3_session = POP3_SESSION(session);
461 gint n;
462
463 g_return_if_fail(session != NULL);
464
465 for (n = 1; n <= pop3_session->count; n++)
466 g_free(pop3_session->msg[n].uidl);
467 g_free(pop3_session->msg);
468
469 if (pop3_session->uidl_table) {
470 hash_free_strings(pop3_session->uidl_table);
471 g_hash_table_destroy(pop3_session->uidl_table);
472 }
473
474 g_free(pop3_session->greeting);
475 g_free(pop3_session->user);
476 g_free(pop3_session->pass);
477 g_free(pop3_session->error_msg);
478}
479
480GHashTable *pop3_get_uidl_table(PrefsAccount *ac_prefs)
481{
482 GHashTable *table;
483 gchar *path;
484 FILE *fp;
485 gchar buf[POPBUFSIZE];
486 gchar uidl[POPBUFSIZE];
487 time_t recv_time;
488 time_t now;
489 gchar *uid;
490
491 table = g_hash_table_new(g_str_hash, g_str_equal);
492
493 uid = uriencode_for_filename(ac_prefs->userid);
494 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
495 UIDL_DIR, G_DIR_SEPARATOR_S, ac_prefs->recv_server,
496 "-", uid, NULL);
497 g_free(uid);
498 if ((fp = g_fopen(path, "rb")) == NULL) {
499 if (ENOENT != errno) FILE_OP_ERROR(path, "fopen");
500 g_free(path);
501 return table;
502 }
503 g_free(path);
504
505 now = time(NULL);
506
507 while (fgets(buf, sizeof(buf), fp) != NULL) {
508 strretchomp(buf);
509 recv_time = RECV_TIME_NONE;
510 if (sscanf(buf, "%s\t%ld", uidl, &recv_time) != 2) {
511 if (sscanf(buf, "%s", uidl) != 1)
512 continue;
513 else
514 recv_time = now;
515 }
516 if (recv_time == RECV_TIME_NONE)
517 recv_time = RECV_TIME_RECEIVED;
518 g_hash_table_insert(table, g_strdup(uidl),
519 GINT_TO_POINTER(recv_time));
520 }
521
522 fclose(fp);
523 return table;
524}
525
526gint pop3_write_uidl_list(Pop3Session *session)
527{
528 gchar *path;
529 PrefFile *pfile;
530 Pop3MsgInfo *msg;
531 gint n;
532 gchar *uid;
533
534 if (!session->uidl_is_valid) return 0;
535
536 uid = uriencode_for_filename(session->ac_prefs->userid);
537 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
538 UIDL_DIR, G_DIR_SEPARATOR_S,
539 session->ac_prefs->recv_server,
540 "-", uid, NULL);
541 g_free(uid);
542 if ((pfile = prefs_file_open(path)) == NULL) {
543 g_free(path);
544 return -1;
545 }
546 prefs_file_set_backup_generation(pfile, 0);
547
548 for (n = 1; n <= session->count; n++) {
549 msg = &session->msg[n];
550 if (!msg->uidl || !msg->received)
551 continue;
552 if (session->state == POP3_DONE && msg->deleted)
553 continue;
554 fprintf(pfile->fp, "%s\t%ld\n", msg->uidl, msg->recv_time);
555 }
556
557 if (prefs_file_close(pfile) < 0)
558 g_warning("%s: failed to write UIDL list.\n", path);
559
560 g_free(path);
561
562 return 0;
563}
564
565gint pop3_write_msg_to_file(const gchar *file, FILE *src_fp, guint len)
566{
567 FILE *fp;
568 gchar buf[BUFFSIZE];
569 gchar last_ch = '\0';
570
571 g_return_val_if_fail(file != NULL, -1);
572
573 if ((fp = g_fopen(file, "wb")) == NULL) {
574 FILE_OP_ERROR(file, "fopen");
575 return -1;
576 }
577
578 if (change_file_mode_rw(fp, file) < 0)
579 FILE_OP_ERROR(file, "chmod");
580
581 while (fgets(buf, sizeof(buf), src_fp) != NULL) {
582 gchar *p = buf;
583 gint len;
584
585 len = strlen(buf);
586 if (len > 0) {
587 last_ch = buf[len - 1];
588 if (last_ch == '\n' && len > 1 &&
589 buf[len - 2] == '\r') {
590 buf[len - 2] = '\n';
591 buf[len - 1] = '\0';
592 } else if (last_ch == '\r')
593 buf[len - 1] = '\0';
594 } else
595 last_ch = '\0';
596
597 if ((last_ch == '\0' || last_ch == '\n') &&
598 *p == '.' && *(p + 1) == '.')
599 p++;
600
601 if (fputs(p, fp) == EOF) {
602 FILE_OP_ERROR(file, "fputs");
603 g_warning("can't write to file: %s\n", file);
604 fclose(fp);
605 g_unlink(file);
606 return -1;
607 }
608 }
609
610 if (ferror(src_fp)) {
611 FILE_OP_ERROR(file, "fgets");
612 fclose(fp);
613 g_unlink(file);
614 return -1;
615 }
616
617 if (fclose(fp) == EOF) {
618 FILE_OP_ERROR(file, "fclose");
619 g_unlink(file);
620 return -1;
621 }
622
623 return 0;
624}
625
626static Pop3State pop3_lookup_next(Pop3Session *session)
627{
628 Pop3MsgInfo *msg;
629 PrefsAccount *ac = session->ac_prefs;
630 gint size;
631 gboolean size_limit_over;
632
633 for (;;) {
634 msg = &session->msg[session->cur_msg];
635 size = msg->size;
636 size_limit_over =
637 (ac->enable_size_limit &&
638 ac->size_limit > 0 &&
639 size > ac->size_limit * 1024);
640
641 if (msg->recv_time == RECV_TIME_DELETE ||
642 (ac->rmmail &&
643 msg->recv_time != RECV_TIME_NONE &&
644 msg->recv_time != RECV_TIME_KEEP &&
645 session->current_time - msg->recv_time >=
646 ac->msg_leave_time * 24 * 60 * 60)) {
647 log_print(_("POP3: Deleting expired message %d\n"),
648 session->cur_msg);
649 session->cur_total_bytes += size;
650 pop3_delete_send(session);
651 return POP3_DELETE;
652 }
653
654 if (size_limit_over && !msg->received) {
655 log_print
656 (_("POP3: Skipping message %d (%d bytes)\n"),
657 session->cur_msg, size);
658 session->skipped_num++;
659 }
660
661 if (size == 0 || msg->received || size_limit_over) {
662 session->cur_total_bytes += size;
663 if (session->cur_msg == session->count) {
664 pop3_logout_send(session);
665 return POP3_LOGOUT;
666 } else
667 session->cur_msg++;
668 } else
669 break;
670 }
671
672 pop3_retr_send(session);
673
674 return POP3_RETR;
675}
676
677Pop3ErrorValue pop3_ok(Pop3Session *session, const gchar *msg)
678{
679 Pop3ErrorValue ok;
680
681 log_print("POP3< %s\n", msg);
682
683 if (!strncmp(msg, "+OK", 3))
684 ok = PS_SUCCESS;
685 else if (!strncmp(msg, "-ERR", 4)) {
686 if (strstr(msg + 4, "lock") ||
687 strstr(msg + 4, "Lock") ||
688 strstr(msg + 4, "LOCK") ||
689 strstr(msg + 4, "wait")) {
690 log_warning(_("mailbox is locked\n"));
691 ok = PS_LOCKBUSY;
692 } else if (strcasestr(msg + 4, "timeout")) {
693 log_warning(_("session timeout\n"));
694 ok = PS_ERROR;
695 } else {
696 switch (session->state) {
697#if USE_SSL
698 case POP3_STLS:
699 log_warning(_("can't start TLS session\n"));
700 ok = PS_ERROR;
701 break;
702#endif
703 case POP3_GETAUTH_USER:
704 case POP3_GETAUTH_PASS:
705 case POP3_GETAUTH_APOP:
706 log_warning(_("error occurred on authentication\n"));
707 ok = PS_AUTHFAIL;
708 break;
709 case POP3_GETRANGE_LAST:
710 case POP3_GETRANGE_UIDL:
711 log_warning(_("command not supported\n"));
712 ok = PS_NOTSUPPORTED;
713 break;
714 default:
715 log_warning(_("error occurred on POP3 session\n"));
716 ok = PS_ERROR;
717 }
718 }
719
720 g_free(session->error_msg);
721 session->error_msg = g_strdup(msg);
722 fprintf(stderr, "POP3: %s\n", msg);
723 } else
724 ok = PS_PROTOCOL;
725
726 /* don't overwrite previous error on logout */
727 if (session->state != POP3_LOGOUT)
728 session->error_val = ok;
729
730 return ok;
731}
732
733static gint pop3_session_recv_msg(Session *session, const gchar *msg)
734{
735 Pop3Session *pop3_session = POP3_SESSION(session);
736 gint val = PS_SUCCESS;
737 const gchar *body;
738
739 body = msg;
740 if (pop3_session->state != POP3_GETRANGE_UIDL_RECV &&
741 pop3_session->state != POP3_GETSIZE_LIST_RECV) {
742 val = pop3_ok(pop3_session, msg);
743 if (val != PS_SUCCESS) {
744 if (val == PS_SOCKET) {
745 pop3_session->state = POP3_ERROR;
746 return -1;
747 }
748 if (val != PS_NOTSUPPORTED) {
749 if (pop3_session->state != POP3_LOGOUT) {
750 if (pop3_logout_send(pop3_session) == PS_SUCCESS)
751 return 0;
752 else
753 return -1;
754 }
755 }
756 }
757
758 if (*body == '+' || *body == '-')
759 body++;
760 while (g_ascii_isalpha(*body))
761 body++;
762 while (g_ascii_isspace(*body))
763 body++;
764 }
765
766 switch (pop3_session->state) {
767 case POP3_READY:
768 case POP3_GREETING:
769 val = pop3_greeting_recv(pop3_session, body);
770#if USE_SSL
771 if (pop3_session->ac_prefs->ssl_pop == SSL_STARTTLS)
772 val = pop3_stls_send(pop3_session);
773 else
774#endif
775 if (pop3_session->ac_prefs->use_apop_auth)
776 val = pop3_getauth_apop_send(pop3_session);
777 else
778 val = pop3_getauth_user_send(pop3_session);
779 break;
780#if USE_SSL
781 case POP3_STLS:
782 if ((val = pop3_stls_recv(pop3_session)) != PS_SUCCESS)
783 return -1;
784 if (pop3_session->ac_prefs->use_apop_auth)
785 val = pop3_getauth_apop_send(pop3_session);
786 else
787 val = pop3_getauth_user_send(pop3_session);
788 break;
789#endif
790 case POP3_GETAUTH_USER:
791 val = pop3_getauth_pass_send(pop3_session);
792 break;
793 case POP3_GETAUTH_PASS:
794 case POP3_GETAUTH_APOP:
795 if (pop3_session->auth_only)
796 val = pop3_logout_send(pop3_session);
797 else
798 val = pop3_getrange_stat_send(pop3_session);
799 break;
800 case POP3_GETRANGE_STAT:
801 if ((val = pop3_getrange_stat_recv(pop3_session, body)) != PS_SUCCESS)
802 return -1;
803 if (pop3_session->count > 0)
804 val = pop3_getrange_uidl_send(pop3_session);
805 else
806 val = pop3_logout_send(pop3_session);
807 break;
808 case POP3_GETRANGE_LAST:
809 if (val == PS_NOTSUPPORTED)
810 pop3_session->error_val = PS_SUCCESS;
811 else if ((val = pop3_getrange_last_recv
812 (pop3_session, body)) != PS_SUCCESS)
813 return -1;
814 if (pop3_session->cur_msg > 0)
815 val = pop3_getsize_list_send(pop3_session);
816 else
817 val = pop3_logout_send(pop3_session);
818 break;
819 case POP3_GETRANGE_UIDL:
820 if (val == PS_NOTSUPPORTED) {
821 pop3_session->error_val = PS_SUCCESS;
822 val = pop3_getrange_last_send(pop3_session);
823 } else {
824 pop3_session->state = POP3_GETRANGE_UIDL_RECV;
825 val = session_recv_data(session, 0, ".\r\n");
826 }
827 break;
828 case POP3_GETSIZE_LIST:
829 pop3_session->state = POP3_GETSIZE_LIST_RECV;
830 val = session_recv_data(session, 0, ".\r\n");
831 break;
832 case POP3_RETR:
833 pop3_session->state = POP3_RETR_RECV;
834 val = session_recv_data_as_file(session, 0, ".\r\n");
835 break;
836 case POP3_DELETE:
837 val = pop3_delete_recv(pop3_session);
838 if (pop3_session->cur_msg == pop3_session->count)
839 val = pop3_logout_send(pop3_session);
840 else {
841 pop3_session->cur_msg++;
842 if (pop3_lookup_next(pop3_session) == POP3_ERROR)
843 return -1;
844 }
845 break;
846 case POP3_LOGOUT:
847 if (val == PS_SUCCESS)
848 pop3_session->state = POP3_DONE;
849 else
850 pop3_session->state = POP3_ERROR;
851 session_disconnect(session);
852 break;
853 case POP3_ERROR:
854 default:
855 return -1;
856 }
857
858 if (val == PS_SUCCESS)
859 return 0;
860 else
861 return -1;
862}
863
864static gint pop3_session_recv_data_finished(Session *session, guchar *data,
865 guint len)
866{
867 Pop3Session *pop3_session = POP3_SESSION(session);
868 Pop3ErrorValue val = PS_SUCCESS;
869
870 switch (pop3_session->state) {
871 case POP3_GETRANGE_UIDL_RECV:
872 val = pop3_getrange_uidl_recv(pop3_session, (gchar *)data, len);
873 if (val == PS_SUCCESS) {
874 if (pop3_session->new_msg_exist)
875 pop3_getsize_list_send(pop3_session);
876 else
877 pop3_logout_send(pop3_session);
878 } else
879 return -1;
880 break;
881 case POP3_GETSIZE_LIST_RECV:
882 val = pop3_getsize_list_recv(pop3_session, (gchar *)data, len);
883 if (val == PS_SUCCESS) {
884 if (pop3_lookup_next(pop3_session) == POP3_ERROR)
885 return -1;
886 } else
887 return -1;
888 break;
889 case POP3_ERROR:
890 default:
891 return -1;
892 }
893
894 return 0;
895}
896
897static gint pop3_session_recv_data_as_file_finished(Session *session, FILE *fp,
898 guint len)
899{
900 Pop3Session *pop3_session = POP3_SESSION(session);
901
902 g_return_val_if_fail(pop3_session->state == POP3_RETR_RECV, -1);
903
904 if (pop3_retr_recv(pop3_session, fp, len) != PS_SUCCESS)
905 return -1;
906
907 /* disconnected? */
908 if (!session->sock)
909 return -1;
910
911 if (pop3_session->msg[pop3_session->cur_msg].recv_time
912 == RECV_TIME_DELETE ||
913 (pop3_session->ac_prefs->rmmail &&
914 pop3_session->ac_prefs->msg_leave_time == 0 &&
915 pop3_session->msg[pop3_session->cur_msg].recv_time
916 != RECV_TIME_KEEP))
917 pop3_delete_send(pop3_session);
918 else if (pop3_session->cur_msg == pop3_session->count)
919 pop3_logout_send(pop3_session);
920 else {
921 pop3_session->cur_msg++;
922 if (pop3_lookup_next(pop3_session) == POP3_ERROR)
923 return -1;
924 }
925
926 return 0;
927}