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