2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2004 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 2 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, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
36 #include "prefs_account.h"
41 #include "procheader.h"
47 static gint pop3_greeting_recv (Pop3Session *session,
49 static gint pop3_getauth_user_send (Pop3Session *session);
50 static gint pop3_getauth_pass_send (Pop3Session *session);
51 static gint pop3_getauth_apop_send (Pop3Session *session);
53 static gint pop3_stls_send (Pop3Session *session);
54 static gint pop3_stls_recv (Pop3Session *session);
56 static gint pop3_getrange_stat_send (Pop3Session *session);
57 static gint pop3_getrange_stat_recv (Pop3Session *session,
59 static gint pop3_getrange_last_send (Pop3Session *session);
60 static gint pop3_getrange_last_recv (Pop3Session *session,
62 static gint pop3_getrange_uidl_send (Pop3Session *session);
63 static gint pop3_getrange_uidl_recv (Pop3Session *session,
66 static gint pop3_getsize_list_send (Pop3Session *session);
67 static gint pop3_getsize_list_recv (Pop3Session *session,
70 static gint pop3_retr_send (Pop3Session *session);
71 static gint pop3_retr_recv (Pop3Session *session,
74 static gint pop3_delete_send (Pop3Session *session);
75 static gint pop3_delete_recv (Pop3Session *session);
76 static gint pop3_logout_send (Pop3Session *session);
78 static void pop3_gen_send (Pop3Session *session,
79 const gchar *format, ...);
81 static void pop3_session_destroy (Session *session);
83 static gint pop3_write_msg_to_file (const gchar *file,
88 static Pop3State pop3_lookup_next (Pop3Session *session);
89 static Pop3ErrorValue pop3_ok (Pop3Session *session,
92 static gint pop3_session_recv_msg (Session *session,
94 static gint pop3_session_recv_data_finished (Session *session,
98 static gchar *pop3_get_filename_for_partial_mail(Pop3Session *session,
100 static void pop3_delete_old_partial (const gchar *file);
102 static gint pop3_greeting_recv(Pop3Session *session, const gchar *msg)
104 session->state = POP3_GREETING;
106 session->greeting = g_strdup(msg);
111 static gint pop3_stls_send(Pop3Session *session)
113 session->state = POP3_STLS;
114 pop3_gen_send(session, "STLS");
118 static gint pop3_stls_recv(Pop3Session *session)
120 if (session_start_tls(SESSION(session)) < 0) {
121 session->error_val = PS_SOCKET;
126 #endif /* USE_OPENSSL */
128 static gint pop3_getauth_user_send(Pop3Session *session)
130 g_return_val_if_fail(session->user != NULL, -1);
132 session->state = POP3_GETAUTH_USER;
133 pop3_gen_send(session, "USER %s", session->user);
137 static gint pop3_getauth_pass_send(Pop3Session *session)
139 g_return_val_if_fail(session->pass != NULL, -1);
141 session->state = POP3_GETAUTH_PASS;
142 pop3_gen_send(session, "PASS %s", session->pass);
146 static gint pop3_getauth_apop_send(Pop3Session *session)
152 g_return_val_if_fail(session->user != NULL, -1);
153 g_return_val_if_fail(session->pass != NULL, -1);
155 session->state = POP3_GETAUTH_APOP;
157 if ((start = strchr(session->greeting, '<')) == NULL) {
158 log_warning(_("Required APOP timestamp not found "
160 session->error_val = PS_PROTOCOL;
164 if ((end = strchr(start, '>')) == NULL || end == start + 1) {
165 log_warning(_("Timestamp syntax error in greeting\n"));
166 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);
181 static gint pop3_getrange_stat_send(Pop3Session *session)
183 session->state = POP3_GETRANGE_STAT;
184 pop3_gen_send(session, "STAT");
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_warning(_("POP3 protocol error\n"));
192 session->error_val = PS_PROTOCOL;
195 if (session->count == 0) {
196 session->uidl_is_valid = TRUE;
198 session->msg = g_new0(Pop3MsgInfo, session->count + 1);
199 session->cur_msg = 1;
206 static gint pop3_getrange_last_send(Pop3Session *session)
208 session->state = POP3_GETRANGE_LAST;
209 pop3_gen_send(session, "LAST");
213 static gint pop3_getrange_last_recv(Pop3Session *session, const gchar *msg)
217 if (sscanf(msg, "%d", &last) == 0) {
218 log_warning(_("POP3 protocol error\n"));
219 session->error_val = PS_PROTOCOL;
222 if (session->count > last) {
223 session->new_msg_exist = TRUE;
224 session->cur_msg = last + 1;
226 session->cur_msg = 0;
232 static gint pop3_getrange_uidl_send(Pop3Session *session)
234 session->state = POP3_GETRANGE_UIDL;
235 pop3_gen_send(session, "UIDL");
239 static gint pop3_getrange_uidl_recv(Pop3Session *session, const gchar *data,
243 gchar buf[POPBUFSIZE];
248 const gchar *p = data;
249 const gchar *lastp = data + len;
250 const gchar *newline;
253 if ((newline = memchr(p, '\r', lastp - p)) == NULL)
255 buf_len = MIN(newline - p, sizeof(buf) - 1);
256 memcpy(buf, p, buf_len);
260 if (p < lastp && *p == '\n') p++;
262 if (sscanf(buf, "%d %" Xstr(IDLEN) "s", &num, id) != 2)
264 if (num <= 0 || num > session->count)
267 session->msg[num].uidl = g_strdup(id);
269 recv_time = (time_t)g_hash_table_lookup(
270 session->uidl_table, id);
271 session->msg[num].recv_time = recv_time;
273 partial_recv = (gint)g_hash_table_lookup(
274 session->partial_recv_table, id);
276 if (!session->ac_prefs->getall && recv_time != RECV_TIME_NONE) {
277 session->msg[num].received =
278 (partial_recv != POP3_MUST_COMPLETE_RECV);
279 session->msg[num].partial_recv = partial_recv;
282 if (!session->new_msg_exist &&
283 (session->ac_prefs->getall || recv_time == RECV_TIME_NONE ||
284 session->ac_prefs->rmmail)) {
285 session->cur_msg = num;
286 session->new_msg_exist = TRUE;
290 session->uidl_is_valid = TRUE;
294 static gint pop3_getsize_list_send(Pop3Session *session)
296 session->state = POP3_GETSIZE_LIST;
297 pop3_gen_send(session, "LIST");
301 static gint pop3_getsize_list_recv(Pop3Session *session, const gchar *data,
304 gchar buf[POPBUFSIZE];
307 const gchar *p = data;
308 const gchar *lastp = data + len;
309 const gchar *newline;
312 if ((newline = memchr(p, '\r', lastp - p)) == NULL)
314 buf_len = MIN(newline - p, sizeof(buf) - 1);
315 memcpy(buf, p, buf_len);
319 if (p < lastp && *p == '\n') p++;
321 if (sscanf(buf, "%u %u", &num, &size) != 2) {
322 session->error_val = PS_PROTOCOL;
326 if (num > 0 && num <= session->count)
327 session->msg[num].size = size;
328 if (num > 0 && num < session->cur_msg)
329 session->cur_total_bytes += size;
335 static gint pop3_retr_send(Pop3Session *session)
337 session->state = POP3_RETR;
338 pop3_gen_send(session, "RETR %d", session->cur_msg);
342 static gint pop3_retr_recv(Pop3Session *session, const gchar *data, guint len)
346 MailReceiveData mail_receive_data;
348 mail_receive_data.session = session;
349 mail_receive_data.data = g_strndup(data, len);
350 hooks_invoke(MAIL_RECEIVE_HOOKLIST, &mail_receive_data);
352 file = get_tmp_file();
353 if (pop3_write_msg_to_file(file, mail_receive_data.data,
354 strlen(mail_receive_data.data), NULL) < 0) {
356 g_free(mail_receive_data.data);
357 session->error_val = PS_IOERR;
360 g_free(mail_receive_data.data);
362 if (session->msg[session->cur_msg].partial_recv
363 == POP3_MUST_COMPLETE_RECV) {
364 gchar *old_file = pop3_get_filename_for_partial_mail(
365 session, session->msg[session->cur_msg].uidl);
368 pop3_delete_old_partial (old_file);
373 /* drop_ok: 0: success 1: don't receive -1: error */
374 drop_ok = session->drop_message(session, file);
378 session->error_val = PS_IOERR;
382 session->cur_total_bytes += session->msg[session->cur_msg].size;
383 session->cur_total_recv_bytes += session->msg[session->cur_msg].size;
384 session->cur_total_num++;
386 session->msg[session->cur_msg].received = TRUE;
387 session->msg[session->cur_msg].partial_recv = POP3_TOTALLY_RECEIVED;
389 session->msg[session->cur_msg].recv_time =
390 drop_ok == 1 ? RECV_TIME_KEEP : session->current_time;
395 static gint pop3_top_send(Pop3Session *session, gint max_size)
397 gint num_lines = (max_size*1024)/82; /* consider lines to be 80 chars */
398 session->state = POP3_TOP;
399 pop3_gen_send(session, "TOP %d %d", session->cur_msg, num_lines);
403 static gint pop3_top_recv(Pop3Session *session, const gchar *data, guint len)
407 MailReceiveData mail_receive_data;
408 gchar *partial_notice = NULL;
410 mail_receive_data.session = session;
411 mail_receive_data.data = g_strndup(data, len);
412 hooks_invoke(MAIL_RECEIVE_HOOKLIST, &mail_receive_data);
414 partial_notice = g_strdup_printf("SC-Marked-For-Download: 0\n"
415 "SC-Partially-Retrieved: %s\n"
416 "SC-Account-Server: %s\n"
417 "SC-Account-Login: %s\n"
418 "SC-Message-Size: %d",
419 session->msg[session->cur_msg].uidl,
420 session->ac_prefs->recv_server,
421 session->ac_prefs->userid,
422 session->msg[session->cur_msg].size);
423 file = get_tmp_file();
424 if (pop3_write_msg_to_file(file, mail_receive_data.data,
425 strlen(mail_receive_data.data), partial_notice) < 0) {
427 g_free(mail_receive_data.data);
428 session->error_val = PS_IOERR;
429 g_free(partial_notice);
432 g_free(mail_receive_data.data);
433 g_free(partial_notice);
435 /* drop_ok: 0: success 1: don't receive -1: error */
436 drop_ok = session->drop_message(session, file);
439 session->error_val = PS_IOERR;
443 session->cur_total_bytes += session->msg[session->cur_msg].size;
444 session->cur_total_recv_bytes += session->msg[session->cur_msg].size;
445 session->cur_total_num++;
447 session->msg[session->cur_msg].received = TRUE;
448 session->msg[session->cur_msg].partial_recv = POP3_PARTIALLY_RECEIVED;
449 session->msg[session->cur_msg].recv_time =
450 drop_ok == 1 ? RECV_TIME_KEEP : session->current_time;
455 static gint pop3_delete_send(Pop3Session *session)
457 session->state = POP3_DELETE;
458 pop3_gen_send(session, "DELE %d", session->cur_msg);
462 static gint pop3_delete_recv(Pop3Session *session)
464 session->msg[session->cur_msg].deleted = TRUE;
468 static gint pop3_logout_send(Pop3Session *session)
470 session->state = POP3_LOGOUT;
471 pop3_gen_send(session, "QUIT");
475 static void pop3_gen_send(Pop3Session *session, const gchar *format, ...)
477 gchar buf[POPBUFSIZE + 1];
480 va_start(args, format);
481 g_vsnprintf(buf, sizeof(buf) - 2, format, args);
484 if (!strncasecmp(buf, "PASS ", 5))
485 log_print("POP3> PASS ********\n");
487 log_print("POP3> %s\n", buf);
489 session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
492 Session *pop3_session_new(PrefsAccount *account)
494 Pop3Session *session;
496 g_return_val_if_fail(account != NULL, NULL);
498 session = g_new0(Pop3Session, 1);
500 session_init(SESSION(session));
502 SESSION(session)->type = SESSION_POP3;
504 SESSION(session)->recv_msg = pop3_session_recv_msg;
505 SESSION(session)->recv_data_finished = pop3_session_recv_data_finished;
506 SESSION(session)->send_data_finished = NULL;
508 SESSION(session)->destroy = pop3_session_destroy;
510 session->state = POP3_READY;
511 session->ac_prefs = account;
512 session->pop_before_smtp = FALSE;
513 pop3_get_uidl_table(account, session);
514 session->current_time = time(NULL);
515 session->error_val = PS_SUCCESS;
516 session->error_msg = NULL;
518 return SESSION(session);
521 static void pop3_session_destroy(Session *session)
523 Pop3Session *pop3_session = POP3_SESSION(session);
526 g_return_if_fail(session != NULL);
528 for (n = 1; n <= pop3_session->count; n++)
529 g_free(pop3_session->msg[n].uidl);
530 g_free(pop3_session->msg);
532 if (pop3_session->uidl_table) {
533 hash_free_strings(pop3_session->uidl_table);
534 g_hash_table_destroy(pop3_session->uidl_table);
537 if (pop3_session->partial_recv_table) {
538 hash_free_strings(pop3_session->partial_recv_table);
539 g_hash_table_destroy(pop3_session->partial_recv_table);
542 g_free(pop3_session->greeting);
543 g_free(pop3_session->user);
544 g_free(pop3_session->pass);
545 g_free(pop3_session->error_msg);
548 void pop3_get_uidl_table(PrefsAccount *ac_prefs, Pop3Session *session)
551 GHashTable *partial_recv_table;
554 gchar buf[POPBUFSIZE];
555 gchar uidl[POPBUFSIZE];
560 table = g_hash_table_new(g_str_hash, g_str_equal);
561 partial_recv_table = g_hash_table_new(g_str_hash, g_str_equal);
563 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
564 "uidl", G_DIR_SEPARATOR_S, ac_prefs->recv_server,
565 "-", ac_prefs->userid, NULL);
566 if ((fp = fopen(path, "rb")) == NULL) {
567 if (ENOENT != errno) FILE_OP_ERROR(path, "fopen");
569 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
570 "uidl-", ac_prefs->recv_server,
571 "-", ac_prefs->userid, NULL);
572 if ((fp = fopen(path, "rb")) == NULL) {
573 if (ENOENT != errno) FILE_OP_ERROR(path, "fopen");
575 session->uidl_table = table;
576 session->partial_recv_table = partial_recv_table;
584 while (fgets(buf, sizeof(buf), fp) != NULL) {
585 gchar tmp[POPBUFSIZE];
587 recv_time = RECV_TIME_NONE;
588 partial_recv = POP3_TOTALLY_RECEIVED;
590 if (sscanf(buf, "%s\t%ld\t%s", uidl, &recv_time, &tmp) < 2) {
591 if (sscanf(buf, "%s", uidl) != 1)
597 if (recv_time == RECV_TIME_NONE)
598 recv_time = RECV_TIME_RECEIVED;
599 g_hash_table_insert(table, g_strdup(uidl),
600 GINT_TO_POINTER(recv_time));
601 if (strlen(tmp) == 1)
602 partial_recv = atoi(tmp); /* totally received ?*/
604 partial_recv = POP3_MUST_COMPLETE_RECV;
606 g_hash_table_insert(partial_recv_table, g_strdup(uidl),
607 GINT_TO_POINTER(partial_recv));
611 session->uidl_table = table;
612 session->partial_recv_table = partial_recv_table;
617 int pop3_msg_in_uidl_list(const gchar *server, const gchar *login,
622 gchar buf[POPBUFSIZE];
623 gchar uidl[POPBUFSIZE];
628 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
629 "uidl", G_DIR_SEPARATOR_S, server,
631 if ((fp = fopen(path, "rb")) == NULL) {
632 if (ENOENT != errno) FILE_OP_ERROR(path, "fopen");
634 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
637 if ((fp = fopen(path, "rb")) == NULL) {
638 if (ENOENT != errno) FILE_OP_ERROR(path, "fopen");
647 while (fgets(buf, sizeof(buf), fp) != NULL) {
648 gchar tmp[POPBUFSIZE];
650 recv_time = RECV_TIME_NONE;
651 partial_recv = POP3_TOTALLY_RECEIVED;
653 if (sscanf(buf, "%s\t%ld\t%s", uidl, &recv_time, &tmp) < 2) {
654 if (sscanf(buf, "%s", uidl) != 1)
660 if (!strcmp(uidl, muidl)) {
670 static gchar *pop3_get_filename_for_partial_mail(Pop3Session *session,
674 gchar *result = NULL;
676 gchar buf[POPBUFSIZE];
677 gchar uidl[POPBUFSIZE];
682 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
683 "uidl", G_DIR_SEPARATOR_S,
684 session->ac_prefs->recv_server,
685 "-", session->ac_prefs->userid, NULL);
686 if ((fp = fopen(path, "rb")) == NULL) {
687 if (ENOENT != errno) FILE_OP_ERROR(path, "fopen");
689 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
690 "uidl-", session->ac_prefs->recv_server,
691 "-", session->ac_prefs->userid, NULL);
692 if ((fp = fopen(path, "rb")) == NULL) {
693 if (ENOENT != errno) FILE_OP_ERROR(path, "fopen");
702 while (fgets(buf, sizeof(buf), fp) != NULL) {
703 gchar tmp[POPBUFSIZE];
705 recv_time = RECV_TIME_NONE;
706 partial_recv = POP3_TOTALLY_RECEIVED;
708 if (sscanf(buf, "%s\t%ld\t%s", uidl, &recv_time, &tmp) < 2) {
709 if (sscanf(buf, "%s", uidl) != 1)
715 if (!strcmp(muidl, uidl)) {
716 result = strdup(tmp);
726 static int pop3_uidl_mark_mail(MsgInfo *msginfo, int download)
732 gchar buf[POPBUFSIZE];
733 gchar uidl[POPBUFSIZE];
738 gchar partial_recv[POPBUFSIZE];
743 filename = procmsg_get_message_file_path(msginfo);
745 g_warning("can't get message file path.\n");
748 tinfo = procheader_parse_file(filename, msginfo->flags, TRUE, TRUE);
750 if (!tinfo->account_server
751 || !tinfo->account_login
752 || !tinfo->partial_recv) {
755 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
756 "uidl", G_DIR_SEPARATOR_S, tinfo->account_server,
757 "-", tinfo->account_login, NULL);
758 if ((fp = fopen(path, "rb")) == NULL) {
760 if (ENOENT != errno) FILE_OP_ERROR(path, "fopen");
762 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
763 "uidl-", tinfo->account_server,
764 "-", tinfo->account_login, NULL);
765 if ((fp = fopen(path, "rb")) == NULL) {
766 if (ENOENT != errno) FILE_OP_ERROR(path, "fopen");
772 pathnew = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
773 "uidl", G_DIR_SEPARATOR_S, tinfo->account_server,
774 "-", tinfo->account_login, ".new", NULL);
775 if ((fpnew = fopen(pathnew, "wb")) == NULL) {
784 while (fgets(buf, sizeof(buf), fp) != NULL) {
786 recv_time = RECV_TIME_NONE;
787 sprintf(partial_recv,"0");
789 if (sscanf(buf, "%s\t%ld\t%s",
790 uidl, &recv_time, &partial_recv) < 2) {
791 if (sscanf(buf, "%s", uidl) != 1)
797 if (strcmp(tinfo->partial_recv, uidl)) {
798 fprintf(fpnew, "%s\t%ld\t%s\n",
799 uidl, recv_time, partial_recv);
802 if (download == POP3_PARTIAL_DLOAD_DLOAD) {
803 gchar *folder_id = folder_item_get_identifier(
805 stat = g_strdup_printf("%s:%d",
806 folder_id, msginfo->msgnum);
809 else if (download == POP3_PARTIAL_DLOAD_UNKN)
811 else if (download == POP3_PARTIAL_DLOAD_DELE)
814 fprintf(fpnew, "%s\t%ld\t%s\n",
815 uidl, recv_time, stat);
822 move_file(pathnew, path, TRUE);
827 if ((fp = fopen(filename,"rb")) == NULL) {
831 pathnew = g_strdup_printf("%s.new", filename);
832 if ((fpnew = fopen(pathnew, "wb")) == NULL) {
839 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
843 fprintf(fpnew, "SC-Marked-For-Download: %d\n",
846 if(strlen(buf) > strlen("SC-Marked-For-Download: x\n")
847 && !strncmp(buf, "SC-Marked-For-Download:",
848 strlen("SC-Marked-For-Download:"))) {
850 buf+strlen("SC-Marked-For-Download: x\n"));
854 fprintf(fpnew, "%s", buf);
859 rename(pathnew, filename);
861 msginfo->planned_download = download;
862 msgcache_update_msg(msginfo->folder->cache, msginfo);
867 procmsg_msginfo_free(tinfo);
873 * A mail which has been completely downloaded will have no special headers,
874 * and its entry in the uidl file will end by 0 (POP3_TOTALLY_RECEIVED);
876 * A mail which has been partially downloaded will have some special headers,
877 * and its entry in the uidl file will first be 1 (POP3_PARTIALLY_RECEIVED);
878 * the special headers will be including "SC-Marked-For-Download" which can
880 * 0 (POP3_PARTIAL_DLOAD_UNKN) meaning that the user has not yet chosen to
881 * download the mail or let it be deleted - this header is absent until the
882 * user first chooses an action
883 * 1 (POP3_PARTIAL_DLOAD_DLOAD) meaning that the user wants to finish
884 * downloading the mail
885 * 2 (POP3_PARTIAL_DLOAD_DELE) meaning that the user does not want to finish
886 * downloading the mail
887 * When updating this header to POP3_PARTIAL_DLOAD_DLOAD, the uidl line of
888 * this mail will end with the mail's physical path, which Sylpheed will remove
889 * after having downloaded the complete mail. msg->partial_recv will equal
890 * 2 (POP3_MUST_COMPLETE_RECV).
891 * When updating this header to POP3_PARTIAL_DLOAD_DELE, the uidl line of
892 * this mail will be 0 (POP3_TOTALLY_RECEIVED), which will let Sylpheed delete
893 * this mail from the server as soon as the leave_time preference specifies.
896 int pop3_mark_for_delete(MsgInfo *msginfo)
898 return pop3_uidl_mark_mail(msginfo, POP3_PARTIAL_DLOAD_DELE);
901 int pop3_mark_for_download(MsgInfo *msginfo)
903 return pop3_uidl_mark_mail(msginfo, POP3_PARTIAL_DLOAD_DLOAD);
906 int pop3_unmark(MsgInfo *msginfo)
908 return pop3_uidl_mark_mail(msginfo, POP3_PARTIAL_DLOAD_UNKN);
911 static void pop3_delete_old_partial(const gchar *file)
913 gchar *id = strdup(file);
914 gchar *snum = strrchr(file, ':');
916 FolderItem *item = NULL;
918 debug_print("too big message updated,should remove %s\n", file);
924 return; /* not a real problem */
929 if (strrchr(id, ':'))
930 *(strrchr(id, ':'))='\0';
932 item = folder_find_item_from_identifier(id);
934 debug_print("removing %d in %s\n", num, id);
935 folder_item_remove_msg(item, num);
940 gint pop3_write_uidl_list(Pop3Session *session)
947 if (!session->uidl_is_valid) return 0;
949 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
950 "uidl", G_DIR_SEPARATOR_S,
951 session->ac_prefs->recv_server,
952 "-", session->ac_prefs->userid, NULL);
953 if ((fp = fopen(path, "wb")) == NULL) {
954 FILE_OP_ERROR(path, "fopen");
959 for (n = 1; n <= session->count; n++) {
960 msg = &session->msg[n];
961 if (msg->uidl && msg->received && !msg->deleted) {
962 fprintf(fp, "%s\t%ld\t%d\n",
963 msg->uidl, msg->recv_time, msg->partial_recv);
967 if (fclose(fp) == EOF) FILE_OP_ERROR(path, "fclose");
973 static gint pop3_write_msg_to_file(const gchar *file, const gchar *data,
974 guint len, const gchar *prefix)
977 const gchar *prev, *cur;
979 g_return_val_if_fail(file != NULL, -1);
981 if ((fp = fopen(file, "wb")) == NULL) {
982 FILE_OP_ERROR(file, "fopen");
986 if (change_file_mode_rw(fp, file) < 0)
987 FILE_OP_ERROR(file, "chmod");
989 if (prefix != NULL) {
994 /* +------------------+----------------+--------------------------+ *
995 * ^data ^prev ^cur data+len-1^ */
998 while ((cur = memchr(prev, '\r', len - (prev - data))) != NULL) {
999 if ((cur > prev && fwrite(prev, cur - prev, 1, fp) < 1) ||
1000 fputc('\n', fp) == EOF) {
1001 FILE_OP_ERROR(file, "fwrite");
1002 g_warning("can't write to file: %s\n", file);
1008 if (cur == data + len - 1) {
1013 if (*(cur + 1) == '\n')
1018 if (prev - data < len - 1 && *prev == '.' && *(prev + 1) == '.')
1021 if (prev - data >= len)
1025 if (prev - data < len &&
1026 fwrite(prev, len - (prev - data), 1, fp) < 1) {
1027 FILE_OP_ERROR(file, "fwrite");
1028 g_warning("can't write to file: %s\n", file);
1033 if (data[len - 1] != '\r' && data[len - 1] != '\n') {
1034 if (fputc('\n', fp) == EOF) {
1035 FILE_OP_ERROR(file, "fputc");
1036 g_warning("can't write to file: %s\n", file);
1043 if (fclose(fp) == EOF) {
1044 FILE_OP_ERROR(file, "fclose");
1052 static Pop3State pop3_lookup_next(Pop3Session *session)
1055 PrefsAccount *ac = session->ac_prefs;
1057 gboolean size_limit_over;
1060 msg = &session->msg[session->cur_msg];
1063 (ac->enable_size_limit &&
1064 ac->size_limit > 0 &&
1065 size > ac->size_limit * 1024);
1068 msg->recv_time != RECV_TIME_NONE &&
1069 msg->recv_time != RECV_TIME_KEEP &&
1070 msg->partial_recv == POP3_TOTALLY_RECEIVED &&
1071 session->current_time - msg->recv_time >=
1072 ac->msg_leave_time * 24 * 60 * 60) {
1074 (_("POP3: Deleting expired message "
1075 "%d\n"), session->cur_msg);
1076 pop3_delete_send(session);
1080 if (size_limit_over) {
1081 if (!msg->received && msg->partial_recv !=
1082 POP3_MUST_COMPLETE_RECV) {
1083 pop3_top_send(session, ac->size_limit);
1085 } else if (msg->partial_recv == POP3_MUST_COMPLETE_RECV)
1089 (_("POP3: Skipping message %d (%d bytes)\n"),
1090 session->cur_msg, size);
1093 if (size == 0 || msg->received || size_limit_over) {
1094 session->cur_total_bytes += size;
1095 if (session->cur_msg == session->count) {
1096 pop3_logout_send(session);
1104 pop3_retr_send(session);
1108 static Pop3ErrorValue pop3_ok(Pop3Session *session, const gchar *msg)
1112 log_print("POP3< %s\n", msg);
1114 if (!strncmp(msg, "+OK", 3))
1116 else if (!strncmp(msg, "-ERR", 4)) {
1117 if (strstr(msg + 4, "lock") ||
1118 strstr(msg + 4, "Lock") ||
1119 strstr(msg + 4, "LOCK") ||
1120 strstr(msg + 4, "wait")) {
1121 log_warning(_("mailbox is locked\n"));
1123 } else if (strcasestr(msg + 4, "timeout")) {
1124 log_warning(_("session timeout\n"));
1127 switch (session->state) {
1130 log_warning(_("can't start TLS session\n"));
1134 case POP3_GETAUTH_USER:
1135 case POP3_GETAUTH_PASS:
1136 case POP3_GETAUTH_APOP:
1137 log_warning(_("error occurred on authentication\n"));
1140 case POP3_GETRANGE_LAST:
1141 case POP3_GETRANGE_UIDL:
1143 log_warning(_("command not supported\n"));
1144 ok = PS_NOTSUPPORTED;
1148 log_warning(_("error occurred on POP3 session\n"));
1153 g_free(session->error_msg);
1154 session->error_msg = g_strdup(msg);
1155 fprintf(stderr, "POP3: %s\n", msg);
1159 session->error_val = ok;
1163 static gint pop3_session_recv_msg(Session *session, const gchar *msg)
1165 Pop3Session *pop3_session = POP3_SESSION(session);
1166 Pop3ErrorValue val = PS_SUCCESS;
1170 if (pop3_session->state != POP3_GETRANGE_UIDL_RECV &&
1171 pop3_session->state != POP3_GETSIZE_LIST_RECV) {
1172 val = pop3_ok(pop3_session, msg);
1173 if (val != PS_SUCCESS) {
1174 if (val != PS_NOTSUPPORTED) {
1175 pop3_session->state = POP3_ERROR;
1180 if (*body == '+' || *body == '-')
1182 while (isalpha(*body))
1184 while (isspace(*body))
1188 switch (pop3_session->state) {
1191 pop3_greeting_recv(pop3_session, body);
1193 if (pop3_session->ac_prefs->ssl_pop == SSL_STARTTLS)
1194 pop3_stls_send(pop3_session);
1197 if (pop3_session->ac_prefs->protocol == A_APOP)
1198 pop3_getauth_apop_send(pop3_session);
1200 pop3_getauth_user_send(pop3_session);
1204 if (pop3_stls_recv(pop3_session) != PS_SUCCESS)
1206 if (pop3_session->ac_prefs->protocol == A_APOP)
1207 pop3_getauth_apop_send(pop3_session);
1209 pop3_getauth_user_send(pop3_session);
1212 case POP3_GETAUTH_USER:
1213 pop3_getauth_pass_send(pop3_session);
1215 case POP3_GETAUTH_PASS:
1216 case POP3_GETAUTH_APOP:
1217 if (!pop3_session->pop_before_smtp)
1218 pop3_getrange_stat_send(pop3_session);
1220 pop3_logout_send(pop3_session);
1222 case POP3_GETRANGE_STAT:
1223 if (pop3_getrange_stat_recv(pop3_session, body) < 0)
1225 if (pop3_session->count > 0)
1226 pop3_getrange_uidl_send(pop3_session);
1228 pop3_logout_send(pop3_session);
1230 case POP3_GETRANGE_LAST:
1231 if (val == PS_NOTSUPPORTED)
1232 pop3_session->error_val = PS_SUCCESS;
1233 else if (pop3_getrange_last_recv(pop3_session, body) < 0)
1235 if (pop3_session->cur_msg > 0)
1236 pop3_getsize_list_send(pop3_session);
1238 pop3_logout_send(pop3_session);
1240 case POP3_GETRANGE_UIDL:
1241 if (val == PS_NOTSUPPORTED) {
1242 pop3_session->error_val = PS_SUCCESS;
1243 pop3_getrange_last_send(pop3_session);
1245 pop3_session->state = POP3_GETRANGE_UIDL_RECV;
1246 session_recv_data(session, 0, ".\r\n");
1249 case POP3_GETSIZE_LIST:
1250 pop3_session->state = POP3_GETSIZE_LIST_RECV;
1251 session_recv_data(session, 0, ".\r\n");
1254 pop3_session->state = POP3_RETR_RECV;
1255 session_recv_data(session, 0, ".\r\n");
1258 if (val == PS_NOTSUPPORTED) {
1259 pop3_session->error_val = PS_SUCCESS;
1261 pop3_session->state = POP3_TOP_RECV;
1262 session_recv_data(session, 0, ".\r\n");
1266 pop3_delete_recv(pop3_session);
1267 if (pop3_session->cur_msg == pop3_session->count)
1268 pop3_logout_send(pop3_session);
1270 pop3_session->cur_msg++;
1271 if (pop3_lookup_next(pop3_session) == POP3_ERROR)
1276 session_disconnect(session);
1286 static gint pop3_session_recv_data_finished(Session *session, guchar *data,
1289 Pop3Session *pop3_session = POP3_SESSION(session);
1290 Pop3ErrorValue val = PS_SUCCESS;
1292 switch (pop3_session->state) {
1293 case POP3_GETRANGE_UIDL_RECV:
1294 val = pop3_getrange_uidl_recv(pop3_session, data, len);
1295 if (val == PS_SUCCESS) {
1296 if (pop3_session->new_msg_exist)
1297 pop3_getsize_list_send(pop3_session);
1299 pop3_logout_send(pop3_session);
1303 case POP3_GETSIZE_LIST_RECV:
1304 val = pop3_getsize_list_recv(pop3_session, data, len);
1305 if (val == PS_SUCCESS) {
1306 if (pop3_lookup_next(pop3_session) == POP3_ERROR)
1311 case POP3_RETR_RECV:
1312 if (pop3_retr_recv(pop3_session, data, len) < 0)
1315 if (pop3_session->ac_prefs->rmmail &&
1316 pop3_session->ac_prefs->msg_leave_time == 0 &&
1317 pop3_session->msg[pop3_session->cur_msg].recv_time
1319 pop3_delete_send(pop3_session);
1320 else if (pop3_session->cur_msg == pop3_session->count)
1321 pop3_logout_send(pop3_session);
1323 pop3_session->cur_msg++;
1324 if (pop3_lookup_next(pop3_session) == POP3_ERROR)
1329 if (pop3_top_recv(pop3_session, data, len) < 0)
1332 if (pop3_session->cur_msg == pop3_session->count)
1333 pop3_logout_send(pop3_session);
1335 pop3_session->cur_msg++;
1336 if (pop3_lookup_next(pop3_session) == POP3_ERROR)
1341 log_warning(_("TOP command unsupported\n"));
1342 if (pop3_session->cur_msg == pop3_session->count)
1343 pop3_logout_send(pop3_session);
1345 pop3_session->cur_msg++;
1346 if (pop3_lookup_next(pop3_session) == POP3_ERROR)