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
39 using namespace BPrivate;
40
41
BMailProtocol(const char * name,const BMailAccountSettings & settings)42 BMailProtocol::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
~BMailProtocol()53 BMailProtocol::~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
66 const BMailAccountSettings&
AccountSettings() const67 BMailProtocol::AccountSettings() const
68 {
69 return fAccountSettings;
70 }
71
72
73 void
SetMailNotifier(BMailNotifier * mailNotifier)74 BMailProtocol::SetMailNotifier(BMailNotifier* mailNotifier)
75 {
76 delete fMailNotifier;
77 fMailNotifier = mailNotifier;
78 }
79
80
81 BMailNotifier*
MailNotifier() const82 BMailProtocol::MailNotifier() const
83 {
84 return fMailNotifier;
85 }
86
87
88 bool
AddFilter(BMailFilter * filter)89 BMailProtocol::AddFilter(BMailFilter* filter)
90 {
91 BAutolock locker(const_cast< BMailProtocol * >(this));
92 return fFilterList.AddItem(filter);
93 }
94
95
96 int32
CountFilter() const97 BMailProtocol::CountFilter() const
98 {
99 BAutolock locker(const_cast< BMailProtocol * >(this));
100 return fFilterList.CountItems();
101 }
102
103
104 BMailFilter*
FilterAt(int32 index) const105 BMailProtocol::FilterAt(int32 index) const
106 {
107 BAutolock locker(const_cast< BMailProtocol * >(this));
108 return fFilterList.ItemAt(index);
109 }
110
111
112 BMailFilter*
RemoveFilter(int32 index)113 BMailProtocol::RemoveFilter(int32 index)
114 {
115 BAutolock locker(const_cast< BMailProtocol * >(this));
116 return fFilterList.RemoveItemAt(index);
117 }
118
119
120 bool
RemoveFilter(BMailFilter * filter)121 BMailProtocol::RemoveFilter(BMailFilter* filter)
122 {
123 BAutolock locker(const_cast< BMailProtocol * >(this));
124 return fFilterList.RemoveItem(filter);
125 }
126
127
128 void
MessageReceived(BMessage * message)129 BMailProtocol::MessageReceived(BMessage* message)
130 {
131 BLooper::MessageReceived(message);
132 }
133
134
135 void
ShowError(const char * error)136 BMailProtocol::ShowError(const char* error)
137 {
138 if (MailNotifier() != NULL)
139 MailNotifier()->ShowError(error);
140 }
141
142
143 void
ShowMessage(const char * message)144 BMailProtocol::ShowMessage(const char* message)
145 {
146 if (MailNotifier() != NULL)
147 MailNotifier()->ShowMessage(message);
148 }
149
150
151 void
SetTotalItems(uint32 items)152 BMailProtocol::SetTotalItems(uint32 items)
153 {
154 if (MailNotifier() != NULL)
155 MailNotifier()->SetTotalItems(items);
156 }
157
158
159 void
SetTotalItemsSize(uint64 size)160 BMailProtocol::SetTotalItemsSize(uint64 size)
161 {
162 if (MailNotifier() != NULL)
163 MailNotifier()->SetTotalItemsSize(size);
164 }
165
166
167 void
ReportProgress(uint32 messages,uint64 bytes,const char * message)168 BMailProtocol::ReportProgress(uint32 messages, uint64 bytes,
169 const char* message)
170 {
171 if (MailNotifier() != NULL)
172 MailNotifier()->ReportProgress(messages, bytes, message);
173 }
174
175
176 void
ResetProgress(const char * message)177 BMailProtocol::ResetProgress(const char* message)
178 {
179 if (MailNotifier() != NULL)
180 MailNotifier()->ResetProgress(message);
181 }
182
183
184 void
NotifyNewMessagesToFetch(int32 count)185 BMailProtocol::NotifyNewMessagesToFetch(int32 count)
186 {
187 ResetProgress();
188 SetTotalItems(count);
189 }
190
191
192 BMailFilterAction
ProcessHeaderFetched(entry_ref & ref,BFile & file,BMessage & attributes)193 BMailProtocol::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
204 void
NotifyBodyFetched(const entry_ref & ref,BFile & file,BMessage & attributes)205 BMailProtocol::NotifyBodyFetched(const entry_ref& ref, BFile& file,
206 BMessage& attributes)
207 {
208 _NotifyBodyFetched(ref, file, attributes);
209 file << attributes;
210 }
211
212
213 BMailFilterAction
ProcessMessageFetched(entry_ref & ref,BFile & file,BMessage & attributes)214 BMailProtocol::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
227 void
NotifyMessageReadyToSend(const entry_ref & ref,BFile & file)228 BMailProtocol::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
235 void
NotifyMessageSent(const entry_ref & ref,BFile & file)236 BMailProtocol::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
243 void
LoadFilters(const BMailProtocolSettings & settings)244 BMailProtocol::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
_LooperName(const char * addOnName,const BMailAccountSettings & settings)256 BMailProtocol::_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
269 BMailFilter*
_LoadFilter(const BMailAddOnSettings & settings)270 BMailProtocol::_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
298 BMailFilterAction
_ProcessHeaderFetched(entry_ref & ref,BFile & file,BMessage & attributes)299 BMailProtocol::_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
358 void
_NotifyBodyFetched(const entry_ref & ref,BFile & file,BMessage & attributes)359 BMailProtocol::_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
BInboundMailProtocol(const char * name,const BMailAccountSettings & settings)370 BInboundMailProtocol::BInboundMailProtocol(const char* name,
371 const BMailAccountSettings& settings)
372 :
373 BMailProtocol(name, settings)
374 {
375 LoadFilters(fAccountSettings.InboundSettings());
376 }
377
378
~BInboundMailProtocol()379 BInboundMailProtocol::~BInboundMailProtocol()
380 {
381 }
382
383
384 void
MessageReceived(BMessage * message)385 BInboundMailProtocol::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
425 status_t
FetchBody(const entry_ref & ref,BMessenger * replyTo)426 BInboundMailProtocol::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
437 status_t
MarkMessageAsRead(const entry_ref & ref,read_flags flag)438 BInboundMailProtocol::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
ReplyBodyFetched(const BMessenger & replyTo,const entry_ref & ref,status_t status)446 BInboundMailProtocol::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
456 void
NotiyMailboxSynchronized(status_t status)457 BInboundMailProtocol::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
BOutboundMailProtocol(const char * name,const BMailAccountSettings & settings)467 BOutboundMailProtocol::BOutboundMailProtocol(const char* name,
468 const BMailAccountSettings& settings)
469 :
470 BMailProtocol(name, settings)
471 {
472 LoadFilters(fAccountSettings.OutboundSettings());
473 }
474
475
~BOutboundMailProtocol()476 BOutboundMailProtocol::~BOutboundMailProtocol()
477 {
478 }
479
480
481 status_t
SendMessages(const BMessage & files,off_t totalBytes)482 BOutboundMailProtocol::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
492 void
MessageReceived(BMessage * message)493 BOutboundMailProtocol::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