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