vfs: check userland buffers before reading them.
[haiku.git] / src / servers / mail / DeskbarView.cpp
blobd4d9f65b3221d1ff77a2ad76b4388cc57d22e3d5
1 /*
2 * Copyright 2004-2012, Haiku Inc. All Rights Reserved.
3 * Copyright 2001 Dr. Zoidberg Enterprises. All rights reserved.
5 * Distributed under the terms of the MIT License.
6 */
9 //! mail_daemon's deskbar menu and view
12 #include "DeskbarView.h"
14 #include <stdio.h>
15 #include <malloc.h>
17 #include <Bitmap.h>
18 #include <Catalog.h>
19 #include <Deskbar.h>
20 #include <Directory.h>
21 #include <Entry.h>
22 #include <FindDirectory.h>
23 #include <IconUtils.h>
24 #include <kernel/fs_info.h>
25 #include <kernel/fs_index.h>
26 #include <MenuItem.h>
27 #include <MessageFormat.h>
28 #include <Messenger.h>
29 #include <NodeInfo.h>
30 #include <NodeMonitor.h>
31 #include <OpenWithTracker.h>
32 #include <Path.h>
33 #include <PopUpMenu.h>
34 #include <Query.h>
35 #include <Rect.h>
36 #include <Resources.h>
37 #include <Roster.h>
38 #include <String.h>
39 #include <SymLink.h>
40 #include <VolumeRoster.h>
41 #include <Window.h>
43 #include <E-mail.h>
44 #include <MailDaemon.h>
45 #include <MailSettings.h>
47 #include <MailPrivate.h>
49 #include "DeskbarViewIcons.h"
52 #undef B_TRANSLATION_CONTEXT
53 #define B_TRANSLATION_CONTEXT "DeskbarView"
56 const char* kTrackerSignature = "application/x-vnd.Be-TRAK";
59 //-----The following #defines get around a bug in get_image_info on ppc---
60 #if __INTEL__
61 #define text_part text
62 #define text_part_size text_size
63 #else
64 #define text_part data
65 #define text_part_size data_size
66 #endif
68 extern "C" _EXPORT BView* instantiate_deskbar_item();
71 status_t our_image(image_info* image)
73 int32 cookie = 0;
74 status_t ret;
75 while ((ret = get_next_image_info(0,&cookie,image)) == B_OK)
77 if ((char*)our_image >= (char*)image->text_part &&
78 (char*)our_image <= (char*)image->text_part + image->text_part_size)
79 break;
82 return ret;
85 BView* instantiate_deskbar_item(void)
87 return new DeskbarView(BRect(0, 0, 15, 15));
91 //-------------------------------------------------------------------------------
92 // #pragma mark -
95 DeskbarView::DeskbarView(BRect frame)
97 BView(frame, "mail_daemon", B_FOLLOW_NONE, B_WILL_DRAW | B_PULSE_NEEDED),
98 fStatus(kStatusNoMail),
99 fLastButtons(0)
101 _InitBitmaps();
105 DeskbarView::DeskbarView(BMessage *message)
107 BView(message),
108 fStatus(kStatusNoMail),
109 fLastButtons(0)
111 _InitBitmaps();
115 DeskbarView::~DeskbarView()
117 for (int i = 0; i < kStatusCount; i++)
118 delete fBitmaps[i];
120 for (int32 i = 0; i < fNewMailQueries.CountItems(); i++)
121 delete ((BQuery *)(fNewMailQueries.ItemAt(i)));
125 void DeskbarView::AttachedToWindow()
127 BView::AttachedToWindow();
128 AdoptParentColors();
130 if (ViewUIColor() == B_NO_COLOR)
131 SetLowColor(ViewColor());
132 else
133 SetLowUIColor(ViewUIColor());
135 if (be_roster->IsRunning(B_MAIL_DAEMON_SIGNATURE)) {
136 _RefreshMailQuery();
137 } else {
138 BDeskbar deskbar;
139 deskbar.RemoveItem("mail_daemon");
144 bool DeskbarView::_EntryInTrash(const entry_ref* ref)
146 BEntry entry(ref);
147 BVolume volume(ref->device);
148 BPath path;
149 if (volume.InitCheck() != B_OK
150 || find_directory(B_TRASH_DIRECTORY, &path, false, &volume) != B_OK)
151 return false;
153 BDirectory trash(path.Path());
154 return trash.Contains(&entry);
158 void DeskbarView::_RefreshMailQuery()
160 for (int32 i = 0; i < fNewMailQueries.CountItems(); i++)
161 delete ((BQuery *)(fNewMailQueries.ItemAt(i)));
162 fNewMailQueries.MakeEmpty();
164 BVolumeRoster volumes;
165 BVolume volume;
166 fNewMessages = 0;
168 while (volumes.GetNextVolume(&volume) == B_OK) {
169 BQuery *newMailQuery = new BQuery;
170 newMailQuery->SetTarget(this);
171 newMailQuery->SetVolume(&volume);
172 newMailQuery->PushAttr(B_MAIL_ATTR_READ);
173 newMailQuery->PushInt32(B_UNREAD);
174 newMailQuery->PushOp(B_EQ);
175 newMailQuery->PushAttr("BEOS:TYPE");
176 newMailQuery->PushString("text/x-email");
177 newMailQuery->PushOp(B_EQ);
178 newMailQuery->PushAttr("BEOS:TYPE");
179 newMailQuery->PushString("text/x-partial-email");
180 newMailQuery->PushOp(B_EQ);
181 newMailQuery->PushOp(B_OR);
182 newMailQuery->PushOp(B_AND);
183 newMailQuery->Fetch();
185 BEntry entry;
186 while (newMailQuery->GetNextEntry(&entry) == B_OK) {
187 if (entry.InitCheck() == B_OK) {
188 entry_ref ref;
189 entry.GetRef(&ref);
190 if (!_EntryInTrash(&ref))
191 fNewMessages++;
195 fNewMailQueries.AddItem(newMailQuery);
198 fStatus = (fNewMessages > 0) ? kStatusNewMail : kStatusNoMail;
199 Invalidate();
203 DeskbarView* DeskbarView::Instantiate(BMessage *data)
205 if (!validate_instantiation(data, "DeskbarView"))
206 return NULL;
208 return new DeskbarView(data);
212 status_t DeskbarView::Archive(BMessage *data,bool deep) const
214 BView::Archive(data, deep);
216 data->AddString("add_on", B_MAIL_DAEMON_SIGNATURE);
217 return B_NO_ERROR;
221 void
222 DeskbarView::Draw(BRect /*updateRect*/)
224 if (fBitmaps[fStatus] == NULL)
225 return;
227 SetDrawingMode(B_OP_ALPHA);
228 DrawBitmap(fBitmaps[fStatus]);
229 SetDrawingMode(B_OP_COPY);
233 void
234 DeskbarView::MessageReceived(BMessage* message)
236 switch (message->what) {
237 case MD_CHECK_SEND_NOW:
238 // also happens in DeskbarView::MouseUp() with
239 // B_TERTIARY_MOUSE_BUTTON pressed
240 BMailDaemon().CheckAndSendQueuedMail();
241 break;
242 case MD_CHECK_FOR_MAILS:
243 BMailDaemon().CheckMail(message->FindInt32("account"));
244 break;
245 case MD_SEND_MAILS:
246 BMailDaemon().SendQueuedMail();
247 break;
249 case MD_OPEN_NEW:
251 char* argv[] = {(char *)"New Message", (char *)"mailto:"};
252 be_roster->Launch("text/x-email", 2, argv);
253 break;
255 case MD_OPEN_PREFS:
256 be_roster->Launch("application/x-vnd.Haiku-Mail");
257 break;
259 case MD_REFRESH_QUERY:
260 _RefreshMailQuery();
261 break;
263 case B_QUERY_UPDATE:
265 int32 what;
266 dev_t device;
267 ino_t directory;
268 const char *name;
269 entry_ref ref;
270 message->FindInt32("opcode", &what);
271 message->FindInt32("device", &device);
272 message->FindInt64("directory", &directory);
273 switch (what) {
274 case B_ENTRY_CREATED:
275 if (message->FindString("name", &name) == B_OK) {
276 ref.device = device;
277 ref.directory = directory;
278 ref.set_name(name);
279 if (!_EntryInTrash(&ref))
280 fNewMessages++;
282 break;
283 case B_ENTRY_REMOVED:
284 node_ref node;
285 node.device = device;
286 node.node = directory;
287 BDirectory dir(&node);
288 BEntry entry(&dir, NULL);
289 entry.GetRef(&ref);
290 if (!_EntryInTrash(&ref))
291 fNewMessages--;
292 break;
294 fStatus = fNewMessages > 0 ? kStatusNewMail : kStatusNoMail;
295 Invalidate();
296 break;
298 case B_QUIT_REQUESTED:
299 BMailDaemon().Quit();
300 break;
302 // open received files in the standard mail application
303 case B_REFS_RECEIVED:
305 BMessage argv(B_ARGV_RECEIVED);
306 argv.AddString("argv", "E-mail");
308 entry_ref ref;
309 BPath path;
310 int i = 0;
312 while (message->FindRef("refs", i++, &ref) == B_OK
313 && path.SetTo(&ref) == B_OK) {
314 //fprintf(stderr,"got %s\n", path.Path());
315 argv.AddString("argv", path.Path());
318 if (i > 1) {
319 argv.AddInt32("argc", i);
320 be_roster->Launch("text/x-email", &argv);
322 break;
324 default:
325 BView::MessageReceived(message);
330 void
331 DeskbarView::_InitBitmaps()
333 for (int i = 0; i < kStatusCount; i++)
334 fBitmaps[i] = NULL;
336 image_info info;
337 if (our_image(&info) != B_OK)
338 return;
340 BFile file(info.name, B_READ_ONLY);
341 if (file.InitCheck() != B_OK)
342 return;
344 BResources resources(&file);
345 if (resources.InitCheck() != B_OK)
346 return;
348 for (int i = 0; i < kStatusCount; i++) {
349 const void* data = NULL;
350 size_t size;
351 data = resources.LoadResource(B_VECTOR_ICON_TYPE,
352 kIconNoMail + i, &size);
353 if (data != NULL) {
354 BBitmap* icon = new BBitmap(Bounds(), B_RGBA32);
355 if (icon->InitCheck() == B_OK
356 && BIconUtils::GetVectorIcon((const uint8 *)data,
357 size, icon) == B_OK) {
358 fBitmaps[i] = icon;
359 } else
360 delete icon;
366 void
367 DeskbarView::Pulse()
369 // TODO: Check if mail_daemon is still running
373 void
374 DeskbarView::MouseUp(BPoint pos)
376 if ((fLastButtons & B_PRIMARY_MOUSE_BUTTON) !=0
377 && OpenWithTracker(B_USER_SETTINGS_DIRECTORY, "Mail/mailbox") != B_OK) {
378 entry_ref ref;
379 _GetNewQueryRef(ref);
381 BMessenger trackerMessenger(kTrackerSignature);
382 BMessage message(B_REFS_RECEIVED);
383 message.AddRef("refs", &ref);
385 trackerMessenger.SendMessage(&message);
388 if ((fLastButtons & B_TERTIARY_MOUSE_BUTTON) != 0)
389 BMailDaemon().CheckMail();
393 void
394 DeskbarView::MouseDown(BPoint pos)
396 Looper()->CurrentMessage()->FindInt32("buttons", &fLastButtons);
398 if ((fLastButtons & B_SECONDARY_MOUSE_BUTTON) != 0) {
399 ConvertToScreen(&pos);
401 BPopUpMenu* menu = _BuildMenu();
402 menu->Go(pos, true, true, BRect(pos.x - 2, pos.y - 2,
403 pos.x + 2, pos.y + 2), true);
408 bool
409 DeskbarView::_CreateMenuLinks(BDirectory& directory, BPath& path)
411 status_t status = directory.SetTo(path.Path());
412 if (status == B_OK)
413 return true;
415 // Check if the directory has to be created (and do it in this case,
416 // filling it with some standard links). Normally the installer will
417 // create the directory and fill it with links, so normally this doesn't
418 // get used.
420 BEntry entry(path.Path());
421 if (status != B_ENTRY_NOT_FOUND
422 || entry.GetParent(&directory) < B_OK
423 || directory.CreateDirectory(path.Leaf(), NULL) < B_OK
424 || directory.SetTo(path.Path()) < B_OK)
425 return false;
427 BPath targetPath;
428 find_directory(B_USER_DIRECTORY, &targetPath);
429 targetPath.Append("mail/in");
431 directory.CreateSymLink("Open Inbox Folder", targetPath.Path(), NULL);
432 targetPath.GetParent(&targetPath);
433 directory.CreateSymLink("Open Mail Folder", targetPath.Path(), NULL);
435 // create the draft query
437 BFile file;
438 if (directory.CreateFile("Open Draft", &file) < B_OK)
439 return true;
441 BString string("MAIL:draft==1");
442 file.WriteAttrString("_trk/qrystr", &string);
443 string = "E-mail";
444 file.WriteAttrString("_trk/qryinitmime", &string);
445 BNodeInfo(&file).SetType("application/x-vnd.Be-query");
447 return true;
451 void
452 DeskbarView::_CreateNewMailQuery(BEntry& query)
454 BFile file(&query, B_READ_WRITE | B_CREATE_FILE);
455 if (file.InitCheck() != B_OK)
456 return;
458 BString string("((" B_MAIL_ATTR_READ "<2)&&((BEOS:TYPE=="
459 "\"text/x-email\")||(BEOS:TYPE==\"text/x-partial-email\")))");
460 file.WriteAttrString("_trk/qrystr", &string);
461 file.WriteAttrString("_trk/qryinitstr", &string);
462 int32 mode = 'Fbyq';
463 file.WriteAttr("_trk/qryinitmode", B_INT32_TYPE, 0, &mode, sizeof(int32));
464 string = "E-mail";
465 file.WriteAttrString("_trk/qryinitmime", &string);
466 BNodeInfo(&file).SetType("application/x-vnd.Be-query");
470 BPopUpMenu*
471 DeskbarView::_BuildMenu()
473 BPopUpMenu* menu = new BPopUpMenu(B_EMPTY_STRING, false, false);
474 menu->SetFont(be_plain_font);
476 menu->AddItem(new BMenuItem(B_TRANSLATE("Create new message"
477 B_UTF8_ELLIPSIS), new BMessage(MD_OPEN_NEW)));
478 menu->AddSeparatorItem();
480 BMessenger tracker(kTrackerSignature);
481 BNavMenu* navMenu;
482 BMenuItem* item;
483 BMessage* msg;
484 entry_ref ref;
486 BPath path;
487 find_directory(B_USER_SETTINGS_DIRECTORY, &path);
488 path.Append("Mail/Menu Links");
490 BDirectory directory;
491 if (_CreateMenuLinks(directory, path)) {
492 int32 count = 0;
494 while (directory.GetNextRef(&ref) == B_OK) {
495 count++;
497 path.SetTo(&ref);
498 // the true here dereferences the symlinks all the way :)
499 BEntry entry(&ref, true);
501 // do we want to use the NavMenu, or just an ordinary BMenuItem?
502 // we are using the NavMenu only for directories and queries
503 bool useNavMenu = false;
505 if (entry.InitCheck() == B_OK) {
506 if (entry.IsDirectory())
507 useNavMenu = true;
508 else if (entry.IsFile()) {
509 // Files should use the BMenuItem unless they are queries
510 char mimeString[B_MIME_TYPE_LENGTH];
511 BNode node(&entry);
512 BNodeInfo info(&node);
513 if (info.GetType(mimeString) == B_OK
514 && strcmp(mimeString, "application/x-vnd.Be-query")
515 == 0)
516 useNavMenu = true;
518 // clobber the existing ref only if the symlink derefernces
519 // completely, otherwise we'll stick with what we have
520 entry.GetRef(&ref);
523 msg = new BMessage(B_REFS_RECEIVED);
524 msg->AddRef("refs", &ref);
526 if (useNavMenu) {
527 item = new BMenuItem(navMenu = new BNavMenu(path.Leaf(),
528 B_REFS_RECEIVED, tracker), msg);
529 navMenu->SetNavDir(&ref);
530 } else
531 item = new BMenuItem(path.Leaf(), msg);
533 menu->AddItem(item);
534 if (entry.InitCheck() != B_OK)
535 item->SetEnabled(false);
537 if (count > 0)
538 menu->AddSeparatorItem();
541 // Hack for R5's buggy Query Notification
542 #ifdef HAIKU_TARGET_PLATFORM_BEOS
543 menu->AddItem(new BMenuItem(B_TRANSLATE("Refresh New Mail Count"),
544 new BMessage(MD_REFRESH_QUERY)));
545 #endif
547 // The New E-mail query
549 if (fNewMessages > 0) {
550 static BMessageFormat format(B_TRANSLATE(
551 "{0, plural, one{# new message} other{# new messages}}"));
552 BString string;
553 format.Format(string, fNewMessages);
555 _GetNewQueryRef(ref);
557 item = new BMenuItem(navMenu = new BNavMenu(string.String(),
558 B_REFS_RECEIVED, BMessenger(kTrackerSignature)),
559 msg = new BMessage(B_REFS_RECEIVED));
560 msg->AddRef("refs", &ref);
561 navMenu->SetNavDir(&ref);
563 menu->AddItem(item);
564 } else {
565 menu->AddItem(item = new BMenuItem(B_TRANSLATE("No new messages"),
566 NULL));
567 item->SetEnabled(false);
570 BMailAccounts accounts;
571 if ((modifiers() & B_SHIFT_KEY) != 0) {
572 BMenu *accountMenu = new BMenu(B_TRANSLATE("Check for mails only"));
573 BFont font;
574 menu->GetFont(&font);
575 accountMenu->SetFont(&font);
577 for (int32 i = 0; i < accounts.CountAccounts(); i++) {
578 BMailAccountSettings* account = accounts.AccountAt(i);
580 BMessage* message = new BMessage(MD_CHECK_FOR_MAILS);
581 message->AddInt32("account", account->AccountID());
583 accountMenu->AddItem(new BMenuItem(account->Name(), message));
585 if (accounts.CountAccounts() == 0) {
586 item = new BMenuItem(B_TRANSLATE("<no accounts>"), NULL);
587 item->SetEnabled(false);
588 accountMenu->AddItem(item);
590 accountMenu->SetTargetForItems(this);
591 menu->AddItem(new BMenuItem(accountMenu,
592 new BMessage(MD_CHECK_FOR_MAILS)));
594 // Not used:
595 // menu->AddItem(new BMenuItem(B_TRANSLATE("Check For Mails Only"),
596 // new BMessage(MD_CHECK_FOR_MAILS)));
597 menu->AddItem(new BMenuItem(B_TRANSLATE("Send pending mails"),
598 new BMessage(MD_SEND_MAILS)));
599 } else {
600 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Check for mail now"),
601 new BMessage(MD_CHECK_SEND_NOW)));
602 if (accounts.CountAccounts() == 0)
603 item->SetEnabled(false);
606 menu->AddSeparatorItem();
607 menu->AddItem(new BMenuItem(B_TRANSLATE("Settings" B_UTF8_ELLIPSIS),
608 new BMessage(MD_OPEN_PREFS)));
610 if (modifiers() & B_SHIFT_KEY) {
611 menu->AddItem(new BMenuItem(B_TRANSLATE("Shutdown mail services"),
612 new BMessage(B_QUIT_REQUESTED)));
615 // Reset Item Targets (only those which aren't already set)
617 for (int32 i = menu->CountItems(); i-- > 0;) {
618 item = menu->ItemAt(i);
619 if (item != NULL && (msg = item->Message()) != NULL) {
620 if (msg->what == B_REFS_RECEIVED)
621 item->SetTarget(tracker);
622 else
623 item->SetTarget(this);
626 return menu;
630 status_t
631 DeskbarView::_GetNewQueryRef(entry_ref& ref)
633 BPath path;
634 find_directory(B_USER_SETTINGS_DIRECTORY, &path);
635 path.Append("Mail/New E-mail");
636 BEntry query(path.Path());
637 if (!query.Exists())
638 _CreateNewMailQuery(query);
639 return query.GetRef(&ref);