pop.c (26432B)
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 4 * 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. 9 * 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. 14 * 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/>. 17 */ 18 19 #include <glib.h> 20 #include <glib/gi18n.h> 21 #include <stdio.h> 22 #include <string.h> 23 #include <stdarg.h> 24 #include <ctype.h> 25 #include <unistd.h> 26 #include <time.h> 27 #include <errno.h> 28 29 #include "pop.h" 30 #include "prefs_account.h" 31 #include "utils.h" 32 #include "log.h" 33 #include "hooks.h" 34 #include "file-utils.h" 35 36 #include "defs.h" 37 38 typedef enum { 39 POP3_TOTALLY_RECEIVED = 0, 40 POP3_PARTIALLY_RECEIVED = 1, 41 POP3_MUST_COMPLETE_RECV = 2 42 } PartialDownloadStatus; 43 44 static gint pop3_greeting_recv (Pop3Session *session, 45 const gchar *msg); 46 static gint pop3_getauth_user_send (Pop3Session *session); 47 static gint pop3_getauth_pass_send (Pop3Session *session); 48 static gint pop3_stls_send (Pop3Session *session); 49 static gint pop3_stls_recv (Pop3Session *session); 50 static gint pop3_getrange_stat_send (Pop3Session *session); 51 static gint pop3_getrange_stat_recv (Pop3Session *session, 52 const gchar *msg); 53 static gint pop3_getrange_last_send (Pop3Session *session); 54 static gint pop3_getrange_last_recv (Pop3Session *session, 55 const gchar *msg); 56 static gint pop3_getrange_uidl_send (Pop3Session *session); 57 static gint pop3_getrange_uidl_recv (Pop3Session *session, 58 const gchar *data, 59 guint len); 60 static gint pop3_getsize_list_send (Pop3Session *session); 61 static gint pop3_getsize_list_recv (Pop3Session *session, 62 const gchar *data, 63 guint len); 64 static gint pop3_retr_send (Pop3Session *session); 65 static gint pop3_retr_recv (Pop3Session *session, 66 const gchar *data, 67 guint len); 68 static gint pop3_delete_send (Pop3Session *session); 69 static gint pop3_delete_recv (Pop3Session *session); 70 static gint pop3_logout_send (Pop3Session *session); 71 72 static void pop3_gen_send (Pop3Session *session, 73 const gchar *format, ...); 74 75 static void pop3_session_destroy (Session *session); 76 77 static gint pop3_write_msg_to_file (const gchar *file, 78 const gchar *data, 79 guint len, 80 const gchar *prefix); 81 82 static Pop3State pop3_lookup_next (Pop3Session *session); 83 static Pop3ErrorValue pop3_ok (Pop3Session *session, 84 const gchar *msg); 85 86 static gint pop3_session_recv_msg (Session *session, 87 const gchar *msg); 88 static gint pop3_session_recv_data_finished (Session *session, 89 guchar *data, 90 guint len); 91 static void pop3_get_uidl_table(PrefsAccount *ac_prefs, Pop3Session *session); 92 93 static gint pop3_greeting_recv(Pop3Session *session, const gchar *msg) 94 { 95 session->state = POP3_GREETING; 96 97 session->greeting = g_strdup(msg); 98 return PS_SUCCESS; 99 } 100 101 static gint pop3_stls_send(Pop3Session *session) 102 { 103 session->state = POP3_STLS; 104 pop3_gen_send(session, "STLS"); 105 return PS_SUCCESS; 106 } 107 108 static gint pop3_stls_recv(Pop3Session *session) 109 { 110 if (session_start_tls(SESSION(session)) < 0) { 111 session->error_val = PS_SOCKET; 112 return -1; 113 } 114 return PS_SUCCESS; 115 } 116 117 static gint pop3_getauth_user_send(Pop3Session *session) 118 { 119 cm_return_val_if_fail(session->user != NULL, -1); 120 121 session->state = POP3_GETAUTH_USER; 122 pop3_gen_send(session, "USER %s", session->user); 123 return PS_SUCCESS; 124 } 125 126 static gint pop3_getauth_pass_send(Pop3Session *session) 127 { 128 cm_return_val_if_fail(session->pass != NULL, -1); 129 130 session->state = POP3_GETAUTH_PASS; 131 pop3_gen_send(session, "PASS %s", session->pass); 132 return PS_SUCCESS; 133 } 134 135 static gint pop3_getrange_stat_send(Pop3Session *session) 136 { 137 session->state = POP3_GETRANGE_STAT; 138 pop3_gen_send(session, "STAT"); 139 return PS_SUCCESS; 140 } 141 142 static gint pop3_getrange_stat_recv(Pop3Session *session, const gchar *msg) 143 { 144 if (sscanf(msg, "%d %d", &session->count, &session->total_bytes) != 2) { 145 log_error(LOG_PROTOCOL, _("POP protocol error\n")); 146 session->error_val = PS_PROTOCOL; 147 return -1; 148 } else { 149 if (session->count == 0) { 150 session->uidl_is_valid = TRUE; 151 } else { 152 session->msg = g_new0(Pop3MsgInfo, session->count + 1); 153 session->cur_msg = 1; 154 } 155 } 156 157 return PS_SUCCESS; 158 } 159 160 static gint pop3_getrange_last_send(Pop3Session *session) 161 { 162 session->state = POP3_GETRANGE_LAST; 163 pop3_gen_send(session, "LAST"); 164 return PS_SUCCESS; 165 } 166 167 static gint pop3_getrange_last_recv(Pop3Session *session, const gchar *msg) 168 { 169 gint last; 170 171 if (sscanf(msg, "%d", &last) == 0) { 172 log_warning(LOG_PROTOCOL, _("POP protocol error\n")); 173 session->error_val = PS_PROTOCOL; 174 return -1; 175 } else { 176 if (session->count > last) { 177 session->new_msg_exist = TRUE; 178 session->cur_msg = last + 1; 179 } else 180 session->cur_msg = 0; 181 } 182 183 return PS_SUCCESS; 184 } 185 186 static gint pop3_getrange_uidl_send(Pop3Session *session) 187 { 188 session->state = POP3_GETRANGE_UIDL; 189 pop3_gen_send(session, "UIDL"); 190 return PS_SUCCESS; 191 } 192 193 static gint pop3_getrange_uidl_recv(Pop3Session *session, const gchar *data, 194 guint len) 195 { 196 gchar id[IDLEN + 1]; 197 gchar buf[POPBUFSIZE]; 198 gint buf_len; 199 guint32 num; 200 time_t recv_time; 201 const gchar *p = data; 202 const gchar *lastp = data + len; 203 const gchar *newline; 204 205 while (p < lastp) { 206 if ((newline = memchr(p, '\r', lastp - p)) == NULL) 207 return -1; 208 buf_len = MIN(newline - p, sizeof(buf) - 1); 209 memcpy(buf, p, buf_len); 210 buf[buf_len] = '\0'; 211 212 p = newline + 1; 213 if (p < lastp && *p == '\n') p++; 214 215 if (sscanf(buf, "%d %" Xstr(IDLEN) "s", &num, id) != 2 || 216 num <= 0 || num > session->count) { 217 log_warning(LOG_PROTOCOL, _("invalid UIDL response: %s\n"), buf); 218 continue; 219 } 220 221 session->msg[num].uidl = g_strdup(id); 222 223 recv_time = (time_t)(GPOINTER_TO_INT(g_hash_table_lookup( 224 session->uidl_table, id))); 225 session->msg[num].recv_time = recv_time; 226 227 if (recv_time != RECV_TIME_NONE) { 228 debug_print("num %d uidl %s: already got it\n", num, id); 229 } else { 230 debug_print("num %d uidl %s: unknown\n", num, id); 231 } 232 233 if (!session->new_msg_exist && 234 (recv_time == RECV_TIME_NONE || 235 session->ac_prefs->rmmail)) { 236 session->cur_msg = num; 237 session->new_msg_exist = TRUE; 238 } 239 } 240 241 session->uidl_is_valid = TRUE; 242 return PS_SUCCESS; 243 } 244 245 static gint pop3_getsize_list_send(Pop3Session *session) 246 { 247 session->state = POP3_GETSIZE_LIST; 248 pop3_gen_send(session, "LIST"); 249 return PS_SUCCESS; 250 } 251 252 static gint pop3_getsize_list_recv(Pop3Session *session, const gchar *data, 253 guint len) 254 { 255 gchar buf[POPBUFSIZE]; 256 gint buf_len; 257 guint num, size; 258 const gchar *p = data; 259 const gchar *lastp = data + len; 260 const gchar *newline; 261 262 while (p < lastp) { 263 if ((newline = memchr(p, '\r', lastp - p)) == NULL) 264 return -1; 265 buf_len = MIN(newline - p, sizeof(buf) - 1); 266 memcpy(buf, p, buf_len); 267 buf[buf_len] = '\0'; 268 269 p = newline + 1; 270 if (p < lastp && *p == '\n') p++; 271 272 if (sscanf(buf, "%u %u", &num, &size) != 2) { 273 session->error_val = PS_PROTOCOL; 274 return -1; 275 } 276 277 if (num > 0 && num <= session->count) 278 session->msg[num].size = size; 279 if (num > 0 && num < session->cur_msg) 280 session->cur_total_bytes += size; 281 } 282 283 return PS_SUCCESS; 284 } 285 286 static gint pop3_retr_send(Pop3Session *session) 287 { 288 session->state = POP3_RETR; 289 debug_print("retrieving %d [%s]\n", session->cur_msg, 290 session->msg[session->cur_msg].uidl ? 291 session->msg[session->cur_msg].uidl:" "); 292 pop3_gen_send(session, "RETR %d", session->cur_msg); 293 return PS_SUCCESS; 294 } 295 296 static gint pop3_retr_recv(Pop3Session *session, const gchar *data, guint len) 297 { 298 gchar *file; 299 gint drop_ok; 300 MailReceiveData mail_receive_data; 301 302 /* NOTE: we allocate a slightly larger buffer with a zero terminator 303 * because some plugins may think that it has a C string. */ 304 mail_receive_data.session = session; 305 mail_receive_data.data = g_new0(gchar, len + 1); 306 mail_receive_data.data_len = len; 307 memcpy(mail_receive_data.data, data, len); 308 309 hooks_invoke(MAIL_RECEIVE_HOOKLIST, &mail_receive_data); 310 311 file = get_tmp_file(); 312 if (pop3_write_msg_to_file(file, mail_receive_data.data, 313 mail_receive_data.data_len, NULL) < 0) { 314 g_free(file); 315 g_free(mail_receive_data.data); 316 session->error_val = PS_IOERR; 317 return -1; 318 } 319 g_free(mail_receive_data.data); 320 321 /* drop_ok: 0: success 1: don't receive -1: error */ 322 drop_ok = session->drop_message(session, file); 323 g_free(file); 324 if (drop_ok < 0) { 325 session->error_val = PS_IOERR; 326 return -1; 327 } 328 329 session->cur_total_bytes += session->msg[session->cur_msg].size; 330 session->cur_total_recv_bytes += session->msg[session->cur_msg].size; 331 session->cur_total_num++; 332 333 session->msg[session->cur_msg].received = TRUE; 334 session->msg[session->cur_msg].partial_recv = POP3_TOTALLY_RECEIVED; 335 336 session->msg[session->cur_msg].recv_time = 337 drop_ok == 1 ? RECV_TIME_KEEP : session->current_time; 338 339 return PS_SUCCESS; 340 } 341 342 static gint pop3_top_recv(Pop3Session *session, const gchar *data, guint len) 343 { 344 gchar *file; 345 gint drop_ok; 346 MailReceiveData mail_receive_data; 347 gchar *partial_notice = NULL; 348 349 /* NOTE: we allocate a slightly larger buffer with a zero terminator 350 * because some plugins may think that it has a C string. */ 351 mail_receive_data.session = session; 352 mail_receive_data.data = g_new0(gchar, len + 1); 353 mail_receive_data.data_len = len; 354 memcpy(mail_receive_data.data, data, len); 355 356 hooks_invoke(MAIL_RECEIVE_HOOKLIST, &mail_receive_data); 357 358 partial_notice = g_strdup_printf("SC-Marked-For-Download: 0\n" 359 "SC-Partially-Retrieved: %s\n" 360 "SC-Account-Server: %s\n" 361 "SC-Account-Login: %s\n" 362 "SC-Message-Size: %d", 363 session->msg[session->cur_msg].uidl, 364 session->ac_prefs->recv_server, 365 session->ac_prefs->userid, 366 session->msg[session->cur_msg].size); 367 file = get_tmp_file(); 368 if (pop3_write_msg_to_file(file, mail_receive_data.data, 369 mail_receive_data.data_len, 370 partial_notice) < 0) { 371 g_free(file); 372 g_free(mail_receive_data.data); 373 session->error_val = PS_IOERR; 374 g_free(partial_notice); 375 return -1; 376 } 377 g_free(mail_receive_data.data); 378 g_free(partial_notice); 379 380 /* drop_ok: 0: success 1: don't receive -1: error */ 381 drop_ok = session->drop_message(session, file); 382 g_free(file); 383 if (drop_ok < 0) { 384 session->error_val = PS_IOERR; 385 return -1; 386 } 387 388 session->cur_total_bytes += session->msg[session->cur_msg].size; 389 session->cur_total_recv_bytes += session->msg[session->cur_msg].size; 390 session->cur_total_num++; 391 392 session->msg[session->cur_msg].received = TRUE; 393 session->msg[session->cur_msg].partial_recv = POP3_PARTIALLY_RECEIVED; 394 session->msg[session->cur_msg].recv_time = 395 drop_ok == 1 ? RECV_TIME_KEEP : session->current_time; 396 397 return PS_SUCCESS; 398 } 399 400 static gint pop3_delete_send(Pop3Session *session) 401 { 402 session->state = POP3_DELETE; 403 pop3_gen_send(session, "DELE %d", session->cur_msg); 404 return PS_SUCCESS; 405 } 406 407 static gint pop3_delete_recv(Pop3Session *session) 408 { 409 session->msg[session->cur_msg].deleted = TRUE; 410 return PS_SUCCESS; 411 } 412 413 static gint pop3_logout_send(Pop3Session *session) 414 { 415 session->state = POP3_LOGOUT; 416 pop3_gen_send(session, "QUIT"); 417 return PS_SUCCESS; 418 } 419 420 static void pop3_gen_send(Pop3Session *session, const gchar *format, ...) 421 { 422 gchar buf[POPBUFSIZE + 1]; 423 int length; 424 va_list args; 425 426 va_start(args, format); 427 length = g_vsnprintf(buf, sizeof(buf) - 2, format, args); 428 va_end(args); 429 if (length > POPBUFSIZE) 430 g_warning("POP buffer length overflow! (wanted to write = %d > %d = buf size)\n", length, POPBUFSIZE); 431 432 if (!g_ascii_strncasecmp(buf, "PASS ", 5)) 433 log_print(LOG_PROTOCOL, "POP> PASS ********\n"); 434 else if (!g_ascii_strncasecmp(buf, "AUTH XOAUTH2", 12)) 435 log_print(LOG_PROTOCOL, "POP> AUTH XOAUTH2 ********\n"); 436 else if (length > 128) 437 log_print(LOG_PROTOCOL, "POP> %.128s... (truncated from %d)\n", buf, length); 438 else 439 log_print(LOG_PROTOCOL, "POP> %s\n", buf); 440 441 session_send_msg(SESSION(session), buf); 442 } 443 444 Session *pop3_session_new(PrefsAccount *account) 445 { 446 Pop3Session *session; 447 448 cm_return_val_if_fail(account != NULL, NULL); 449 450 account->receive_in_progress = TRUE; 451 452 session = g_new0(Pop3Session, 1); 453 454 session_init(SESSION(session), account, FALSE); 455 456 SESSION(session)->type = SESSION_POP3; 457 458 SESSION(session)->recv_msg = pop3_session_recv_msg; 459 SESSION(session)->recv_data_finished = pop3_session_recv_data_finished; 460 SESSION(session)->send_data_finished = NULL; 461 SESSION(session)->ssl_cert_auto_accept = TRUE; 462 SESSION(session)->destroy = pop3_session_destroy; 463 464 if (account->set_gnutls_priority && account->gnutls_priority && 465 strlen(account->gnutls_priority) != 0) 466 SESSION(session)->gnutls_priority = g_strdup(account->gnutls_priority); 467 SESSION(session)->use_tls_sni = account->use_tls_sni; 468 469 session->state = POP3_READY; 470 session->ac_prefs = account; 471 session->pop_before_smtp = FALSE; 472 pop3_get_uidl_table(account, session); 473 session->current_time = time(NULL); 474 session->error_val = PS_SUCCESS; 475 session->error_msg = NULL; 476 return SESSION(session); 477 } 478 479 static void pop3_session_destroy(Session *session) 480 { 481 Pop3Session *pop3_session = POP3_SESSION(session); 482 gint n; 483 484 cm_return_if_fail(session != NULL); 485 486 for (n = 1; n <= pop3_session->count; n++) 487 g_free(pop3_session->msg[n].uidl); 488 g_free(pop3_session->msg); 489 490 if (pop3_session->uidl_table) { 491 hash_free_strings(pop3_session->uidl_table); 492 g_hash_table_destroy(pop3_session->uidl_table); 493 } 494 495 if (pop3_session->partial_recv_table) { 496 hash_free_strings(pop3_session->partial_recv_table); 497 g_hash_table_destroy(pop3_session->partial_recv_table); 498 } 499 500 g_free(pop3_session->greeting); 501 g_free(pop3_session->user); 502 g_free(pop3_session->pass); 503 g_free(pop3_session->error_msg); 504 505 pop3_session->ac_prefs->receive_in_progress = FALSE; 506 } 507 508 static void pop3_get_uidl_table(PrefsAccount *ac_prefs, Pop3Session *session) 509 { 510 GHashTable *table; 511 GHashTable *partial_recv_table; 512 gchar *path; 513 FILE *fp; 514 gchar buf[POPBUFSIZE]; 515 gchar uidl[POPBUFSIZE]; 516 time_t recv_time; 517 time_t now; 518 gint partial_recv; 519 gchar *sanitized_uid = g_strdup(ac_prefs->userid); 520 521 subst_for_filename(sanitized_uid); 522 523 table = g_hash_table_new(g_str_hash, g_str_equal); 524 partial_recv_table = g_hash_table_new(g_str_hash, g_str_equal); 525 526 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, 527 "uidl", G_DIR_SEPARATOR_S, ac_prefs->recv_server, 528 "-", sanitized_uid, NULL); 529 530 g_free(sanitized_uid); 531 if ((fp = g_fopen(path, "rb")) == NULL) { 532 if (ENOENT != errno) FILE_OP_ERROR(path, "g_fopen"); 533 g_free(path); 534 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, 535 "uidl-", ac_prefs->recv_server, 536 "-", ac_prefs->userid, NULL); 537 if ((fp = g_fopen(path, "rb")) == NULL) { 538 if (ENOENT != errno) FILE_OP_ERROR(path, "g_fopen"); 539 g_free(path); 540 session->uidl_table = table; 541 session->partial_recv_table = partial_recv_table; 542 return; 543 } 544 } 545 g_free(path); 546 547 now = time(NULL); 548 549 while (fgets(buf, sizeof(buf), fp) != NULL) { 550 gchar tmp[POPBUFSIZE]; 551 strretchomp(buf); 552 recv_time = RECV_TIME_NONE; 553 partial_recv = POP3_TOTALLY_RECEIVED; 554 555 if (sscanf(buf, "%s\t%ld\t%s", uidl, (long int *) &recv_time, tmp) < 3) { 556 if (sscanf(buf, "%s\t%ld", uidl, (long int *) &recv_time) != 2) { 557 if (sscanf(buf, "%s", uidl) != 1) 558 continue; 559 else { 560 recv_time = now; 561 strcpy(tmp, "0"); 562 } 563 } else { 564 strcpy(tmp, "0"); 565 } 566 } 567 568 if (recv_time == RECV_TIME_NONE) 569 recv_time = RECV_TIME_RECEIVED; 570 g_hash_table_insert(table, g_strdup(uidl), 571 GINT_TO_POINTER(recv_time)); 572 if (strlen(tmp) == 1) 573 partial_recv = atoi(tmp); /* totally received ?*/ 574 else 575 partial_recv = POP3_MUST_COMPLETE_RECV; 576 577 g_hash_table_insert(partial_recv_table, g_strdup(uidl), 578 GINT_TO_POINTER(partial_recv)); 579 } 580 581 fclose(fp); 582 session->uidl_table = table; 583 session->partial_recv_table = partial_recv_table; 584 585 return; 586 } 587 588 #define TRY(func) \ 589 if (!(func)) \ 590 { \ 591 g_warning("failed to write"); \ 592 goto err_write; \ 593 } \ 594 595 gint pop3_write_uidl_list(Pop3Session *session) 596 { 597 gchar *path, *tmp_path; 598 FILE *fp; 599 Pop3MsgInfo *msg; 600 gint n; 601 gchar *sanitized_uid = g_strdup(session->ac_prefs->userid); 602 603 subst_for_filename(sanitized_uid); 604 605 if (!session->uidl_is_valid) { 606 g_free(sanitized_uid); 607 return 0; 608 } 609 610 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, 611 "uidl", G_DIR_SEPARATOR_S, 612 session->ac_prefs->recv_server, 613 "-", sanitized_uid, NULL); 614 tmp_path = g_strconcat(path, ".tmp", NULL); 615 616 g_free(sanitized_uid); 617 618 if ((fp = g_fopen(tmp_path, "wb")) == NULL) { 619 FILE_OP_ERROR(tmp_path, "g_fopen"); 620 goto err_write; 621 } 622 623 for (n = 1; n <= session->count; n++) { 624 msg = &session->msg[n]; 625 if (msg->uidl && msg->received && 626 (!msg->deleted || session->state != POP3_DONE)) 627 TRY(fprintf(fp, "%s\t%ld\t%d\n", 628 msg->uidl, (long int) 629 msg->recv_time, 630 msg->partial_recv) 631 > 0); 632 } 633 634 if (fclose(fp) == EOF) { 635 FILE_OP_ERROR(tmp_path, "fclose"); 636 fp = NULL; 637 goto err_write; 638 } 639 fp = NULL; 640 if (g_rename(tmp_path, path) < 0) { 641 FILE_OP_ERROR(path, "rename"); 642 goto err_write; 643 } 644 g_free(path); 645 g_free(tmp_path); 646 return 0; 647 err_write: 648 if (fp) 649 fclose(fp); 650 g_free(path); 651 g_free(tmp_path); 652 return -1; 653 } 654 655 #undef TRY 656 657 static gint pop3_write_msg_to_file(const gchar *file, const gchar *data, 658 guint len, const gchar *prefix) 659 { 660 FILE *fp; 661 const gchar *prev, *cur; 662 663 cm_return_val_if_fail(file != NULL, -1); 664 665 if ((fp = g_fopen(file, "wb")) == NULL) { 666 FILE_OP_ERROR(file, "g_fopen"); 667 return -1; 668 } 669 670 if (prefix != NULL) { 671 if (fprintf(fp, "%s\n", prefix) < 0) { 672 FILE_OP_ERROR(file, "fprintf"); 673 fclose(fp); 674 unlink(file); 675 return -1; 676 } 677 } 678 679 /* +------------------+----------------+--------------------------+ * 680 * ^data ^prev ^cur data+len-1^ */ 681 682 prev = data; 683 while ((cur = (gchar *)my_memmem(prev, len - (prev - data), "\r\n", 2)) 684 != NULL) { 685 if ((cur > prev && fwrite(prev, 1, cur - prev, fp) < 1) || 686 fputc('\n', fp) == EOF) { 687 FILE_OP_ERROR(file, "fwrite"); 688 g_warning("can't write to file: %s", file); 689 fclose(fp); 690 unlink(file); 691 return -1; 692 } 693 694 if (cur == data + len - 1) { 695 prev = cur + 1; 696 break; 697 } 698 699 if (*(cur + 1) == '\n') 700 prev = cur + 2; 701 else 702 prev = cur + 1; 703 704 if (prev - data < len - 1 && *prev == '.' && *(prev + 1) == '.') 705 prev++; 706 707 if (prev - data >= len) 708 break; 709 } 710 711 if (prev - data < len && 712 fwrite(prev, 1, len - (prev - data), fp) < 1) { 713 FILE_OP_ERROR(file, "fwrite"); 714 g_warning("can't write to file: %s", file); 715 fclose(fp); 716 unlink(file); 717 return -1; 718 } 719 if (data[len - 1] != '\r' && data[len - 1] != '\n') { 720 if (fputc('\n', fp) == EOF) { 721 FILE_OP_ERROR(file, "fputc"); 722 g_warning("can't write to file: %s", file); 723 fclose(fp); 724 unlink(file); 725 return -1; 726 } 727 } 728 729 if (fclose(fp) == EOF) { 730 FILE_OP_ERROR(file, "fclose"); 731 unlink(file); 732 return -1; 733 } 734 735 return 0; 736 } 737 738 static Pop3State pop3_lookup_next(Pop3Session *session) 739 { 740 Pop3MsgInfo *msg; 741 PrefsAccount *ac = session->ac_prefs; 742 gint size; 743 744 for (;;) { 745 msg = &session->msg[session->cur_msg]; 746 size = msg->size; 747 748 if (ac->rmmail && 749 msg->recv_time != RECV_TIME_NONE && 750 msg->recv_time != RECV_TIME_KEEP && 751 msg->partial_recv == POP3_TOTALLY_RECEIVED && 752 session->current_time - msg->recv_time >= 753 ((ac->msg_leave_time * 24 * 60 * 60) + 754 (ac->msg_leave_hour * 60 * 60))) { 755 log_message(LOG_PROTOCOL, 756 _("POP: Deleting expired message %d [%s]\n"), 757 session->cur_msg, msg->uidl?msg->uidl:" "); 758 session->cur_total_bytes += size; 759 pop3_delete_send(session); 760 return POP3_DELETE; 761 } 762 763 if (size == 0 || msg->received) { 764 session->cur_total_bytes += size; 765 if (session->cur_msg == session->count) { 766 pop3_logout_send(session); 767 return POP3_LOGOUT; 768 } else 769 session->cur_msg++; 770 } else 771 break; 772 } 773 774 pop3_retr_send(session); 775 return POP3_RETR; 776 } 777 778 static Pop3ErrorValue pop3_ok(Pop3Session *session, const gchar *msg) 779 { 780 Pop3ErrorValue ok; 781 782 log_print(LOG_PROTOCOL, "POP< %s\n", msg); 783 784 /* exchange replies '+' in response to first line of auth xoauth */ 785 if (!strncmp(msg, "+OK", 3) || !strncmp(msg, "+", 1)) 786 ok = PS_SUCCESS; 787 else if (!strncmp(msg, "-ERR", 4)) { 788 if (strstr(msg + 4, "lock") || 789 strstr(msg + 4, "Lock") || 790 strstr(msg + 4, "LOCK") || 791 strstr(msg + 4, "wait")) { 792 log_error(LOG_PROTOCOL, _("mailbox is locked\n")); 793 ok = PS_LOCKBUSY; 794 } else if (strcasestr(msg + 4, "timeout")) { 795 log_error(LOG_PROTOCOL, _("Session timeout\n")); 796 ok = PS_ERROR; 797 } else { 798 switch (session->state) { 799 case POP3_STLS: 800 log_error(LOG_PROTOCOL, _("couldn't start STARTTLS session\n")); 801 ok = PS_ERROR; 802 break; 803 case POP3_GETAUTH_USER: 804 case POP3_GETAUTH_PASS: 805 log_error(LOG_PROTOCOL, _("error occurred on authentication\n")); 806 ok = PS_AUTHFAIL; 807 break; 808 case POP3_GETRANGE_LAST: 809 case POP3_GETRANGE_UIDL: 810 case POP3_TOP: 811 log_warning(LOG_PROTOCOL, _("command not supported\n")); 812 ok = PS_NOTSUPPORTED; 813 break; 814 815 default: 816 log_error(LOG_PROTOCOL, _("error occurred on POP session\n")); 817 ok = PS_ERROR; 818 } 819 } 820 821 g_free(session->error_msg); 822 session->error_msg = g_strdup(msg); 823 g_printerr("POP: %s\n", msg); 824 } else 825 ok = PS_PROTOCOL; 826 827 session->error_val = ok; 828 return ok; 829 } 830 831 static gint pop3_session_recv_msg(Session *session, const gchar *msg) 832 { 833 Pop3Session *pop3_session = POP3_SESSION(session); 834 Pop3ErrorValue val = PS_SUCCESS; 835 const gchar *body; 836 837 body = msg; 838 if (pop3_session->state != POP3_GETRANGE_UIDL_RECV && 839 pop3_session->state != POP3_GETSIZE_LIST_RECV) { 840 val = pop3_ok(pop3_session, msg); 841 if (val != PS_SUCCESS) { 842 if (val != PS_NOTSUPPORTED) { 843 pop3_session->state = POP3_ERROR; 844 return -1; 845 } 846 } 847 848 if (*body == '+' || *body == '-') 849 body++; 850 while (g_ascii_isalpha(*body)) 851 body++; 852 while (g_ascii_isspace(*body)) 853 body++; 854 } 855 856 switch (pop3_session->state) { 857 case POP3_READY: 858 case POP3_GREETING: 859 pop3_greeting_recv(pop3_session, body); 860 if (pop3_session->ac_prefs->ssl_pop == SSL_STARTTLS) 861 val = pop3_stls_send(pop3_session); 862 else 863 val = pop3_getauth_user_send(pop3_session); 864 break; 865 case POP3_STLS: 866 if (pop3_stls_recv(pop3_session) != PS_SUCCESS) 867 return -1; 868 val = pop3_getauth_user_send(pop3_session); 869 break; 870 case POP3_GETAUTH_USER: 871 val = pop3_getauth_pass_send(pop3_session); 872 break; 873 case POP3_GETAUTH_PASS: 874 if (!pop3_session->pop_before_smtp) 875 val = pop3_getrange_stat_send(pop3_session); 876 else 877 val = pop3_logout_send(pop3_session); 878 break; 879 case POP3_GETRANGE_STAT: 880 if (pop3_getrange_stat_recv(pop3_session, body) < 0) 881 return -1; 882 if (pop3_session->count > 0) 883 val = pop3_getrange_uidl_send(pop3_session); 884 else 885 val = pop3_logout_send(pop3_session); 886 break; 887 case POP3_GETRANGE_LAST: 888 if (val == PS_NOTSUPPORTED) 889 pop3_session->error_val = PS_SUCCESS; 890 else if (pop3_getrange_last_recv(pop3_session, body) < 0) 891 return -1; 892 if (pop3_session->cur_msg > 0) 893 val = pop3_getsize_list_send(pop3_session); 894 else 895 val = pop3_logout_send(pop3_session); 896 break; 897 case POP3_GETRANGE_UIDL: 898 if (val == PS_NOTSUPPORTED) { 899 pop3_session->error_val = PS_SUCCESS; 900 val = pop3_getrange_last_send(pop3_session); 901 } else { 902 pop3_session->state = POP3_GETRANGE_UIDL_RECV; 903 session_recv_data(session, 0, ".\r\n"); 904 } 905 break; 906 case POP3_GETSIZE_LIST: 907 pop3_session->state = POP3_GETSIZE_LIST_RECV; 908 session_recv_data(session, 0, ".\r\n"); 909 break; 910 case POP3_RETR: 911 pop3_session->state = POP3_RETR_RECV; 912 session_recv_data(session, 0, ".\r\n"); 913 break; 914 case POP3_TOP: 915 if (val == PS_NOTSUPPORTED) { 916 pop3_session->error_val = PS_SUCCESS; 917 } else { 918 pop3_session->state = POP3_TOP_RECV; 919 session_recv_data(session, 0, ".\r\n"); 920 } 921 break; 922 case POP3_DELETE: 923 pop3_delete_recv(pop3_session); 924 if (pop3_session->cur_msg == pop3_session->count) 925 val = pop3_logout_send(pop3_session); 926 else { 927 pop3_session->cur_msg++; 928 if (pop3_lookup_next(pop3_session) == POP3_ERROR) 929 return -1; 930 } 931 break; 932 case POP3_LOGOUT: 933 pop3_session->state = POP3_DONE; 934 session_disconnect(session); 935 break; 936 case POP3_ERROR: 937 default: 938 return -1; 939 } 940 941 return val == PS_SUCCESS?0:-1; 942 } 943 944 static gint pop3_session_recv_data_finished(Session *session, guchar *data, 945 guint len) 946 { 947 Pop3Session *pop3_session = POP3_SESSION(session); 948 Pop3ErrorValue val = PS_SUCCESS; 949 950 switch (pop3_session->state) { 951 case POP3_GETRANGE_UIDL_RECV: 952 val = pop3_getrange_uidl_recv(pop3_session, data, len); 953 if (val == PS_SUCCESS) { 954 if (pop3_session->new_msg_exist) 955 pop3_getsize_list_send(pop3_session); 956 else 957 pop3_logout_send(pop3_session); 958 } else 959 return -1; 960 break; 961 case POP3_GETSIZE_LIST_RECV: 962 val = pop3_getsize_list_recv(pop3_session, data, len); 963 if (val == PS_SUCCESS) { 964 if (pop3_lookup_next(pop3_session) == POP3_ERROR) 965 return -1; 966 } else 967 return -1; 968 break; 969 case POP3_RETR_RECV: 970 if (pop3_retr_recv(pop3_session, data, len) < 0) 971 return -1; 972 973 if (pop3_session->ac_prefs->rmmail && 974 pop3_session->ac_prefs->msg_leave_time == 0 && 975 pop3_session->ac_prefs->msg_leave_hour == 0 && 976 pop3_session->msg[pop3_session->cur_msg].recv_time 977 != RECV_TIME_KEEP) 978 pop3_delete_send(pop3_session); 979 else if (pop3_session->cur_msg == pop3_session->count) 980 pop3_logout_send(pop3_session); 981 else { 982 pop3_session->cur_msg++; 983 if (pop3_lookup_next(pop3_session) == POP3_ERROR) 984 return -1; 985 } 986 break; 987 case POP3_TOP_RECV: 988 if (pop3_top_recv(pop3_session, data, len) < 0) 989 return -1; 990 991 if (pop3_session->cur_msg == pop3_session->count) 992 pop3_logout_send(pop3_session); 993 else { 994 pop3_session->cur_msg++; 995 if (pop3_lookup_next(pop3_session) == POP3_ERROR) 996 return -1; 997 } 998 break; 999 case POP3_TOP: 1000 log_warning(LOG_PROTOCOL, _("TOP command unsupported\n")); 1001 if (pop3_session->cur_msg == pop3_session->count) 1002 pop3_logout_send(pop3_session); 1003 else { 1004 pop3_session->cur_msg++; 1005 if (pop3_lookup_next(pop3_session) == POP3_ERROR) 1006 return -1; 1007 } 1008 break; 1009 case POP3_ERROR: 1010 default: 1011 return -1; 1012 } 1013 1014 return 0; 1015 }