vfs: check userland buffers before reading them.
[haiku.git] / src / apps / mail / Enclosures.cpp
blob25f5a492ece6f8031def9f11ba08299845dc72d3
1 /*
2 Open Tracker License
4 Terms and Conditions
6 Copyright (c) 1991-2001, Be Incorporated. All rights reserved.
8 Permission is hereby granted, free of charge, to any person obtaining a copy of
9 this software and associated documentation files (the "Software"), to deal in
10 the Software without restriction, including without limitation the rights to
11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 of the Software, and to permit persons to whom the Software is furnished to do
13 so, subject to the following conditions:
15 The above copyright notice and this permission notice applies to all licensees
16 and shall be included in all copies or substantial portions of the Software.
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN
23 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 Except as contained in this notice, the name of Be Incorporated shall not be
26 used in advertising or otherwise to promote the sale, use or other dealings in
27 this Software without prior written authorization from Be Incorporated.
29 BeMail(TM), Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or
30 registered trademarks of Be Incorporated in the United States and other
31 countries. Other brand product names are registered trademarks or trademarks
32 of their respective holders. All rights reserved.
35 //--------------------------------------------------------------------
37 // Enclosures.cpp
38 // The enclosures list view (TListView), the list items (TListItem),
39 // and the view containing the list
40 // and handling the messages (TEnclosuresView).
41 //--------------------------------------------------------------------
43 #include "Enclosures.h"
45 #include <Alert.h>
46 #include <Beep.h>
47 #include <Bitmap.h>
48 #include <Debug.h>
49 #include <Locale.h>
50 #include <MenuItem.h>
51 #include <NodeMonitor.h>
52 #include <PopUpMenu.h>
54 #include <MailAttachment.h>
55 #include <MailMessage.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
61 #include "MailApp.h"
62 #include "MailSupport.h"
63 #include "MailWindow.h"
64 #include "Messages.h"
67 #define B_TRANSLATION_CONTEXT "Mail"
70 static const float kPlainFontSizeScale = 0.9;
72 static status_t
73 GetTrackerIcon(BMimeType &type, BBitmap *icon, icon_size iconSize)
75 // set some icon size related variables
76 status_t error = B_OK;
77 BRect bounds;
78 switch (iconSize) {
79 case B_MINI_ICON:
80 bounds.Set(0, 0, 15, 15);
81 break;
82 case B_LARGE_ICON:
83 bounds.Set(0, 0, 31, 31);
84 break;
85 default:
86 error = B_BAD_VALUE;
87 break;
89 // check parameters and initialization
90 if (error == B_OK
91 && (!icon || icon->InitCheck() != B_OK || icon->Bounds() != bounds))
92 return B_BAD_VALUE;
94 bool success = false;
96 // Ask the MIME database for the preferred application for the file type
97 // and whether this application has a special icon for the type.
98 char signature[B_MIME_TYPE_LENGTH];
99 if (type.GetPreferredApp(signature) == B_OK) {
100 BMimeType type(signature);
101 success = (type.GetIconForType(type.Type(), icon, iconSize) == B_OK);
104 // Ask the MIME database whether there is an icon for the node's file type.
105 if (error == B_OK && !success)
106 success = (type.GetIcon(icon, iconSize) == B_OK);
108 // Ask the MIME database for the super type and start all over
109 if (error == B_OK && !success) {
110 BMimeType super;
111 if (type.GetSupertype(&super) == B_OK)
112 return GetTrackerIcon(super, icon, iconSize);
115 // Return the icon for "application/octet-stream" from the MIME database.
116 if (error == B_OK && !success) {
117 // get the "application/octet-stream" icon
118 BMimeType type("application/octet-stream");
119 error = type.GetIcon(icon, iconSize);
122 return error;
126 static void
127 recursive_attachment_search(TEnclosuresView* us, BMailContainer* mail,
128 BMailComponent *body)
130 if (mail == NULL)
131 return;
133 for (int32 i = 0; i < mail->CountComponents(); i++) {
134 BMailComponent *component = mail->GetComponent(i);
135 if (component == body)
136 continue;
138 if (component->ComponentType() == B_MAIL_MULTIPART_CONTAINER) {
139 recursive_attachment_search(us,
140 dynamic_cast<BMIMEMultipartMailContainer *>(component), body);
143 us->fList->AddItem(new TListItem(component));
148 // #pragma mark -
151 TEnclosuresView::TEnclosuresView(BRect rect, BRect windowRect)
153 BView(rect, "m_enclosures", B_FOLLOW_TOP | B_FOLLOW_LEFT_RIGHT,
154 B_WILL_DRAW),
155 fFocus(false)
157 SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
159 BFont font(be_plain_font);
160 font.SetSize(font.Size() * kPlainFontSizeScale);
161 SetFont(&font);
163 fOffset = 12;
165 BRect r;
166 r.left = ENCLOSE_TEXT_H + font.StringWidth(
167 B_TRANSLATE("Attachments: ")) + 5;
168 r.top = ENCLOSE_FIELD_V;
169 r.right = windowRect.right - windowRect.left - B_V_SCROLL_BAR_WIDTH - 9;
170 r.bottom = Frame().Height() - 8;
171 fList = new TListView(r, this);
172 fList->SetInvocationMessage(new BMessage(LIST_INVOKED));
174 BScrollView *scroll = new BScrollView("", fList, B_FOLLOW_LEFT_RIGHT |
175 B_FOLLOW_TOP, 0, false, true);
176 AddChild(scroll);
177 scroll->ScrollBar(B_VERTICAL)->SetRange(0, 0);
181 TEnclosuresView::~TEnclosuresView()
183 for (int32 index = fList->CountItems();index-- > 0;)
185 TListItem *item = static_cast<TListItem *>(fList->ItemAt(index));
186 fList->RemoveItem(index);
188 if (item->Component() == NULL)
189 watch_node(item->NodeRef(), B_STOP_WATCHING, this);
190 delete item;
195 void
196 TEnclosuresView::Draw(BRect where)
198 BView::Draw(where);
200 SetHighColor(0, 0, 0);
201 SetLowColor(ViewColor());
203 font_height fh;
204 GetFontHeight(&fh);
206 MovePenTo(ENCLOSE_TEXT_H, ENCLOSE_TEXT_V + fh.ascent);
207 DrawString(ENCLOSE_TEXT);
211 void
212 TEnclosuresView::MessageReceived(BMessage *msg)
214 switch (msg->what)
216 case LIST_INVOKED:
218 BListView *list;
219 msg->FindPointer("source", (void **)&list);
220 if (list)
222 TListItem *item = (TListItem *) (list->ItemAt(msg->FindInt32("index")));
223 if (item)
225 BMessenger tracker("application/x-vnd.Be-TRAK");
226 if (tracker.IsValid())
228 BMessage message(B_REFS_RECEIVED);
229 message.AddRef("refs", item->Ref());
231 tracker.SendMessage(&message);
235 break;
238 case M_REMOVE:
240 int32 index;
241 while ((index = fList->CurrentSelection()) >= 0)
243 TListItem *item = (TListItem *) fList->ItemAt(index);
244 fList->RemoveItem(index);
246 if (item->Component())
248 TMailWindow *window = dynamic_cast<TMailWindow *>(Window());
249 if (window && window->Mail())
250 window->Mail()->RemoveComponent(item->Component());
252 BAlert* alert = new BAlert("", B_TRANSLATE(
253 "Removing attachments from a forwarded mail is not yet "
254 "implemented!\nIt will not yet work correctly."),
255 B_TRANSLATE("OK"));
256 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
257 alert->Go();
259 else
260 watch_node(item->NodeRef(), B_STOP_WATCHING, this);
261 delete item;
263 break;
266 case M_SELECT:
267 fList->Select(0, fList->CountItems() - 1, true);
268 break;
270 case B_SIMPLE_DATA:
271 case B_REFS_RECEIVED:
272 case REFS_RECEIVED:
273 if (msg->HasRef("refs"))
275 bool badType = false;
277 int32 index = 0;
278 entry_ref ref;
279 while (msg->FindRef("refs", index++, &ref) == B_NO_ERROR)
281 BFile file(&ref, O_RDONLY);
282 if (file.InitCheck() == B_OK && file.IsFile())
284 TListItem *item;
285 for (int16 loop = 0; loop < fList->CountItems(); loop++)
287 item = (TListItem *) fList->ItemAt(loop);
288 if (ref == *(item->Ref()))
290 fList->Select(loop);
291 fList->ScrollToSelection();
292 continue;
295 fList->AddItem(item = new TListItem(&ref));
296 fList->Select(fList->CountItems() - 1);
297 fList->ScrollToSelection();
299 watch_node(item->NodeRef(), B_WATCH_NAME, this);
301 else
302 badType = true;
304 if (badType)
306 beep();
307 BAlert* alert = new BAlert("",
308 B_TRANSLATE("Only files can be added as attachments."),
309 B_TRANSLATE("OK"));
310 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
311 alert->Go();
314 break;
316 case B_NODE_MONITOR:
318 int32 opcode;
319 if (msg->FindInt32("opcode", &opcode) == B_NO_ERROR)
321 dev_t device;
322 if (msg->FindInt32("device", &device) < B_OK)
323 break;
324 ino_t inode;
325 if (msg->FindInt64("node", &inode) < B_OK)
326 break;
328 for (int32 index = fList->CountItems();index-- > 0;)
330 TListItem *item = static_cast<TListItem *>(fList->ItemAt(index));
332 if (device == item->NodeRef()->device
333 && inode == item->NodeRef()->node)
335 if (opcode == B_ENTRY_REMOVED)
337 // don't hide the <missing attachment> item
339 //fList->RemoveItem(index);
341 //watch_node(item->NodeRef(), B_STOP_WATCHING, this);
342 //delete item;
344 else if (opcode == B_ENTRY_MOVED)
346 item->Ref()->device = device;
347 msg->FindInt64("to directory", &item->Ref()->directory);
349 const char *name;
350 msg->FindString("name", &name);
351 item->Ref()->set_name(name);
354 fList->InvalidateItem(index);
355 break;
359 break;
362 default:
363 BView::MessageReceived(msg);
368 void
369 TEnclosuresView::Focus(bool focus)
371 if (fFocus != focus)
373 fFocus = focus;
374 Draw(Frame());
379 void
380 TEnclosuresView::AddEnclosuresFromMail(BEmailMessage *mail)
382 for (int32 i = 0; i < mail->CountComponents(); i++)
384 BMailComponent *component = mail->GetComponent(i);
385 if (component == mail->Body())
386 continue;
388 if (component->ComponentType() == B_MAIL_MULTIPART_CONTAINER)
389 recursive_attachment_search(this,dynamic_cast<BMIMEMultipartMailContainer *>(component),mail->Body());
391 fList->AddItem(new TListItem(component));
396 // #pragma mark -
399 TListView::TListView(BRect rect, TEnclosuresView *view)
401 BListView(rect, "", B_MULTIPLE_SELECTION_LIST,
402 B_FOLLOW_TOP | B_FOLLOW_LEFT_RIGHT),
403 fParent(view)
408 void
409 TListView::AttachedToWindow()
411 BListView::AttachedToWindow();
413 BFont font(be_plain_font);
414 font.SetSize(font.Size() * kPlainFontSizeScale);
415 SetFont(&font);
419 void
420 TListView::MakeFocus(bool focus)
422 BListView::MakeFocus(focus);
423 fParent->Focus(focus);
427 void
428 TListView::MouseDown(BPoint point)
430 int32 buttons;
431 Looper()->CurrentMessage()->FindInt32("buttons", &buttons);
433 BListView::MouseDown(point);
435 if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0 && IndexOf(point) >= 0) {
436 BPopUpMenu menu("enclosure", false, false);
437 menu.SetFont(be_plain_font);
438 menu.AddItem(new BMenuItem(B_TRANSLATE("Open attachment"),
439 new BMessage(LIST_INVOKED)));
440 menu.AddItem(new BMenuItem(B_TRANSLATE("Remove attachment"),
441 new BMessage(M_REMOVE)));
443 BPoint menuStart = ConvertToScreen(point);
445 BMenuItem* item = menu.Go(menuStart);
446 if (item != NULL) {
447 if (item->Command() == LIST_INVOKED) {
448 BMessage msg(LIST_INVOKED);
449 msg.AddPointer("source",this);
450 msg.AddInt32("index",IndexOf(point));
451 Window()->PostMessage(&msg,fParent);
452 } else {
453 Select(IndexOf(point));
454 Window()->PostMessage(item->Command(),fParent);
461 void
462 TListView::KeyDown(const char *bytes, int32 numBytes)
464 BListView::KeyDown(bytes,numBytes);
466 if (numBytes == 1 && *bytes == B_DELETE)
467 Window()->PostMessage(M_REMOVE, fParent);
471 // #pragma mark -
474 TListItem::TListItem(entry_ref *ref)
476 fComponent = NULL;
477 fRef = *ref;
479 BEntry entry(ref);
480 entry.GetNodeRef(&fNodeRef);
484 TListItem::TListItem(BMailComponent *component)
486 fComponent(component)
491 void
492 TListItem::Update(BView *owner, const BFont *font)
494 BListItem::Update(owner, font);
496 if (Height() < 17) // mini icon height + 1
497 SetHeight(17);
501 void
502 TListItem::DrawItem(BView *owner, BRect r, bool /* complete */)
504 if (IsSelected()) {
505 owner->SetHighColor(180, 180, 180);
506 owner->SetLowColor(180, 180, 180);
507 } else {
508 owner->SetHighColor(255, 255, 255);
509 owner->SetLowColor(255, 255, 255);
511 owner->FillRect(r);
512 owner->SetHighColor(0, 0, 0);
514 BFont font = *be_plain_font;
515 font.SetSize(font.Size() * kPlainFontSizeScale);
516 owner->SetFont(&font);
517 owner->MovePenTo(r.left + 24, r.bottom - 4);
519 if (fComponent) {
520 // if it's already a mail component, we don't have an icon to
521 // draw, and the entry_ref is invalid
522 BMailAttachment *attachment = dynamic_cast<BMailAttachment *>(fComponent);
524 char name[B_FILE_NAME_LENGTH * 2];
525 if ((attachment == NULL) || (attachment->FileName(name) < B_OK))
526 strcpy(name, "unnamed");
528 BMimeType type;
529 if (fComponent->MIMEType(&type) == B_OK)
530 sprintf(name + strlen(name), ", Type: %s", type.Type());
532 owner->DrawString(name);
534 BRect iconRect(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1);
536 BBitmap bitmap(iconRect, B_RGBA32);
537 if (GetTrackerIcon(type, &bitmap, B_MINI_ICON) == B_NO_ERROR) {
538 BRect rect(r.left + 4, r.top + 1, r.left + 4 + 15, r.top + 1 + 15);
539 owner->SetDrawingMode(B_OP_ALPHA);
540 owner->DrawBitmap(&bitmap, iconRect, rect);
541 owner->SetDrawingMode(B_OP_COPY);
542 } else {
543 // ToDo: find some nicer image for this :-)
544 owner->SetHighColor(150, 150, 150);
545 owner->FillEllipse(BRect(r.left + 8, r.top + 4, r.left + 16, r.top + 13));
547 return;
550 BFile file(&fRef, O_RDONLY);
551 BEntry entry(&fRef);
552 BPath path;
553 if (entry.GetPath(&path) == B_OK && file.InitCheck() == B_OK) {
554 owner->DrawString(path.Path());
556 BNodeInfo info(&file);
557 BRect sr(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1);
559 BBitmap bitmap(sr, B_RGBA32);
560 if (info.GetTrackerIcon(&bitmap, B_MINI_ICON) == B_NO_ERROR) {
561 BRect dr(r.left + 4, r.top + 1, r.left + 4 + 15, r.top + 1 + 15);
562 owner->SetDrawingMode(B_OP_ALPHA);
563 owner->DrawBitmap(&bitmap, sr, dr);
564 owner->SetDrawingMode(B_OP_COPY);
566 } else
567 owner->DrawString("<missing attachment>");