1 /* 2 * Copyright 2007-2016, Haiku, Inc. All rights reserved. 3 * Copyright 2001-2002 Dr. Zoidberg Enterprises. All rights reserved. 4 * Copyright 2011, Clemens Zeidler <haiku@clemens-zeidler.de> 5 * 6 * Distributed under the terms of the MIT License. 7 */ 8 9 10 //! POP3Protocol - implementation of the POP3 protocol 11 12 13 #include "POP3.h" 14 15 #include <errno.h> 16 #include <netdb.h> 17 #include <stdio.h> 18 #include <stdlib.h> 19 #include <sys/socket.h> 20 #include <sys/time.h> 21 #include <unistd.h> 22 23 #include <arpa/inet.h> 24 25 #include "md5.h" 26 27 #include <Alert.h> 28 #include <Catalog.h> 29 #include <Debug.h> 30 #include <Directory.h> 31 #include <fs_attr.h> 32 #include <Path.h> 33 #include <SecureSocket.h> 34 #include <String.h> 35 #include <VolumeRoster.h> 36 #include <Query.h> 37 38 #include <mail_util.h> 39 40 #include "crypt.h" 41 #include "MailSettings.h" 42 #include "MessageIO.h" 43 44 45 #undef B_TRANSLATION_CONTEXT 46 #define B_TRANSLATION_CONTEXT "pop3" 47 48 49 #define POP3_RETRIEVAL_TIMEOUT 60000000 50 #define CRLF "\r\n" 51 52 53 static void 54 NotHere(BStringList& that, BStringList& otherList, BStringList* results) 55 { 56 for (int32 i = 0; i < otherList.CountStrings(); i++) { 57 if (!that.HasString(otherList.StringAt(i))) 58 results->Add(otherList.StringAt(i)); 59 } 60 } 61 62 63 // #pragma mark - 64 65 66 POP3Protocol::POP3Protocol(const BMailAccountSettings& settings) 67 : 68 BInboundMailProtocol("POP3", settings), 69 fNumMessages(-1), 70 fMailDropSize(0), 71 fServerConnection(NULL) 72 { 73 printf("POP3Protocol::POP3Protocol(BMailAccountSettings* settings)\n"); 74 fSettings = fAccountSettings.InboundSettings(); 75 76 fUseSSL = fSettings.FindInt32("flavor") == 1 ? true : false; 77 78 if (fSettings.FindString("destination", &fDestinationDir) != B_OK) 79 fDestinationDir = "/boot/home/mail/in"; 80 81 create_directory(fDestinationDir, 0777); 82 83 fFetchBodyLimit = -1; 84 if (fSettings.HasInt32("partial_download_limit")) 85 fFetchBodyLimit = fSettings.FindInt32("partial_download_limit"); 86 } 87 88 89 POP3Protocol::~POP3Protocol() 90 { 91 Disconnect(); 92 } 93 94 95 status_t 96 POP3Protocol::Connect() 97 { 98 status_t error = Open(fSettings.FindString("server"), 99 fSettings.FindInt32("port"), fSettings.FindInt32("flavor")); 100 if (error != B_OK) 101 return error; 102 103 char* password = get_passwd(&fSettings, "cpasswd"); 104 105 error = Login(fSettings.FindString("username"), password, 106 fSettings.FindInt32("auth_method")); 107 delete[] password; 108 109 if (error != B_OK) 110 fServerConnection->Disconnect(); 111 return error; 112 } 113 114 115 status_t 116 POP3Protocol::Disconnect() 117 { 118 if (fServerConnection == NULL) 119 return B_OK; 120 121 SendCommand("QUIT" CRLF); 122 123 fServerConnection->Disconnect(); 124 delete fServerConnection; 125 fServerConnection = NULL; 126 127 return B_OK; 128 } 129 130 131 status_t 132 POP3Protocol::SyncMessages() 133 { 134 bool leaveOnServer; 135 if (fSettings.FindBool("leave_mail_on_server", &leaveOnServer) != B_OK) 136 leaveOnServer = true; 137 138 // create directory if not exist 139 create_directory(fDestinationDir, 0777); 140 141 printf("POP3Protocol::SyncMessages()\n"); 142 _ReadManifest(); 143 144 SetTotalItems(2); 145 ReportProgress(1, 0, B_TRANSLATE("Connect to server" B_UTF8_ELLIPSIS)); 146 147 status_t error = Connect(); 148 if (error != B_OK) { 149 printf("POP3 could not connect: %s\n", strerror(error)); 150 ResetProgress(); 151 return error; 152 } 153 154 ReportProgress(1, 0, B_TRANSLATE("Getting UniqueIDs" B_UTF8_ELLIPSIS)); 155 156 error = _RetrieveUniqueIDs(); 157 if (error < B_OK) { 158 ResetProgress(); 159 Disconnect(); 160 return error; 161 } 162 163 BStringList toDownload; 164 NotHere(fManifest, fUniqueIDs, &toDownload); 165 166 int32 numMessages = toDownload.CountStrings(); 167 if (numMessages == 0) { 168 CheckForDeletedMessages(); 169 ResetProgress(); 170 Disconnect(); 171 return B_OK; 172 } 173 174 ResetProgress(); 175 SetTotalItems(toDownload.CountStrings()); 176 SetTotalItemsSize(fTotalSize); 177 178 printf("POP3: Messages to download: %i\n", (int)toDownload.CountStrings()); 179 for (int32 i = 0; i < toDownload.CountStrings(); i++) { 180 const char* uid = toDownload.StringAt(i); 181 int32 toRetrieve = fUniqueIDs.IndexOf(uid); 182 183 if (toRetrieve < 0) { 184 // should not happen! 185 error = B_NAME_NOT_FOUND; 186 printf("POP3: uid %s index %i not found in fUniqueIDs!\n", uid, 187 (int)toRetrieve); 188 continue; 189 } 190 191 BPath path(fDestinationDir); 192 BString fileName = "Downloading file... uid: "; 193 fileName += uid; 194 fileName.ReplaceAll("/", "_SLASH_"); 195 path.Append(fileName); 196 BEntry entry(path.Path()); 197 BFile file(&entry, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE); 198 error = file.InitCheck(); 199 if (error != B_OK) { 200 printf("POP3: Can't create file %s\n ", path.Path()); 201 break; 202 } 203 BMailMessageIO mailIO(this, &file, toRetrieve); 204 BMessage attributes; 205 206 entry_ref ref; 207 entry.GetRef(&ref); 208 209 int32 size = MessageSize(toRetrieve); 210 if (fFetchBodyLimit < 0 || size <= fFetchBodyLimit) { 211 error = mailIO.Seek(0, SEEK_END); 212 if (error < 0) { 213 printf("POP3: Failed to download body %s\n ", uid); 214 break; 215 } 216 ProcessMessageFetched(ref, file, attributes); 217 218 if (!leaveOnServer) 219 Delete(toRetrieve); 220 } else { 221 int32 dummy; 222 error = mailIO.ReadAt(0, &dummy, 1); 223 if (error < 0) { 224 printf("POP3: Failed to download header %s\n ", uid); 225 break; 226 } 227 ProcessHeaderFetched(ref, file, attributes); 228 } 229 ReportProgress(1, 0); 230 231 const BString uidStr(uid); 232 if (file.WriteAttrString("MAIL:unique_id", &uidStr) < 0) 233 error = B_ERROR; 234 235 file.WriteAttr("MAIL:size", B_INT32_TYPE, 0, &size, sizeof(int32)); 236 write_read_attr(file, B_UNREAD); 237 238 // save manifest in case we get disturbed 239 fManifest.Add(uid); 240 _WriteManifest(); 241 } 242 243 ResetProgress(); 244 245 CheckForDeletedMessages(); 246 Disconnect(); 247 return error; 248 } 249 250 251 status_t 252 POP3Protocol::HandleFetchBody(const entry_ref& ref, const BMessenger& replyTo) 253 { 254 ResetProgress("Fetch body"); 255 SetTotalItems(1); 256 257 status_t error = Connect(); 258 if (error != B_OK) 259 return error; 260 261 error = _RetrieveUniqueIDs(); 262 if (error != B_OK) { 263 Disconnect(); 264 return error; 265 } 266 267 BFile file(&ref, B_READ_WRITE); 268 status_t status = file.InitCheck(); 269 if (status != B_OK) { 270 Disconnect(); 271 return status; 272 } 273 274 char uidString[256]; 275 BNode node(&ref); 276 if (node.ReadAttr("MAIL:unique_id", B_STRING_TYPE, 0, uidString, 256) < 0) { 277 Disconnect(); 278 return B_ERROR; 279 } 280 281 int32 toRetrieve = fUniqueIDs.IndexOf(uidString); 282 if (toRetrieve < 0) { 283 Disconnect(); 284 return B_NAME_NOT_FOUND; 285 } 286 287 bool leaveOnServer; 288 if (fSettings.FindBool("leave_mail_on_server", &leaveOnServer) != B_OK) 289 leaveOnServer = true; 290 291 // TODO: get rid of this BMailMessageIO! 292 BMailMessageIO io(this, &file, toRetrieve); 293 // read body 294 status = io.Seek(0, SEEK_END); 295 if (status < 0) { 296 Disconnect(); 297 return status; 298 } 299 300 BMessage attributes; 301 NotifyBodyFetched(ref, file, attributes); 302 ReplyBodyFetched(replyTo, ref, B_OK); 303 304 if (!leaveOnServer) 305 Delete(toRetrieve); 306 307 ReportProgress(1, 0); 308 ResetProgress(); 309 310 Disconnect(); 311 return B_OK; 312 } 313 314 315 status_t 316 POP3Protocol::Open(const char* server, int port, int) 317 { 318 ReportProgress(0, 0, B_TRANSLATE("Connecting to POP3 server" 319 B_UTF8_ELLIPSIS)); 320 321 if (port <= 0) 322 port = fUseSSL ? 995 : 110; 323 324 fLog = ""; 325 326 // Prime the error message 327 BString errorMessage(B_TRANSLATE("Error while connecting to server %serv")); 328 errorMessage.ReplaceFirst("%serv", server); 329 if (port != 110) 330 errorMessage << ":" << port; 331 332 delete fServerConnection; 333 fServerConnection = NULL; 334 335 BNetworkAddress address(server, port); 336 if (fUseSSL) 337 fServerConnection = new(std::nothrow) BSecureSocket(address); 338 else 339 fServerConnection = new(std::nothrow) BSocket(address); 340 341 status_t status = B_NO_MEMORY; 342 if (fServerConnection != NULL) 343 status = fServerConnection->InitCheck(); 344 345 BString line; 346 if (status == B_OK) { 347 ssize_t length = ReceiveLine(line); 348 if (length < 0) 349 status = length; 350 } 351 352 if (status != B_OK) { 353 fServerConnection->Disconnect(); 354 errorMessage << ": " << strerror(status); 355 ShowError(errorMessage.String()); 356 return status; 357 } 358 359 if (strncmp(line.String(), "+OK", 3) != 0) { 360 if (line.Length() > 0) { 361 errorMessage << B_TRANSLATE(". The server said:\n") 362 << line.String(); 363 } else 364 errorMessage << B_TRANSLATE(": No reply.\n"); 365 366 ShowError(errorMessage.String()); 367 fServerConnection->Disconnect(); 368 return B_ERROR; 369 } 370 371 fLog = line; 372 return B_OK; 373 } 374 375 376 status_t 377 POP3Protocol::Login(const char* uid, const char* password, int method) 378 { 379 status_t err; 380 381 BString errorMessage(B_TRANSLATE("Error while authenticating user %user")); 382 errorMessage.ReplaceFirst("%user", uid); 383 384 if (method == 1) { //APOP 385 int32 index = fLog.FindFirst("<"); 386 if(index != B_ERROR) { 387 ReportProgress(0, 0, B_TRANSLATE("Sending APOP authentication" 388 B_UTF8_ELLIPSIS)); 389 int32 end = fLog.FindFirst(">", index); 390 BString timestamp(""); 391 fLog.CopyInto(timestamp, index, end - index + 1); 392 timestamp += password; 393 char md5sum[33]; 394 MD5Digest((unsigned char*)timestamp.String(), md5sum); 395 BString cmd = "APOP "; 396 cmd += uid; 397 cmd += " "; 398 cmd += md5sum; 399 cmd += CRLF; 400 401 err = SendCommand(cmd.String()); 402 if (err != B_OK) { 403 errorMessage << B_TRANSLATE(". The server said:\n") << fLog; 404 ShowError(errorMessage.String()); 405 return err; 406 } 407 408 return B_OK; 409 } else { 410 errorMessage << B_TRANSLATE(": The server does not support APOP."); 411 ShowError(errorMessage.String()); 412 return B_NOT_ALLOWED; 413 } 414 } 415 ReportProgress(0, 0, B_TRANSLATE("Sending username" B_UTF8_ELLIPSIS)); 416 417 BString cmd = "USER "; 418 cmd += uid; 419 cmd += CRLF; 420 421 err = SendCommand(cmd.String()); 422 if (err != B_OK) { 423 errorMessage << B_TRANSLATE(". The server said:\n") << fLog; 424 ShowError(errorMessage.String()); 425 return err; 426 } 427 428 ReportProgress(0, 0, B_TRANSLATE("Sending password" B_UTF8_ELLIPSIS)); 429 cmd = "PASS "; 430 cmd += password; 431 cmd += CRLF; 432 433 err = SendCommand(cmd.String()); 434 if (err != B_OK) { 435 errorMessage << B_TRANSLATE(". The server said:\n") << fLog; 436 ShowError(errorMessage.String()); 437 return err; 438 } 439 440 return B_OK; 441 } 442 443 444 status_t 445 POP3Protocol::Stat() 446 { 447 ReportProgress(0, 0, B_TRANSLATE("Getting mailbox size" B_UTF8_ELLIPSIS)); 448 449 if (SendCommand("STAT" CRLF) < B_OK) 450 return B_ERROR; 451 452 int32 messages; 453 int32 dropSize; 454 if (sscanf(fLog.String(), "+OK %" B_SCNd32" %" B_SCNd32, &messages, 455 &dropSize) < 2) 456 return B_ERROR; 457 458 fNumMessages = messages; 459 fMailDropSize = dropSize; 460 461 return B_OK; 462 } 463 464 465 int32 466 POP3Protocol::Messages() 467 { 468 if (fNumMessages < 0) 469 Stat(); 470 471 return fNumMessages; 472 } 473 474 475 size_t 476 POP3Protocol::MailDropSize() 477 { 478 if (fNumMessages < 0) 479 Stat(); 480 481 return fMailDropSize; 482 } 483 484 485 void 486 POP3Protocol::CheckForDeletedMessages() 487 { 488 { 489 // Delete things from the manifest no longer on the server 490 BStringList list; 491 NotHere(fUniqueIDs, fManifest, &list); 492 fManifest.Remove(list); 493 } 494 495 if (!fSettings.FindBool("delete_remote_when_local") 496 || fManifest.CountStrings() == 0) 497 return; 498 499 BStringList toDelete; 500 501 BStringList queryContents; 502 BVolumeRoster volumes; 503 BVolume volume; 504 505 while (volumes.GetNextVolume(&volume) == B_OK) { 506 BQuery fido; 507 entry_ref entry; 508 509 fido.SetVolume(&volume); 510 fido.PushAttr(B_MAIL_ATTR_ACCOUNT_ID); 511 fido.PushInt32(fAccountSettings.AccountID()); 512 fido.PushOp(B_EQ); 513 514 fido.Fetch(); 515 516 BString uid; 517 while (fido.GetNextRef(&entry) == B_OK) { 518 BNode(&entry).ReadAttrString("MAIL:unique_id", &uid); 519 queryContents.Add(uid); 520 } 521 } 522 NotHere(queryContents, fManifest, &toDelete); 523 524 for (int32 i = 0; i < toDelete.CountStrings(); i++) { 525 printf("delete mail on server uid %s\n", toDelete.StringAt(i).String()); 526 Delete(fUniqueIDs.IndexOf(toDelete.StringAt(i))); 527 } 528 529 // Don't remove ids from fUniqueIDs, the indices have to stay the same when 530 // retrieving new messages. 531 fManifest.Remove(toDelete); 532 533 // TODO: at some point the purged manifest should be written to disk 534 // otherwise it will grow forever 535 } 536 537 538 status_t 539 POP3Protocol::Retrieve(int32 message, BPositionIO* to) 540 { 541 BString cmd; 542 cmd << "RETR " << message + 1 << CRLF; 543 status_t status = RetrieveInternal(cmd.String(), message, to, true); 544 ReportProgress(1, 0); 545 546 if (status == B_OK) { 547 // Check if the actual message size matches the expected one 548 int32 size = MessageSize(message); 549 to->Seek(0, SEEK_END); 550 if (to->Position() != size) { 551 printf("POP3Protocol::Retrieve Note: message size is %" B_PRIdOFF 552 ", was expecting %" B_PRId32 ", for message #%" B_PRId32 553 ". Could be a transmission error or a bad POP server " 554 "implementation (does it remove escape codes when it counts " 555 "size?).\n", to->Position(), size, message); 556 } 557 } 558 559 return status; 560 } 561 562 563 status_t 564 POP3Protocol::GetHeader(int32 message, BPositionIO* to) 565 { 566 BString cmd; 567 cmd << "TOP " << message + 1 << " 0" << CRLF; 568 return RetrieveInternal(cmd.String(), message, to, false); 569 } 570 571 572 status_t 573 POP3Protocol::RetrieveInternal(const char* command, int32 message, 574 BPositionIO* to, bool postProgress) 575 { 576 const int bufSize = 1024 * 30; 577 578 // To avoid waiting for the non-arrival of the next data packet, try to 579 // receive only the message size, plus the 3 extra bytes for the ".\r\n" 580 // after the message. Of course, if we get it wrong (or it is a huge 581 // message or has lines starting with escaped periods), it will then switch 582 // back to receiving full buffers until the message is done. 583 int amountToReceive = MessageSize(message) + 3; 584 if (amountToReceive >= bufSize || amountToReceive <= 0) 585 amountToReceive = bufSize - 1; 586 587 BString bufBString; // Used for auto-dealloc on return feature. 588 char* buf = bufBString.LockBuffer(bufSize); 589 int amountInBuffer = 0; 590 int amountReceived; 591 int testIndex; 592 char* testStr; 593 bool cont = true; 594 bool flushWholeBuffer = false; 595 to->Seek(0, SEEK_SET); 596 597 if (SendCommand(command) != B_OK) 598 return B_ERROR; 599 600 while (cont) { 601 status_t result = fServerConnection->WaitForReadable( 602 POP3_RETRIEVAL_TIMEOUT); 603 if (result == B_TIMED_OUT) { 604 // No data available, even after waiting a minute. 605 fLog = "POP3 timeout - no data received after a long wait."; 606 return B_TIMED_OUT; 607 } 608 if (amountToReceive > bufSize - 1 - amountInBuffer) 609 amountToReceive = bufSize - 1 - amountInBuffer; 610 611 amountReceived = fServerConnection->Read(buf + amountInBuffer, 612 amountToReceive); 613 614 if (amountReceived < 0) { 615 fLog = strerror(amountReceived); 616 return amountReceived; 617 } 618 if (amountReceived == 0) { 619 fLog = "POP3 data supposedly ready to receive but not received!"; 620 return B_ERROR; 621 } 622 623 amountToReceive = bufSize - 1; // For next time, read a full buffer. 624 amountInBuffer += amountReceived; 625 buf[amountInBuffer] = 0; // NUL stops tests past the end of buffer. 626 627 // Look for lines starting with a period. A single period by itself on 628 // a line "\r\n.\r\n" marks the end of the message (thus the need for 629 // at least five characters in the buffer for testing). A period 630 // "\r\n.Stuff" at the start of a line get deleted "\r\nStuff", since 631 // POP adds one as an escape code to let you have message text with 632 // lines starting with a period. For convenience, assume that no 633 // messages start with a period on the very first line, so we can 634 // search for the previous line's "\r\n". 635 636 for (testIndex = 0; testIndex <= amountInBuffer - 5; testIndex++) { 637 testStr = buf + testIndex; 638 if (testStr[0] == '\r' && testStr[1] == '\n' && testStr[2] == '.') { 639 if (testStr[3] == '\r' && testStr[4] == '\n') { 640 // Found the end of the message marker. 641 // Ignore remaining data. 642 if (amountInBuffer > testIndex + 5) { 643 printf("POP3Protocol::RetrieveInternal Ignoring %d " 644 "bytes of extra data past message end.\n", 645 amountInBuffer - (testIndex + 5)); 646 } 647 amountInBuffer = testIndex + 2; // Don't include ".\r\n". 648 buf[amountInBuffer] = 0; 649 cont = false; 650 } else { 651 // Remove an extra period at the start of a line. 652 // Inefficient, but it doesn't happen often that you have a 653 // dot starting a line of text. Of course, a file with a 654 // lot of double period lines will get processed very 655 // slowly. 656 memmove(buf + testIndex + 2, buf + testIndex + 3, 657 amountInBuffer - (testIndex + 3) + 1); 658 amountInBuffer--; 659 // Watch out for the end of buffer case, when the POP text 660 // is "\r\n..X". Don't want to leave the resulting 661 // "\r\n.X" in the buffer (flush out the whole buffer), 662 // since that will get mistakenly evaluated again in the 663 // next loop and delete a character by mistake. 664 if (testIndex >= amountInBuffer - 4 && testStr[2] == '.') { 665 printf("POP3Protocol::RetrieveInternal: Jackpot! " 666 "You have hit the rare situation with an escaped " 667 "period at the end of the buffer. Aren't you happy" 668 "it decodes it correctly?\n"); 669 flushWholeBuffer = true; 670 } 671 } 672 } 673 } 674 675 if (cont && !flushWholeBuffer) { 676 // Dump out most of the buffer, but leave the last 4 characters for 677 // comparison continuity, in case the line starting with a period 678 // crosses a buffer boundary. 679 if (amountInBuffer > 4) { 680 to->Write(buf, amountInBuffer - 4); 681 if (postProgress) 682 ReportProgress(0, amountInBuffer - 4); 683 memmove(buf, buf + amountInBuffer - 4, 4); 684 amountInBuffer = 4; 685 } 686 } else { 687 // Dump everything - end of message or flushing the whole buffer. 688 to->Write(buf, amountInBuffer); 689 if (postProgress) 690 ReportProgress(0, amountInBuffer); 691 amountInBuffer = 0; 692 } 693 } 694 return B_OK; 695 } 696 697 698 void 699 POP3Protocol::Delete(int32 index) 700 { 701 BString cmd = "DELE "; 702 cmd << (index + 1) << CRLF; 703 if (SendCommand(cmd.String()) != B_OK) { 704 // Error 705 } 706 #if DEBUG 707 puts(fLog.String()); 708 #endif 709 // The mail is just marked as deleted and removed from the server when 710 // sending the QUIT command. Because of that the message number stays 711 // the same and we keep the uid in the uid list. 712 } 713 714 715 size_t 716 POP3Protocol::MessageSize(int32 index) 717 { 718 return fSizes[index]; 719 } 720 721 722 ssize_t 723 POP3Protocol::ReceiveLine(BString& line) 724 { 725 int32 length = 0; 726 bool flag = false; 727 728 line = ""; 729 730 status_t result = fServerConnection->WaitForReadable( 731 POP3_RETRIEVAL_TIMEOUT); 732 if (result == B_TIMED_OUT) 733 return errno; 734 735 while (true) { 736 // Hope there's an end of line out there else this gets stuck. 737 int32 bytesReceived; 738 uint8 c = 0; 739 740 bytesReceived = fServerConnection->Read((char*)&c, 1); 741 if (bytesReceived < 0) 742 return bytesReceived; 743 744 if (c == '\n' || bytesReceived == 0) 745 break; 746 747 if (c == '\r') { 748 flag = true; 749 } else { 750 if (flag) { 751 length++; 752 line += '\r'; 753 flag = false; 754 } 755 length++; 756 line += (char)c; 757 } 758 } 759 760 return length; 761 } 762 763 764 status_t 765 POP3Protocol::SendCommand(const char* cmd) 766 { 767 // Flush any accumulated garbage data before we send our command, so we 768 // don't misinterrpret responses from previous commands (that got left over 769 // due to bugs) as being from this command. 770 while (fServerConnection->WaitForReadable(1000) == B_OK) { 771 char buffer[4096]; 772 ssize_t amountReceived = fServerConnection->Read(buffer, 773 sizeof(buffer) - 1); 774 if (amountReceived < 0) 775 return amountReceived; 776 777 buffer[amountReceived] = 0; 778 printf("POP3Protocol::SendCommand Bug! Had to flush %" B_PRIdSSIZE 779 " bytes: %s\n", amountReceived, buffer); 780 } 781 782 if (fServerConnection->Write(cmd, ::strlen(cmd)) < 0) { 783 fLog = strerror(errno); 784 printf("POP3Protocol::SendCommand Send \"%s\" failed, code %d: %s\n", 785 cmd, errno, fLog.String()); 786 return errno; 787 } 788 789 fLog = ""; 790 int32 length = ReceiveLine(fLog); 791 if (length <= 0 || fLog.ICompare("+OK", 3) == 0) 792 return B_OK; 793 794 if (fLog.ICompare("-ERR", 4) == 0) { 795 printf("POP3Protocol::SendCommand \"%s\" got error message " 796 "from server: %s\n", cmd, fLog.String()); 797 return B_ERROR; 798 } 799 800 printf("POP3Protocol::SendCommand \"%s\" got nonsense message " 801 "from server: %s\n", cmd, fLog.String()); 802 return B_BAD_DATA; 803 // If it's not +OK, and it's not -ERR, then what the heck 804 // is it? Presume an error 805 } 806 807 808 void 809 POP3Protocol::MD5Digest(unsigned char* in, char* asciiDigest) 810 { 811 unsigned char digest[16]; 812 813 MD5_CTX context; 814 815 MD5Init(&context); 816 MD5Update(&context, in, ::strlen((char*)in)); 817 MD5Final(digest, &context); 818 819 for (int i = 0; i < 16; i++) { 820 sprintf(asciiDigest + 2 * i, "%02x", digest[i]); 821 } 822 823 return; 824 } 825 826 827 status_t 828 POP3Protocol::_RetrieveUniqueIDs() 829 { 830 fUniqueIDs.MakeEmpty(); 831 fSizes.clear(); 832 fTotalSize = 0; 833 834 status_t status = SendCommand("UIDL" CRLF); 835 if (status != B_OK) 836 return status; 837 838 BString result; 839 int32 uidOffset; 840 while (ReceiveLine(result) > 0) { 841 if (result.ByteAt(0) == '.') 842 break; 843 844 uidOffset = result.FindFirst(' ') + 1; 845 result.Remove(0, uidOffset); 846 fUniqueIDs.Add(result); 847 } 848 849 if (SendCommand("LIST" CRLF) != B_OK) 850 return B_ERROR; 851 852 while (ReceiveLine(result) > 0) { 853 if (result.ByteAt(0) == '.') 854 break; 855 856 int32 index = result.FindLast(" "); 857 int32 size; 858 if (index >= 0) 859 size = atol(&result.String()[index]); 860 else 861 size = 0; 862 863 fTotalSize += size; 864 fSizes.push_back(size); 865 } 866 867 return B_OK; 868 } 869 870 871 void 872 POP3Protocol::_ReadManifest() 873 { 874 fManifest.MakeEmpty(); 875 BString attribute = "MAIL:"; 876 attribute << fAccountSettings.AccountID() << ":manifest"; 877 // In case someone puts multiple accounts in the same directory 878 879 BNode node(fDestinationDir); 880 if (node.InitCheck() != B_OK) 881 return; 882 883 // We already have a directory so we can try to read metadata 884 // from it. Note that it is normal for this directory not to 885 // be found on the first run as it will be later created by 886 // the INBOX system filter. 887 attr_info info; 888 if (node.GetAttrInfo(attribute.String(), &info) != B_OK || info.size == 0) 889 return; 890 891 void* flatmanifest = malloc(info.size); 892 node.ReadAttr(attribute.String(), fManifest.TypeCode(), 0, 893 flatmanifest, info.size); 894 fManifest.Unflatten(fManifest.TypeCode(), flatmanifest, info.size); 895 free(flatmanifest); 896 } 897 898 899 void 900 POP3Protocol::_WriteManifest() 901 { 902 BString attribute = "MAIL:"; 903 attribute << fAccountSettings.AccountID() << ":manifest"; 904 // In case someone puts multiple accounts in the same directory 905 BNode node(fDestinationDir); 906 if (node.InitCheck() != B_OK) { 907 ShowError("Error while saving account manifest: cannot use " 908 "destination directory."); 909 return; 910 } 911 912 node.RemoveAttr(attribute.String()); 913 ssize_t manifestsize = fManifest.FlattenedSize(); 914 void* flatmanifest = malloc(manifestsize); 915 fManifest.Flatten(flatmanifest, manifestsize); 916 status_t err = node.WriteAttr(attribute.String(), 917 fManifest.TypeCode(), 0, flatmanifest, manifestsize); 918 if (err < 0) { 919 BString error = "Error while saving account manifest: "; 920 error << strerror(err); 921 printf("moep error\n"); 922 ShowError(error.String()); 923 } 924 925 free(flatmanifest); 926 } 927 928 929 // #pragma mark - 930 931 932 BInboundMailProtocol* 933 instantiate_inbound_protocol(const BMailAccountSettings& settings) 934 { 935 return new POP3Protocol(settings); 936 } 937 938 939 status_t 940 pop3_smtp_auth(const BMailAccountSettings& settings) 941 { 942 POP3Protocol protocol(settings); 943 protocol.Connect(); 944 protocol.Disconnect(); 945 return B_OK; 946 } 947