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