Improve some sieve-related translations
[claws.git] / src / pop.c
blobb4f907fc37bac0306592a3e745b87fc9a3020c86
1 /*
2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2022 the Claws Mail team and Hiroyuki Yamamoto
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 3 of the License, or
8 * (at your option) any later version.
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.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #include "claws-features.h"
23 #endif
25 #include <glib.h>
26 #include <glib/gi18n.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <stdarg.h>
30 #include <ctype.h>
31 #include <unistd.h>
32 #include <time.h>
33 #include <errno.h>
35 #include "pop.h"
36 #include "md5.h"
37 #include "prefs_account.h"
38 #include "utils.h"
39 #include "recv.h"
40 #include "partial_download.h"
41 #include "log.h"
42 #include "hooks.h"
43 #include "file-utils.h"
45 static gint pop3_greeting_recv (Pop3Session *session,
46 const gchar *msg);
47 static gint pop3_getauth_user_send (Pop3Session *session);
48 static gint pop3_getauth_pass_send (Pop3Session *session);
49 static gint pop3_getauth_apop_send (Pop3Session *session);
50 #ifdef USE_GNUTLS
51 static gint pop3_stls_send (Pop3Session *session);
52 static gint pop3_stls_recv (Pop3Session *session);
53 #endif
54 static gint pop3_getrange_stat_send (Pop3Session *session);
55 static gint pop3_getrange_stat_recv (Pop3Session *session,
56 const gchar *msg);
57 static gint pop3_getrange_last_send (Pop3Session *session);
58 static gint pop3_getrange_last_recv (Pop3Session *session,
59 const gchar *msg);
60 static gint pop3_getrange_uidl_send (Pop3Session *session);
61 static gint pop3_getrange_uidl_recv (Pop3Session *session,
62 const gchar *data,
63 guint len);
64 static gint pop3_getsize_list_send (Pop3Session *session);
65 static gint pop3_getsize_list_recv (Pop3Session *session,
66 const gchar *data,
67 guint len);
68 static gint pop3_retr_send (Pop3Session *session);
69 static gint pop3_retr_recv (Pop3Session *session,
70 const gchar *data,
71 guint len);
72 static gint pop3_delete_send (Pop3Session *session);
73 static gint pop3_delete_recv (Pop3Session *session);
74 static gint pop3_logout_send (Pop3Session *session);
76 static void pop3_gen_send (Pop3Session *session,
77 const gchar *format, ...);
79 static void pop3_session_destroy (Session *session);
81 static gint pop3_write_msg_to_file (const gchar *file,
82 const gchar *data,
83 guint len,
84 const gchar *prefix);
86 static Pop3State pop3_lookup_next (Pop3Session *session);
87 static Pop3ErrorValue pop3_ok (Pop3Session *session,
88 const gchar *msg);
90 static gint pop3_session_recv_msg (Session *session,
91 const gchar *msg);
92 static gint pop3_session_recv_data_finished (Session *session,
93 guchar *data,
94 guint len);
95 static void pop3_get_uidl_table(PrefsAccount *ac_prefs, Pop3Session *session);
97 static gint pop3_greeting_recv(Pop3Session *session, const gchar *msg)
99 session->state = POP3_GREETING;
101 session->greeting = g_strdup(msg);
102 return PS_SUCCESS;
105 #ifdef USE_GNUTLS
106 static gint pop3_stls_send(Pop3Session *session)
108 session->state = POP3_STLS;
109 pop3_gen_send(session, "STLS");
110 return PS_SUCCESS;
113 static gint pop3_stls_recv(Pop3Session *session)
115 if (session_start_tls(SESSION(session)) < 0) {
116 session->error_val = PS_SOCKET;
117 return -1;
119 return PS_SUCCESS;
121 #endif /* USE_GNUTLS */
123 static gint pop3_getauth_user_send(Pop3Session *session)
125 cm_return_val_if_fail(session->user != NULL, -1);
127 session->state = POP3_GETAUTH_USER;
128 pop3_gen_send(session, "USER %s", session->user);
129 return PS_SUCCESS;
132 static gint pop3_getauth_pass_send(Pop3Session *session)
134 cm_return_val_if_fail(session->pass != NULL, -1);
136 session->state = POP3_GETAUTH_PASS;
137 pop3_gen_send(session, "PASS %s", session->pass);
138 return PS_SUCCESS;
141 static gint pop3_getauth_apop_send(Pop3Session *session)
143 gchar *start, *end;
144 gchar *apop_str;
145 gchar md5sum[33];
147 cm_return_val_if_fail(session->user != NULL, -1);
148 cm_return_val_if_fail(session->pass != NULL, -1);
150 session->state = POP3_GETAUTH_APOP;
152 if ((start = strchr(session->greeting, '<')) == NULL) {
153 log_error(LOG_PROTOCOL, _("Required APOP timestamp not found "
154 "in greeting\n"));
155 session->error_val = PS_PROTOCOL;
156 return -1;
159 if ((end = strchr(start, '>')) == NULL || end == start + 1) {
160 log_error(LOG_PROTOCOL, _("Timestamp syntax error in greeting\n"));
161 session->error_val = PS_PROTOCOL;
162 return -1;
164 *(end + 1) = '\0';
166 if (!is_ascii_str(start)) {
167 log_error(LOG_PROTOCOL, _("Timestamp syntax error in greeting (not ASCII)\n"));
168 session->error_val = PS_PROTOCOL;
169 return -1;
172 apop_str = g_strconcat(start, session->pass, NULL);
173 md5_hex_digest(md5sum, apop_str);
174 g_free(apop_str);
176 pop3_gen_send(session, "APOP %s %s", session->user, md5sum);
178 return PS_SUCCESS;
181 #ifdef USE_OAUTH2
182 static gint pop3_getauth_oauth2_send(Pop3Session *session)
184 gchar buf[MESSAGEBUFSIZE], *b64buf, *out;
185 gint len;
187 cm_return_val_if_fail(session->user != NULL, -1);
188 cm_return_val_if_fail(session->pass != NULL, -1);
190 session->state = POP3_GETAUTH_OAUTH2;
191 memset(buf, 0, sizeof buf);
193 /* "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"*/
194 /* session->pass contains the OAUTH2 Access Token*/
195 len = sprintf(buf, "user=%s\1auth=Bearer %s\1\1", session->user, session->pass);
196 b64buf = g_base64_encode(buf, len);
197 out = g_strconcat("AUTH XOAUTH2 ", b64buf, NULL);
198 g_free(b64buf);
200 pop3_gen_send(session, "%s", out);
201 /* Any error response contains base64({JSON-Body}) containing three values: status, schemes, and scope */
202 /* This could be dealt with but is currently written to the log in a fairly graceful fail - not crucial */
203 g_free(out);
204 return PS_SUCCESS;
206 #endif
208 static gint pop3_getrange_stat_send(Pop3Session *session)
210 session->state = POP3_GETRANGE_STAT;
211 pop3_gen_send(session, "STAT");
212 return PS_SUCCESS;
215 static gint pop3_getrange_stat_recv(Pop3Session *session, const gchar *msg)
217 if (sscanf(msg, "%d %d", &session->count, &session->total_bytes) != 2) {
218 log_error(LOG_PROTOCOL, _("POP protocol error\n"));
219 session->error_val = PS_PROTOCOL;
220 return -1;
221 } else {
222 if (session->count == 0) {
223 session->uidl_is_valid = TRUE;
224 } else {
225 session->msg = g_new0(Pop3MsgInfo, session->count + 1);
226 session->cur_msg = 1;
230 return PS_SUCCESS;
233 static gint pop3_getrange_last_send(Pop3Session *session)
235 session->state = POP3_GETRANGE_LAST;
236 pop3_gen_send(session, "LAST");
237 return PS_SUCCESS;
240 static gint pop3_getrange_last_recv(Pop3Session *session, const gchar *msg)
242 gint last;
244 if (sscanf(msg, "%d", &last) == 0) {
245 log_warning(LOG_PROTOCOL, _("POP protocol error\n"));
246 session->error_val = PS_PROTOCOL;
247 return -1;
248 } else {
249 if (session->count > last) {
250 session->new_msg_exist = TRUE;
251 session->cur_msg = last + 1;
252 } else
253 session->cur_msg = 0;
256 return PS_SUCCESS;
259 static gint pop3_getrange_uidl_send(Pop3Session *session)
261 session->state = POP3_GETRANGE_UIDL;
262 pop3_gen_send(session, "UIDL");
263 return PS_SUCCESS;
266 static gint pop3_getrange_uidl_recv(Pop3Session *session, const gchar *data,
267 guint len)
269 gchar id[IDLEN + 1];
270 gchar buf[POPBUFSIZE];
271 gint buf_len;
272 guint32 num;
273 time_t recv_time;
274 gint partial_recv;
275 const gchar *p = data;
276 const gchar *lastp = data + len;
277 const gchar *newline;
279 while (p < lastp) {
280 if ((newline = memchr(p, '\r', lastp - p)) == NULL)
281 return -1;
282 buf_len = MIN(newline - p, sizeof(buf) - 1);
283 memcpy(buf, p, buf_len);
284 buf[buf_len] = '\0';
286 p = newline + 1;
287 if (p < lastp && *p == '\n') p++;
289 if (sscanf(buf, "%d %" Xstr(IDLEN) "s", &num, id) != 2 ||
290 num <= 0 || num > session->count) {
291 log_warning(LOG_PROTOCOL, _("invalid UIDL response: %s\n"), buf);
292 continue;
295 session->msg[num].uidl = g_strdup(id);
297 recv_time = (time_t)(GPOINTER_TO_INT(g_hash_table_lookup(
298 session->uidl_table, id)));
299 session->msg[num].recv_time = recv_time;
301 if (recv_time != RECV_TIME_NONE) {
302 debug_print("num %d uidl %s: already got it\n", num, id);
303 } else {
304 debug_print("num %d uidl %s: unknown\n", num, id);
307 partial_recv = (gint)(GPOINTER_TO_INT(g_hash_table_lookup(
308 session->partial_recv_table, id)));
310 if (recv_time != RECV_TIME_NONE
311 || partial_recv != POP3_TOTALLY_RECEIVED) {
312 session->msg[num].received =
313 (partial_recv != POP3_MUST_COMPLETE_RECV);
314 session->msg[num].partial_recv = partial_recv;
315 if (partial_recv == POP3_MUST_COMPLETE_RECV)
316 session->new_msg_exist = TRUE;
318 if (!session->new_msg_exist &&
319 (recv_time == RECV_TIME_NONE ||
320 session->ac_prefs->rmmail)) {
321 session->cur_msg = num;
322 session->new_msg_exist = TRUE;
326 session->uidl_is_valid = TRUE;
327 return PS_SUCCESS;
330 static gint pop3_getsize_list_send(Pop3Session *session)
332 session->state = POP3_GETSIZE_LIST;
333 pop3_gen_send(session, "LIST");
334 return PS_SUCCESS;
337 static gint pop3_getsize_list_recv(Pop3Session *session, const gchar *data,
338 guint len)
340 gchar buf[POPBUFSIZE];
341 gint buf_len;
342 guint num, size;
343 const gchar *p = data;
344 const gchar *lastp = data + len;
345 const gchar *newline;
347 while (p < lastp) {
348 if ((newline = memchr(p, '\r', lastp - p)) == NULL)
349 return -1;
350 buf_len = MIN(newline - p, sizeof(buf) - 1);
351 memcpy(buf, p, buf_len);
352 buf[buf_len] = '\0';
354 p = newline + 1;
355 if (p < lastp && *p == '\n') p++;
357 if (sscanf(buf, "%u %u", &num, &size) != 2) {
358 session->error_val = PS_PROTOCOL;
359 return -1;
362 if (num > 0 && num <= session->count)
363 session->msg[num].size = size;
364 if (num > 0 && num < session->cur_msg)
365 session->cur_total_bytes += size;
368 return PS_SUCCESS;
371 static gint pop3_retr_send(Pop3Session *session)
373 session->state = POP3_RETR;
374 debug_print("retrieving %d [%s]\n", session->cur_msg,
375 session->msg[session->cur_msg].uidl ?
376 session->msg[session->cur_msg].uidl:" ");
377 pop3_gen_send(session, "RETR %d", session->cur_msg);
378 return PS_SUCCESS;
381 static gint pop3_retr_recv(Pop3Session *session, const gchar *data, guint len)
383 gchar *file;
384 gint drop_ok;
385 MailReceiveData mail_receive_data;
387 /* NOTE: we allocate a slightly larger buffer with a zero terminator
388 * because some plugins may think that it has a C string. */
389 mail_receive_data.session = session;
390 mail_receive_data.data = g_new0(gchar, len + 1);
391 mail_receive_data.data_len = len;
392 memcpy(mail_receive_data.data, data, len);
394 hooks_invoke(MAIL_RECEIVE_HOOKLIST, &mail_receive_data);
396 file = get_tmp_file();
397 if (pop3_write_msg_to_file(file, mail_receive_data.data,
398 mail_receive_data.data_len, NULL) < 0) {
399 g_free(file);
400 g_free(mail_receive_data.data);
401 session->error_val = PS_IOERR;
402 return -1;
404 g_free(mail_receive_data.data);
406 if (session->msg[session->cur_msg].partial_recv
407 == POP3_MUST_COMPLETE_RECV) {
408 gchar *old_file = partial_get_filename(
409 session->ac_prefs->recv_server,
410 session->ac_prefs->userid,
411 session->msg[session->cur_msg].uidl);
413 if (old_file) {
414 partial_delete_old(old_file);
415 g_free(old_file);
419 /* drop_ok: 0: success 1: don't receive -1: error */
420 drop_ok = session->drop_message(session, file);
422 g_free(file);
423 if (drop_ok < 0) {
424 session->error_val = PS_IOERR;
425 return -1;
428 session->cur_total_bytes += session->msg[session->cur_msg].size;
429 session->cur_total_recv_bytes += session->msg[session->cur_msg].size;
430 session->cur_total_num++;
432 session->msg[session->cur_msg].received = TRUE;
433 session->msg[session->cur_msg].partial_recv = POP3_TOTALLY_RECEIVED;
435 session->msg[session->cur_msg].recv_time =
436 drop_ok == 1 ? RECV_TIME_KEEP : session->current_time;
438 return PS_SUCCESS;
441 static gint pop3_top_send(Pop3Session *session, gint max_size)
443 gint num_lines = (max_size*1024)/82; /* consider lines to be 80 chars */
444 session->state = POP3_TOP;
445 pop3_gen_send(session, "TOP %d %d", session->cur_msg, num_lines);
446 return PS_SUCCESS;
449 static gint pop3_top_recv(Pop3Session *session, const gchar *data, guint len)
451 gchar *file;
452 gint drop_ok;
453 MailReceiveData mail_receive_data;
454 gchar *partial_notice = NULL;
456 /* NOTE: we allocate a slightly larger buffer with a zero terminator
457 * because some plugins may think that it has a C string. */
458 mail_receive_data.session = session;
459 mail_receive_data.data = g_new0(gchar, len + 1);
460 mail_receive_data.data_len = len;
461 memcpy(mail_receive_data.data, data, len);
463 hooks_invoke(MAIL_RECEIVE_HOOKLIST, &mail_receive_data);
465 partial_notice = g_strdup_printf("SC-Marked-For-Download: 0\n"
466 "SC-Partially-Retrieved: %s\n"
467 "SC-Account-Server: %s\n"
468 "SC-Account-Login: %s\n"
469 "SC-Message-Size: %d",
470 session->msg[session->cur_msg].uidl,
471 session->ac_prefs->recv_server,
472 session->ac_prefs->userid,
473 session->msg[session->cur_msg].size);
474 file = get_tmp_file();
475 if (pop3_write_msg_to_file(file, mail_receive_data.data,
476 mail_receive_data.data_len,
477 partial_notice) < 0) {
478 g_free(file);
479 g_free(mail_receive_data.data);
480 session->error_val = PS_IOERR;
481 g_free(partial_notice);
482 return -1;
484 g_free(mail_receive_data.data);
485 g_free(partial_notice);
487 /* drop_ok: 0: success 1: don't receive -1: error */
488 drop_ok = session->drop_message(session, file);
489 g_free(file);
490 if (drop_ok < 0) {
491 session->error_val = PS_IOERR;
492 return -1;
495 session->cur_total_bytes += session->msg[session->cur_msg].size;
496 session->cur_total_recv_bytes += session->msg[session->cur_msg].size;
497 session->cur_total_num++;
499 session->msg[session->cur_msg].received = TRUE;
500 session->msg[session->cur_msg].partial_recv = POP3_PARTIALLY_RECEIVED;
501 session->msg[session->cur_msg].recv_time =
502 drop_ok == 1 ? RECV_TIME_KEEP : session->current_time;
504 return PS_SUCCESS;
507 static gint pop3_delete_send(Pop3Session *session)
509 session->state = POP3_DELETE;
510 pop3_gen_send(session, "DELE %d", session->cur_msg);
511 return PS_SUCCESS;
514 static gint pop3_delete_recv(Pop3Session *session)
516 session->msg[session->cur_msg].deleted = TRUE;
517 return PS_SUCCESS;
520 static gint pop3_logout_send(Pop3Session *session)
522 session->state = POP3_LOGOUT;
523 pop3_gen_send(session, "QUIT");
524 return PS_SUCCESS;
527 static void pop3_gen_send(Pop3Session *session, const gchar *format, ...)
529 gchar buf[POPBUFSIZE + 1];
530 va_list args;
532 va_start(args, format);
533 g_vsnprintf(buf, sizeof(buf) - 2, format, args);
534 va_end(args);
536 if (!g_ascii_strncasecmp(buf, "PASS ", 5))
537 log_print(LOG_PROTOCOL, "POP> PASS ********\n");
538 #ifdef USE_OAUTH2
539 else if (!g_ascii_strncasecmp(buf, "AUTH XOAUTH2 ", 13))
540 log_print(LOG_PROTOCOL, "POP> AUTH XOAUTH2 ********\n");
541 #endif
542 else
543 log_print(LOG_PROTOCOL, "POP> %s\n", buf);
545 session_send_msg(SESSION(session), buf);
548 Session *pop3_session_new(PrefsAccount *account)
550 Pop3Session *session;
552 cm_return_val_if_fail(account != NULL, NULL);
554 account->receive_in_progress = TRUE;
556 session = g_new0(Pop3Session, 1);
558 session_init(SESSION(session), account, FALSE);
560 SESSION(session)->type = SESSION_POP3;
562 SESSION(session)->recv_msg = pop3_session_recv_msg;
563 SESSION(session)->recv_data_finished = pop3_session_recv_data_finished;
564 SESSION(session)->send_data_finished = NULL;
565 SESSION(session)->ssl_cert_auto_accept = account->ssl_certs_auto_accept;
566 SESSION(session)->destroy = pop3_session_destroy;
568 #ifdef USE_GNUTLS
569 if (account->set_gnutls_priority && account->gnutls_priority &&
570 strlen(account->gnutls_priority) != 0)
571 SESSION(session)->gnutls_priority = g_strdup(account->gnutls_priority);
572 SESSION(session)->use_tls_sni = account->use_tls_sni;
573 #endif
575 session->state = POP3_READY;
576 session->ac_prefs = account;
577 session->pop_before_smtp = FALSE;
578 pop3_get_uidl_table(account, session);
579 session->current_time = time(NULL);
580 session->error_val = PS_SUCCESS;
581 session->error_msg = NULL;
583 return SESSION(session);
586 static void pop3_session_destroy(Session *session)
588 Pop3Session *pop3_session = POP3_SESSION(session);
589 gint n;
591 cm_return_if_fail(session != NULL);
593 for (n = 1; n <= pop3_session->count; n++)
594 g_free(pop3_session->msg[n].uidl);
595 g_free(pop3_session->msg);
597 if (pop3_session->uidl_table) {
598 hash_free_strings(pop3_session->uidl_table);
599 g_hash_table_destroy(pop3_session->uidl_table);
602 if (pop3_session->partial_recv_table) {
603 hash_free_strings(pop3_session->partial_recv_table);
604 g_hash_table_destroy(pop3_session->partial_recv_table);
607 g_free(pop3_session->greeting);
608 g_free(pop3_session->user);
609 g_free(pop3_session->pass);
610 g_free(pop3_session->error_msg);
612 pop3_session->ac_prefs->receive_in_progress = FALSE;
615 static void pop3_get_uidl_table(PrefsAccount *ac_prefs, Pop3Session *session)
617 GHashTable *table;
618 GHashTable *partial_recv_table;
619 gchar *path;
620 FILE *fp;
621 gchar buf[POPBUFSIZE];
622 gchar uidl[POPBUFSIZE];
623 time_t recv_time;
624 time_t now;
625 gint partial_recv;
626 gchar *sanitized_uid = g_strdup(ac_prefs->userid);
628 subst_for_filename(sanitized_uid);
630 table = g_hash_table_new(g_str_hash, g_str_equal);
631 partial_recv_table = g_hash_table_new(g_str_hash, g_str_equal);
633 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
634 "uidl", G_DIR_SEPARATOR_S, ac_prefs->recv_server,
635 "-", sanitized_uid, NULL);
637 g_free(sanitized_uid);
638 if ((fp = claws_fopen(path, "rb")) == NULL) {
639 if (ENOENT != errno) FILE_OP_ERROR(path, "claws_fopen");
640 g_free(path);
641 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
642 "uidl-", ac_prefs->recv_server,
643 "-", ac_prefs->userid, NULL);
644 if ((fp = claws_fopen(path, "rb")) == NULL) {
645 if (ENOENT != errno) FILE_OP_ERROR(path, "claws_fopen");
646 g_free(path);
647 session->uidl_table = table;
648 session->partial_recv_table = partial_recv_table;
649 return;
652 g_free(path);
654 now = time(NULL);
656 while (claws_fgets(buf, sizeof(buf), fp) != NULL) {
657 gchar tmp[POPBUFSIZE];
658 strretchomp(buf);
659 recv_time = RECV_TIME_NONE;
660 partial_recv = POP3_TOTALLY_RECEIVED;
662 if (sscanf(buf, "%s\t%ld\t%s", uidl, (long int *) &recv_time, tmp) < 3) {
663 if (sscanf(buf, "%s\t%ld", uidl, (long int *) &recv_time) != 2) {
664 if (sscanf(buf, "%s", uidl) != 1)
665 continue;
666 else {
667 recv_time = now;
668 strcpy(tmp, "0");
670 } else {
671 strcpy(tmp, "0");
675 if (recv_time == RECV_TIME_NONE)
676 recv_time = RECV_TIME_RECEIVED;
677 g_hash_table_insert(table, g_strdup(uidl),
678 GINT_TO_POINTER(recv_time));
679 if (strlen(tmp) == 1)
680 partial_recv = atoi(tmp); /* totally received ?*/
681 else
682 partial_recv = POP3_MUST_COMPLETE_RECV;
684 g_hash_table_insert(partial_recv_table, g_strdup(uidl),
685 GINT_TO_POINTER(partial_recv));
688 claws_fclose(fp);
689 session->uidl_table = table;
690 session->partial_recv_table = partial_recv_table;
692 return;
695 #define TRY(func) \
696 if (!(func)) \
698 g_warning("failed to write"); \
699 goto err_write; \
702 gint pop3_write_uidl_list(Pop3Session *session)
704 gchar *path, *tmp_path;
705 FILE *fp;
706 Pop3MsgInfo *msg;
707 gint n;
708 gchar *sanitized_uid = g_strdup(session->ac_prefs->userid);
710 subst_for_filename(sanitized_uid);
712 if (!session->uidl_is_valid) {
713 g_free(sanitized_uid);
714 return 0;
717 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
718 "uidl", G_DIR_SEPARATOR_S,
719 session->ac_prefs->recv_server,
720 "-", sanitized_uid, NULL);
721 tmp_path = g_strconcat(path, ".tmp", NULL);
723 g_free(sanitized_uid);
725 if ((fp = claws_fopen(tmp_path, "wb")) == NULL) {
726 FILE_OP_ERROR(tmp_path, "claws_fopen");
727 goto err_write;
730 for (n = 1; n <= session->count; n++) {
731 msg = &session->msg[n];
732 if (msg->uidl && msg->received &&
733 (!msg->deleted || session->state != POP3_DONE))
734 TRY(fprintf(fp, "%s\t%ld\t%d\n",
735 msg->uidl, (long int)
736 msg->recv_time,
737 msg->partial_recv)
738 > 0);
741 if (claws_safe_fclose(fp) == EOF) {
742 FILE_OP_ERROR(tmp_path, "claws_fclose");
743 fp = NULL;
744 goto err_write;
746 fp = NULL;
747 #ifdef G_OS_WIN32
748 claws_unlink(path);
749 #endif
750 if (g_rename(tmp_path, path) < 0) {
751 FILE_OP_ERROR(path, "rename");
752 goto err_write;
754 g_free(path);
755 g_free(tmp_path);
756 return 0;
757 err_write:
758 if (fp)
759 claws_fclose(fp);
760 g_free(path);
761 g_free(tmp_path);
762 return -1;
765 #undef TRY
767 static gint pop3_write_msg_to_file(const gchar *file, const gchar *data,
768 guint len, const gchar *prefix)
770 FILE *fp;
771 const gchar *prev, *cur;
773 cm_return_val_if_fail(file != NULL, -1);
775 if ((fp = claws_fopen(file, "wb")) == NULL) {
776 FILE_OP_ERROR(file, "claws_fopen");
777 return -1;
780 if (change_file_mode_rw(fp, file) < 0)
781 FILE_OP_ERROR(file, "chmod");
783 if (prefix != NULL) {
784 if (fprintf(fp, "%s\n", prefix) < 0) {
785 FILE_OP_ERROR(file, "fprintf");
786 claws_fclose(fp);
787 claws_unlink(file);
788 return -1;
792 /* +------------------+----------------+--------------------------+ *
793 * ^data ^prev ^cur data+len-1^ */
795 prev = data;
796 while ((cur = (gchar *)my_memmem(prev, len - (prev - data), "\r\n", 2))
797 != NULL) {
798 if ((cur > prev && claws_fwrite(prev, 1, cur - prev, fp) < 1) ||
799 claws_fputc('\n', fp) == EOF) {
800 FILE_OP_ERROR(file, "claws_fwrite");
801 g_warning("can't write to file: %s", file);
802 claws_fclose(fp);
803 claws_unlink(file);
804 return -1;
807 if (cur == data + len - 1) {
808 prev = cur + 1;
809 break;
812 if (*(cur + 1) == '\n')
813 prev = cur + 2;
814 else
815 prev = cur + 1;
817 if (prev - data < len - 1 && *prev == '.' && *(prev + 1) == '.')
818 prev++;
820 if (prev - data >= len)
821 break;
824 if (prev - data < len &&
825 claws_fwrite(prev, 1, len - (prev - data), fp) < 1) {
826 FILE_OP_ERROR(file, "claws_fwrite");
827 g_warning("can't write to file: %s", file);
828 claws_fclose(fp);
829 claws_unlink(file);
830 return -1;
832 if (data[len - 1] != '\r' && data[len - 1] != '\n') {
833 if (claws_fputc('\n', fp) == EOF) {
834 FILE_OP_ERROR(file, "claws_fputc");
835 g_warning("can't write to file: %s", file);
836 claws_fclose(fp);
837 claws_unlink(file);
838 return -1;
842 if (claws_safe_fclose(fp) == EOF) {
843 FILE_OP_ERROR(file, "claws_fclose");
844 claws_unlink(file);
845 return -1;
848 return 0;
851 static Pop3State pop3_lookup_next(Pop3Session *session)
853 Pop3MsgInfo *msg;
854 PrefsAccount *ac = session->ac_prefs;
855 gint size;
856 gboolean size_limit_over;
858 for (;;) {
859 msg = &session->msg[session->cur_msg];
860 size = msg->size;
861 size_limit_over =
862 (ac->enable_size_limit &&
863 ac->size_limit > 0 &&
864 size > ac->size_limit * 1024);
866 if (ac->rmmail &&
867 msg->recv_time != RECV_TIME_NONE &&
868 msg->recv_time != RECV_TIME_KEEP &&
869 msg->partial_recv == POP3_TOTALLY_RECEIVED &&
870 session->current_time - msg->recv_time >=
871 ((ac->msg_leave_time * 24 * 60 * 60) +
872 (ac->msg_leave_hour * 60 * 60))) {
873 log_message(LOG_PROTOCOL,
874 _("POP: Deleting expired message %d [%s]\n"),
875 session->cur_msg, msg->uidl?msg->uidl:" ");
876 session->cur_total_bytes += size;
877 pop3_delete_send(session);
878 return POP3_DELETE;
881 if (size_limit_over) {
882 if (!msg->received && msg->partial_recv !=
883 POP3_MUST_COMPLETE_RECV) {
884 pop3_top_send(session, ac->size_limit);
885 return POP3_TOP;
886 } else if (msg->partial_recv == POP3_MUST_COMPLETE_RECV)
887 break;
889 log_message(LOG_PROTOCOL,
890 _("POP: Skipping message %d [%s] (%d bytes)\n"),
891 session->cur_msg, msg->uidl?msg->uidl:" ", size);
894 if (size == 0 || msg->received || size_limit_over) {
895 session->cur_total_bytes += size;
896 if (session->cur_msg == session->count) {
897 pop3_logout_send(session);
898 return POP3_LOGOUT;
899 } else
900 session->cur_msg++;
901 } else
902 break;
905 pop3_retr_send(session);
906 return POP3_RETR;
909 static Pop3ErrorValue pop3_ok(Pop3Session *session, const gchar *msg)
911 Pop3ErrorValue ok;
913 log_print(LOG_PROTOCOL, "POP< %s\n", msg);
915 if (!strncmp(msg, "+OK", 3))
916 ok = PS_SUCCESS;
917 else if (!strncmp(msg, "-ERR", 4)) {
918 if (strstr(msg + 4, "lock") ||
919 strstr(msg + 4, "Lock") ||
920 strstr(msg + 4, "LOCK") ||
921 strstr(msg + 4, "wait")) {
922 log_error(LOG_PROTOCOL, _("mailbox is locked\n"));
923 ok = PS_LOCKBUSY;
924 } else if (strcasestr(msg + 4, "timeout")) {
925 log_error(LOG_PROTOCOL, _("Session timeout\n"));
926 ok = PS_ERROR;
927 } else {
928 switch (session->state) {
929 #ifdef USE_GNUTLS
930 case POP3_STLS:
931 log_error(LOG_PROTOCOL, _("couldn't start STARTTLS session\n"));
932 ok = PS_ERROR;
933 break;
934 #endif
935 case POP3_GETAUTH_USER:
936 case POP3_GETAUTH_PASS:
937 case POP3_GETAUTH_APOP:
938 log_error(LOG_PROTOCOL, _("error occurred on authentication\n"));
939 ok = PS_AUTHFAIL;
940 break;
941 case POP3_GETRANGE_LAST:
942 case POP3_GETRANGE_UIDL:
943 case POP3_TOP:
944 log_warning(LOG_PROTOCOL, _("command not supported\n"));
945 ok = PS_NOTSUPPORTED;
946 break;
948 default:
949 log_error(LOG_PROTOCOL, _("error occurred on POP session\n"));
950 ok = PS_ERROR;
954 g_free(session->error_msg);
955 session->error_msg = g_strdup(msg);
956 g_printerr("POP: %s\n", msg);
957 } else
958 ok = PS_PROTOCOL;
960 session->error_val = ok;
961 return ok;
964 static gint pop3_session_recv_msg(Session *session, const gchar *msg)
966 Pop3Session *pop3_session = POP3_SESSION(session);
967 Pop3ErrorValue val = PS_SUCCESS;
968 const gchar *body;
970 body = msg;
971 if (pop3_session->state != POP3_GETRANGE_UIDL_RECV &&
972 pop3_session->state != POP3_GETSIZE_LIST_RECV) {
973 val = pop3_ok(pop3_session, msg);
974 if (val != PS_SUCCESS) {
975 if (val != PS_NOTSUPPORTED) {
976 pop3_session->state = POP3_ERROR;
977 return -1;
981 if (*body == '+' || *body == '-')
982 body++;
983 while (g_ascii_isalpha(*body))
984 body++;
985 while (g_ascii_isspace(*body))
986 body++;
989 switch (pop3_session->state) {
990 case POP3_READY:
991 case POP3_GREETING:
992 pop3_greeting_recv(pop3_session, body);
993 #ifdef USE_GNUTLS
994 if (pop3_session->ac_prefs->ssl_pop == SSL_STARTTLS)
995 val = pop3_stls_send(pop3_session);
996 else
997 #endif
998 #ifdef USE_OAUTH2
999 if (pop3_session->ac_prefs->use_pop_auth && pop3_session->ac_prefs->pop_auth_type == POPAUTH_OAUTH2)
1000 val = pop3_getauth_oauth2_send(pop3_session);
1001 else
1002 #endif
1003 if (pop3_session->ac_prefs->use_pop_auth && pop3_session->ac_prefs->pop_auth_type == POPAUTH_APOP)
1004 val = pop3_getauth_apop_send(pop3_session);
1005 else
1006 val = pop3_getauth_user_send(pop3_session);
1007 break;
1008 #ifdef USE_GNUTLS
1009 case POP3_STLS:
1010 if (pop3_stls_recv(pop3_session) != PS_SUCCESS)
1011 return -1;
1012 if (pop3_session->ac_prefs->use_pop_auth && pop3_session->ac_prefs->pop_auth_type == POPAUTH_APOP)
1013 val = pop3_getauth_apop_send(pop3_session);
1014 #ifdef USE_OAUTH2
1015 else if (pop3_session->ac_prefs->use_pop_auth && pop3_session->ac_prefs->pop_auth_type == POPAUTH_OAUTH2)
1016 val = pop3_getauth_oauth2_send(pop3_session);
1017 #endif
1018 else
1019 val = pop3_getauth_user_send(pop3_session);
1020 break;
1021 #endif
1022 case POP3_GETAUTH_USER:
1023 val = pop3_getauth_pass_send(pop3_session);
1024 break;
1025 case POP3_GETAUTH_PASS:
1026 case POP3_GETAUTH_APOP:
1027 #ifdef USE_OAUTH2
1028 case POP3_GETAUTH_OAUTH2:
1029 #endif
1030 if (!pop3_session->pop_before_smtp)
1031 val = pop3_getrange_stat_send(pop3_session);
1032 else
1033 val = pop3_logout_send(pop3_session);
1034 break;
1035 case POP3_GETRANGE_STAT:
1036 if (pop3_getrange_stat_recv(pop3_session, body) < 0)
1037 return -1;
1038 if (pop3_session->count > 0)
1039 val = pop3_getrange_uidl_send(pop3_session);
1040 else
1041 val = pop3_logout_send(pop3_session);
1042 break;
1043 case POP3_GETRANGE_LAST:
1044 if (val == PS_NOTSUPPORTED)
1045 pop3_session->error_val = PS_SUCCESS;
1046 else if (pop3_getrange_last_recv(pop3_session, body) < 0)
1047 return -1;
1048 if (pop3_session->cur_msg > 0)
1049 val = pop3_getsize_list_send(pop3_session);
1050 else
1051 val = pop3_logout_send(pop3_session);
1052 break;
1053 case POP3_GETRANGE_UIDL:
1054 if (val == PS_NOTSUPPORTED) {
1055 pop3_session->error_val = PS_SUCCESS;
1056 val = pop3_getrange_last_send(pop3_session);
1057 } else {
1058 pop3_session->state = POP3_GETRANGE_UIDL_RECV;
1059 session_recv_data(session, 0, ".\r\n");
1061 break;
1062 case POP3_GETSIZE_LIST:
1063 pop3_session->state = POP3_GETSIZE_LIST_RECV;
1064 session_recv_data(session, 0, ".\r\n");
1065 break;
1066 case POP3_RETR:
1067 pop3_session->state = POP3_RETR_RECV;
1068 session_recv_data(session, 0, ".\r\n");
1069 break;
1070 case POP3_TOP:
1071 if (val == PS_NOTSUPPORTED) {
1072 pop3_session->error_val = PS_SUCCESS;
1073 } else {
1074 pop3_session->state = POP3_TOP_RECV;
1075 session_recv_data(session, 0, ".\r\n");
1077 break;
1078 case POP3_DELETE:
1079 pop3_delete_recv(pop3_session);
1080 if (pop3_session->cur_msg == pop3_session->count)
1081 val = pop3_logout_send(pop3_session);
1082 else {
1083 pop3_session->cur_msg++;
1084 if (pop3_lookup_next(pop3_session) == POP3_ERROR)
1085 return -1;
1087 break;
1088 case POP3_LOGOUT:
1089 pop3_session->state = POP3_DONE;
1090 session_disconnect(session);
1091 break;
1092 case POP3_ERROR:
1093 default:
1094 return -1;
1097 return val == PS_SUCCESS?0:-1;
1100 static gint pop3_session_recv_data_finished(Session *session, guchar *data,
1101 guint len)
1103 Pop3Session *pop3_session = POP3_SESSION(session);
1104 Pop3ErrorValue val = PS_SUCCESS;
1106 switch (pop3_session->state) {
1107 case POP3_GETRANGE_UIDL_RECV:
1108 val = pop3_getrange_uidl_recv(pop3_session, data, len);
1109 if (val == PS_SUCCESS) {
1110 if (pop3_session->new_msg_exist)
1111 pop3_getsize_list_send(pop3_session);
1112 else
1113 pop3_logout_send(pop3_session);
1114 } else
1115 return -1;
1116 break;
1117 case POP3_GETSIZE_LIST_RECV:
1118 val = pop3_getsize_list_recv(pop3_session, data, len);
1119 if (val == PS_SUCCESS) {
1120 if (pop3_lookup_next(pop3_session) == POP3_ERROR)
1121 return -1;
1122 } else
1123 return -1;
1124 break;
1125 case POP3_RETR_RECV:
1126 if (pop3_retr_recv(pop3_session, data, len) < 0)
1127 return -1;
1129 if (pop3_session->ac_prefs->rmmail &&
1130 pop3_session->ac_prefs->msg_leave_time == 0 &&
1131 pop3_session->ac_prefs->msg_leave_hour == 0 &&
1132 pop3_session->msg[pop3_session->cur_msg].recv_time
1133 != RECV_TIME_KEEP)
1134 pop3_delete_send(pop3_session);
1135 else if (pop3_session->cur_msg == pop3_session->count)
1136 pop3_logout_send(pop3_session);
1137 else {
1138 pop3_session->cur_msg++;
1139 if (pop3_lookup_next(pop3_session) == POP3_ERROR)
1140 return -1;
1142 break;
1143 case POP3_TOP_RECV:
1144 if (pop3_top_recv(pop3_session, data, len) < 0)
1145 return -1;
1147 if (pop3_session->cur_msg == pop3_session->count)
1148 pop3_logout_send(pop3_session);
1149 else {
1150 pop3_session->cur_msg++;
1151 if (pop3_lookup_next(pop3_session) == POP3_ERROR)
1152 return -1;
1154 break;
1155 case POP3_TOP:
1156 log_warning(LOG_PROTOCOL, _("TOP command unsupported\n"));
1157 if (pop3_session->cur_msg == pop3_session->count)
1158 pop3_logout_send(pop3_session);
1159 else {
1160 pop3_session->cur_msg++;
1161 if (pop3_lookup_next(pop3_session) == POP3_ERROR)
1162 return -1;
1164 break;
1165 case POP3_ERROR:
1166 default:
1167 return -1;
1170 return 0;