1/*
2 * Copyright 2011, Haiku, Inc. All rights reserved.
3 * Copyright 2011, Clemens Zeidler <haiku@clemens-zeidler.de>
4 * Distributed under the terms of the MIT License.
5 */
6
7
8#include "IMAPMailbox.h"
9
10#include "IMAPHandler.h"
11#include "IMAPStorage.h"
12
13
14#define DEBUG_IMAP_MAILBOX
15#ifdef DEBUG_IMAP_MAILBOX
16#	include <stdio.h>
17#	define TRACE(x...) printf(x)
18#else
19#	define TRACE(x...) ;
20#endif
21
22
23MinMessage::MinMessage()
24{
25	uid = 0;
26	flags = 0;
27}
28
29
30// #pragma mark -
31
32
33IMAPMailbox::IMAPMailbox(IMAPStorage& storage)
34	:
35	fStorage(storage),
36
37	fMailboxSelectHandler(*this),
38	fExistsHandler(*this),
39	fExpungeHandler(*this),
40	fFlagsHandler(*this),
41
42	fWatching(0),
43
44	fFetchBodyLimit(0)
45{
46	fHandlerList.AddItem(&fCapabilityHandler);
47	fIMAPMailboxListener = &fNULLListener;
48}
49
50
51IMAPMailbox::~IMAPMailbox()
52{
53	Disconnect();
54}
55
56
57void
58IMAPMailbox::SetListener(IMAPMailboxListener* listener)
59{
60	if (listener == NULL)
61		fIMAPMailboxListener = &fNULLListener;
62	else
63		fIMAPMailboxListener = listener;
64}
65
66
67status_t
68IMAPMailbox::SelectMailbox(const char* mailbox)
69{
70	TRACE("SELECT %s\n", mailbox);
71	fMailboxSelectHandler.SetTo(mailbox);
72	status_t status = ProcessCommand(&fMailboxSelectHandler);
73	if (status != B_OK)
74		return status;
75	fSelectedMailbox = mailbox;
76
77	if (fCapabilityHandler.Capabilities() != "")
78		return status;
79	//else get them
80	status = ProcessCommand(fCapabilityHandler.Command());
81	if (status != B_OK)
82		return status;
83
84	return B_OK;
85}
86
87
88BString
89IMAPMailbox::Mailbox()
90{
91	return fSelectedMailbox;
92}
93
94
95status_t
96IMAPMailbox::Sync()
97{
98	TRACE("Sync\n");
99	if (fMailboxSelectHandler.NextUID() <= 1)
100		return B_OK;
101
102	_InstallUnsolicitedHandler(false);
103
104	fMessageList.clear();
105	FetchMessageListCommand fetchMessageListCommand(*this, &fMessageList,
106		fMailboxSelectHandler.NextUID());
107	status_t status = ProcessCommand(&fetchMessageListCommand);
108	if (status != B_OK)
109		return status;
110
111	_InstallUnsolicitedHandler(true);
112	return B_OK;
113}
114
115
116bool
117IMAPMailbox::SupportWatching()
118{
119	if (fCapabilityHandler.Capabilities().FindFirst("IDLE") < 0)
120		return false;
121	return true;
122}
123
124
125status_t
126IMAPMailbox::StartWatchingMailbox(sem_id startedSem)
127{
128	atomic_set(&fWatching, 1);
129
130	bool firstIDLE = true;
131	// refresh every 29 min
132	bigtime_t timeout = 1000 * 1000 * 60 * 29; // 29 min
133	status_t status;
134	while (true) {
135		int32 commandID = NextCommandID();
136		TRACE("IDLE ...\n");
137		status = SendCommand("IDLE", commandID);
138		if (firstIDLE) {
139			release_sem(startedSem);
140			firstIDLE = false;
141		}
142		if (status != B_OK)
143			break;
144
145		status = HandleResponse(commandID, timeout, false);
146		ProcessAfterQuacks(kIMAP4ClientTimeout);
147
148		if (atomic_get(&fWatching) == 0)
149			break;
150
151		if (status == B_TIMED_OUT) {
152			TRACE("Renew IDLE connection.\n");
153			status = SendRawCommand("DONE");
154			if (status != B_OK)
155				break;
156			// handle IDLE response and more
157			status = ProcessCommand("NOOP");
158			if (status != B_OK)
159				break;
160			else
161				continue;
162		}
163		if (status != B_OK)
164			break;
165	}
166
167	atomic_set(&fWatching, 0);
168	return status;
169}
170
171
172
173status_t
174IMAPMailbox::StopWatchingMailbox()
175{
176	if (atomic_get(&fWatching) == 0)
177		return B_OK;
178	atomic_set(&fWatching, 0);
179	return SendRawCommand("DONE");
180}
181
182
183status_t
184IMAPMailbox::CheckMailbox()
185{
186	return ProcessCommand("NOOP");
187}
188
189
190status_t
191IMAPMailbox::FetchMinMessage(int32 messageNumber, BPositionIO** data)
192{
193	if (messageNumber <= 0)
194		return B_BAD_VALUE;
195	FetchMinMessageCommand fetchMinMessageCommand(*this, messageNumber,
196		&fMessageList, data);
197	return ProcessCommand(&fetchMinMessageCommand);
198}
199
200
201status_t
202IMAPMailbox::FetchBody(int32 messageNumber, BPositionIO* data)
203{
204	if (data == NULL || messageNumber <= 0) {
205		delete data;
206		return B_BAD_VALUE;
207	}
208
209	FetchBodyCommand fetchBodyCommand(*this, messageNumber, data);
210	status_t status = ProcessCommand(&fetchBodyCommand);
211
212	return status;
213}
214
215
216void
217IMAPMailbox::SetFetchBodyLimit(int32 limit)
218{
219	fFetchBodyLimit = limit;
220}
221
222
223status_t
224IMAPMailbox::FetchMessage(int32 messageNumber)
225{
226	return FetchMessages(messageNumber, -1);
227}
228
229
230status_t
231IMAPMailbox::FetchMessages(int32 firstMessage, int32 lastMessage)
232{
233	FetchMessageCommand fetchCommand(*this, firstMessage, lastMessage,
234		fFetchBodyLimit);
235	return ProcessCommand(&fetchCommand);
236}
237
238
239status_t
240IMAPMailbox::FetchBody(int32 messageNumber)
241{
242	if (fStorage.BodyFetched(MessageNumberToUID(messageNumber)))
243		return B_BAD_VALUE;
244
245	BPositionIO* file;
246	status_t status = fStorage.OpenMessage(MessageNumberToUID(messageNumber),
247		&file);
248	if (status != B_OK)
249		return status;
250
251	// fetch command deletes the file
252	status = FetchBody(messageNumber, file);
253	return status;
254}
255
256
257status_t
258IMAPMailbox::SetFlags(int32 messageNumber, int32 flags)
259{
260	if (messageNumber <= 0)
261		return B_BAD_VALUE;
262
263	SetFlagsCommand setFlagsCommand(*this, messageNumber, flags);
264	return ProcessCommand(&setFlagsCommand);
265}
266
267
268status_t
269IMAPMailbox::AppendMessage(BPositionIO& message, off_t size, int32 flags,
270	time_t time)
271{
272	AppendCommand appendCommand(*this, message, size, flags, time);
273	return ProcessCommand(&appendCommand);
274}
275
276
277int32
278IMAPMailbox::UIDToMessageNumber(int32 uid)
279{
280	for (unsigned int i = 0; i < fMessageList.size(); i++) {
281		if (fMessageList[i].uid == uid)
282			return i + 1;
283	}
284	return -1;
285}
286
287
288int32
289IMAPMailbox::MessageNumberToUID(int32 messageNumber)
290{
291	int32 index = messageNumber - 1;
292	if (index < 0 || index >= (int32)fMessageList.size())
293		return -1;
294	return fMessageList[index].uid;
295}
296
297
298status_t
299IMAPMailbox::DeleteMessage(int32 uid, bool permanently)
300{
301	int32 flags = fStorage.GetFlags(uid);
302	flags |= kDeleted;
303	status_t status = SetFlags(UIDToMessageNumber(uid), flags);
304
305	if (!permanently || status != B_OK)
306		return status;
307
308	// delete permanently by invoking expunge
309	ExpungeCommmand expungeCommand(*this);
310	return ProcessCommand(&expungeCommand);
311}
312
313
314void
315IMAPMailbox::_InstallUnsolicitedHandler(bool install)
316{
317	if (install) {
318		fHandlerList.AddItem(&fFlagsHandler, 0);
319		fHandlerList.AddItem(&fExpungeHandler, 0);
320		fHandlerList.AddItem(&fExistsHandler, 0);
321	} else {
322		fHandlerList.RemoveItem(&fFlagsHandler);
323		fHandlerList.RemoveItem(&fExpungeHandler);
324		fHandlerList.RemoveItem(&fExistsHandler);
325	}
326}
327