status_t POP3Protocol::SyncMessages() { bool leaveOnServer; if (fSettings.FindBool("leave_mail_on_server", &leaveOnServer) != B_OK) leaveOnServer = true; // create directory if not exist create_directory(fDestinationDir, 0777); printf("POP3Protocol::SyncMessages()\n"); _ReadManifest(); SetTotalItems(2); ReportProgress(0, 1, "Connect to server..."); status_t error = Connect(); if (error < B_OK) { ResetProgress(); return error; } ReportProgress(0, 1, MDR_DIALECT_CHOICE("Getting UniqueIDs...", "固有のIDを取得中...")); error = _UniqueIDs(); if (error < B_OK) { ResetProgress(); return error; } BStringList toDownload; fManifest.NotHere(fUniqueIDs, &toDownload); int32 numMessages = toDownload.CountItems(); if (numMessages == 0) { CheckForDeletedMessages(); ResetProgress(); return B_OK; } ResetProgress(); SetTotalItems(toDownload.CountItems()); printf("POP3: Messages to download: %i\n", (int)toDownload.CountItems()); for (int32 i = 0; i < toDownload.CountItems(); i++) { const char* uid = toDownload.ItemAt(i); int32 toRetrieve = fUniqueIDs.IndexOf(uid); if (toRetrieve < 0) { // should not happen! error = B_NAME_NOT_FOUND; printf("POP3: uid %s index %i not found in fUniqueIDs!\n", uid, (int)toRetrieve); continue; } BPath path(fDestinationDir); BString fileName = "Downloading file... uid: "; fileName += uid; fileName.ReplaceAll("/", "_SLASH_"); path.Append(fileName); BEntry entry(path.Path()); BFile file(&entry, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE); error = file.InitCheck(); if (error != B_OK) { printf("POP3: Can't create file %s\n ", path.Path()); break; } BMailMessageIO mailIO(this, &file, toRetrieve); entry_ref ref; entry.GetRef(&ref); // the ref becomes invalid after renaming the file thus we already // write the status here MarkMessageAsRead(ref, B_UNREAD); int32 size = MessageSize(toRetrieve); if (fFetchBodyLimit < 0 || size <= fFetchBodyLimit) { error = mailIO.Seek(0, SEEK_END); if (error < 0) { printf("POP3: Failed to download body %s\n ", uid); break; } NotifyHeaderFetched(ref, &file); NotifyBodyFetched(ref, &file); if (!leaveOnServer) Delete(toRetrieve); } else { int32 dummy; error = mailIO.ReadAt(0, &dummy, 1); if (error < 0) { printf("POP3: Failed to download header %s\n ", uid); break; } NotifyHeaderFetched(ref, &file); } ReportProgress(0, 1); if (file.WriteAttr("MAIL:unique_id", B_STRING_TYPE, 0, uid, strlen(uid)) < 0) { error = B_ERROR; } file.WriteAttr("MAIL:size", B_INT32_TYPE, 0, &size, sizeof(int32)); // save manifest in case we get disturbed fManifest += uid; _WriteManifest(); } ResetProgress(); CheckForDeletedMessages(); Disconnect(); return error; }
status_t POP3Protocol::Retrieve(int32 message, BPositionIO *write_to) { status_t returnCode; BString cmd; cmd << "RETR " << message + 1 << CRLF; returnCode = RetrieveInternal(cmd.String(), message, write_to, true); runner->ReportProgress(0 /* bytes */, 1 /* messages */); if (returnCode == B_OK) { // Some debug code. int32 message_len = MessageSize(message); write_to->Seek (0, SEEK_END); if (write_to->Position() != message_len) { printf ("POP3Protocol::Retrieve Note: message size is %d, was " "expecting %ld, for message #%ld. Could be a transmission error " "or a bad POP server implementation (does it remove escape codes " "when it counts size?).\n", (int) write_to->Position(), message_len, message); } } return returnCode; }
status_t POP3Protocol::Retrieve(int32 message, BPositionIO* to) { BString cmd; cmd << "RETR " << message + 1 << CRLF; status_t status = RetrieveInternal(cmd.String(), message, to, true); ReportProgress(1, 0); if (status == B_OK) { // Check if the actual message size matches the expected one int32 size = MessageSize(message); to->Seek(0, SEEK_END); if (to->Position() != size) { printf("POP3Protocol::Retrieve Note: message size is %" B_PRIdOFF ", was expecting %" B_PRId32 ", for message #%" B_PRId32 ". Could be a transmission error or a bad POP server " "implementation (does it remove escape codes when it counts " "size?).\n", to->Position(), size, message); } } return status; }
status_t POP3Protocol::RetrieveInternal(const char *command, int32 message, BPositionIO *write_to, bool post_progress) { const int bufSize = 1024 * 30; // To avoid waiting for the non-arrival of the next data packet, try to // receive only the message size, plus the 3 extra bytes for the ".\r\n" // after the message. Of course, if we get it wrong (or it is a huge // message or has lines starting with escaped periods), it will then switch // back to receiving full buffers until the message is done. int amountToReceive = MessageSize (message) + 3; if (amountToReceive >= bufSize || amountToReceive <= 0) amountToReceive = bufSize - 1; BString bufBString; // Used for auto-dealloc on return feature. char *buf = bufBString.LockBuffer (bufSize); int amountInBuffer = 0; int amountReceived; int testIndex; char *testStr; bool cont = true; bool flushWholeBuffer = false; write_to->Seek(0,SEEK_SET); if (SendCommand(command) != B_OK) return B_ERROR; struct timeval tv; tv.tv_sec = POP3_RETRIEVAL_TIMEOUT / 1000000; tv.tv_usec = POP3_RETRIEVAL_TIMEOUT % 1000000; struct fd_set readSet; FD_ZERO(&readSet); FD_SET(fSocket, &readSet); while (cont) { int result = 0; #ifdef USE_SSL if (fUseSSL && SSL_pending(fSSL)) result = 1; else #endif result = select(fSocket + 1, &readSet, NULL, NULL, &tv); if (result == 0) { // No data available, even after waiting a minute. fLog = "POP3 timeout - no data received after a long wait."; runner->Stop(true); return B_ERROR; } if (amountToReceive > bufSize - 1 - amountInBuffer) amountToReceive = bufSize - 1 - amountInBuffer; #ifdef USE_SSL if (fUseSSL) { amountReceived = SSL_read(fSSL, buf + amountInBuffer, amountToReceive); } else #endif amountReceived = recv(fSocket, buf + amountInBuffer, amountToReceive, 0); if (amountReceived < 0) { fLog = strerror(errno); return errno; } if (amountReceived == 0) { fLog = "POP3 data supposedly ready to receive but not received!"; return B_ERROR; } amountToReceive = bufSize - 1; // For next time, read a full buffer. amountInBuffer += amountReceived; buf[amountInBuffer] = 0; // NUL stops tests past the end of buffer. // Look for lines starting with a period. A single period by itself on // a line "\r\n.\r\n" marks the end of the message (thus the need for // at least five characters in the buffer for testing). A period // "\r\n.Stuff" at the start of a line get deleted "\r\nStuff", since // POP adds one as an escape code to let you have message text with // lines starting with a period. For convenience, assume that no // messages start with a period on the very first line, so we can // search for the previous line's "\r\n". for (testIndex = 0; testIndex <= amountInBuffer - 5; testIndex++) { testStr = buf + testIndex; if (testStr[0] == '\r' && testStr[1] == '\n' && testStr[2] == '.') { if (testStr[3] == '\r' && testStr[4] == '\n') { // Found the end of the message marker. Ignore remaining data. if (amountInBuffer > testIndex + 5) printf ("POP3Protocol::RetrieveInternal Ignoring %d bytes " "of extra data past message end.\n", amountInBuffer - (testIndex + 5)); amountInBuffer = testIndex + 2; // Don't include ".\r\n". buf[amountInBuffer] = 0; cont = false; } else { // Remove an extra period at the start of a line. // Inefficient, but it doesn't happen often that you have a // dot starting a line of text. Of course, a file with a // lot of double period lines will get processed very // slowly. memmove (buf + testIndex + 2, buf + testIndex + 3, amountInBuffer - (testIndex + 3) + 1 /* for NUL at end */); amountInBuffer--; // Watch out for the end of buffer case, when the POP text // is "\r\n..X". Don't want to leave the resulting // "\r\n.X" in the buffer (flush out the whole buffer), // since that will get mistakenly evaluated again in the // next loop and delete a character by mistake. if (testIndex >= amountInBuffer - 4 && testStr[2] == '.') { printf ("POP3Protocol::RetrieveInternal: Jackpot! You have " "hit the rare situation with an escaped period at the " "end of the buffer. Aren't you happy it decodes it " "correctly?\n"); flushWholeBuffer = true; } } } } if (cont && !flushWholeBuffer) { // Dump out most of the buffer, but leave the last 4 characters for // comparison continuity, in case the line starting with a period // crosses a buffer boundary. if (amountInBuffer > 4) { write_to->Write(buf, amountInBuffer - 4); if (post_progress) runner->ReportProgress(amountInBuffer - 4,0); memmove (buf, buf + amountInBuffer - 4, 4); amountInBuffer = 4; } } else { // Dump everything - end of message or flushing the whole buffer. write_to->Write(buf, amountInBuffer); if (post_progress) runner->ReportProgress(amountInBuffer,0); amountInBuffer = 0; } } return B_OK; }
status_t POP3Protocol::SyncMessages() { bool leaveOnServer; if (fSettings.FindBool("leave_mail_on_server", &leaveOnServer) != B_OK) leaveOnServer = true; // create directory if not exist create_directory(fDestinationDir, 0777); printf("POP3Protocol::SyncMessages()\n"); _ReadManifest(); SetTotalItems(2); ReportProgress(1, 0, B_TRANSLATE("Connect to server" B_UTF8_ELLIPSIS)); status_t error = Connect(); if (error != B_OK) { printf("POP3 could not connect: %s\n", strerror(error)); ResetProgress(); return error; } ReportProgress(1, 0, B_TRANSLATE("Getting UniqueIDs" B_UTF8_ELLIPSIS)); error = _RetrieveUniqueIDs(); if (error < B_OK) { ResetProgress(); Disconnect(); return error; } BStringList toDownload; NotHere(fManifest, fUniqueIDs, &toDownload); int32 numMessages = toDownload.CountStrings(); if (numMessages == 0) { CheckForDeletedMessages(); ResetProgress(); Disconnect(); return B_OK; } ResetProgress(); SetTotalItems(toDownload.CountStrings()); SetTotalItemsSize(fTotalSize); printf("POP3: Messages to download: %i\n", (int)toDownload.CountStrings()); for (int32 i = 0; i < toDownload.CountStrings(); i++) { const char* uid = toDownload.StringAt(i); int32 toRetrieve = fUniqueIDs.IndexOf(uid); if (toRetrieve < 0) { // should not happen! error = B_NAME_NOT_FOUND; printf("POP3: uid %s index %i not found in fUniqueIDs!\n", uid, (int)toRetrieve); continue; } BPath path(fDestinationDir); BString fileName = "Downloading file... uid: "; fileName += uid; fileName.ReplaceAll("/", "_SLASH_"); path.Append(fileName); BEntry entry(path.Path()); BFile file(&entry, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE); error = file.InitCheck(); if (error != B_OK) { printf("POP3: Can't create file %s\n ", path.Path()); break; } BMailMessageIO mailIO(this, &file, toRetrieve); BMessage attributes; entry_ref ref; entry.GetRef(&ref); int32 size = MessageSize(toRetrieve); if (fFetchBodyLimit < 0 || size <= fFetchBodyLimit) { error = mailIO.Seek(0, SEEK_END); if (error < 0) { printf("POP3: Failed to download body %s\n ", uid); break; } ProcessMessageFetched(ref, file, attributes); if (!leaveOnServer) Delete(toRetrieve); } else { int32 dummy; error = mailIO.ReadAt(0, &dummy, 1); if (error < 0) { printf("POP3: Failed to download header %s\n ", uid); break; } ProcessHeaderFetched(ref, file, attributes); } ReportProgress(1, 0); if (file.WriteAttr("MAIL:unique_id", B_STRING_TYPE, 0, uid, strlen(uid)) < 0) error = B_ERROR; file.WriteAttr("MAIL:size", B_INT32_TYPE, 0, &size, sizeof(int32)); write_read_attr(file, B_UNREAD); // save manifest in case we get disturbed fManifest.Add(uid); _WriteManifest(); } ResetProgress(); CheckForDeletedMessages(); Disconnect(); return error; }