1/*
2 * Copyright 2011-2016, Haiku, Inc. All rights reserved.
3 * Copyright 2001-2003 Dr. Zoidberg Enterprises. All rights reserved.
4 */
5
6
7#include <stdio.h>
8#include <stdlib.h>
9
10#include <fs_attr.h>
11
12#include <Alert.h>
13#include <Autolock.h>
14#include <Directory.h>
15#include <E-mail.h>
16#include <FindDirectory.h>
17#include <Node.h>
18#include <NodeInfo.h>
19#include <NodeMonitor.h>
20#include <Path.h>
21#include <Query.h>
22#include <Roster.h>
23#include <String.h>
24#include <StringList.h>
25#include <VolumeRoster.h>
26
27#include <MailFilter.h>
28#include <MailDaemon.h>
29#include <MailProtocol.h>
30#include <MailSettings.h>
31
32#include <mail_util.h>
33#include <MailPrivate.h>
34#include <NodeMessage.h>
35
36#include "HaikuMailFormatFilter.h"
37
38
39using namespace BPrivate;
40
41
42BMailProtocol::BMailProtocol(const char* name,
43	const BMailAccountSettings& settings)
44	:
45	BLooper(_LooperName(name, settings)),
46	fAccountSettings(settings),
47	fMailNotifier(NULL)
48{
49	AddFilter(new HaikuMailFormatFilter(*this, settings));
50}
51
52
53BMailProtocol::~BMailProtocol()
54{
55	delete fMailNotifier;
56
57	for (int i = 0; i < fFilterList.CountItems(); i++)
58		delete fFilterList.ItemAt(i);
59
60	std::map<entry_ref, image_id>::iterator it = fFilterImages.begin();
61	for (; it != fFilterImages.end(); it++)
62		unload_add_on(it->second);
63}
64
65
66const BMailAccountSettings&
67BMailProtocol::AccountSettings() const
68{
69	return fAccountSettings;
70}
71
72
73void
74BMailProtocol::SetMailNotifier(BMailNotifier* mailNotifier)
75{
76	delete fMailNotifier;
77	fMailNotifier = mailNotifier;
78}
79
80
81BMailNotifier*
82BMailProtocol::MailNotifier() const
83{
84	return fMailNotifier;
85}
86
87
88bool
89BMailProtocol::AddFilter(BMailFilter* filter)
90{
91	BAutolock locker(const_cast< BMailProtocol * >(this));
92	return fFilterList.AddItem(filter);
93}
94
95
96int32
97BMailProtocol::CountFilter() const
98{
99	BAutolock locker(const_cast< BMailProtocol * >(this));
100	return fFilterList.CountItems();
101}
102
103
104BMailFilter*
105BMailProtocol::FilterAt(int32 index) const
106{
107	BAutolock locker(const_cast< BMailProtocol * >(this));
108	return fFilterList.ItemAt(index);
109}
110
111
112BMailFilter*
113BMailProtocol::RemoveFilter(int32 index)
114{
115	BAutolock locker(const_cast< BMailProtocol * >(this));
116	return fFilterList.RemoveItemAt(index);
117}
118
119
120bool
121BMailProtocol::RemoveFilter(BMailFilter* filter)
122{
123	BAutolock locker(const_cast< BMailProtocol * >(this));
124	return fFilterList.RemoveItem(filter);
125}
126
127
128void
129BMailProtocol::MessageReceived(BMessage* message)
130{
131	BLooper::MessageReceived(message);
132}
133
134
135void
136BMailProtocol::ShowError(const char* error)
137{
138	if (MailNotifier() != NULL)
139		MailNotifier()->ShowError(error);
140}
141
142
143void
144BMailProtocol::ShowMessage(const char* message)
145{
146	if (MailNotifier() != NULL)
147		MailNotifier()->ShowMessage(message);
148}
149
150
151void
152BMailProtocol::SetTotalItems(uint32 items)
153{
154	if (MailNotifier() != NULL)
155		MailNotifier()->SetTotalItems(items);
156}
157
158
159void
160BMailProtocol::SetTotalItemsSize(uint64 size)
161{
162	if (MailNotifier() != NULL)
163		MailNotifier()->SetTotalItemsSize(size);
164}
165
166
167void
168BMailProtocol::ReportProgress(uint32 messages, uint64 bytes,
169	const char* message)
170{
171	if (MailNotifier() != NULL)
172		MailNotifier()->ReportProgress(messages, bytes, message);
173}
174
175
176void
177BMailProtocol::ResetProgress(const char* message)
178{
179	if (MailNotifier() != NULL)
180		MailNotifier()->ResetProgress(message);
181}
182
183
184void
185BMailProtocol::NotifyNewMessagesToFetch(int32 count)
186{
187	ResetProgress();
188	SetTotalItems(count);
189}
190
191
192BMailFilterAction
193BMailProtocol::ProcessHeaderFetched(entry_ref& ref, BFile& file,
194	BMessage& attributes)
195{
196	BMailFilterAction action = _ProcessHeaderFetched(ref, file, attributes);
197	if (action >= B_OK && action != B_DELETE_MAIL_ACTION)
198		file << attributes;
199
200	return action;
201}
202
203
204void
205BMailProtocol::NotifyBodyFetched(const entry_ref& ref, BFile& file,
206	BMessage& attributes)
207{
208	_NotifyBodyFetched(ref, file, attributes);
209	file << attributes;
210}
211
212
213BMailFilterAction
214BMailProtocol::ProcessMessageFetched(entry_ref& ref, BFile& file,
215	BMessage& attributes)
216{
217	BMailFilterAction action = _ProcessHeaderFetched(ref, file, attributes);
218	if (action >= B_OK && action != B_DELETE_MAIL_ACTION) {
219		_NotifyBodyFetched(ref, file, attributes);
220		file << attributes;
221	}
222
223	return action;
224}
225
226
227void
228BMailProtocol::NotifyMessageReadyToSend(const entry_ref& ref, BFile& file)
229{
230	for (int i = 0; i < fFilterList.CountItems(); i++)
231		fFilterList.ItemAt(i)->MessageReadyToSend(ref, file);
232}
233
234
235void
236BMailProtocol::NotifyMessageSent(const entry_ref& ref, BFile& file)
237{
238	for (int i = 0; i < fFilterList.CountItems(); i++)
239		fFilterList.ItemAt(i)->MessageSent(ref, file);
240}
241
242
243void
244BMailProtocol::LoadFilters(const BMailProtocolSettings& settings)
245{
246	for (int i = 0; i < settings.CountFilterSettings(); i++) {
247		BMailAddOnSettings* filterSettings = settings.FilterSettingsAt(i);
248		BMailFilter* filter = _LoadFilter(*filterSettings);
249		if (filter != NULL)
250			AddFilter(filter);
251	}
252}
253
254
255/*static*/ BString
256BMailProtocol::_LooperName(const char* addOnName,
257	const BMailAccountSettings& settings)
258{
259	BString name = addOnName;
260
261	const char* accountName = settings.Name();
262	if (accountName != NULL && accountName[0] != '\0')
263		name << " " << accountName;
264
265	return name;
266}
267
268
269BMailFilter*
270BMailProtocol::_LoadFilter(const BMailAddOnSettings& settings)
271{
272	const entry_ref& ref = settings.AddOnRef();
273	std::map<entry_ref, image_id>::iterator it = fFilterImages.find(ref);
274	image_id image;
275	if (it != fFilterImages.end())
276		image = it->second;
277	else {
278		BEntry entry(&ref);
279		BPath path(&entry);
280		image = load_add_on(path.Path());
281	}
282	if (image < 0)
283		return NULL;
284
285	BMailFilter* (*instantiateFilter)(BMailProtocol& protocol,
286		const BMailAddOnSettings& settings);
287	if (get_image_symbol(image, "instantiate_filter", B_SYMBOL_TYPE_TEXT,
288			(void**)&instantiateFilter) != B_OK) {
289		unload_add_on(image);
290		return NULL;
291	}
292
293	fFilterImages[ref] = image;
294	return instantiateFilter(*this, settings);
295}
296
297
298BMailFilterAction
299BMailProtocol::_ProcessHeaderFetched(entry_ref& ref, BFile& file,
300	BMessage& attributes)
301{
302	entry_ref outRef = ref;
303
304	for (int i = 0; i < fFilterList.CountItems(); i++) {
305		BMailFilterAction action = fFilterList.ItemAt(i)->HeaderFetched(outRef,
306			file, attributes);
307		if (action == B_DELETE_MAIL_ACTION) {
308			// We have to delete the message
309			BEntry entry(&ref);
310			status_t status = entry.Remove();
311			if (status != B_OK) {
312				fprintf(stderr, "BMailProtocol::NotifyHeaderFetched(): could "
313					"not delete mail: %s\n", strerror(status));
314			}
315			return B_DELETE_MAIL_ACTION;
316		}
317	}
318
319	if (ref == outRef)
320		return B_NO_MAIL_ACTION;
321
322	// We have to rename the file
323	node_ref newParentRef;
324	newParentRef.device = outRef.device;
325	newParentRef.node = outRef.directory;
326
327	BDirectory newParent(&newParentRef);
328	status_t status = newParent.InitCheck();
329	BString workerName;
330	if (status == B_OK) {
331		int32 uniqueNumber = 1;
332		do {
333			workerName = outRef.name;
334			if (uniqueNumber > 1)
335				workerName << "_" << uniqueNumber;
336
337			// TODO: support copying to another device!
338			BEntry entry(&ref);
339			status = entry.Rename(workerName);
340
341			uniqueNumber++;
342		} while (status == B_FILE_EXISTS);
343	}
344
345	if (status != B_OK) {
346		fprintf(stderr, "BMailProtocol::NotifyHeaderFetched(): could not "
347			"rename mail (%s)! (should be: %s)\n", strerror(status),
348			workerName.String());
349	}
350
351	ref = outRef;
352	ref.set_name(workerName.String());
353
354	return B_MOVE_MAIL_ACTION;
355}
356
357
358void
359BMailProtocol::_NotifyBodyFetched(const entry_ref& ref, BFile& file,
360	BMessage& attributes)
361{
362	for (int i = 0; i < fFilterList.CountItems(); i++)
363		fFilterList.ItemAt(i)->BodyFetched(ref, file, attributes);
364}
365
366
367// #pragma mark -
368
369
370BInboundMailProtocol::BInboundMailProtocol(const char* name,
371	const BMailAccountSettings& settings)
372	:
373	BMailProtocol(name, settings)
374{
375	LoadFilters(fAccountSettings.InboundSettings());
376}
377
378
379BInboundMailProtocol::~BInboundMailProtocol()
380{
381}
382
383
384void
385BInboundMailProtocol::MessageReceived(BMessage* message)
386{
387	switch (message->what) {
388		case kMsgSyncMessages:
389		{
390			NotiyMailboxSynchronized(SyncMessages());
391			break;
392		}
393
394		case kMsgFetchBody:
395		{
396			entry_ref ref;
397			if (message->FindRef("ref", &ref) != B_OK)
398				break;
399
400			BMessenger target;
401			message->FindMessenger("target", &target);
402
403			status_t status = HandleFetchBody(ref, target);
404			if (status != B_OK)
405				ReplyBodyFetched(target, ref, status);
406			break;
407		}
408
409		case kMsgMarkMessageAsRead:
410		{
411			entry_ref ref;
412			message->FindRef("ref", &ref);
413			read_flags read = (read_flags)message->FindInt32("read");
414			MarkMessageAsRead(ref, read);
415			break;
416		}
417
418		default:
419			BMailProtocol::MessageReceived(message);
420			break;
421	}
422}
423
424
425status_t
426BInboundMailProtocol::FetchBody(const entry_ref& ref, BMessenger* replyTo)
427{
428	BMessage message(kMsgFetchBody);
429	message.AddRef("ref", &ref);
430	if (replyTo != NULL)
431		message.AddMessenger("target", *replyTo);
432
433	return BMessenger(this).SendMessage(&message);
434}
435
436
437status_t
438BInboundMailProtocol::MarkMessageAsRead(const entry_ref& ref, read_flags flag)
439{
440	BNode node(&ref);
441	return write_read_attr(node, flag);
442}
443
444
445/*static*/ void
446BInboundMailProtocol::ReplyBodyFetched(const BMessenger& replyTo,
447	const entry_ref& ref, status_t status)
448{
449	BMessage message(B_MAIL_BODY_FETCHED);
450	message.AddInt32("status", status);
451	message.AddRef("ref", &ref);
452	replyTo.SendMessage(&message);
453}
454
455
456void
457BInboundMailProtocol::NotiyMailboxSynchronized(status_t status)
458{
459	for (int32 i = 0; i < CountFilter(); i++)
460		FilterAt(i)->MailboxSynchronized(status);
461}
462
463
464// #pragma mark -
465
466
467BOutboundMailProtocol::BOutboundMailProtocol(const char* name,
468	const BMailAccountSettings& settings)
469	:
470	BMailProtocol(name, settings)
471{
472	LoadFilters(fAccountSettings.OutboundSettings());
473}
474
475
476BOutboundMailProtocol::~BOutboundMailProtocol()
477{
478}
479
480
481status_t
482BOutboundMailProtocol::SendMessages(const BMessage& files, off_t totalBytes)
483{
484	BMessage message(kMsgSendMessages);
485	message.Append(files);
486	message.AddInt64("bytes", totalBytes);
487
488	return BMessenger(this).SendMessage(&message);
489}
490
491
492void
493BOutboundMailProtocol::MessageReceived(BMessage* message)
494{
495	switch (message->what) {
496		case kMsgSendMessages:
497			HandleSendMessages(*message, message->FindInt64("bytes"));
498			break;
499
500		default:
501			BMailProtocol::MessageReceived(message);
502	}
503}
504