1f06916d5SAxel Dörfler/*
281805393SAxel Dörfler * Copyright 2012-2016, Axel D��rfler, axeld@pinc-software.de.
3f06916d5SAxel Dörfler * Distributed under the terms of the MIT License.
4f06916d5SAxel Dörfler */
5f06916d5SAxel Dörfler
6f06916d5SAxel Dörfler
7f06916d5SAxel Dörfler#include "IMAPFolder.h"
8f06916d5SAxel Dörfler
9f06916d5SAxel Dörfler#include <set>
10f06916d5SAxel Dörfler
11f06916d5SAxel Dörfler#include <ByteOrder.h>
12f06916d5SAxel Dörfler#include <Debug.h>
13f06916d5SAxel Dörfler#include <Directory.h>
143302df14SAxel Dörfler#include <File.h>
15f06916d5SAxel Dörfler#include <fs_attr.h>
1681805393SAxel Dörfler#include <Messenger.h>
17f06916d5SAxel Dörfler#include <Node.h>
183302df14SAxel Dörfler#include <Path.h>
19f06916d5SAxel Dörfler
2081805393SAxel Dörfler#include <NodeMessage.h>
21eba458b9SAxel Dörfler
22eba458b9SAxel Dörfler#include "IMAPProtocol.h"
23eba458b9SAxel Dörfler
24f06916d5SAxel Dörfler
25f06916d5SAxel Dörflerstatic const char* kMailboxNameAttribute = "IMAP:mailbox";
26f06916d5SAxel Dörflerstatic const char* kUIDValidityAttribute = "IMAP:uidvalidity";
2747b68f58SAxel Dörflerstatic const char* kLastUIDAttribute = "IMAP:lastuid";
28f06916d5SAxel Dörflerstatic const char* kStateAttribute = "IMAP:state";
29eba458b9SAxel Dörflerstatic const char* kFlagsAttribute = "IMAP:flags";
30f06916d5SAxel Dörflerstatic const char* kUIDAttribute = "MAIL:unique_id";
31f06916d5SAxel Dörfler
32f06916d5SAxel Dörfler
33eba458b9SAxel Dörflerclass TemporaryFile : public BFile {
34eba458b9SAxel Dörflerpublic:
35eba458b9SAxel Dörfler	TemporaryFile(BFile& file)
36eba458b9SAxel Dörfler		:
37eba458b9SAxel Dörfler		fFile(file),
38eba458b9SAxel Dörfler		fDeleteFile(false)
39eba458b9SAxel Dörfler	{
40eba458b9SAxel Dörfler	}
41eba458b9SAxel Dörfler
42eba458b9SAxel Dörfler	~TemporaryFile()
43eba458b9SAxel Dörfler	{
44eba458b9SAxel Dörfler		if (fDeleteFile) {
45eba458b9SAxel Dörfler			fFile.Unset();
46eba458b9SAxel Dörfler			BEntry(fPath.Path()).Remove();
47eba458b9SAxel Dörfler		}
48eba458b9SAxel Dörfler	}
49eba458b9SAxel Dörfler
50eba458b9SAxel Dörfler	status_t Init(const BPath& path, entry_ref& ref)
51eba458b9SAxel Dörfler	{
52eba458b9SAxel Dörfler		int32 tries = 53;
53eba458b9SAxel Dörfler		while (true) {
54eba458b9SAxel Dörfler			BString name("temp-mail-");
55eba458b9SAxel Dörfler			name << system_time();
56eba458b9SAxel Dörfler
57eba458b9SAxel Dörfler			fPath = path;
58eba458b9SAxel Dörfler			fPath.Append(name.String());
59eba458b9SAxel Dörfler
60eba458b9SAxel Dörfler			status_t status = fFile.SetTo(fPath.Path(),
61eba458b9SAxel Dörfler				B_CREATE_FILE | B_FAIL_IF_EXISTS | B_READ_WRITE);
62eba458b9SAxel Dörfler			if (status == B_FILE_EXISTS && tries-- > 0)
63eba458b9SAxel Dörfler				continue;
64eba458b9SAxel Dörfler			if (status != B_OK)
65eba458b9SAxel Dörfler				return status;
66eba458b9SAxel Dörfler
67eba458b9SAxel Dörfler			fDeleteFile = true;
68eba458b9SAxel Dörfler			return get_ref_for_path(fPath.Path(), &ref);
69eba458b9SAxel Dörfler		}
70eba458b9SAxel Dörfler	}
71eba458b9SAxel Dörfler
72eba458b9SAxel Dörfler	void KeepFile()
73eba458b9SAxel Dörfler	{
74eba458b9SAxel Dörfler		fDeleteFile = false;
75eba458b9SAxel Dörfler	}
76eba458b9SAxel Dörfler
77eba458b9SAxel Dörflerprivate:
78eba458b9SAxel Dörfler			BFile&				fFile;
79eba458b9SAxel Dörfler			BPath				fPath;
80eba458b9SAxel Dörfler			bool				fDeleteFile;
81eba458b9SAxel Dörfler};
82eba458b9SAxel Dörfler
83eba458b9SAxel Dörfler
84eba458b9SAxel Dörfler// #pragma mark -
85eba458b9SAxel Dörfler
86eba458b9SAxel Dörfler
87eba458b9SAxel DörflerIMAPFolder::IMAPFolder(IMAPProtocol& protocol, const BString& mailboxName,
88eba458b9SAxel Dörfler	const entry_ref& ref)
89f06916d5SAxel Dörfler	:
90aebdd0c1SAxel Dörfler	BHandler(mailboxName.String()),
91eba458b9SAxel Dörfler	fProtocol(protocol),
92f06916d5SAxel Dörfler	fRef(ref),
93a4bdd26dSAxel Dörfler	fMailboxName(mailboxName),
9447b68f58SAxel Dörfler	fUIDValidity(UINT32_MAX),
9581805393SAxel Dörfler	fLastUID(0),
9615216b26SAxel Dörfler	fListener(NULL),
9715216b26SAxel Dörfler	fFolderStateInitialized(false),
9815216b26SAxel Dörfler	fQuitFolderState(false)
9981805393SAxel Dörfler{
10081805393SAxel Dörfler	mutex_init(&fLock, "imap folder lock");
10115216b26SAxel Dörfler	mutex_init(&fFolderStateLock, "imap folder state lock");
10281805393SAxel Dörfler}
10381805393SAxel Dörfler
10481805393SAxel Dörfler
10581805393SAxel DörflerIMAPFolder::~IMAPFolder()
10681805393SAxel Dörfler{
1076fa27973SPeter Kosyh	MutexLocker locker(fLock);
1086fa27973SPeter Kosyh	if (!fFolderStateInitialized && fListener != NULL) {
10915216b26SAxel Dörfler		fQuitFolderState = true;
1106fa27973SPeter Kosyh		locker.Unlock();
11115216b26SAxel Dörfler		wait_for_thread(fReadFolderStateThread, NULL);
11215216b26SAxel Dörfler	}
11381805393SAxel Dörfler}
11481805393SAxel Dörfler
11581805393SAxel Dörfler
11681805393SAxel Dörflerstatus_t
11781805393SAxel DörflerIMAPFolder::Init()
118f06916d5SAxel Dörfler{
119f06916d5SAxel Dörfler	// Initialize from folder attributes
12081805393SAxel Dörfler	BNode node(&fRef);
12181805393SAxel Dörfler	status_t status = node.InitCheck();
12281805393SAxel Dörfler	if (status != B_OK)
12381805393SAxel Dörfler		return status;
12481805393SAxel Dörfler
12581805393SAxel Dörfler	node_ref nodeRef;
12681805393SAxel Dörfler	status = node.GetNodeRef(&nodeRef);
12781805393SAxel Dörfler	if (status != B_OK)
12881805393SAxel Dörfler		return status;
12981805393SAxel Dörfler
13081805393SAxel Dörfler	fNodeID = nodeRef.node;
131f06916d5SAxel Dörfler
13247b68f58SAxel Dörfler	BString originalMailboxName;
13347b68f58SAxel Dörfler	if (node.ReadAttrString(kMailboxNameAttribute, &originalMailboxName) == B_OK
13481805393SAxel Dörfler		&& originalMailboxName != fMailboxName) {
13547b68f58SAxel Dörfler		// TODO: mailbox name has changed
13647b68f58SAxel Dörfler	}
137f06916d5SAxel Dörfler
13847b68f58SAxel Dörfler	fUIDValidity = _ReadUInt32(node, kUIDValidityAttribute);
13947b68f58SAxel Dörfler	fLastUID = _ReadUInt32(node, kLastUIDAttribute);
140b9962ceaSJérôme Duval	printf("IMAP: %s, last UID %" B_PRIu32 "\n", fMailboxName.String(),
141b9962ceaSJérôme Duval		fLastUID);
142f06916d5SAxel Dörfler
143f06916d5SAxel Dörfler	attr_info info;
14481805393SAxel Dörfler	status = node.GetAttrInfo(kStateAttribute, &info);
145f06916d5SAxel Dörfler	if (status == B_OK) {
146f06916d5SAxel Dörfler		struct entry {
147f06916d5SAxel Dörfler			uint32	uid;
148f06916d5SAxel Dörfler			uint32	flags;
149f06916d5SAxel Dörfler		} _PACKED;
150f06916d5SAxel Dörfler		struct entry* entries = (struct entry*)malloc(info.size);
15181805393SAxel Dörfler		if (entries == NULL)
15281805393SAxel Dörfler			return B_NO_MEMORY;
153f06916d5SAxel Dörfler
154f06916d5SAxel Dörfler		ssize_t bytesRead = node.ReadAttr(kStateAttribute, B_RAW_TYPE, 0,
155f06916d5SAxel Dörfler			entries, info.size);
15681805393SAxel Dörfler		if (bytesRead != info.size)
15781805393SAxel Dörfler			return B_BAD_DATA;
158f06916d5SAxel Dörfler
159f06916d5SAxel Dörfler		for (size_t i = 0; i < info.size / sizeof(entry); i++) {
160f06916d5SAxel Dörfler			uint32 uid = B_BENDIAN_TO_HOST_INT32(entries[i].uid);
161f06916d5SAxel Dörfler			uint32 flags = B_BENDIAN_TO_HOST_INT32(entries[i].flags);
162f06916d5SAxel Dörfler
1631052525dSAxel Dörfler			fFlagsMap.insert(std::make_pair(uid, flags));
164f06916d5SAxel Dörfler		}
165f06916d5SAxel Dörfler	}
166f06916d5SAxel Dörfler
16781805393SAxel Dörfler	return B_OK;
168f06916d5SAxel Dörfler}
169f06916d5SAxel Dörfler
170f06916d5SAxel Dörfler
171f06916d5SAxel Dörflervoid
172a4bdd26dSAxel DörflerIMAPFolder::SetListener(FolderListener* listener)
173f06916d5SAxel Dörfler{
17481805393SAxel Dörfler	ASSERT(fListener == NULL);
17581805393SAxel Dörfler
176a4bdd26dSAxel Dörfler	fListener = listener;
17781805393SAxel Dörfler
17881805393SAxel Dörfler	// Initialize current state from actual folder
17981805393SAxel Dörfler	// TODO: this should be done in another thread
18081805393SAxel Dörfler	_InitializeFolderState();
181a4bdd26dSAxel Dörfler}
182a4bdd26dSAxel Dörfler
183a4bdd26dSAxel Dörfler
184a4bdd26dSAxel Dörflervoid
185a4bdd26dSAxel DörflerIMAPFolder::SetUIDValidity(uint32 uidValidity)
186a4bdd26dSAxel Dörfler{
18747b68f58SAxel Dörfler	if (fUIDValidity == uidValidity)
18847b68f58SAxel Dörfler		return;
18947b68f58SAxel Dörfler
190aeaf68cfSAxel Dörfler	// TODO: delete all mails that have the same UID validity value we had
191a4bdd26dSAxel Dörfler	fUIDValidity = uidValidity;
19247b68f58SAxel Dörfler
19347b68f58SAxel Dörfler	BNode node(&fRef);
19447b68f58SAxel Dörfler	_WriteUInt32(node, kUIDValidityAttribute, uidValidity);
195f06916d5SAxel Dörfler}
196f06916d5SAxel Dörfler
197f06916d5SAxel Dörfler
1981052525dSAxel Dörflerstatus_t
19981805393SAxel DörflerIMAPFolder::GetMessageEntryRef(uint32 uid, entry_ref& ref)
2001052525dSAxel Dörfler{
20181805393SAxel Dörfler	MutexLocker locker(fLock);
20281805393SAxel Dörfler	return _GetMessageEntryRef(uid, ref);
20381805393SAxel Dörfler}
20481805393SAxel Dörfler
20581805393SAxel Dörfler
20681805393SAxel Dörflerstatus_t
20781805393SAxel DörflerIMAPFolder::GetMessageUID(const entry_ref& ref, uint32& uid) const
20881805393SAxel Dörfler{
20981805393SAxel Dörfler	BNode node(&ref);
21081805393SAxel Dörfler	status_t status = node.InitCheck();
21181805393SAxel Dörfler	if (status != B_OK)
21281805393SAxel Dörfler		return status;
21381805393SAxel Dörfler
21481805393SAxel Dörfler	uid = _ReadUniqueID(node);
21581805393SAxel Dörfler	if (uid == 0)
2161052525dSAxel Dörfler		return B_ENTRY_NOT_FOUND;
2171052525dSAxel Dörfler
2181052525dSAxel Dörfler	return B_OK;
2191052525dSAxel Dörfler}
2201052525dSAxel Dörfler
2211052525dSAxel Dörfler
2221052525dSAxel Dörfleruint32
22381805393SAxel DörflerIMAPFolder::MessageFlags(uint32 uid)
2241052525dSAxel Dörfler{
22581805393SAxel Dörfler	MutexLocker locker(fLock);
2261052525dSAxel Dörfler	UIDToFlagsMap::const_iterator found = fFlagsMap.find(uid);
2271052525dSAxel Dörfler	if (found == fFlagsMap.end())
2281052525dSAxel Dörfler		return 0;
2291052525dSAxel Dörfler
2301052525dSAxel Dörfler	return found->second;
2311052525dSAxel Dörfler}
2321052525dSAxel Dörfler
2331052525dSAxel Dörfler
234aeaf68cfSAxel Dörfler/*!	Synchronizes the message flags/state from the server with the local
235aeaf68cfSAxel Dörfler	one.
236aeaf68cfSAxel Dörfler*/
23781805393SAxel Dörflervoid
238aeaf68cfSAxel DörflerIMAPFolder::SyncMessageFlags(uint32 uid, uint32 mailboxFlags)
23981805393SAxel Dörfler{
24081805393SAxel Dörfler	if (uid > LastUID())
24181805393SAxel Dörfler		return;
24281805393SAxel Dörfler
24381805393SAxel Dörfler	entry_ref ref;
24481805393SAxel Dörfler	BNode node;
24581805393SAxel Dörfler
24681805393SAxel Dörfler	while (true) {
24781805393SAxel Dörfler		status_t status = GetMessageEntryRef(uid, ref);
24881805393SAxel Dörfler		if (status == B_ENTRY_NOT_FOUND) {
249aeaf68cfSAxel Dörfler			// The message does not exist anymore locally, delete it on the
250aeaf68cfSAxel Dörfler			// server
25181805393SAxel Dörfler			// TODO: copy it to the trash directory first!
252eef12cecSPeter Kosyh			if (fProtocol.Settings()->DeleteRemoteWhenLocal())
253eef12cecSPeter Kosyh				fProtocol.UpdateMessageFlags(*this, uid, IMAP::kDeleted);
25481805393SAxel Dörfler			return;
25581805393SAxel Dörfler		}
25681805393SAxel Dörfler		if (status == B_OK)
25781805393SAxel Dörfler			status = node.SetTo(&ref);
25815216b26SAxel Dörfler		if (status == B_TIMED_OUT) {
25915216b26SAxel Dörfler			// We don't know the message state yet
26015216b26SAxel Dörfler			fPendingFlagsMap.insert(std::make_pair(uid, mailboxFlags));
26115216b26SAxel Dörfler		}
26281805393SAxel Dörfler		if (status != B_OK)
26381805393SAxel Dörfler			return;
26481805393SAxel Dörfler
26581805393SAxel Dörfler		break;
26681805393SAxel Dörfler	}
267aeaf68cfSAxel Dörfler	fSynchronizedUIDsSet.insert(uid);
26881805393SAxel Dörfler
26981805393SAxel Dörfler	uint32 previousFlags = MessageFlags(uid);
27081805393SAxel Dörfler	uint32 currentFlags = previousFlags;
27181805393SAxel Dörfler	if (_MailToIMAPFlags(node, currentFlags) != B_OK)
27281805393SAxel Dörfler		return;
27381805393SAxel Dörfler
27481805393SAxel Dörfler	// Compare flags to previous/current flags, and update either the
27581805393SAxel Dörfler	// message on the server, or the message locally (or even both)
27681805393SAxel Dörfler
27781805393SAxel Dörfler	uint32 nextFlags = mailboxFlags;
27881805393SAxel Dörfler	_TestMessageFlags(previousFlags, mailboxFlags, currentFlags,
27981805393SAxel Dörfler		IMAP::kSeen, nextFlags);
28081805393SAxel Dörfler	_TestMessageFlags(previousFlags, mailboxFlags, currentFlags,
28181805393SAxel Dörfler		IMAP::kAnswered, nextFlags);
28281805393SAxel Dörfler
28381805393SAxel Dörfler	if (nextFlags != previousFlags)
28481805393SAxel Dörfler		_WriteFlags(node, nextFlags);
28581805393SAxel Dörfler	if (currentFlags != nextFlags) {
28681805393SAxel Dörfler		// Update mail message attributes
28781805393SAxel Dörfler		BMessage attributes;
28881805393SAxel Dörfler		_IMAPToMailFlags(nextFlags, attributes);
28981805393SAxel Dörfler		node << attributes;
29081805393SAxel Dörfler
29181805393SAxel Dörfler		fFlagsMap[uid] = nextFlags;
29281805393SAxel Dörfler	}
29381805393SAxel Dörfler	if (mailboxFlags != nextFlags) {
29481805393SAxel Dörfler		// Update server flags
29581805393SAxel Dörfler		fProtocol.UpdateMessageFlags(*this, uid, nextFlags);
29681805393SAxel Dörfler	}
29781805393SAxel Dörfler}
29881805393SAxel Dörfler
29981805393SAxel Dörfler
300aeaf68cfSAxel Dörflervoid
301aeaf68cfSAxel DörflerIMAPFolder::MessageEntriesFetched()
302aeaf68cfSAxel Dörfler{
30315216b26SAxel Dörfler	_WaitForFolderState();
30415216b26SAxel Dörfler
30515216b26SAxel Dörfler	// Synchronize all pending flags first
30615216b26SAxel Dörfler	UIDToFlagsMap::const_iterator pendingIterator = fPendingFlagsMap.begin();
30715216b26SAxel Dörfler	for (; pendingIterator != fPendingFlagsMap.end(); pendingIterator++)
30815216b26SAxel Dörfler		SyncMessageFlags(pendingIterator->first, pendingIterator->second);
30915216b26SAxel Dörfler
31015216b26SAxel Dörfler	fPendingFlagsMap.clear();
31115216b26SAxel Dörfler
31215216b26SAxel Dörfler	// Delete all local messages that are no longer found on the server
31315216b26SAxel Dörfler
31415216b26SAxel Dörfler	MutexLocker locker(fLock);
31515216b26SAxel Dörfler	UIDSet deleteUIDs;
316aeaf68cfSAxel Dörfler	UIDToRefMap::const_iterator iterator = fRefMap.begin();
317aeaf68cfSAxel Dörfler	for (; iterator != fRefMap.end(); iterator++) {
318aeaf68cfSAxel Dörfler		uint32 uid = iterator->first;
319aeaf68cfSAxel Dörfler		if (fSynchronizedUIDsSet.find(uid) == fSynchronizedUIDsSet.end())
32015216b26SAxel Dörfler			deleteUIDs.insert(uid);
321aeaf68cfSAxel Dörfler	}
322aeaf68cfSAxel Dörfler
323aeaf68cfSAxel Dörfler	fSynchronizedUIDsSet.clear();
32415216b26SAxel Dörfler	locker.Unlock();
32515216b26SAxel Dörfler
32615216b26SAxel Dörfler	UIDSet::const_iterator deleteIterator = deleteUIDs.begin();
32715216b26SAxel Dörfler	for (; deleteIterator != deleteUIDs.end(); deleteIterator++)
32815216b26SAxel Dörfler		_DeleteLocalMessage(*deleteIterator);
329aeaf68cfSAxel Dörfler}
330aeaf68cfSAxel Dörfler
331aeaf68cfSAxel Dörfler
332eba458b9SAxel Dörfler/*!	Stores the given \a stream into a temporary file using the provided
333eba458b9SAxel Dörfler	BFile object. A new file will be created, and the \a ref object will
334eba458b9SAxel Dörfler	point to it. The file will remain open when this method exits without
335eba458b9SAxel Dörfler	an error.
336eba458b9SAxel Dörfler
337eba458b9SAxel Dörfler	\a length will reflect how many bytes are left to read in case there
33881805393SAxel Dörfler	was an error.
339eba458b9SAxel Dörfler*/
3403302df14SAxel Dörflerstatus_t
3411052525dSAxel DörflerIMAPFolder::StoreMessage(uint32 fetchFlags, BDataIO& stream,
3421052525dSAxel Dörfler	size_t& length, entry_ref& ref, BFile& file)
3433302df14SAxel Dörfler{
3443302df14SAxel Dörfler	BPath path;
3453302df14SAxel Dörfler	status_t status = path.SetTo(&fRef);
3463302df14SAxel Dörfler	if (status != B_OK)
3473302df14SAxel Dörfler		return status;
3483302df14SAxel Dörfler
349eba458b9SAxel Dörfler	TemporaryFile temporaryFile(file);
350eba458b9SAxel Dörfler	status = temporaryFile.Init(path, ref);
3513302df14SAxel Dörfler	if (status != B_OK)
3523302df14SAxel Dörfler		return status;
3533302df14SAxel Dörfler
3541052525dSAxel Dörfler	status = _WriteStream(file, stream, length);
3551052525dSAxel Dörfler	if (status == B_OK)
3561052525dSAxel Dörfler		temporaryFile.KeepFile();
3573302df14SAxel Dörfler
3581052525dSAxel Dörfler	return status;
3593302df14SAxel Dörfler}
3603302df14SAxel Dörfler
3613302df14SAxel Dörfler
362eba458b9SAxel Dörfler/*!	Writes UID, and flags to the message, and notifies the protocol that a
363eba458b9SAxel Dörfler	message has been fetched. This method also closes the \a file passed in.
364eba458b9SAxel Dörfler*/
365f06916d5SAxel Dörflervoid
366eba458b9SAxel DörflerIMAPFolder::MessageStored(entry_ref& ref, BFile& file, uint32 fetchFlags,
367eba458b9SAxel Dörfler	uint32 uid, uint32 flags)
3683302df14SAxel Dörfler{
36981805393SAxel Dörfler	_WriteUniqueIDValidity(file);
370eba458b9SAxel Dörfler	_WriteUniqueID(file, uid);
371eba458b9SAxel Dörfler	if ((fetchFlags & IMAP::kFetchFlags) != 0)
372eba458b9SAxel Dörfler		_WriteFlags(file, flags);
3733302df14SAxel Dörfler
374d33e4744SAxel Dörfler	BMessage attributes;
37581805393SAxel Dörfler	_IMAPToMailFlags(flags, attributes);
376d33e4744SAxel Dörfler
377d33e4744SAxel Dörfler	fProtocol.MessageStored(*this, ref, file, fetchFlags, attributes);
378eba458b9SAxel Dörfler	file.Unset();
37947b68f58SAxel Dörfler
3801052525dSAxel Dörfler	fRefMap.insert(std::make_pair(uid, ref));
3811052525dSAxel Dörfler
38247b68f58SAxel Dörfler	if (uid > fLastUID) {
38347b68f58SAxel Dörfler		// Update last known UID
38447b68f58SAxel Dörfler		fLastUID = uid;
38547b68f58SAxel Dörfler
38647b68f58SAxel Dörfler		BNode directory(&fRef);
38747b68f58SAxel Dörfler		status_t status = _WriteUInt32(directory, kLastUIDAttribute, uid);
3881052525dSAxel Dörfler		if (status != B_OK) {
38947b68f58SAxel Dörfler			fprintf(stderr, "IMAP: Could not write last UID for mailbox "
39047b68f58SAxel Dörfler				"%s: %s\n", fMailboxName.String(), strerror(status));
3911052525dSAxel Dörfler		}
39247b68f58SAxel Dörfler	}
393f06916d5SAxel Dörfler}
394f06916d5SAxel Dörfler
395f06916d5SAxel Dörfler
39681805393SAxel Dörfler/*!	Pushes the refs for the pending bodies to the pending bodies list.
39781805393SAxel Dörfler	This allows to prevent retrieving bodies more than once.
39881805393SAxel Dörfler*/
39981805393SAxel Dörflervoid
40081805393SAxel DörflerIMAPFolder::RegisterPendingBodies(IMAP::MessageUIDList& uids,
40181805393SAxel Dörfler	const BMessenger* replyTo)
40281805393SAxel Dörfler{
40381805393SAxel Dörfler	MutexLocker locker(fLock);
40481805393SAxel Dörfler
40581805393SAxel Dörfler	MessengerList messengers;
40681805393SAxel Dörfler	if (replyTo != NULL)
40781805393SAxel Dörfler		messengers.push_back(*replyTo);
40881805393SAxel Dörfler
40981805393SAxel Dörfler	IMAP::MessageUIDList::const_iterator iterator = uids.begin();
41081805393SAxel Dörfler	for (; iterator != uids.end(); iterator++) {
41181805393SAxel Dörfler		if (replyTo != NULL)
41281805393SAxel Dörfler			fPendingBodies[*iterator].push_back(*replyTo);
41381805393SAxel Dörfler		else
41481805393SAxel Dörfler			fPendingBodies[*iterator].begin();
41581805393SAxel Dörfler	}
41681805393SAxel Dörfler}
41781805393SAxel Dörfler
41881805393SAxel Dörfler
4191052525dSAxel Dörfler/*!	Appends the given \a stream as body to the message file for the
4201052525dSAxel Dörfler	specified unique ID. The file will remain open when this method exits
4211052525dSAxel Dörfler	without an error.
4221052525dSAxel Dörfler
4231052525dSAxel Dörfler	\a length will reflect how many bytes are left to read in case there
4241052525dSAxel Dörfler	were an error.
4251052525dSAxel Dörfler*/
4261052525dSAxel Dörflerstatus_t
4271052525dSAxel DörflerIMAPFolder::StoreBody(uint32 uid, BDataIO& stream, size_t& length,
4281052525dSAxel Dörfler	entry_ref& ref, BFile& file)
4291052525dSAxel Dörfler{
4301052525dSAxel Dörfler	status_t status = GetMessageEntryRef(uid, ref);
4311052525dSAxel Dörfler	if (status != B_OK)
4321052525dSAxel Dörfler		return status;
4331052525dSAxel Dörfler
4341052525dSAxel Dörfler	status = file.SetTo(&ref, B_OPEN_AT_END | B_WRITE_ONLY);
4351052525dSAxel Dörfler	if (status != B_OK)
4361052525dSAxel Dörfler		return status;
4371052525dSAxel Dörfler
4381052525dSAxel Dörfler	BPath path(&ref);
4391052525dSAxel Dörfler	printf("IMAP: write body to %s\n", path.Path());
4401052525dSAxel Dörfler
4411052525dSAxel Dörfler	return _WriteStream(file, stream, length);
4421052525dSAxel Dörfler}
4431052525dSAxel Dörfler
4441052525dSAxel Dörfler
4451052525dSAxel Dörfler/*!	Notifies the protocol that a body has been fetched.
4461052525dSAxel Dörfler	This method also closes the \a file passed in.
4471052525dSAxel Dörfler*/
4481052525dSAxel Dörflervoid
4491052525dSAxel DörflerIMAPFolder::BodyStored(entry_ref& ref, BFile& file, uint32 uid)
4501052525dSAxel Dörfler{
451d33e4744SAxel Dörfler	BMessage attributes;
452d33e4744SAxel Dörfler	fProtocol.MessageStored(*this, ref, file, IMAP::kFetchBody, attributes);
4531052525dSAxel Dörfler	file.Unset();
45481805393SAxel Dörfler
45581805393SAxel Dörfler	_NotifyStoredBody(ref, uid, B_OK);
4561052525dSAxel Dörfler}
4571052525dSAxel Dörfler
4581052525dSAxel Dörfler
459f06916d5SAxel Dörflervoid
46081805393SAxel DörflerIMAPFolder::StoringBodyFailed(const entry_ref& ref, uint32 uid, status_t error)
461f06916d5SAxel Dörfler{
46281805393SAxel Dörfler	_NotifyStoredBody(ref, uid, error);
463f06916d5SAxel Dörfler}
464f06916d5SAxel Dörfler
465f06916d5SAxel Dörfler
466f06916d5SAxel Dörflervoid
46781805393SAxel DörflerIMAPFolder::DeleteMessage(uint32 uid)
468f06916d5SAxel Dörfler{
469aeaf68cfSAxel Dörfler	// TODO: move message to trash (server side)
470aeaf68cfSAxel Dörfler
471aeaf68cfSAxel Dörfler	_DeleteLocalMessage(uid);
472f06916d5SAxel Dörfler}
473f06916d5SAxel Dörfler
474f06916d5SAxel Dörfler
475f06916d5SAxel Dörflervoid
476f06916d5SAxel DörflerIMAPFolder::MessageReceived(BMessage* message)
477f06916d5SAxel Dörfler{
47815216b26SAxel Dörfler	switch (message->what) {
47915216b26SAxel Dörfler		default:
48015216b26SAxel Dörfler			BHandler::MessageReceived(message);
48115216b26SAxel Dörfler			break;
48215216b26SAxel Dörfler	}
48315216b26SAxel Dörfler}
48415216b26SAxel Dörfler
48515216b26SAxel Dörfler
48615216b26SAxel Dörflervoid
48715216b26SAxel DörflerIMAPFolder::_WaitForFolderState()
48815216b26SAxel Dörfler{
48915216b26SAxel Dörfler	while (true) {
49015216b26SAxel Dörfler		MutexLocker locker(fFolderStateLock);
49115216b26SAxel Dörfler		if (fFolderStateInitialized)
49215216b26SAxel Dörfler			return;
49315216b26SAxel Dörfler	}
494f06916d5SAxel Dörfler}
495f06916d5SAxel Dörfler
496f06916d5SAxel Dörfler
497f06916d5SAxel Dörflervoid
498f06916d5SAxel DörflerIMAPFolder::_InitializeFolderState()
499f06916d5SAxel Dörfler{
50015216b26SAxel Dörfler	mutex_lock(&fFolderStateLock);
50115216b26SAxel Dörfler
50215216b26SAxel Dörfler	fReadFolderStateThread = spawn_thread(&IMAPFolder::_ReadFolderState,
50315216b26SAxel Dörfler		"IMAP folder state", B_NORMAL_PRIORITY, this);
50415216b26SAxel Dörfler	if (fReadFolderStateThread >= 0)
50515216b26SAxel Dörfler		resume_thread(fReadFolderStateThread);
50615216b26SAxel Dörfler	else
50715216b26SAxel Dörfler		mutex_unlock(&fFolderStateLock);
50815216b26SAxel Dörfler}
50915216b26SAxel Dörfler
510f06916d5SAxel Dörfler
51115216b26SAxel Dörflervoid
51215216b26SAxel DörflerIMAPFolder::_ReadFolderState()
51315216b26SAxel Dörfler{
514f06916d5SAxel Dörfler	BDirectory directory(&fRef);
515f06916d5SAxel Dörfler	BEntry entry;
516f06916d5SAxel Dörfler	while (directory.GetNextEntry(&entry) == B_OK) {
517f06916d5SAxel Dörfler		entry_ref ref;
518f06916d5SAxel Dörfler		BNode node;
519f06916d5SAxel Dörfler		if (!entry.IsFile() || entry.GetRef(&ref) != B_OK
520f06916d5SAxel Dörfler			|| node.SetTo(&entry) != B_OK)
521f06916d5SAxel Dörfler			continue;
522f06916d5SAxel Dörfler
52381805393SAxel Dörfler		uint32 uidValidity = _ReadUniqueIDValidity(node);
52481805393SAxel Dörfler		if (uidValidity != fUIDValidity) {
52581805393SAxel Dörfler			// TODO: add file to mailbox
52681805393SAxel Dörfler			continue;
52781805393SAxel Dörfler		}
528f06916d5SAxel Dörfler		uint32 uid = _ReadUniqueID(node);
529f06916d5SAxel Dörfler		uint32 flags = _ReadFlags(node);
530f06916d5SAxel Dörfler
53181805393SAxel Dörfler		MutexLocker locker(fLock);
53215216b26SAxel Dörfler		if (fQuitFolderState)
53315216b26SAxel Dörfler			return;
534f06916d5SAxel Dörfler
535f06916d5SAxel Dörfler		fRefMap.insert(std::make_pair(uid, ref));
53681805393SAxel Dörfler		fFlagsMap.insert(std::make_pair(uid, flags));
53781805393SAxel Dörfler
53881805393SAxel Dörfler//		// TODO: make sure a listener exists at this point!
53981805393SAxel Dörfler//		std::set<uint32>::iterator found = lastUIDs.find(uid);
54081805393SAxel Dörfler//		if (found != lastUIDs.end()) {
54181805393SAxel Dörfler//			// The message is still around
54281805393SAxel Dörfler//			lastUIDs.erase(found);
54381805393SAxel Dörfler//
54481805393SAxel Dörfler//			uint32 flagsFound = MessageFlags(uid);
54581805393SAxel Dörfler//			if (flagsFound != flags) {
54681805393SAxel Dörfler//				// Its flags have changed locally, and need to be updated
54781805393SAxel Dörfler//				fListener->MessageFlagsChanged(_Token(uid), ref,
54881805393SAxel Dörfler//					flagsFound, flags);
54981805393SAxel Dörfler//			}
55081805393SAxel Dörfler//		} else {
55181805393SAxel Dörfler//			// This is a new message
55281805393SAxel Dörfler//			// TODO: the token must be the originating token!
55381805393SAxel Dörfler//			uid = fListener->MessageAdded(_Token(uid), ref);
55481805393SAxel Dörfler//			_WriteUniqueID(node, uid);
55581805393SAxel Dörfler//		}
55681805393SAxel Dörfler//
557f06916d5SAxel Dörfler	}
55881805393SAxel Dörfler
55915216b26SAxel Dörfler	fFolderStateInitialized = true;
56015216b26SAxel Dörfler	mutex_unlock(&fFolderStateLock);
56115216b26SAxel Dörfler}
56215216b26SAxel Dörfler
56315216b26SAxel Dörfler
56415216b26SAxel Dörfler/*static*/ status_t
56515216b26SAxel DörflerIMAPFolder::_ReadFolderState(void* self)
56615216b26SAxel Dörfler{
56715216b26SAxel Dörfler	((IMAPFolder*)self)->_ReadFolderState();
56815216b26SAxel Dörfler	return B_OK;
569f06916d5SAxel Dörfler}
570f06916d5SAxel Dörfler
571f06916d5SAxel Dörfler
572f06916d5SAxel Dörflerconst MessageToken
573f06916d5SAxel DörflerIMAPFolder::_Token(uint32 uid) const
574f06916d5SAxel Dörfler{
575f06916d5SAxel Dörfler	MessageToken token;
576f06916d5SAxel Dörfler	token.mailboxName = fMailboxName;
577f06916d5SAxel Dörfler	token.uidValidity = fUIDValidity;
578f06916d5SAxel Dörfler	token.uid = uid;
579f06916d5SAxel Dörfler
580f06916d5SAxel Dörfler	return token;
581f06916d5SAxel Dörfler}
582f06916d5SAxel Dörfler
583f06916d5SAxel Dörfler
58481805393SAxel Dörflervoid
58581805393SAxel DörflerIMAPFolder::_NotifyStoredBody(const entry_ref& ref, uint32 uid, status_t status)
58681805393SAxel Dörfler{
58781805393SAxel Dörfler	MutexLocker locker(fLock);
58881805393SAxel Dörfler	MessengerMap::iterator found = fPendingBodies.find(uid);
58981805393SAxel Dörfler	if (found != fPendingBodies.end()) {
59081805393SAxel Dörfler		MessengerList messengers = found->second;
59181805393SAxel Dörfler		fPendingBodies.erase(found);
59281805393SAxel Dörfler		locker.Unlock();
59381805393SAxel Dörfler
59481805393SAxel Dörfler		MessengerList::iterator iterator = messengers.begin();
59581805393SAxel Dörfler		for (; iterator != messengers.end(); iterator++)
59681805393SAxel Dörfler			BInboundMailProtocol::ReplyBodyFetched(*iterator, ref, status);
59781805393SAxel Dörfler	}
59881805393SAxel Dörfler}
59981805393SAxel Dörfler
60081805393SAxel Dörfler
60181805393SAxel Dörflerstatus_t
60281805393SAxel DörflerIMAPFolder::_GetMessageEntryRef(uint32 uid, entry_ref& ref) const
60381805393SAxel Dörfler{
60481805393SAxel Dörfler	UIDToRefMap::const_iterator found = fRefMap.find(uid);
60581805393SAxel Dörfler	if (found == fRefMap.end())
60615216b26SAxel Dörfler		return !fFolderStateInitialized ? B_TIMED_OUT : B_ENTRY_NOT_FOUND;
60781805393SAxel Dörfler
60881805393SAxel Dörfler	ref = found->second;
60981805393SAxel Dörfler	return B_OK;
61081805393SAxel Dörfler}
61181805393SAxel Dörfler
61281805393SAxel Dörfler
613aeaf68cfSAxel Dörflerstatus_t
614aeaf68cfSAxel DörflerIMAPFolder::_DeleteLocalMessage(uint32 uid)
615aeaf68cfSAxel Dörfler{
616aeaf68cfSAxel Dörfler	entry_ref ref;
617aeaf68cfSAxel Dörfler	status_t status = GetMessageEntryRef(uid, ref);
618aeaf68cfSAxel Dörfler	if (status != B_OK)
619aeaf68cfSAxel Dörfler		return status;
620aeaf68cfSAxel Dörfler
621aeaf68cfSAxel Dörfler	fRefMap.erase(uid);
622aeaf68cfSAxel Dörfler	fFlagsMap.erase(uid);
623aeaf68cfSAxel Dörfler
624aeaf68cfSAxel Dörfler	BEntry entry(&ref);
625aeaf68cfSAxel Dörfler	return entry.Remove();
626aeaf68cfSAxel Dörfler}
627aeaf68cfSAxel Dörfler
628aeaf68cfSAxel Dörfler
62981805393SAxel Dörflervoid
63081805393SAxel DörflerIMAPFolder::_IMAPToMailFlags(uint32 flags, BMessage& attributes)
63181805393SAxel Dörfler{
63281805393SAxel Dörfler	// TODO: add some utility function for this in libmail.so
63381805393SAxel Dörfler	if ((flags & IMAP::kAnswered) != 0)
63481805393SAxel Dörfler		attributes.AddString(B_MAIL_ATTR_STATUS, "Answered");
63581805393SAxel Dörfler	else if ((flags & IMAP::kFlagged) != 0)
63681805393SAxel Dörfler		attributes.AddString(B_MAIL_ATTR_STATUS, "Starred");
63781805393SAxel Dörfler	else if ((flags & IMAP::kSeen) != 0)
63881805393SAxel Dörfler		attributes.AddString(B_MAIL_ATTR_STATUS, "Read");
63981805393SAxel Dörfler}
64081805393SAxel Dörfler
64181805393SAxel Dörfler
64281805393SAxel Dörflerstatus_t
64381805393SAxel DörflerIMAPFolder::_MailToIMAPFlags(BNode& node, uint32& flags)
64481805393SAxel Dörfler{
64581805393SAxel Dörfler	BString mailStatus;
64681805393SAxel Dörfler	status_t status = node.ReadAttrString(B_MAIL_ATTR_STATUS, &mailStatus);
64781805393SAxel Dörfler	if (status != B_OK)
64881805393SAxel Dörfler		return status;
64981805393SAxel Dörfler
65081805393SAxel Dörfler	flags &= ~(IMAP::kAnswered | IMAP::kSeen);
65181805393SAxel Dörfler
65281805393SAxel Dörfler	// TODO: add some utility function for this in libmail.so
65381805393SAxel Dörfler	if (mailStatus == "Answered")
65481805393SAxel Dörfler		flags |= IMAP::kAnswered | IMAP::kSeen;
65581805393SAxel Dörfler	else if (mailStatus == "Read")
65681805393SAxel Dörfler		flags |= IMAP::kSeen;
65781805393SAxel Dörfler	else if (mailStatus == "Starred")
65881805393SAxel Dörfler		flags |= IMAP::kFlagged | IMAP::kSeen;
65981805393SAxel Dörfler
66081805393SAxel Dörfler	return B_OK;
66181805393SAxel Dörfler}
66281805393SAxel Dörfler
66381805393SAxel Dörfler
66481805393SAxel Dörflervoid
66581805393SAxel DörflerIMAPFolder::_TestMessageFlags(uint32 previousFlags, uint32 mailboxFlags,
66681805393SAxel Dörfler	uint32 currentFlags, uint32 testFlag, uint32& nextFlags)
66781805393SAxel Dörfler{
66881805393SAxel Dörfler	if ((previousFlags & testFlag) != (mailboxFlags & testFlag)) {
66981805393SAxel Dörfler		if ((previousFlags & testFlag) == (currentFlags & testFlag)) {
67081805393SAxel Dörfler			// The flags on the mailbox changed, update local flags
67181805393SAxel Dörfler			nextFlags &= ~testFlag;
67281805393SAxel Dörfler			nextFlags |= mailboxFlags & testFlag;
67381805393SAxel Dörfler		} else {
67481805393SAxel Dörfler			// Both flags changed. Since we don't have the means to do
67581805393SAxel Dörfler			// conflict resolution, we use a best effort mechanism
67681805393SAxel Dörfler			nextFlags |= testFlag;
67781805393SAxel Dörfler		}
67881805393SAxel Dörfler		return;
67981805393SAxel Dörfler	}
68081805393SAxel Dörfler
68181805393SAxel Dörfler	// Previous message flags, and mailbox flags are identical, see
68281805393SAxel Dörfler	// if the user changed the flag locally
68381805393SAxel Dörfler	if ((currentFlags & testFlag) != (previousFlags & testFlag)) {
68481805393SAxel Dörfler		// Flag changed, update mailbox
68581805393SAxel Dörfler		nextFlags &= ~testFlag;
68681805393SAxel Dörfler		nextFlags |= currentFlags & testFlag;
68781805393SAxel Dörfler	}
68881805393SAxel Dörfler}
68981805393SAxel Dörfler
69081805393SAxel Dörfler
691f06916d5SAxel Dörfleruint32
69281805393SAxel DörflerIMAPFolder::_ReadUniqueID(BNode& node) const
693f06916d5SAxel Dörfler{
694f06916d5SAxel Dörfler	// For compatibility we must assume that the UID is stored as a string
695f06916d5SAxel Dörfler	BString string;
696f06916d5SAxel Dörfler	if (node.ReadAttrString(kUIDAttribute, &string) != B_OK)
697f06916d5SAxel Dörfler		return 0;
698f06916d5SAxel Dörfler
699f06916d5SAxel Dörfler	return strtoul(string.String(), NULL, 0);
700f06916d5SAxel Dörfler}
701f06916d5SAxel Dörfler
702f06916d5SAxel Dörfler
703eba458b9SAxel Dörflerstatus_t
70481805393SAxel DörflerIMAPFolder::_WriteUniqueID(BNode& node, uint32 uid) const
705eba458b9SAxel Dörfler{
70681805393SAxel Dörfler	// For compatibility we must assume that the UID is stored as a string
707eba458b9SAxel Dörfler	BString string;
708eba458b9SAxel Dörfler	string << uid;
709eba458b9SAxel Dörfler
710eba458b9SAxel Dörfler	return node.WriteAttrString(kUIDAttribute, &string);
711eba458b9SAxel Dörfler}
712eba458b9SAxel Dörfler
713eba458b9SAxel Dörfler
714f06916d5SAxel Dörfleruint32
71581805393SAxel DörflerIMAPFolder::_ReadUniqueIDValidity(BNode& node) const
71681805393SAxel Dörfler{
71781805393SAxel Dörfler
71881805393SAxel Dörfler	return _ReadUInt32(node, kUIDValidityAttribute);
71981805393SAxel Dörfler}
72081805393SAxel Dörfler
72181805393SAxel Dörfler
72281805393SAxel Dörflerstatus_t
72381805393SAxel DörflerIMAPFolder::_WriteUniqueIDValidity(BNode& node) const
72481805393SAxel Dörfler{
72581805393SAxel Dörfler	return _WriteUInt32(node, kUIDValidityAttribute, fUIDValidity);
72681805393SAxel Dörfler}
72781805393SAxel Dörfler
72881805393SAxel Dörfler
72981805393SAxel Dörfleruint32
73081805393SAxel DörflerIMAPFolder::_ReadFlags(BNode& node) const
731f06916d5SAxel Dörfler{
73247b68f58SAxel Dörfler	return _ReadUInt32(node, kFlagsAttribute);
733f06916d5SAxel Dörfler}
734eba458b9SAxel Dörfler
735eba458b9SAxel Dörfler
736eba458b9SAxel Dörflerstatus_t
73781805393SAxel DörflerIMAPFolder::_WriteFlags(BNode& node, uint32 flags) const
738eba458b9SAxel Dörfler{
73947b68f58SAxel Dörfler	return _WriteUInt32(node, kFlagsAttribute, flags);
74047b68f58SAxel Dörfler}
74147b68f58SAxel Dörfler
74247b68f58SAxel Dörfler
74347b68f58SAxel Dörfleruint32
74481805393SAxel DörflerIMAPFolder::_ReadUInt32(BNode& node, const char* attribute) const
74547b68f58SAxel Dörfler{
74647b68f58SAxel Dörfler	uint32 value;
74747b68f58SAxel Dörfler	ssize_t bytesRead = node.ReadAttr(attribute, B_UINT32_TYPE, 0,
74847b68f58SAxel Dörfler		&value, sizeof(uint32));
74947b68f58SAxel Dörfler	if (bytesRead == (ssize_t)sizeof(uint32))
75047b68f58SAxel Dörfler		return value;
75147b68f58SAxel Dörfler
75247b68f58SAxel Dörfler	return 0;
75347b68f58SAxel Dörfler}
75447b68f58SAxel Dörfler
75547b68f58SAxel Dörfler
75647b68f58SAxel Dörflerstatus_t
75781805393SAxel DörflerIMAPFolder::_WriteUInt32(BNode& node, const char* attribute, uint32 value) const
75847b68f58SAxel Dörfler{
75947b68f58SAxel Dörfler	ssize_t bytesWritten = node.WriteAttr(attribute, B_UINT32_TYPE, 0,
76047b68f58SAxel Dörfler		&value, sizeof(uint32));
761eba458b9SAxel Dörfler	if (bytesWritten == (ssize_t)sizeof(uint32))
762eba458b9SAxel Dörfler		return B_OK;
763eba458b9SAxel Dörfler
764eba458b9SAxel Dörfler	return bytesWritten < 0 ? bytesWritten : B_IO_ERROR;
765eba458b9SAxel Dörfler}
7661052525dSAxel Dörfler
7671052525dSAxel Dörfler
7681052525dSAxel Dörflerstatus_t
76981805393SAxel DörflerIMAPFolder::_WriteStream(BFile& file, BDataIO& stream, size_t& length) const
7701052525dSAxel Dörfler{
7711052525dSAxel Dörfler	char buffer[65535];
7721052525dSAxel Dörfler	while (length > 0) {
7731052525dSAxel Dörfler		ssize_t bytesRead = stream.Read(buffer,
7741052525dSAxel Dörfler			std::min(sizeof(buffer), length));
7751052525dSAxel Dörfler		if (bytesRead < 0)
7761052525dSAxel Dörfler			return bytesRead;
7771052525dSAxel Dörfler		if (bytesRead <= 0)
7781052525dSAxel Dörfler			break;
7791052525dSAxel Dörfler
7801052525dSAxel Dörfler		length -= bytesRead;
7811052525dSAxel Dörfler
7821052525dSAxel Dörfler		ssize_t bytesWritten = file.Write(buffer, bytesRead);
7831052525dSAxel Dörfler		if (bytesWritten < 0)
7841052525dSAxel Dörfler			return bytesWritten;
7851052525dSAxel Dörfler		if (bytesWritten != bytesRead)
7861052525dSAxel Dörfler			return B_IO_ERROR;
7871052525dSAxel Dörfler	}
7881052525dSAxel Dörfler
7891052525dSAxel Dörfler	return B_OK;
7901052525dSAxel Dörfler}