add Greek translation, by Χάρης Καραχριστιανίδης
[claws.git] / src / pop.c
blob2b8c70a6db00d1b5b528f0945dc0c332ed4944bf
1 /*
2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2012 Hiroyuki Yamamoto and the Claws Mail team
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 static gint pop3_getrange_stat_send(Pop3Session *session)
183 session->state = POP3_GETRANGE_STAT;
184 pop3_gen_send(session, "STAT");
185 return PS_SUCCESS;
188 static gint pop3_getrange_stat_recv(Pop3Session *session, const gchar *msg)
190 if (sscanf(msg, "%d %d", &session->count, &session->total_bytes) != 2) {
191 log_error(LOG_PROTOCOL, _("POP protocol error\n"));
192 session->error_val = PS_PROTOCOL;
193 return -1;
194 } else {
195 if (session->count == 0) {
196 session->uidl_is_valid = TRUE;
197 } else {
198 session->msg = g_new0(Pop3MsgInfo, session->count + 1);
199 session->cur_msg = 1;
203 return PS_SUCCESS;
206 static gint pop3_getrange_last_send(Pop3Session *session)
208 session->state = POP3_GETRANGE_LAST;
209 pop3_gen_send(session, "LAST");
210 return PS_SUCCESS;
213 static gint pop3_getrange_last_recv(Pop3Session *session, const gchar *msg)
215 gint last;
217 if (sscanf(msg, "%d", &last) == 0) {
218 log_warning(LOG_PROTOCOL, _("POP protocol error\n"));
219 session->error_val = PS_PROTOCOL;
220 return -1;
221 } else {
222 if (session->count > last) {
223 session->new_msg_exist = TRUE;
224 session->cur_msg = last + 1;
225 } else
226 session->cur_msg = 0;
229 return PS_SUCCESS;
232 static gint pop3_getrange_uidl_send(Pop3Session *session)
234 session->state = POP3_GETRANGE_UIDL;
235 pop3_gen_send(session, "UIDL");
236 return PS_SUCCESS;
239 static gint pop3_getrange_uidl_recv(Pop3Session *session, const gchar *data,
240 guint len)
242 gchar id[IDLEN + 1];
243 gchar buf[POPBUFSIZE];
244 gint buf_len;
245 guint32 num;
246 time_t recv_time;
247 gint partial_recv;
248 const gchar *p = data;
249 const gchar *lastp = data + len;
250 const gchar *newline;
252 while (p < lastp) {
253 if ((newline = memchr(p, '\r', lastp - p)) == NULL)
254 return -1;
255 buf_len = MIN(newline - p, sizeof(buf) - 1);
256 memcpy(buf, p, buf_len);
257 buf[buf_len] = '\0';
259 p = newline + 1;
260 if (p < lastp && *p == '\n') p++;
262 if (sscanf(buf, "%d %" Xstr(IDLEN) "s", &num, id) != 2 ||
263 num <= 0 || num > session->count) {
264 log_warning(LOG_PROTOCOL, _("invalid UIDL response: %s\n"), buf);
265 continue;
268 session->msg[num].uidl = g_strdup(id);
270 recv_time = (time_t)(GPOINTER_TO_INT(g_hash_table_lookup(
271 session->uidl_table, id)));
272 session->msg[num].recv_time = recv_time;
274 if (recv_time != RECV_TIME_NONE) {
275 debug_print("num %d uidl %s: already got it\n", num, id);
276 } else {
277 debug_print("num %d uidl %s: unknown\n", num, id);
280 partial_recv = (gint)(GPOINTER_TO_INT(g_hash_table_lookup(
281 session->partial_recv_table, id)));
283 if (recv_time != RECV_TIME_NONE
284 || partial_recv != POP3_TOTALLY_RECEIVED) {
285 session->msg[num].received =
286 (partial_recv != POP3_MUST_COMPLETE_RECV);
287 session->msg[num].partial_recv = partial_recv;
288 if (partial_recv == POP3_MUST_COMPLETE_RECV)
289 session->new_msg_exist = TRUE;
291 if (!session->new_msg_exist &&
292 (recv_time == RECV_TIME_NONE ||
293 session->ac_prefs->rmmail)) {
294 session->cur_msg = num;
295 session->new_msg_exist = TRUE;
299 session->uidl_is_valid = TRUE;
300 return PS_SUCCESS;
303 static gint pop3_getsize_list_send(Pop3Session *session)
305 session->state = POP3_GETSIZE_LIST;
306 pop3_gen_send(session, "LIST");
307 return PS_SUCCESS;
310 static gint pop3_getsize_list_recv(Pop3Session *session, const gchar *data,
311 guint len)
313 gchar buf[POPBUFSIZE];
314 gint buf_len;
315 guint num, size;
316 const gchar *p = data;
317 const gchar *lastp = data + len;
318 const gchar *newline;
320 while (p < lastp) {
321 if ((newline = memchr(p, '\r', lastp - p)) == NULL)
322 return -1;
323 buf_len = MIN(newline - p, sizeof(buf) - 1);
324 memcpy(buf, p, buf_len);
325 buf[buf_len] = '\0';
327 p = newline + 1;
328 if (p < lastp && *p == '\n') p++;
330 if (sscanf(buf, "%u %u", &num, &size) != 2) {
331 session->error_val = PS_PROTOCOL;
332 return -1;
335 if (num > 0 && num <= session->count)
336 session->msg[num].size = size;
337 if (num > 0 && num < session->cur_msg)
338 session->cur_total_bytes += size;
341 return PS_SUCCESS;
344 static gint pop3_retr_send(Pop3Session *session)
346 session->state = POP3_RETR;
347 debug_print("retrieving %d [%s]\n", session->cur_msg,
348 session->msg[session->cur_msg].uidl ?
349 session->msg[session->cur_msg].uidl:" ");
350 pop3_gen_send(session, "RETR %d", session->cur_msg);
351 return PS_SUCCESS;
354 static gint pop3_retr_recv(Pop3Session *session, const gchar *data, guint len)
356 gchar *file;
357 gint drop_ok;
358 MailReceiveData mail_receive_data;
360 /* NOTE: we allocate a slightly larger buffer with a zero terminator
361 * because some plugins may think that it has a C string. */
362 mail_receive_data.session = session;
363 mail_receive_data.data = g_new0(gchar, len + 1);
364 mail_receive_data.data_len = len;
365 memcpy(mail_receive_data.data, data, len);
367 hooks_invoke(MAIL_RECEIVE_HOOKLIST, &mail_receive_data);
369 file = get_tmp_file();
370 if (pop3_write_msg_to_file(file, mail_receive_data.data,
371 mail_receive_data.data_len, NULL) < 0) {
372 g_free(file);
373 g_free(mail_receive_data.data);
374 session->error_val = PS_IOERR;
375 return -1;
377 g_free(mail_receive_data.data);
379 if (session->msg[session->cur_msg].partial_recv
380 == POP3_MUST_COMPLETE_RECV) {
381 gchar *old_file = partial_get_filename(
382 session->ac_prefs->recv_server,
383 session->ac_prefs->userid,
384 session->msg[session->cur_msg].uidl);
386 if (old_file) {
387 partial_delete_old(old_file);
388 g_free(old_file);
392 /* drop_ok: 0: success 1: don't receive -1: error */
393 drop_ok = session->drop_message(session, file);
395 g_free(file);
396 if (drop_ok < 0) {
397 session->error_val = PS_IOERR;
398 return -1;
401 session->cur_total_bytes += session->msg[session->cur_msg].size;
402 session->cur_total_recv_bytes += session->msg[session->cur_msg].size;
403 session->cur_total_num++;
405 session->msg[session->cur_msg].received = TRUE;
406 session->msg[session->cur_msg].partial_recv = POP3_TOTALLY_RECEIVED;
408 session->msg[session->cur_msg].recv_time =
409 drop_ok == 1 ? RECV_TIME_KEEP : session->current_time;
411 return PS_SUCCESS;
414 static gint pop3_top_send(Pop3Session *session, gint max_size)
416 gint num_lines = (max_size*1024)/82; /* consider lines to be 80 chars */
417 session->state = POP3_TOP;
418 pop3_gen_send(session, "TOP %d %d", session->cur_msg, num_lines);
419 return PS_SUCCESS;
422 static gint pop3_top_recv(Pop3Session *session, const gchar *data, guint len)
424 gchar *file;
425 gint drop_ok;
426 MailReceiveData mail_receive_data;
427 gchar *partial_notice = NULL;
429 /* NOTE: we allocate a slightly larger buffer with a zero terminator
430 * because some plugins may think that it has a C string. */
431 mail_receive_data.session = session;
432 mail_receive_data.data = g_new0(gchar, len + 1);
433 mail_receive_data.data_len = len;
434 memcpy(mail_receive_data.data, data, len);
436 hooks_invoke(MAIL_RECEIVE_HOOKLIST, &mail_receive_data);
438 partial_notice = g_strdup_printf("SC-Marked-For-Download: 0\n"
439 "SC-Partially-Retrieved: %s\n"
440 "SC-Account-Server: %s\n"
441 "SC-Account-Login: %s\n"
442 "SC-Message-Size: %d",
443 session->msg[session->cur_msg].uidl,
444 session->ac_prefs->recv_server,
445 session->ac_prefs->userid,
446 session->msg[session->cur_msg].size);
447 file = get_tmp_file();
448 if (pop3_write_msg_to_file(file, mail_receive_data.data,
449 mail_receive_data.data_len,
450 partial_notice) < 0) {
451 g_free(file);
452 g_free(mail_receive_data.data);
453 session->error_val = PS_IOERR;
454 g_free(partial_notice);
455 return -1;
457 g_free(mail_receive_data.data);
458 g_free(partial_notice);
460 /* drop_ok: 0: success 1: don't receive -1: error */
461 drop_ok = session->drop_message(session, file);
462 g_free(file);
463 if (drop_ok < 0) {
464 session->error_val = PS_IOERR;
465 return -1;
468 session->cur_total_bytes += session->msg[session->cur_msg].size;
469 session->cur_total_recv_bytes += session->msg[session->cur_msg].size;
470 session->cur_total_num++;
472 session->msg[session->cur_msg].received = TRUE;
473 session->msg[session->cur_msg].partial_recv = POP3_PARTIALLY_RECEIVED;
474 session->msg[session->cur_msg].recv_time =
475 drop_ok == 1 ? RECV_TIME_KEEP : session->current_time;
477 return PS_SUCCESS;
480 static gint pop3_delete_send(Pop3Session *session)
482 session->state = POP3_DELETE;
483 pop3_gen_send(session, "DELE %d", session->cur_msg);
484 return PS_SUCCESS;
487 static gint pop3_delete_recv(Pop3Session *session)
489 session->msg[session->cur_msg].deleted = TRUE;
490 return PS_SUCCESS;
493 static gint pop3_logout_send(Pop3Session *session)
495 session->state = POP3_LOGOUT;
496 pop3_gen_send(session, "QUIT");
497 return PS_SUCCESS;
500 static void pop3_gen_send(Pop3Session *session, const gchar *format, ...)
502 gchar buf[POPBUFSIZE + 1];
503 va_list args;
505 va_start(args, format);
506 g_vsnprintf(buf, sizeof(buf) - 2, format, args);
507 va_end(args);
509 if (!g_ascii_strncasecmp(buf, "PASS ", 5))
510 log_print(LOG_PROTOCOL, "POP> PASS ********\n");
511 else
512 log_print(LOG_PROTOCOL, "POP> %s\n", buf);
514 session_send_msg(SESSION(session), buf);
517 Session *pop3_session_new(PrefsAccount *account)
519 Pop3Session *session;
521 cm_return_val_if_fail(account != NULL, NULL);
523 account->receive_in_progress = TRUE;
525 session = g_new0(Pop3Session, 1);
527 session_init(SESSION(session), account, FALSE);
529 SESSION(session)->type = SESSION_POP3;
531 SESSION(session)->recv_msg = pop3_session_recv_msg;
532 SESSION(session)->recv_data_finished = pop3_session_recv_data_finished;
533 SESSION(session)->send_data_finished = NULL;
534 SESSION(session)->ssl_cert_auto_accept = account->ssl_certs_auto_accept;
535 SESSION(session)->destroy = pop3_session_destroy;
537 #ifdef USE_GNUTLS
538 if (account->set_gnutls_priority && account->gnutls_priority &&
539 strlen(account->gnutls_priority) != 0)
540 SESSION(session)->gnutls_priority = g_strdup(account->gnutls_priority);
541 SESSION(session)->use_tls_sni = account->use_tls_sni;
542 #endif
544 session->state = POP3_READY;
545 session->ac_prefs = account;
546 session->pop_before_smtp = FALSE;
547 pop3_get_uidl_table(account, session);
548 session->current_time = time(NULL);
549 session->error_val = PS_SUCCESS;
550 session->error_msg = NULL;
552 return SESSION(session);
555 static void pop3_session_destroy(Session *session)
557 Pop3Session *pop3_session = POP3_SESSION(session);
558 gint n;
560 cm_return_if_fail(session != NULL);
562 for (n = 1; n <= pop3_session->count; n++)
563 g_free(pop3_session->msg[n].uidl);
564 g_free(pop3_session->msg);
566 if (pop3_session->uidl_table) {
567 hash_free_strings(pop3_session->uidl_table);
568 g_hash_table_destroy(pop3_session->uidl_table);
571 if (pop3_session->partial_recv_table) {
572 hash_free_strings(pop3_session->partial_recv_table);
573 g_hash_table_destroy(pop3_session->partial_recv_table);
576 g_free(pop3_session->greeting);
577 g_free(pop3_session->user);
578 g_free(pop3_session->pass);
579 g_free(pop3_session->error_msg);
581 pop3_session->ac_prefs->receive_in_progress = FALSE;
584 static void pop3_get_uidl_table(PrefsAccount *ac_prefs, Pop3Session *session)
586 GHashTable *table;
587 GHashTable *partial_recv_table;
588 gchar *path;
589 FILE *fp;
590 gchar buf[POPBUFSIZE];
591 gchar uidl[POPBUFSIZE];
592 time_t recv_time;
593 time_t now;
594 gint partial_recv;
595 gchar *sanitized_uid = g_strdup(ac_prefs->userid);
597 subst_for_filename(sanitized_uid);
599 table = g_hash_table_new(g_str_hash, g_str_equal);
600 partial_recv_table = g_hash_table_new(g_str_hash, g_str_equal);
602 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
603 "uidl", G_DIR_SEPARATOR_S, ac_prefs->recv_server,
604 "-", sanitized_uid, NULL);
606 g_free(sanitized_uid);
607 if ((fp = claws_fopen(path, "rb")) == NULL) {
608 if (ENOENT != errno) FILE_OP_ERROR(path, "claws_fopen");
609 g_free(path);
610 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
611 "uidl-", ac_prefs->recv_server,
612 "-", ac_prefs->userid, NULL);
613 if ((fp = claws_fopen(path, "rb")) == NULL) {
614 if (ENOENT != errno) FILE_OP_ERROR(path, "claws_fopen");
615 g_free(path);
616 session->uidl_table = table;
617 session->partial_recv_table = partial_recv_table;
618 return;
621 g_free(path);
623 now = time(NULL);
625 while (claws_fgets(buf, sizeof(buf), fp) != NULL) {
626 gchar tmp[POPBUFSIZE];
627 strretchomp(buf);
628 recv_time = RECV_TIME_NONE;
629 partial_recv = POP3_TOTALLY_RECEIVED;
631 if (sscanf(buf, "%s\t%ld\t%s", uidl, (long int *) &recv_time, tmp) < 3) {
632 if (sscanf(buf, "%s\t%ld", uidl, (long int *) &recv_time) != 2) {
633 if (sscanf(buf, "%s", uidl) != 1)
634 continue;
635 else {
636 recv_time = now;
637 strcpy(tmp, "0");
639 } else {
640 strcpy(tmp, "0");
644 if (recv_time == RECV_TIME_NONE)
645 recv_time = RECV_TIME_RECEIVED;
646 g_hash_table_insert(table, g_strdup(uidl),
647 GINT_TO_POINTER(recv_time));
648 if (strlen(tmp) == 1)
649 partial_recv = atoi(tmp); /* totally received ?*/
650 else
651 partial_recv = POP3_MUST_COMPLETE_RECV;
653 g_hash_table_insert(partial_recv_table, g_strdup(uidl),
654 GINT_TO_POINTER(partial_recv));
657 claws_fclose(fp);
658 session->uidl_table = table;
659 session->partial_recv_table = partial_recv_table;
661 return;
664 #define TRY(func) \
665 if (!(func)) \
667 g_warning("failed to write"); \
668 goto err_write; \
671 gint pop3_write_uidl_list(Pop3Session *session)
673 gchar *path, *tmp_path;
674 FILE *fp;
675 Pop3MsgInfo *msg;
676 gint n;
677 gchar *sanitized_uid = g_strdup(session->ac_prefs->userid);
679 subst_for_filename(sanitized_uid);
681 if (!session->uidl_is_valid) {
682 g_free(sanitized_uid);
683 return 0;
686 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
687 "uidl", G_DIR_SEPARATOR_S,
688 session->ac_prefs->recv_server,
689 "-", sanitized_uid, NULL);
690 tmp_path = g_strconcat(path, ".tmp", NULL);
692 g_free(sanitized_uid);
694 if ((fp = claws_fopen(tmp_path, "wb")) == NULL) {
695 FILE_OP_ERROR(tmp_path, "claws_fopen");
696 goto err_write;
699 for (n = 1; n <= session->count; n++) {
700 msg = &session->msg[n];
701 if (msg->uidl && msg->received &&
702 (!msg->deleted || session->state != POP3_DONE))
703 TRY(fprintf(fp, "%s\t%ld\t%d\n",
704 msg->uidl, (long int)
705 msg->recv_time,
706 msg->partial_recv)
707 > 0);
710 if (claws_safe_fclose(fp) == EOF) {
711 FILE_OP_ERROR(tmp_path, "claws_fclose");
712 fp = NULL;
713 goto err_write;
715 fp = NULL;
716 #ifdef G_OS_WIN32
717 claws_unlink(path);
718 #endif
719 if (g_rename(tmp_path, path) < 0) {
720 FILE_OP_ERROR(path, "rename");
721 goto err_write;
723 g_free(path);
724 g_free(tmp_path);
725 return 0;
726 err_write:
727 if (fp)
728 claws_fclose(fp);
729 g_free(path);
730 g_free(tmp_path);
731 return -1;
734 #undef TRY
736 static gint pop3_write_msg_to_file(const gchar *file, const gchar *data,
737 guint len, const gchar *prefix)
739 FILE *fp;
740 const gchar *prev, *cur;
742 cm_return_val_if_fail(file != NULL, -1);
744 if ((fp = claws_fopen(file, "wb")) == NULL) {
745 FILE_OP_ERROR(file, "claws_fopen");
746 return -1;
749 if (change_file_mode_rw(fp, file) < 0)
750 FILE_OP_ERROR(file, "chmod");
752 if (prefix != NULL) {
753 if (fprintf(fp, "%s\n", prefix) < 0) {
754 FILE_OP_ERROR(file, "fprintf");
755 claws_fclose(fp);
756 claws_unlink(file);
757 return -1;
761 /* +------------------+----------------+--------------------------+ *
762 * ^data ^prev ^cur data+len-1^ */
764 prev = data;
765 while ((cur = (gchar *)my_memmem(prev, len - (prev - data), "\r\n", 2))
766 != NULL) {
767 if ((cur > prev && claws_fwrite(prev, 1, cur - prev, fp) < 1) ||
768 claws_fputc('\n', fp) == EOF) {
769 FILE_OP_ERROR(file, "claws_fwrite");
770 g_warning("can't write to file: %s", file);
771 claws_fclose(fp);
772 claws_unlink(file);
773 return -1;
776 if (cur == data + len - 1) {
777 prev = cur + 1;
778 break;
781 if (*(cur + 1) == '\n')
782 prev = cur + 2;
783 else
784 prev = cur + 1;
786 if (prev - data < len - 1 && *prev == '.' && *(prev + 1) == '.')
787 prev++;
789 if (prev - data >= len)
790 break;
793 if (prev - data < len &&
794 claws_fwrite(prev, 1, len - (prev - data), fp) < 1) {
795 FILE_OP_ERROR(file, "claws_fwrite");
796 g_warning("can't write to file: %s", file);
797 claws_fclose(fp);
798 claws_unlink(file);
799 return -1;
801 if (data[len - 1] != '\r' && data[len - 1] != '\n') {
802 if (claws_fputc('\n', fp) == EOF) {
803 FILE_OP_ERROR(file, "claws_fputc");
804 g_warning("can't write to file: %s", file);
805 claws_fclose(fp);
806 claws_unlink(file);
807 return -1;
811 if (claws_safe_fclose(fp) == EOF) {
812 FILE_OP_ERROR(file, "claws_fclose");
813 claws_unlink(file);
814 return -1;
817 return 0;
820 static Pop3State pop3_lookup_next(Pop3Session *session)
822 Pop3MsgInfo *msg;
823 PrefsAccount *ac = session->ac_prefs;
824 gint size;
825 gboolean size_limit_over;
827 for (;;) {
828 msg = &session->msg[session->cur_msg];
829 size = msg->size;
830 size_limit_over =
831 (ac->enable_size_limit &&
832 ac->size_limit > 0 &&
833 size > ac->size_limit * 1024);
835 if (ac->rmmail &&
836 msg->recv_time != RECV_TIME_NONE &&
837 msg->recv_time != RECV_TIME_KEEP &&
838 msg->partial_recv == POP3_TOTALLY_RECEIVED &&
839 session->current_time - msg->recv_time >=
840 ((ac->msg_leave_time * 24 * 60 * 60) +
841 (ac->msg_leave_hour * 60 * 60))) {
842 log_message(LOG_PROTOCOL,
843 _("POP: Deleting expired message %d [%s]\n"),
844 session->cur_msg, msg->uidl?msg->uidl:" ");
845 session->cur_total_bytes += size;
846 pop3_delete_send(session);
847 return POP3_DELETE;
850 if (size_limit_over) {
851 if (!msg->received && msg->partial_recv !=
852 POP3_MUST_COMPLETE_RECV) {
853 pop3_top_send(session, ac->size_limit);
854 return POP3_TOP;
855 } else if (msg->partial_recv == POP3_MUST_COMPLETE_RECV)
856 break;
858 log_message(LOG_PROTOCOL,
859 _("POP: Skipping message %d [%s] (%d bytes)\n"),
860 session->cur_msg, msg->uidl?msg->uidl:" ", size);
863 if (size == 0 || msg->received || size_limit_over) {
864 session->cur_total_bytes += size;
865 if (session->cur_msg == session->count) {
866 pop3_logout_send(session);
867 return POP3_LOGOUT;
868 } else
869 session->cur_msg++;
870 } else
871 break;
874 pop3_retr_send(session);
875 return POP3_RETR;
878 static Pop3ErrorValue pop3_ok(Pop3Session *session, const gchar *msg)
880 Pop3ErrorValue ok;
882 log_print(LOG_PROTOCOL, "POP< %s\n", msg);
884 if (!strncmp(msg, "+OK", 3))
885 ok = PS_SUCCESS;
886 else if (!strncmp(msg, "-ERR", 4)) {
887 if (strstr(msg + 4, "lock") ||
888 strstr(msg + 4, "Lock") ||
889 strstr(msg + 4, "LOCK") ||
890 strstr(msg + 4, "wait")) {
891 log_error(LOG_PROTOCOL, _("mailbox is locked\n"));
892 ok = PS_LOCKBUSY;
893 } else if (strcasestr(msg + 4, "timeout")) {
894 log_error(LOG_PROTOCOL, _("Session timeout\n"));
895 ok = PS_ERROR;
896 } else {
897 switch (session->state) {
898 #ifdef USE_GNUTLS
899 case POP3_STLS:
900 log_error(LOG_PROTOCOL, _("couldn't start STARTTLS session\n"));
901 ok = PS_ERROR;
902 break;
903 #endif
904 case POP3_GETAUTH_USER:
905 case POP3_GETAUTH_PASS:
906 case POP3_GETAUTH_APOP:
907 log_error(LOG_PROTOCOL, _("error occurred on authentication\n"));
908 ok = PS_AUTHFAIL;
909 break;
910 case POP3_GETRANGE_LAST:
911 case POP3_GETRANGE_UIDL:
912 case POP3_TOP:
913 log_warning(LOG_PROTOCOL, _("command not supported\n"));
914 ok = PS_NOTSUPPORTED;
915 break;
917 default:
918 log_error(LOG_PROTOCOL, _("error occurred on POP session\n"));
919 ok = PS_ERROR;
923 g_free(session->error_msg);
924 session->error_msg = g_strdup(msg);
925 g_printerr("POP: %s\n", msg);
926 } else
927 ok = PS_PROTOCOL;
929 session->error_val = ok;
930 return ok;
933 static gint pop3_session_recv_msg(Session *session, const gchar *msg)
935 Pop3Session *pop3_session = POP3_SESSION(session);
936 Pop3ErrorValue val = PS_SUCCESS;
937 const gchar *body;
939 body = msg;
940 if (pop3_session->state != POP3_GETRANGE_UIDL_RECV &&
941 pop3_session->state != POP3_GETSIZE_LIST_RECV) {
942 val = pop3_ok(pop3_session, msg);
943 if (val != PS_SUCCESS) {
944 if (val != PS_NOTSUPPORTED) {
945 pop3_session->state = POP3_ERROR;
946 return -1;
950 if (*body == '+' || *body == '-')
951 body++;
952 while (g_ascii_isalpha(*body))
953 body++;
954 while (g_ascii_isspace(*body))
955 body++;
958 switch (pop3_session->state) {
959 case POP3_READY:
960 case POP3_GREETING:
961 pop3_greeting_recv(pop3_session, body);
962 #ifdef USE_GNUTLS
963 if (pop3_session->ac_prefs->ssl_pop == SSL_STARTTLS)
964 val = pop3_stls_send(pop3_session);
965 else
966 #endif
967 if (pop3_session->ac_prefs->use_apop_auth)
968 val = pop3_getauth_apop_send(pop3_session);
969 else
970 val = pop3_getauth_user_send(pop3_session);
971 break;
972 #ifdef USE_GNUTLS
973 case POP3_STLS:
974 if (pop3_stls_recv(pop3_session) != PS_SUCCESS)
975 return -1;
976 if (pop3_session->ac_prefs->use_apop_auth)
977 val = pop3_getauth_apop_send(pop3_session);
978 else
979 val = pop3_getauth_user_send(pop3_session);
980 break;
981 #endif
982 case POP3_GETAUTH_USER:
983 val = pop3_getauth_pass_send(pop3_session);
984 break;
985 case POP3_GETAUTH_PASS:
986 case POP3_GETAUTH_APOP:
987 if (!pop3_session->pop_before_smtp)
988 val = pop3_getrange_stat_send(pop3_session);
989 else
990 val = pop3_logout_send(pop3_session);
991 break;
992 case POP3_GETRANGE_STAT:
993 if (pop3_getrange_stat_recv(pop3_session, body) < 0)
994 return -1;
995 if (pop3_session->count > 0)
996 val = pop3_getrange_uidl_send(pop3_session);
997 else
998 val = pop3_logout_send(pop3_session);
999 break;
1000 case POP3_GETRANGE_LAST:
1001 if (val == PS_NOTSUPPORTED)
1002 pop3_session->error_val = PS_SUCCESS;
1003 else if (pop3_getrange_last_recv(pop3_session, body) < 0)
1004 return -1;
1005 if (pop3_session->cur_msg > 0)
1006 val = pop3_getsize_list_send(pop3_session);
1007 else
1008 val = pop3_logout_send(pop3_session);
1009 break;
1010 case POP3_GETRANGE_UIDL:
1011 if (val == PS_NOTSUPPORTED) {
1012 pop3_session->error_val = PS_SUCCESS;
1013 val = pop3_getrange_last_send(pop3_session);
1014 } else {
1015 pop3_session->state = POP3_GETRANGE_UIDL_RECV;
1016 session_recv_data(session, 0, ".\r\n");
1018 break;
1019 case POP3_GETSIZE_LIST:
1020 pop3_session->state = POP3_GETSIZE_LIST_RECV;
1021 session_recv_data(session, 0, ".\r\n");
1022 break;
1023 case POP3_RETR:
1024 pop3_session->state = POP3_RETR_RECV;
1025 session_recv_data(session, 0, ".\r\n");
1026 break;
1027 case POP3_TOP:
1028 if (val == PS_NOTSUPPORTED) {
1029 pop3_session->error_val = PS_SUCCESS;
1030 } else {
1031 pop3_session->state = POP3_TOP_RECV;
1032 session_recv_data(session, 0, ".\r\n");
1034 break;
1035 case POP3_DELETE:
1036 pop3_delete_recv(pop3_session);
1037 if (pop3_session->cur_msg == pop3_session->count)
1038 val = pop3_logout_send(pop3_session);
1039 else {
1040 pop3_session->cur_msg++;
1041 if (pop3_lookup_next(pop3_session) == POP3_ERROR)
1042 return -1;
1044 break;
1045 case POP3_LOGOUT:
1046 pop3_session->state = POP3_DONE;
1047 session_disconnect(session);
1048 break;
1049 case POP3_ERROR:
1050 default:
1051 return -1;
1054 return val == PS_SUCCESS?0:-1;
1057 static gint pop3_session_recv_data_finished(Session *session, guchar *data,
1058 guint len)
1060 Pop3Session *pop3_session = POP3_SESSION(session);
1061 Pop3ErrorValue val = PS_SUCCESS;
1063 switch (pop3_session->state) {
1064 case POP3_GETRANGE_UIDL_RECV:
1065 val = pop3_getrange_uidl_recv(pop3_session, data, len);
1066 if (val == PS_SUCCESS) {
1067 if (pop3_session->new_msg_exist)
1068 pop3_getsize_list_send(pop3_session);
1069 else
1070 pop3_logout_send(pop3_session);
1071 } else
1072 return -1;
1073 break;
1074 case POP3_GETSIZE_LIST_RECV:
1075 val = pop3_getsize_list_recv(pop3_session, data, len);
1076 if (val == PS_SUCCESS) {
1077 if (pop3_lookup_next(pop3_session) == POP3_ERROR)
1078 return -1;
1079 } else
1080 return -1;
1081 break;
1082 case POP3_RETR_RECV:
1083 if (pop3_retr_recv(pop3_session, data, len) < 0)
1084 return -1;
1086 if (pop3_session->ac_prefs->rmmail &&
1087 pop3_session->ac_prefs->msg_leave_time == 0 &&
1088 pop3_session->ac_prefs->msg_leave_hour == 0 &&
1089 pop3_session->msg[pop3_session->cur_msg].recv_time
1090 != RECV_TIME_KEEP)
1091 pop3_delete_send(pop3_session);
1092 else if (pop3_session->cur_msg == pop3_session->count)
1093 pop3_logout_send(pop3_session);
1094 else {
1095 pop3_session->cur_msg++;
1096 if (pop3_lookup_next(pop3_session) == POP3_ERROR)
1097 return -1;
1099 break;
1100 case POP3_TOP_RECV:
1101 if (pop3_top_recv(pop3_session, data, len) < 0)
1102 return -1;
1104 if (pop3_session->cur_msg == pop3_session->count)
1105 pop3_logout_send(pop3_session);
1106 else {
1107 pop3_session->cur_msg++;
1108 if (pop3_lookup_next(pop3_session) == POP3_ERROR)
1109 return -1;
1111 break;
1112 case POP3_TOP:
1113 log_warning(LOG_PROTOCOL, _("TOP command unsupported\n"));
1114 if (pop3_session->cur_msg == pop3_session->count)
1115 pop3_logout_send(pop3_session);
1116 else {
1117 pop3_session->cur_msg++;
1118 if (pop3_lookup_next(pop3_session) == POP3_ERROR)
1119 return -1;
1121 break;
1122 case POP3_ERROR:
1123 default:
1124 return -1;
1127 return 0;