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
BMailMessageIO(POP3Protocol * protocol,BPositionIO * dumpTo,int32 messageID)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
~BMailMessageIO()33 BMailMessageIO::~BMailMessageIO()
34 {
35 }
36
37
38 ssize_t
ReadAt(off_t pos,void * buffer,size_t amountToRead)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
WriteAt(off_t pos,const void * buffer,size_t amountToWrite)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
Seek(off_t position,uint32 seekMode)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
Position() const141 BMailMessageIO::Position() const
142 {
143 return fSlave->Position();
144 }
145
146
147 void
_ResetSize()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