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/>.
21 #include "claws-features.h"
25 #include <glib/gi18n.h>
36 #include "prefs_account.h"
39 #include "partial_download.h"
42 #include "file-utils.h"
45 static gint
pop3_greeting_recv (Pop3Session
*session
,
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
);
51 static gint
pop3_stls_send (Pop3Session
*session
);
52 static gint
pop3_stls_recv (Pop3Session
*session
);
54 static gint
pop3_getrange_stat_send (Pop3Session
*session
);
55 static gint
pop3_getrange_stat_recv (Pop3Session
*session
,
57 static gint
pop3_getrange_last_send (Pop3Session
*session
);
58 static gint
pop3_getrange_last_recv (Pop3Session
*session
,
60 static gint
pop3_getrange_uidl_send (Pop3Session
*session
);
61 static gint
pop3_getrange_uidl_recv (Pop3Session
*session
,
64 static gint
pop3_getsize_list_send (Pop3Session
*session
);
65 static gint
pop3_getsize_list_recv (Pop3Session
*session
,
68 static gint
pop3_retr_send (Pop3Session
*session
);
69 static gint
pop3_retr_recv (Pop3Session
*session
,
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
,
86 static Pop3State
pop3_lookup_next (Pop3Session
*session
);
87 static Pop3ErrorValue
pop3_ok (Pop3Session
*session
,
90 static gint
pop3_session_recv_msg (Session
*session
,
92 static gint
pop3_session_recv_data_finished (Session
*session
,
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
);
106 static gint
pop3_stls_send(Pop3Session
*session
)
108 session
->state
= POP3_STLS
;
109 pop3_gen_send(session
, "STLS");
113 static gint
pop3_stls_recv(Pop3Session
*session
)
115 if (session_start_tls(SESSION(session
)) < 0) {
116 session
->error_val
= PS_SOCKET
;
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
);
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
);
141 static gint
pop3_getauth_apop_send(Pop3Session
*session
)
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 "
155 session
->error_val
= PS_PROTOCOL
;
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
;
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
;
172 apop_str
= g_strconcat(start
, session
->pass
, NULL
);
173 md5_hex_digest(md5sum
, apop_str
);
176 pop3_gen_send(session
, "APOP %s %s", session
->user
, md5sum
);
182 static gint
pop3_getauth_oauth2_send_generic(Pop3Session
*session
)
184 gchar buf
[MESSAGEBUFSIZE
], *b64buf
, *out
;
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
);
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 */
207 /* Microsoft requires authentication to be split in two lines */
208 static gint
pop3_getauth_oauth2_send_microsoft_1(Pop3Session
*session
)
210 cm_return_val_if_fail(session
->user
!= NULL
, -1);
211 cm_return_val_if_fail(session
->pass
!= NULL
, -1);
213 session
->state
= POP3_GETAUTH_USER_PHASE2
;
215 pop3_gen_send(session
, "AUTH XOAUTH2");
220 static gint
pop3_getauth_oauth2_send_microsoft_2(Pop3Session
*session
)
222 gchar buf
[MESSAGEBUFSIZE
], *b64buf
;
225 cm_return_val_if_fail(session
->user
!= NULL
, -1);
226 cm_return_val_if_fail(session
->pass
!= NULL
, -1);
228 session
->state
= POP3_GETAUTH_OAUTH2
;
229 memset(buf
, 0, sizeof buf
);
231 /* "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"*/
232 /* session->pass contains the OAUTH2 Access Token*/
233 len
= sprintf(buf
, "user=%s\1auth=Bearer %s\1\1", session
->user
, session
->pass
);
234 b64buf
= g_base64_encode(buf
, len
);
236 pop3_gen_send(session
, "%s", b64buf
);
239 /* Any error response contains base64({JSON-Body}) containing three values: status, schemes, and scope */
240 /* This could be dealt with but is currently written to the log in a fairly graceful fail - not crucial */
244 static gint
pop3_getauth_oauth2_send(Pop3Session
*session
)
246 gint oauth2_provider
= session
->ac_prefs
->oauth2_provider
;
247 return ( oauth2_provider
== OAUTH2AUTH_OUTLOOK
||
248 oauth2_provider
== OAUTH2AUTH_EXCHANGE
||
249 oauth2_provider
== OAUTH2AUTH_MICROSOFT_GCCHIGH
250 ? pop3_getauth_oauth2_send_microsoft_1(session
)
251 : pop3_getauth_oauth2_send_generic(session
)
256 static gint
pop3_getrange_stat_send(Pop3Session
*session
)
258 session
->state
= POP3_GETRANGE_STAT
;
259 pop3_gen_send(session
, "STAT");
263 static gint
pop3_getrange_stat_recv(Pop3Session
*session
, const gchar
*msg
)
265 if (sscanf(msg
, "%d %d", &session
->count
, &session
->total_bytes
) != 2) {
266 log_error(LOG_PROTOCOL
, _("POP protocol error\n"));
267 session
->error_val
= PS_PROTOCOL
;
270 if (session
->count
== 0) {
271 session
->uidl_is_valid
= TRUE
;
273 session
->msg
= g_new0(Pop3MsgInfo
, session
->count
+ 1);
274 session
->cur_msg
= 1;
281 static gint
pop3_getrange_last_send(Pop3Session
*session
)
283 session
->state
= POP3_GETRANGE_LAST
;
284 pop3_gen_send(session
, "LAST");
288 static gint
pop3_getrange_last_recv(Pop3Session
*session
, const gchar
*msg
)
292 if (sscanf(msg
, "%d", &last
) == 0) {
293 log_warning(LOG_PROTOCOL
, _("POP protocol error\n"));
294 session
->error_val
= PS_PROTOCOL
;
297 if (session
->count
> last
) {
298 session
->new_msg_exist
= TRUE
;
299 session
->cur_msg
= last
+ 1;
301 session
->cur_msg
= 0;
307 static gint
pop3_getrange_uidl_send(Pop3Session
*session
)
309 session
->state
= POP3_GETRANGE_UIDL
;
310 pop3_gen_send(session
, "UIDL");
314 static gint
pop3_getrange_uidl_recv(Pop3Session
*session
, const gchar
*data
,
318 gchar buf
[POPBUFSIZE
];
323 const gchar
*p
= data
;
324 const gchar
*lastp
= data
+ len
;
325 const gchar
*newline
;
328 if ((newline
= memchr(p
, '\r', lastp
- p
)) == NULL
)
330 buf_len
= MIN(newline
- p
, sizeof(buf
) - 1);
331 memcpy(buf
, p
, buf_len
);
335 if (p
< lastp
&& *p
== '\n') p
++;
337 if (sscanf(buf
, "%d %" Xstr(IDLEN
) "s", &num
, id
) != 2 ||
338 num
<= 0 || num
> session
->count
) {
339 log_warning(LOG_PROTOCOL
, _("invalid UIDL response: %s\n"), buf
);
343 session
->msg
[num
].uidl
= g_strdup(id
);
345 recv_time
= (time_t)(GPOINTER_TO_INT(g_hash_table_lookup(
346 session
->uidl_table
, id
)));
347 session
->msg
[num
].recv_time
= recv_time
;
349 if (recv_time
!= RECV_TIME_NONE
) {
350 debug_print("num %d uidl %s: already got it\n", num
, id
);
352 debug_print("num %d uidl %s: unknown\n", num
, id
);
355 partial_recv
= (gint
)(GPOINTER_TO_INT(g_hash_table_lookup(
356 session
->partial_recv_table
, id
)));
358 if (recv_time
!= RECV_TIME_NONE
359 || partial_recv
!= POP3_TOTALLY_RECEIVED
) {
360 session
->msg
[num
].received
=
361 (partial_recv
!= POP3_MUST_COMPLETE_RECV
);
362 session
->msg
[num
].partial_recv
= partial_recv
;
363 if (partial_recv
== POP3_MUST_COMPLETE_RECV
)
364 session
->new_msg_exist
= TRUE
;
366 if (!session
->new_msg_exist
&&
367 (recv_time
== RECV_TIME_NONE
||
368 session
->ac_prefs
->rmmail
)) {
369 session
->cur_msg
= num
;
370 session
->new_msg_exist
= TRUE
;
374 session
->uidl_is_valid
= TRUE
;
378 static gint
pop3_getsize_list_send(Pop3Session
*session
)
380 session
->state
= POP3_GETSIZE_LIST
;
381 pop3_gen_send(session
, "LIST");
385 static gint
pop3_getsize_list_recv(Pop3Session
*session
, const gchar
*data
,
388 gchar buf
[POPBUFSIZE
];
391 const gchar
*p
= data
;
392 const gchar
*lastp
= data
+ len
;
393 const gchar
*newline
;
396 if ((newline
= memchr(p
, '\r', lastp
- p
)) == NULL
)
398 buf_len
= MIN(newline
- p
, sizeof(buf
) - 1);
399 memcpy(buf
, p
, buf_len
);
403 if (p
< lastp
&& *p
== '\n') p
++;
405 if (sscanf(buf
, "%u %u", &num
, &size
) != 2) {
406 session
->error_val
= PS_PROTOCOL
;
410 if (num
> 0 && num
<= session
->count
)
411 session
->msg
[num
].size
= size
;
412 if (num
> 0 && num
< session
->cur_msg
)
413 session
->cur_total_bytes
+= size
;
419 static gint
pop3_retr_send(Pop3Session
*session
)
421 session
->state
= POP3_RETR
;
422 debug_print("retrieving %d [%s]\n", session
->cur_msg
,
423 session
->msg
[session
->cur_msg
].uidl
?
424 session
->msg
[session
->cur_msg
].uidl
:" ");
425 pop3_gen_send(session
, "RETR %d", session
->cur_msg
);
429 static gint
pop3_retr_recv(Pop3Session
*session
, const gchar
*data
, guint len
)
433 MailReceiveData mail_receive_data
;
435 /* NOTE: we allocate a slightly larger buffer with a zero terminator
436 * because some plugins may think that it has a C string. */
437 mail_receive_data
.session
= session
;
438 mail_receive_data
.data
= g_new0(gchar
, len
+ 1);
439 mail_receive_data
.data_len
= len
;
440 memcpy(mail_receive_data
.data
, data
, len
);
442 hooks_invoke(MAIL_RECEIVE_HOOKLIST
, &mail_receive_data
);
444 file
= get_tmp_file();
445 if (pop3_write_msg_to_file(file
, mail_receive_data
.data
,
446 mail_receive_data
.data_len
, NULL
) < 0) {
448 g_free(mail_receive_data
.data
);
449 session
->error_val
= PS_IOERR
;
452 g_free(mail_receive_data
.data
);
454 if (session
->msg
[session
->cur_msg
].partial_recv
455 == POP3_MUST_COMPLETE_RECV
) {
456 gchar
*old_file
= partial_get_filename(
457 session
->ac_prefs
->recv_server
,
458 session
->ac_prefs
->userid
,
459 session
->msg
[session
->cur_msg
].uidl
);
462 partial_delete_old(old_file
);
467 /* drop_ok: 0: success 1: don't receive -1: error */
468 drop_ok
= session
->drop_message(session
, file
);
472 session
->error_val
= PS_IOERR
;
476 session
->cur_total_bytes
+= session
->msg
[session
->cur_msg
].size
;
477 session
->cur_total_recv_bytes
+= session
->msg
[session
->cur_msg
].size
;
478 session
->cur_total_num
++;
480 session
->msg
[session
->cur_msg
].received
= TRUE
;
481 session
->msg
[session
->cur_msg
].partial_recv
= POP3_TOTALLY_RECEIVED
;
483 session
->msg
[session
->cur_msg
].recv_time
=
484 drop_ok
== 1 ? RECV_TIME_KEEP
: session
->current_time
;
489 static gint
pop3_top_send(Pop3Session
*session
, gint max_size
)
491 gint num_lines
= (max_size
*1024)/82; /* consider lines to be 80 chars */
492 session
->state
= POP3_TOP
;
493 pop3_gen_send(session
, "TOP %d %d", session
->cur_msg
, num_lines
);
497 static gint
pop3_top_recv(Pop3Session
*session
, const gchar
*data
, guint len
)
501 MailReceiveData mail_receive_data
;
502 gchar
*partial_notice
= NULL
;
504 /* NOTE: we allocate a slightly larger buffer with a zero terminator
505 * because some plugins may think that it has a C string. */
506 mail_receive_data
.session
= session
;
507 mail_receive_data
.data
= g_new0(gchar
, len
+ 1);
508 mail_receive_data
.data_len
= len
;
509 memcpy(mail_receive_data
.data
, data
, len
);
511 hooks_invoke(MAIL_RECEIVE_HOOKLIST
, &mail_receive_data
);
513 partial_notice
= g_strdup_printf("SC-Marked-For-Download: 0\n"
514 "SC-Partially-Retrieved: %s\n"
515 "SC-Account-Server: %s\n"
516 "SC-Account-Login: %s\n"
517 "SC-Message-Size: %d",
518 session
->msg
[session
->cur_msg
].uidl
,
519 session
->ac_prefs
->recv_server
,
520 session
->ac_prefs
->userid
,
521 session
->msg
[session
->cur_msg
].size
);
522 file
= get_tmp_file();
523 if (pop3_write_msg_to_file(file
, mail_receive_data
.data
,
524 mail_receive_data
.data_len
,
525 partial_notice
) < 0) {
527 g_free(mail_receive_data
.data
);
528 session
->error_val
= PS_IOERR
;
529 g_free(partial_notice
);
532 g_free(mail_receive_data
.data
);
533 g_free(partial_notice
);
535 /* drop_ok: 0: success 1: don't receive -1: error */
536 drop_ok
= session
->drop_message(session
, file
);
539 session
->error_val
= PS_IOERR
;
543 session
->cur_total_bytes
+= session
->msg
[session
->cur_msg
].size
;
544 session
->cur_total_recv_bytes
+= session
->msg
[session
->cur_msg
].size
;
545 session
->cur_total_num
++;
547 session
->msg
[session
->cur_msg
].received
= TRUE
;
548 session
->msg
[session
->cur_msg
].partial_recv
= POP3_PARTIALLY_RECEIVED
;
549 session
->msg
[session
->cur_msg
].recv_time
=
550 drop_ok
== 1 ? RECV_TIME_KEEP
: session
->current_time
;
555 static gint
pop3_delete_send(Pop3Session
*session
)
557 session
->state
= POP3_DELETE
;
558 pop3_gen_send(session
, "DELE %d", session
->cur_msg
);
562 static gint
pop3_delete_recv(Pop3Session
*session
)
564 session
->msg
[session
->cur_msg
].deleted
= TRUE
;
568 static gint
pop3_logout_send(Pop3Session
*session
)
570 session
->state
= POP3_LOGOUT
;
571 pop3_gen_send(session
, "QUIT");
575 static void pop3_gen_send(Pop3Session
*session
, const gchar
*format
, ...)
577 gchar buf
[POPBUFSIZE
+ 1];
581 va_start(args
, format
);
582 length
= g_vsnprintf(buf
, sizeof(buf
) - 2, format
, args
);
584 if (length
> POPBUFSIZE
)
585 g_warning("POP buffer length overflow! (wanted to write = %d > %d = buf size)\n", length
, POPBUFSIZE
);
587 if (!g_ascii_strncasecmp(buf
, "PASS ", 5))
588 log_print(LOG_PROTOCOL
, "POP> PASS ********\n");
590 else if (!g_ascii_strncasecmp(buf
, "AUTH XOAUTH2", 12))
591 log_print(LOG_PROTOCOL
, "POP> AUTH XOAUTH2 ********\n");
593 else if (length
> 128)
594 log_print(LOG_PROTOCOL
, "POP> %.128s... (truncated from %d)\n", buf
, length
);
596 log_print(LOG_PROTOCOL
, "POP> %s\n", buf
);
598 session_send_msg(SESSION(session
), buf
);
601 Session
*pop3_session_new(PrefsAccount
*account
)
603 Pop3Session
*session
;
605 cm_return_val_if_fail(account
!= NULL
, NULL
);
607 account
->receive_in_progress
= TRUE
;
609 session
= g_new0(Pop3Session
, 1);
611 session_init(SESSION(session
), account
, FALSE
);
613 SESSION(session
)->type
= SESSION_POP3
;
615 SESSION(session
)->recv_msg
= pop3_session_recv_msg
;
616 SESSION(session
)->recv_data_finished
= pop3_session_recv_data_finished
;
617 SESSION(session
)->send_data_finished
= NULL
;
618 SESSION(session
)->ssl_cert_auto_accept
= account
->ssl_certs_auto_accept
;
619 SESSION(session
)->destroy
= pop3_session_destroy
;
622 if (account
->set_gnutls_priority
&& account
->gnutls_priority
&&
623 strlen(account
->gnutls_priority
) != 0)
624 SESSION(session
)->gnutls_priority
= g_strdup(account
->gnutls_priority
);
625 SESSION(session
)->use_tls_sni
= account
->use_tls_sni
;
628 session
->state
= POP3_READY
;
629 session
->ac_prefs
= account
;
630 session
->pop_before_smtp
= FALSE
;
631 pop3_get_uidl_table(account
, session
);
632 session
->current_time
= time(NULL
);
633 session
->error_val
= PS_SUCCESS
;
634 session
->error_msg
= NULL
;
636 return SESSION(session
);
639 static void pop3_session_destroy(Session
*session
)
641 Pop3Session
*pop3_session
= POP3_SESSION(session
);
644 cm_return_if_fail(session
!= NULL
);
646 for (n
= 1; n
<= pop3_session
->count
; n
++)
647 g_free(pop3_session
->msg
[n
].uidl
);
648 g_free(pop3_session
->msg
);
650 if (pop3_session
->uidl_table
) {
651 hash_free_strings(pop3_session
->uidl_table
);
652 g_hash_table_destroy(pop3_session
->uidl_table
);
655 if (pop3_session
->partial_recv_table
) {
656 hash_free_strings(pop3_session
->partial_recv_table
);
657 g_hash_table_destroy(pop3_session
->partial_recv_table
);
660 g_free(pop3_session
->greeting
);
661 g_free(pop3_session
->user
);
662 g_free(pop3_session
->pass
);
663 g_free(pop3_session
->error_msg
);
665 pop3_session
->ac_prefs
->receive_in_progress
= FALSE
;
668 static void pop3_get_uidl_table(PrefsAccount
*ac_prefs
, Pop3Session
*session
)
671 GHashTable
*partial_recv_table
;
674 gchar buf
[POPBUFSIZE
];
675 gchar uidl
[POPBUFSIZE
];
679 gchar
*sanitized_uid
= g_strdup(ac_prefs
->userid
);
681 subst_for_filename(sanitized_uid
);
683 table
= g_hash_table_new(g_str_hash
, g_str_equal
);
684 partial_recv_table
= g_hash_table_new(g_str_hash
, g_str_equal
);
686 path
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
687 "uidl", G_DIR_SEPARATOR_S
, ac_prefs
->recv_server
,
688 "-", sanitized_uid
, NULL
);
690 g_free(sanitized_uid
);
691 if ((fp
= claws_fopen(path
, "rb")) == NULL
) {
692 if (ENOENT
!= errno
) FILE_OP_ERROR(path
, "claws_fopen");
694 path
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
695 "uidl-", ac_prefs
->recv_server
,
696 "-", ac_prefs
->userid
, NULL
);
697 if ((fp
= claws_fopen(path
, "rb")) == NULL
) {
698 if (ENOENT
!= errno
) FILE_OP_ERROR(path
, "claws_fopen");
700 session
->uidl_table
= table
;
701 session
->partial_recv_table
= partial_recv_table
;
709 while (claws_fgets(buf
, sizeof(buf
), fp
) != NULL
) {
710 gchar tmp
[POPBUFSIZE
];
712 recv_time
= RECV_TIME_NONE
;
713 partial_recv
= POP3_TOTALLY_RECEIVED
;
715 if (sscanf(buf
, "%s\t%ld\t%s", uidl
, (long int *) &recv_time
, tmp
) < 3) {
716 if (sscanf(buf
, "%s\t%ld", uidl
, (long int *) &recv_time
) != 2) {
717 if (sscanf(buf
, "%s", uidl
) != 1)
728 if (recv_time
== RECV_TIME_NONE
)
729 recv_time
= RECV_TIME_RECEIVED
;
730 g_hash_table_insert(table
, g_strdup(uidl
),
731 GINT_TO_POINTER(recv_time
));
732 if (strlen(tmp
) == 1)
733 partial_recv
= atoi(tmp
); /* totally received ?*/
735 partial_recv
= POP3_MUST_COMPLETE_RECV
;
737 g_hash_table_insert(partial_recv_table
, g_strdup(uidl
),
738 GINT_TO_POINTER(partial_recv
));
742 session
->uidl_table
= table
;
743 session
->partial_recv_table
= partial_recv_table
;
751 g_warning("failed to write"); \
755 gint pop3_write_uidl_list(Pop3Session *session)
757 gchar
*path
, *tmp_path
;
761 gchar
*sanitized_uid
= g_strdup(session
->ac_prefs
->userid
);
763 subst_for_filename(sanitized_uid
);
765 if (!session
->uidl_is_valid
) {
766 g_free(sanitized_uid
);
770 path
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
771 "uidl", G_DIR_SEPARATOR_S
,
772 session
->ac_prefs
->recv_server
,
773 "-", sanitized_uid
, NULL
);
774 tmp_path
= g_strconcat(path
, ".tmp", NULL
);
776 g_free(sanitized_uid
);
778 if ((fp
= claws_fopen(tmp_path
, "wb")) == NULL
) {
779 FILE_OP_ERROR(tmp_path
, "claws_fopen");
783 for (n
= 1; n
<= session
->count
; n
++) {
784 msg
= &session
->msg
[n
];
785 if (msg
->uidl
&& msg
->received
&&
786 (!msg
->deleted
|| session
->state
!= POP3_DONE
))
787 TRY(fprintf(fp
, "%s\t%ld\t%d\n",
788 msg
->uidl
, (long int)
794 if (claws_safe_fclose(fp
) == EOF
) {
795 FILE_OP_ERROR(tmp_path
, "claws_fclose");
803 if (g_rename(tmp_path
, path
) < 0) {
804 FILE_OP_ERROR(path
, "rename");
820 static gint
pop3_write_msg_to_file(const gchar
*file
, const gchar
*data
,
821 guint len
, const gchar
*prefix
)
824 const gchar
*prev
, *cur
;
826 cm_return_val_if_fail(file
!= NULL
, -1);
828 if ((fp
= claws_fopen(file
, "wb")) == NULL
) {
829 FILE_OP_ERROR(file
, "claws_fopen");
833 if (change_file_mode_rw(fp
, file
) < 0)
834 FILE_OP_ERROR(file
, "chmod");
836 if (prefix
!= NULL
) {
837 if (fprintf(fp
, "%s\n", prefix
) < 0) {
838 FILE_OP_ERROR(file
, "fprintf");
845 /* +------------------+----------------+--------------------------+ *
846 * ^data ^prev ^cur data+len-1^ */
849 while ((cur
= (gchar
*)my_memmem(prev
, len
- (prev
- data
), "\r\n", 2))
851 if ((cur
> prev
&& claws_fwrite(prev
, 1, cur
- prev
, fp
) < 1) ||
852 claws_fputc('\n', fp
) == EOF
) {
853 FILE_OP_ERROR(file
, "claws_fwrite");
854 g_warning("can't write to file: %s", file
);
860 if (cur
== data
+ len
- 1) {
865 if (*(cur
+ 1) == '\n')
870 if (prev
- data
< len
- 1 && *prev
== '.' && *(prev
+ 1) == '.')
873 if (prev
- data
>= len
)
877 if (prev
- data
< len
&&
878 claws_fwrite(prev
, 1, len
- (prev
- data
), fp
) < 1) {
879 FILE_OP_ERROR(file
, "claws_fwrite");
880 g_warning("can't write to file: %s", file
);
885 if (data
[len
- 1] != '\r' && data
[len
- 1] != '\n') {
886 if (claws_fputc('\n', fp
) == EOF
) {
887 FILE_OP_ERROR(file
, "claws_fputc");
888 g_warning("can't write to file: %s", file
);
895 if (claws_safe_fclose(fp
) == EOF
) {
896 FILE_OP_ERROR(file
, "claws_fclose");
904 static Pop3State
pop3_lookup_next(Pop3Session
*session
)
907 PrefsAccount
*ac
= session
->ac_prefs
;
909 gboolean size_limit_over
;
912 msg
= &session
->msg
[session
->cur_msg
];
915 (ac
->enable_size_limit
&&
916 ac
->size_limit
> 0 &&
917 size
> ac
->size_limit
* 1024);
920 msg
->recv_time
!= RECV_TIME_NONE
&&
921 msg
->recv_time
!= RECV_TIME_KEEP
&&
922 msg
->partial_recv
== POP3_TOTALLY_RECEIVED
&&
923 session
->current_time
- msg
->recv_time
>=
924 ((ac
->msg_leave_time
* 24 * 60 * 60) +
925 (ac
->msg_leave_hour
* 60 * 60))) {
926 log_message(LOG_PROTOCOL
,
927 _("POP: Deleting expired message %d [%s]\n"),
928 session
->cur_msg
, msg
->uidl
?msg
->uidl
:" ");
929 session
->cur_total_bytes
+= size
;
930 pop3_delete_send(session
);
934 if (size_limit_over
) {
935 if (!msg
->received
&& msg
->partial_recv
!=
936 POP3_MUST_COMPLETE_RECV
) {
937 pop3_top_send(session
, ac
->size_limit
);
939 } else if (msg
->partial_recv
== POP3_MUST_COMPLETE_RECV
)
942 log_message(LOG_PROTOCOL
,
943 _("POP: Skipping message %d [%s] (%d bytes)\n"),
944 session
->cur_msg
, msg
->uidl
?msg
->uidl
:" ", size
);
947 if (size
== 0 || msg
->received
|| size_limit_over
) {
948 session
->cur_total_bytes
+= size
;
949 if (session
->cur_msg
== session
->count
) {
950 pop3_logout_send(session
);
958 pop3_retr_send(session
);
962 static Pop3ErrorValue
pop3_ok(Pop3Session
*session
, const gchar
*msg
)
966 log_print(LOG_PROTOCOL
, "POP< %s\n", msg
);
968 /* exchange replies '+' in response to first line of auth xoauth */
969 if (!strncmp(msg
, "+OK", 3) || !strncmp(msg
, "+", 1))
971 else if (!strncmp(msg
, "-ERR", 4)) {
972 if (strstr(msg
+ 4, "lock") ||
973 strstr(msg
+ 4, "Lock") ||
974 strstr(msg
+ 4, "LOCK") ||
975 strstr(msg
+ 4, "wait")) {
976 log_error(LOG_PROTOCOL
, _("mailbox is locked\n"));
978 } else if (strcasestr(msg
+ 4, "timeout")) {
979 log_error(LOG_PROTOCOL
, _("Session timeout\n"));
982 switch (session
->state
) {
985 log_error(LOG_PROTOCOL
, _("couldn't start STARTTLS session\n"));
989 case POP3_GETAUTH_USER
:
990 case POP3_GETAUTH_PASS
:
991 case POP3_GETAUTH_APOP
:
992 log_error(LOG_PROTOCOL
, _("error occurred on authentication\n"));
995 case POP3_GETRANGE_LAST
:
996 case POP3_GETRANGE_UIDL
:
998 log_warning(LOG_PROTOCOL
, _("command not supported\n"));
999 ok
= PS_NOTSUPPORTED
;
1003 log_error(LOG_PROTOCOL
, _("error occurred on POP session\n"));
1008 g_free(session
->error_msg
);
1009 session
->error_msg
= g_strdup(msg
);
1010 g_printerr("POP: %s\n", msg
);
1014 session
->error_val
= ok
;
1018 static gint
pop3_session_recv_msg(Session
*session
, const gchar
*msg
)
1020 Pop3Session
*pop3_session
= POP3_SESSION(session
);
1021 Pop3ErrorValue val
= PS_SUCCESS
;
1025 if (pop3_session
->state
!= POP3_GETRANGE_UIDL_RECV
&&
1026 pop3_session
->state
!= POP3_GETSIZE_LIST_RECV
) {
1027 val
= pop3_ok(pop3_session
, msg
);
1028 if (val
!= PS_SUCCESS
) {
1029 if (val
!= PS_NOTSUPPORTED
) {
1030 pop3_session
->state
= POP3_ERROR
;
1035 if (*body
== '+' || *body
== '-')
1037 while (g_ascii_isalpha(*body
))
1039 while (g_ascii_isspace(*body
))
1043 switch (pop3_session
->state
) {
1046 pop3_greeting_recv(pop3_session
, body
);
1048 if (pop3_session
->ac_prefs
->ssl_pop
== SSL_STARTTLS
)
1049 val
= pop3_stls_send(pop3_session
);
1053 if (pop3_session
->ac_prefs
->use_pop_auth
&& pop3_session
->ac_prefs
->pop_auth_type
== POPAUTH_OAUTH2
)
1054 val
= pop3_getauth_oauth2_send(pop3_session
);
1057 if (pop3_session
->ac_prefs
->use_pop_auth
&& pop3_session
->ac_prefs
->pop_auth_type
== POPAUTH_APOP
)
1058 val
= pop3_getauth_apop_send(pop3_session
);
1060 val
= pop3_getauth_user_send(pop3_session
);
1064 if (pop3_stls_recv(pop3_session
) != PS_SUCCESS
)
1066 if (pop3_session
->ac_prefs
->use_pop_auth
&& pop3_session
->ac_prefs
->pop_auth_type
== POPAUTH_APOP
)
1067 val
= pop3_getauth_apop_send(pop3_session
);
1069 else if (pop3_session
->ac_prefs
->use_pop_auth
&& pop3_session
->ac_prefs
->pop_auth_type
== POPAUTH_OAUTH2
)
1070 val
= pop3_getauth_oauth2_send(pop3_session
);
1073 val
= pop3_getauth_user_send(pop3_session
);
1076 case POP3_GETAUTH_USER
:
1077 val
= pop3_getauth_pass_send(pop3_session
);
1080 case POP3_GETAUTH_USER_PHASE2
:
1081 val
= pop3_getauth_oauth2_send_microsoft_2(pop3_session
);
1084 case POP3_GETAUTH_PASS
:
1085 case POP3_GETAUTH_APOP
:
1087 case POP3_GETAUTH_OAUTH2
:
1089 if (!pop3_session
->pop_before_smtp
)
1090 val
= pop3_getrange_stat_send(pop3_session
);
1092 val
= pop3_logout_send(pop3_session
);
1094 case POP3_GETRANGE_STAT
:
1095 if (pop3_getrange_stat_recv(pop3_session
, body
) < 0)
1097 if (pop3_session
->count
> 0)
1098 val
= pop3_getrange_uidl_send(pop3_session
);
1100 val
= pop3_logout_send(pop3_session
);
1102 case POP3_GETRANGE_LAST
:
1103 if (val
== PS_NOTSUPPORTED
)
1104 pop3_session
->error_val
= PS_SUCCESS
;
1105 else if (pop3_getrange_last_recv(pop3_session
, body
) < 0)
1107 if (pop3_session
->cur_msg
> 0)
1108 val
= pop3_getsize_list_send(pop3_session
);
1110 val
= pop3_logout_send(pop3_session
);
1112 case POP3_GETRANGE_UIDL
:
1113 if (val
== PS_NOTSUPPORTED
) {
1114 pop3_session
->error_val
= PS_SUCCESS
;
1115 val
= pop3_getrange_last_send(pop3_session
);
1117 pop3_session
->state
= POP3_GETRANGE_UIDL_RECV
;
1118 session_recv_data(session
, 0, ".\r\n");
1121 case POP3_GETSIZE_LIST
:
1122 pop3_session
->state
= POP3_GETSIZE_LIST_RECV
;
1123 session_recv_data(session
, 0, ".\r\n");
1126 pop3_session
->state
= POP3_RETR_RECV
;
1127 session_recv_data(session
, 0, ".\r\n");
1130 if (val
== PS_NOTSUPPORTED
) {
1131 pop3_session
->error_val
= PS_SUCCESS
;
1133 pop3_session
->state
= POP3_TOP_RECV
;
1134 session_recv_data(session
, 0, ".\r\n");
1138 pop3_delete_recv(pop3_session
);
1139 if (pop3_session
->cur_msg
== pop3_session
->count
)
1140 val
= pop3_logout_send(pop3_session
);
1142 pop3_session
->cur_msg
++;
1143 if (pop3_lookup_next(pop3_session
) == POP3_ERROR
)
1148 pop3_session
->state
= POP3_DONE
;
1149 session_disconnect(session
);
1156 return val
== PS_SUCCESS
?0:-1;
1159 static gint
pop3_session_recv_data_finished(Session
*session
, guchar
*data
,
1162 Pop3Session
*pop3_session
= POP3_SESSION(session
);
1163 Pop3ErrorValue val
= PS_SUCCESS
;
1165 switch (pop3_session
->state
) {
1166 case POP3_GETRANGE_UIDL_RECV
:
1167 val
= pop3_getrange_uidl_recv(pop3_session
, data
, len
);
1168 if (val
== PS_SUCCESS
) {
1169 if (pop3_session
->new_msg_exist
)
1170 pop3_getsize_list_send(pop3_session
);
1172 pop3_logout_send(pop3_session
);
1176 case POP3_GETSIZE_LIST_RECV
:
1177 val
= pop3_getsize_list_recv(pop3_session
, data
, len
);
1178 if (val
== PS_SUCCESS
) {
1179 if (pop3_lookup_next(pop3_session
) == POP3_ERROR
)
1184 case POP3_RETR_RECV
:
1185 if (pop3_retr_recv(pop3_session
, data
, len
) < 0)
1188 if (pop3_session
->ac_prefs
->rmmail
&&
1189 pop3_session
->ac_prefs
->msg_leave_time
== 0 &&
1190 pop3_session
->ac_prefs
->msg_leave_hour
== 0 &&
1191 pop3_session
->msg
[pop3_session
->cur_msg
].recv_time
1193 pop3_delete_send(pop3_session
);
1194 else if (pop3_session
->cur_msg
== pop3_session
->count
)
1195 pop3_logout_send(pop3_session
);
1197 pop3_session
->cur_msg
++;
1198 if (pop3_lookup_next(pop3_session
) == POP3_ERROR
)
1203 if (pop3_top_recv(pop3_session
, data
, len
) < 0)
1206 if (pop3_session
->cur_msg
== pop3_session
->count
)
1207 pop3_logout_send(pop3_session
);
1209 pop3_session
->cur_msg
++;
1210 if (pop3_lookup_next(pop3_session
) == POP3_ERROR
)
1215 log_warning(LOG_PROTOCOL
, _("TOP command unsupported\n"));
1216 if (pop3_session
->cur_msg
== pop3_session
->count
)
1217 pop3_logout_send(pop3_session
);
1219 pop3_session
->cur_msg
++;
1220 if (pop3_lookup_next(pop3_session
) == POP3_ERROR
)