1 /* 2 * Copyright 2007-2011, Haiku, Inc. All rights reserved. 3 * Copyright 2001 Dr. Zoidberg Enterprises. All rights reserved. 4 */ 5 6 7 /*! 8 Glue code for reading/writing messages directly from the protocols but 9 present a BPositionIO interface to the caller, while caching the data 10 read/written in a slave file. 11 */ 12 13 14 #include "MessageIO.h" 15 16 #include <stdio.h> 17 #include <stdlib.h> 18 #include <string.h> 19 20 21 BMailMessageIO::BMailMessageIO(POP3Protocol* protocol, BPositionIO* dumpTo, 22 int32 messageID) 23 : 24 fSlave(dumpTo), 25 fMessageID(messageID), 26 fProtocol(protocol), 27 fSize(0), 28 fState(READ_HEADER_NEXT) 29 { 30 } 31 32 33 BMailMessageIO::~BMailMessageIO() 34 { 35 } 36 37 38 ssize_t 39 BMailMessageIO::ReadAt(off_t pos, void* buffer, size_t amountToRead) 40 { 41 char lastBytes[5]; 42 off_t oldPosition = fSlave->Position(); 43 44 while (pos + amountToRead > fSize) { 45 if (fState >= ALL_READING_DONE) 46 break; 47 48 switch (fState) { 49 // Read (download from the mail server) just the message headers, 50 // and append a blank line if needed (so the header processing code 51 // can tell where the end of the headers is). Don't append too 52 // much otherwise the part after the header will appear mangled 53 // when it is overwritten in a full read. This can be useful for 54 // filters which discard the message after only reading the header, 55 // thus avoiding the time it takes to download the whole message. 56 case READ_HEADER_NEXT: 57 { 58 fSlave->SetSize(0); // Truncate the file. 59 fSlave->Seek(0,SEEK_SET); 60 status_t status = fProtocol->GetHeader(fMessageID, fSlave); 61 if (status != B_OK) 62 return status; 63 // See if it already ends in a blank line, if not, add enough 64 // end-of-lines to give a blank line. 65 fSlave->Seek(-4, SEEK_END); 66 strcpy(lastBytes, "xxxx"); 67 fSlave->Read(lastBytes, 4); 68 69 if (strcmp(lastBytes, "\r\n\r\n") != 0) { 70 if (strcmp(lastBytes + 2, "\r\n") == 0) 71 fSlave->Write("\r\n", 2); 72 else 73 fSlave->Write("\r\n\r\n", 4); 74 } 75 fState = READ_BODY_NEXT; 76 break; 77 } 78 79 // OK, they want more than the headers. Read the whole message, 80 // starting from the beginning (network->Retrieve does a seek to 81 // the start of the file for POP3 so we have to read the whole 82 // thing). This wastes a slight amount of time on high speed 83 // connections, and on dial-up modem connections, hopefully the 84 // modem's V.90 data compression will make it very quick to 85 // retransmit the header portion. 86 case READ_BODY_NEXT: 87 { 88 fSlave->SetSize(0); // Truncate the file. 89 fSlave->Seek(0,SEEK_SET); 90 status_t status = fProtocol->Retrieve(fMessageID, fSlave); 91 if (status < 0) 92 return status; 93 fState = ALL_READING_DONE; 94 break; 95 } 96 97 default: // Shouldn't happen. 98 return -1; 99 } 100 _ResetSize(); 101 } 102 103 // Put the file position back at where it was, if possible. That's because 104 // ReadAt isn't supposed to affect the file position. 105 if (oldPosition < (off_t)fSize) 106 fSlave->Seek (oldPosition, SEEK_SET); 107 else 108 fSlave->Seek (0, SEEK_END); 109 110 return fSlave->ReadAt(pos, buffer, amountToRead); 111 } 112 113 114 ssize_t 115 BMailMessageIO::WriteAt(off_t pos, const void* buffer, size_t amountToWrite) 116 { 117 ssize_t bytesWritten = fSlave->WriteAt(pos, buffer, amountToWrite); 118 _ResetSize(); 119 120 return bytesWritten; 121 } 122 123 124 off_t 125 BMailMessageIO::Seek(off_t position, uint32 seekMode) 126 { 127 if (seekMode == SEEK_END && fState != ALL_READING_DONE) { 128 // Force it to read the whole message to find the size of it. 129 char tempBuffer; 130 fState = READ_BODY_NEXT; 131 // Skip the header reading step. 132 ssize_t bytesRead = ReadAt(fSize + 1, &tempBuffer, sizeof(tempBuffer)); 133 if (bytesRead < 0) 134 return bytesRead; 135 } 136 return fSlave->Seek(position, seekMode); 137 } 138 139 140 off_t 141 BMailMessageIO::Position() const 142 { 143 return fSlave->Position(); 144 } 145 146 147 void 148 BMailMessageIO::_ResetSize() 149 { 150 off_t old = fSlave->Position(); 151 152 fSlave->Seek(0,SEEK_END); 153 fSize = fSlave->Position(); 154 155 fSlave->Seek(old,SEEK_SET); 156 } 157