xref: /haiku/src/add-ons/mail_daemon/inbound_protocols/pop3/MessageIO.cpp (revision fce4895d1884da5ae6fb299d23c735c598e690b1)
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