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
21BMailMessageIO::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
33BMailMessageIO::~BMailMessageIO()
34{
35}
36
37
38ssize_t
39BMailMessageIO::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
114ssize_t
115BMailMessageIO::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
124off_t
125BMailMessageIO::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
140off_t
141BMailMessageIO::Position() const
142{
143	return fSlave->Position();
144}
145
146
147void
148BMailMessageIO::_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