/*
 * Copyright 2007-2016, Haiku, Inc. All rights reserved.
 * Copyright 2001-2002 Dr. Zoidberg Enterprises. All rights reserved.
 * Copyright 2011, Clemens Zeidler <haiku@clemens-zeidler.de>
 *
 * Distributed under the terms of the MIT License.
 */
 
 
//! POP3Protocol - implementation of the POP3 protocol
 
 
#include "POP3.h"
 
#include <errno.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
 
#include <arpa/inet.h>
 
#include "md5.h"
 
#include <Alert.h>
#include <Catalog.h>
#include <Debug.h>
#include <Directory.h>
#include <fs_attr.h>
#include <Path.h>
#include <SecureSocket.h>
#include <String.h>
#include <VolumeRoster.h>
#include <Query.h>
 
#include <mail_util.h>
 
#include "crypt.h"
#include "MailSettings.h"
#include "MessageIO.h"
 
 
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "pop3"
 
 
#define POP3_RETRIEVAL_TIMEOUT 60000000
#define CRLF	"\r\n"
 
 
static void
NotHere(BStringList& that, BStringList& otherList, BStringList* results)
{
	for (int32 i = 0; i < otherList.CountStrings(); i++) {
		if (!that.HasString(otherList.StringAt(i)))
			results->Add(otherList.StringAt(i));
	}
}
 
 
// #pragma mark -
 
 
POP3Protocol::POP3Protocol(const BMailAccountSettings& settings)
	:
	BInboundMailProtocol("POP3", settings),
	fNumMessages(-1),
	fMailDropSize(0),
	fServerConnection(NULL)
{
	printf("POP3Protocol::POP3Protocol(BMailAccountSettings* settings)\n");
	fSettings = fAccountSettings.InboundSettings();
 
	fUseSSL = fSettings.FindInt32("flavor") == 1 ? true : false;
 
	if (fSettings.FindString("destination", &fDestinationDir) != B_OK)
		fDestinationDir = "/boot/home/mail/in";
 
	create_directory(fDestinationDir, 0777);
 
	fFetchBodyLimit = -1;
	if (fSettings.HasInt32("partial_download_limit"))
		fFetchBodyLimit = fSettings.FindInt32("partial_download_limit");
}
 
 
POP3Protocol::~POP3Protocol()
{
	Disconnect();
}
 
 
status_t
POP3Protocol::Connect()
{
	status_t error = Open(fSettings.FindString("server"),
		fSettings.FindInt32("port"), fSettings.FindInt32("flavor"));
	if (error != B_OK)
		return error;
 
	char* password = get_passwd(&fSettings, "cpasswd");
 
	error = Login(fSettings.FindString("username"), password,
		fSettings.FindInt32("auth_method"));
	delete[] password;
 
	if (error != B_OK)
		fServerConnection->Disconnect();
	return error;
}
 
 
status_t
POP3Protocol::Disconnect()
{
	if (fServerConnection == NULL)
		return B_OK;
 
	SendCommand("QUIT" CRLF);
 
	fServerConnection->Disconnect();
	delete fServerConnection;
	fServerConnection = NULL;
 
	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);
 
		const BString uidStr(uid);
		if (file.WriteAttrString("MAIL:unique_id", &uidStr) < 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;
}
 
 
status_t
POP3Protocol::HandleFetchBody(const entry_ref& ref, const BMessenger& replyTo)
{
	ResetProgress("Fetch body");
	SetTotalItems(1);
 
	status_t error = Connect();
	if (error != B_OK)
		return error;
 
	error = _RetrieveUniqueIDs();
	if (error != B_OK) {
		Disconnect();
		return error;
	}
 
	BFile file(&ref, B_READ_WRITE);
	status_t status = file.InitCheck();
	if (status != B_OK) {
		Disconnect();
		return status;
	}
 
	char uidString[256];
	BNode node(&ref);
	if (node.ReadAttr("MAIL:unique_id", B_STRING_TYPE, 0, uidString, 256) < 0) {
		Disconnect();
		return B_ERROR;
	}
 
	int32 toRetrieve = fUniqueIDs.IndexOf(uidString);
	if (toRetrieve < 0) {
		Disconnect();
		return B_NAME_NOT_FOUND;
	}
 
	bool leaveOnServer;
	if (fSettings.FindBool("leave_mail_on_server", &leaveOnServer) != B_OK)
		leaveOnServer = true;
 
	// TODO: get rid of this BMailMessageIO!
	BMailMessageIO io(this, &file, toRetrieve);
	// read body
	status = io.Seek(0, SEEK_END);
	if (status < 0) {
		Disconnect();
		return status;
	}
 
	BMessage attributes;
	NotifyBodyFetched(ref, file, attributes);
	ReplyBodyFetched(replyTo, ref, B_OK);
 
	if (!leaveOnServer)
		Delete(toRetrieve);
 
	ReportProgress(1, 0);
	ResetProgress();
 
	Disconnect();
	return B_OK;
}
 
 
status_t
POP3Protocol::Open(const char* server, int port, int)
{
	ReportProgress(0, 0, B_TRANSLATE("Connecting to POP3 server"
		B_UTF8_ELLIPSIS));
 
	if (port <= 0)
		port = fUseSSL ? 995 : 110;
 
	fLog = "";
 
	// Prime the error message
	BString errorMessage(B_TRANSLATE("Error while connecting to server %serv"));
	errorMessage.ReplaceFirst("%serv", server);
	if (port != 110)
		errorMessage << ":" << port;
 
	delete fServerConnection;
	fServerConnection = NULL;
 
	BNetworkAddress address(server, port);
	if (fUseSSL)
		fServerConnection = new(std::nothrow) BSecureSocket(address);
	else
		fServerConnection = new(std::nothrow) BSocket(address);
 
	status_t status = B_NO_MEMORY;
	if (fServerConnection != NULL)
		status = fServerConnection->InitCheck();
 
	BString line;
	if (status == B_OK) {
		ssize_t length = ReceiveLine(line);
		if (length < 0)
			status = length;
	}
 
	if (status != B_OK) {
		fServerConnection->Disconnect();
		errorMessage << ": " << strerror(status);
		ShowError(errorMessage.String());
		return status;
	}
 
	if (strncmp(line.String(), "+OK", 3) != 0) {
		if (line.Length() > 0) {
			errorMessage << B_TRANSLATE(". The server said:\n")
				<< line.String();
		} else
			errorMessage << B_TRANSLATE(": No reply.\n");
 
		ShowError(errorMessage.String());
		fServerConnection->Disconnect();
		return B_ERROR;
	}
 
	fLog = line;
	return B_OK;
}
 
 
status_t
POP3Protocol::Login(const char* uid, const char* password, int method)
{
	status_t err;
 
	BString errorMessage(B_TRANSLATE("Error while authenticating user %user"));
	errorMessage.ReplaceFirst("%user", uid);
 
	if (method == 1) {	//APOP
		int32 index = fLog.FindFirst("<");
		if(index != B_ERROR) {
			ReportProgress(0, 0, B_TRANSLATE("Sending APOP authentication"
				B_UTF8_ELLIPSIS));
			int32 end = fLog.FindFirst(">", index);
			BString timestamp("");
			fLog.CopyInto(timestamp, index, end - index + 1);
			timestamp += password;
			char md5sum[33];
			MD5Digest((unsigned char*)timestamp.String(), md5sum);
			BString cmd = "APOP ";
			cmd += uid;
			cmd += " ";
			cmd += md5sum;
			cmd += CRLF;
 
			err = SendCommand(cmd.String());
			if (err != B_OK) {
				errorMessage << B_TRANSLATE(". The server said:\n") << fLog;
				ShowError(errorMessage.String());
				return err;
			}
 
			return B_OK;
		} else {
			errorMessage << B_TRANSLATE(": The server does not support APOP.");
			ShowError(errorMessage.String());
			return B_NOT_ALLOWED;
		}
	}
	ReportProgress(0, 0, B_TRANSLATE("Sending username" B_UTF8_ELLIPSIS));
 
	BString cmd = "USER ";
	cmd += uid;
	cmd += CRLF;
 
	err = SendCommand(cmd.String());
	if (err != B_OK) {
		errorMessage << B_TRANSLATE(". The server said:\n") << fLog;
		ShowError(errorMessage.String());
		return err;
	}
 
	ReportProgress(0, 0, B_TRANSLATE("Sending password" B_UTF8_ELLIPSIS));
	cmd = "PASS ";
	cmd += password;
	cmd += CRLF;
 
	err = SendCommand(cmd.String());
	if (err != B_OK) {
		errorMessage << B_TRANSLATE(". The server said:\n") << fLog;
		ShowError(errorMessage.String());
		return err;
	}
 
	return B_OK;
}
 
 
status_t
POP3Protocol::Stat()
{
	ReportProgress(0, 0, B_TRANSLATE("Getting mailbox size" B_UTF8_ELLIPSIS));
 
	if (SendCommand("STAT" CRLF) < B_OK)
		return B_ERROR;
 
	int32 messages;
	int32 dropSize;
	if (sscanf(fLog.String(), "+OK %" B_SCNd32" %" B_SCNd32, &messages,
			&dropSize) < 2)
		return B_ERROR;
 
	fNumMessages = messages;
	fMailDropSize = dropSize;
 
	return B_OK;
}
 
 
int32
POP3Protocol::Messages()
{
	if (fNumMessages < 0)
		Stat();
 
	return fNumMessages;
}
 
 
size_t
POP3Protocol::MailDropSize()
{
	if (fNumMessages < 0)
		Stat();
 
	return fMailDropSize;
}
 
 
void
POP3Protocol::CheckForDeletedMessages()
{
	{
		// Delete things from the manifest no longer on the server
		BStringList list;
		NotHere(fUniqueIDs, fManifest, &list);
		fManifest.Remove(list);
	}
 
	if (!fSettings.FindBool("delete_remote_when_local")
		|| fManifest.CountStrings() == 0)
		return;
 
	BStringList toDelete;
 
	BStringList queryContents;
	BVolumeRoster volumes;
	BVolume volume;
 
	while (volumes.GetNextVolume(&volume) == B_OK) {
		BQuery fido;
		entry_ref entry;
 
		fido.SetVolume(&volume);
		fido.PushAttr(B_MAIL_ATTR_ACCOUNT_ID);
		fido.PushInt32(fAccountSettings.AccountID());
		fido.PushOp(B_EQ);
 
		fido.Fetch();
 
		BString uid;
		while (fido.GetNextRef(&entry) == B_OK) {
			BNode(&entry).ReadAttrString("MAIL:unique_id", &uid);
			queryContents.Add(uid);
		}
	}
	NotHere(queryContents, fManifest, &toDelete);
 
	for (int32 i = 0; i < toDelete.CountStrings(); i++) {
		printf("delete mail on server uid %s\n", toDelete.StringAt(i).String());
		Delete(fUniqueIDs.IndexOf(toDelete.StringAt(i)));
	}
 
	// Don't remove ids from fUniqueIDs, the indices have to stay the same when
	// retrieving new messages.
	fManifest.Remove(toDelete);
 
	// TODO: at some point the purged manifest should be written to disk
	// otherwise it will grow forever
}
 
 
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::GetHeader(int32 message, BPositionIO* to)
{
	BString cmd;
	cmd << "TOP " << message + 1 << " 0" << CRLF;
	return RetrieveInternal(cmd.String(), message, to, false);
}
 
 
status_t
POP3Protocol::RetrieveInternal(const char* command, int32 message,
	BPositionIO* to, bool postProgress)
{
	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;
	to->Seek(0, SEEK_SET);
 
	if (SendCommand(command) != B_OK)
		return B_ERROR;
 
	while (cont) {
		status_t result = fServerConnection->WaitForReadable(
			POP3_RETRIEVAL_TIMEOUT);
		if (result == B_TIMED_OUT) {
			// No data available, even after waiting a minute.
			fLog = "POP3 timeout - no data received after a long wait.";
			return B_TIMED_OUT;
		}
		if (amountToReceive > bufSize - 1 - amountInBuffer)
			amountToReceive = bufSize - 1 - amountInBuffer;
 
		amountReceived = fServerConnection->Read(buf + amountInBuffer,
			amountToReceive);
 
		if (amountReceived < 0) {
			fLog = strerror(amountReceived);
			return amountReceived;
		}
		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);
					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) {
				to->Write(buf, amountInBuffer - 4);
				if (postProgress)
					ReportProgress(0, amountInBuffer - 4);
				memmove(buf, buf + amountInBuffer - 4, 4);
				amountInBuffer = 4;
			}
		} else {
			// Dump everything - end of message or flushing the whole buffer.
			to->Write(buf, amountInBuffer);
			if (postProgress)
				ReportProgress(0, amountInBuffer);
			amountInBuffer = 0;
		}
	}
	return B_OK;
}
 
 
void
POP3Protocol::Delete(int32 index)
{
	BString cmd = "DELE ";
	cmd << (index + 1) << CRLF;
	if (SendCommand(cmd.String()) != B_OK) {
		// Error
	}
#if DEBUG
	puts(fLog.String());
#endif
	// The mail is just marked as deleted and removed from the server when
	// sending the QUIT command. Because of that the message number stays
	// the same and we keep the uid in the uid list.
}
 
 
size_t
POP3Protocol::MessageSize(int32 index)
{
	return fSizes[index];
}
 
 
ssize_t
POP3Protocol::ReceiveLine(BString& line)
{
	int32 length = 0;
	bool flag = false;
 
	line = "";
 
	status_t result = fServerConnection->WaitForReadable(
		POP3_RETRIEVAL_TIMEOUT);
	if (result == B_TIMED_OUT)
		return errno;
 
	while (true) {
		// Hope there's an end of line out there else this gets stuck.
		int32 bytesReceived;
		uint8 c = 0;
 
		bytesReceived = fServerConnection->Read((char*)&c, 1);
		if (bytesReceived < 0)
			return bytesReceived;
 
		if (c == '\n' || bytesReceived == 0)
			break;
 
		if (c == '\r') {
			flag = true;
		} else {
			if (flag) {
				length++;
				line += '\r';
				flag = false;
			}
			length++;
			line += (char)c;
		}
	}
 
	return length;
}
 
 
status_t
POP3Protocol::SendCommand(const char* cmd)
{
	// Flush any accumulated garbage data before we send our command, so we
	// don't misinterrpret responses from previous commands (that got left over
	// due to bugs) as being from this command.
	while (fServerConnection->WaitForReadable(1000) == B_OK) {
		char buffer[4096];
		ssize_t amountReceived = fServerConnection->Read(buffer,
			sizeof(buffer) - 1);
		if (amountReceived < 0)
			return amountReceived;
 
		buffer[amountReceived] = 0;
		printf("POP3Protocol::SendCommand Bug! Had to flush %" B_PRIdSSIZE
			" bytes: %s\n", amountReceived, buffer);
	}
 
	if (fServerConnection->Write(cmd, ::strlen(cmd)) < 0) {
		fLog = strerror(errno);
		printf("POP3Protocol::SendCommand Send \"%s\" failed, code %d: %s\n",
			cmd, errno, fLog.String());
		return errno;
	}
 
	fLog = "";
	int32 length = ReceiveLine(fLog);
	if (length <= 0 || fLog.ICompare("+OK", 3) == 0)
		return B_OK;
 
	if (fLog.ICompare("-ERR", 4) == 0) {
		printf("POP3Protocol::SendCommand \"%s\" got error message "
			"from server: %s\n", cmd, fLog.String());
		return B_ERROR;
	}
 
	printf("POP3Protocol::SendCommand \"%s\" got nonsense message "
		"from server: %s\n", cmd, fLog.String());
	return B_BAD_DATA;
		// If it's not +OK, and it's not -ERR, then what the heck
		// is it? Presume an error
}
 
 
void
POP3Protocol::MD5Digest(unsigned char* in, char* asciiDigest)
{
	unsigned char digest[16];
 
	MD5_CTX context;
 
	MD5Init(&context);
	MD5Update(&context, in, ::strlen((char*)in));
	MD5Final(digest, &context);
 
	for (int i = 0;  i < 16;  i++) {
		sprintf(asciiDigest + 2 * i, "%02x", digest[i]);
	}
 
	return;
}
 
 
status_t
POP3Protocol::_RetrieveUniqueIDs()
{
	fUniqueIDs.MakeEmpty();
	fSizes.clear();
	fTotalSize = 0;
 
	status_t status = SendCommand("UIDL" CRLF);
	if (status != B_OK)
		return status;
 
	BString result;
	int32 uidOffset;
	while (ReceiveLine(result) > 0) {
		if (result.ByteAt(0) == '.')
			break;
 
		uidOffset = result.FindFirst(' ') + 1;
		result.Remove(0, uidOffset);
		fUniqueIDs.Add(result);
	}
 
	if (SendCommand("LIST" CRLF) != B_OK)
		return B_ERROR;
 
	while (ReceiveLine(result) > 0) {
		if (result.ByteAt(0) == '.')
			break;
 
		int32 index = result.FindLast(" ");
		int32 size;
		if (index >= 0)
			size = atol(&result.String()[index]);
		else
			size = 0;
 
		fTotalSize += size;
		fSizes.push_back(size);
	}
 
	return B_OK;
}
 
 
void
POP3Protocol::_ReadManifest()
{
	fManifest.MakeEmpty();
	BString attribute = "MAIL:";
	attribute << fAccountSettings.AccountID() << ":manifest";
		// In case someone puts multiple accounts in the same directory
 
	BNode node(fDestinationDir);
	if (node.InitCheck() != B_OK)
		return;
 
	// We already have a directory so we can try to read metadata
	// from it. Note that it is normal for this directory not to
	// be found on the first run as it will be later created by
	// the INBOX system filter.
	attr_info info;
	if (node.GetAttrInfo(attribute.String(), &info) != B_OK || info.size == 0)
		return;
 
	void* flatmanifest = malloc(info.size);
	node.ReadAttr(attribute.String(), fManifest.TypeCode(), 0,
		flatmanifest, info.size);
	fManifest.Unflatten(fManifest.TypeCode(), flatmanifest, info.size);
	free(flatmanifest);
}
 
 
void
POP3Protocol::_WriteManifest()
{
	BString attribute = "MAIL:";
	attribute << fAccountSettings.AccountID() << ":manifest";
		// In case someone puts multiple accounts in the same directory
	BNode node(fDestinationDir);
	if (node.InitCheck() != B_OK) {
		ShowError("Error while saving account manifest: cannot use "
			"destination directory.");
		return;
	}
 
	node.RemoveAttr(attribute.String());
	ssize_t manifestsize = fManifest.FlattenedSize();
	void* flatmanifest = malloc(manifestsize);
	fManifest.Flatten(flatmanifest, manifestsize);
	status_t err = node.WriteAttr(attribute.String(),
		fManifest.TypeCode(), 0, flatmanifest, manifestsize);
	if (err < 0) {
		BString error = "Error while saving account manifest: ";
		error << strerror(err);
			printf("moep error\n");
		ShowError(error.String());
	}
 
	free(flatmanifest);
}
 
 
//	#pragma mark -
 
 
BInboundMailProtocol*
instantiate_inbound_protocol(const BMailAccountSettings& settings)
{
	return new POP3Protocol(settings);
}
 
 
status_t
pop3_smtp_auth(const BMailAccountSettings& settings)
{
	POP3Protocol protocol(settings);
	protocol.Connect();
	protocol.Disconnect();
	return B_OK;
}

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fTotalSize.