vfs: check userland buffers before reading them.
[haiku.git] / src / kits / tracker / Model.cpp
blobc1db78ac753ddaea73a0413dd7cae464212dafc8
1 /*
2 Open Tracker License
4 Terms and Conditions
6 Copyright (c) 1991-2000, 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 CONNECTION
23 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 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30 of Be Incorporated in the United States and other countries. Other brand product
31 names are registered trademarks or trademarks of their respective holders.
32 All rights reserved.
35 // Dedicated to BModel
37 // ToDo:
38 // Consider moving iconFrom logic to BPose
39 // use a more efficient way of storing file type and preferred app strings
42 #include "Model.h"
44 #include <stdlib.h>
45 #include <strings.h>
47 #include <fs_info.h>
48 #include <fs_attr.h>
50 #include <AppDefs.h>
51 #include <Bitmap.h>
52 #include <Catalog.h>
53 #include <Debug.h>
54 #include <Directory.h>
55 #include <Entry.h>
56 #include <File.h>
57 #include <Locale.h>
58 #include <NodeInfo.h>
59 #include <NodeMonitor.h>
60 #include <Path.h>
61 #include <SymLink.h>
62 #include <Query.h>
63 #include <Volume.h>
64 #include <VolumeRoster.h>
66 #include "Attributes.h"
67 #include "Bitmaps.h"
68 #include "FindPanel.h"
69 #include "FSUtils.h"
70 #include "MimeTypes.h"
71 #include "Tracker.h"
72 #include "Utilities.h"
75 #undef B_TRANSLATION_CONTEXT
76 #define B_TRANSLATION_CONTEXT "Model"
79 #ifdef CHECK_OPEN_MODEL_LEAKS
80 BObjectList<Model>* writableOpenModelList = NULL;
81 BObjectList<Model>* readOnlyOpenModelList = NULL;
82 #endif
85 static bool
86 CheckNodeIconHint(BNode* node)
88 if (node == NULL)
89 return false;
91 attr_info info;
92 if (node->GetAttrInfo(kAttrIcon, &info) == B_OK
93 // has a vector icon, or
94 || (node->GetAttrInfo(kAttrMiniIcon, &info) == B_OK
95 && node->GetAttrInfo(kAttrLargeIcon, &info) == B_OK)) {
96 // has a mini _and_ large icon
97 return true;
100 return false;
104 // #pragma mark - Model()
107 Model::Model()
109 fPreferredAppName(NULL),
110 fBaseType(kUnknownNode),
111 fIconFrom(kUnknownSource),
112 fWritable(false),
113 fNode(NULL),
114 fStatus(B_NO_INIT),
115 fHasLocalizedName(false),
116 fLocalizedNameIsCached(false)
121 Model::Model(const Model& other)
123 fEntryRef(other.fEntryRef),
124 fMimeType(other.fMimeType),
125 fPreferredAppName(NULL),
126 fBaseType(other.fBaseType),
127 fIconFrom(other.fIconFrom),
128 fWritable(false),
129 fNode(NULL),
130 fLocalizedName(other.fLocalizedName),
131 fHasLocalizedName(other.fHasLocalizedName),
132 fLocalizedNameIsCached(other.fLocalizedNameIsCached)
134 fStatBuf.st_dev = other.NodeRef()->device;
135 fStatBuf.st_ino = other.NodeRef()->node;
137 if (other.IsSymLink() && other.LinkTo())
138 fLinkTo = new Model(*other.LinkTo());
140 fStatus = OpenNode(other.IsNodeOpenForWriting());
141 if (fStatus == B_OK) {
142 ASSERT(fNode);
143 fNode->GetStat(&fStatBuf);
144 ASSERT(fStatBuf.st_dev == other.NodeRef()->device);
145 ASSERT(fStatBuf.st_ino == other.NodeRef()->node);
147 if (!other.IsNodeOpen())
148 CloseNode();
152 Model::Model(const node_ref* dirNode, const node_ref* node, const char* name,
153 bool open, bool writable)
155 fPreferredAppName(NULL),
156 fWritable(false),
157 fNode(NULL),
158 fHasLocalizedName(false),
159 fLocalizedNameIsCached(false)
161 SetTo(dirNode, node, name, open, writable);
165 Model::Model(const BEntry* entry, bool open, bool writable)
167 fPreferredAppName(NULL),
168 fWritable(false),
169 fNode(NULL),
170 fHasLocalizedName(false),
171 fLocalizedNameIsCached(false)
173 SetTo(entry, open, writable);
177 Model::Model(const entry_ref* ref, bool traverse, bool open, bool writable)
179 fPreferredAppName(NULL),
180 fBaseType(kUnknownNode),
181 fIconFrom(kUnknownSource),
182 fWritable(false),
183 fNode(NULL),
184 fHasLocalizedName(false),
185 fLocalizedNameIsCached(false)
187 BEntry entry(ref, traverse);
188 fStatus = entry.InitCheck();
189 if (fStatus == B_OK)
190 SetTo(&entry, open, writable);
194 void
195 Model::DeletePreferredAppVolumeNameLinkTo()
197 if (IsSymLink()) {
198 Model* tmp = fLinkTo;
199 // deal with link to link to self
200 fLinkTo = NULL;
201 delete tmp;
202 } else if (IsVolume())
203 free(fVolumeName);
204 else
205 free(fPreferredAppName);
207 fPreferredAppName = NULL;
211 Model::~Model()
213 #ifdef CHECK_OPEN_MODEL_LEAKS
214 if (writableOpenModelList != NULL)
215 writableOpenModelList->RemoveItem(this);
217 if (readOnlyOpenModelList != NULL)
218 readOnlyOpenModelList->RemoveItem(this);
219 #endif
221 DeletePreferredAppVolumeNameLinkTo();
222 if (IconCache::NeedsDeletionNotification((IconSource)fIconFrom)) {
223 // this check allows us to use temporary Model in the IconCache
224 // without the danger of a deadlock
225 IconCache::sIconCache->Deleting(this);
227 #if xDEBUG
228 if (fNode != NULL)
229 PRINT(("destructor closing node for %s\n", Name()));
230 #endif
232 delete fNode;
236 status_t
237 Model::SetTo(const BEntry* entry, bool open, bool writable)
239 delete fNode;
240 fNode = NULL;
241 DeletePreferredAppVolumeNameLinkTo();
242 fIconFrom = kUnknownSource;
243 fBaseType = kUnknownNode;
244 fMimeType = "";
246 fStatus = entry->GetRef(&fEntryRef);
247 if (fStatus != B_OK)
248 return fStatus;
250 fStatus = entry->GetStat(&fStatBuf);
251 if (fStatus != B_OK)
252 return fStatus;
254 fStatus = OpenNode(writable);
255 if (!open)
256 CloseNode();
258 return fStatus;
262 status_t
263 Model::SetTo(const entry_ref* newRef, bool traverse, bool open, bool writable)
265 delete fNode;
266 fNode = NULL;
267 DeletePreferredAppVolumeNameLinkTo();
268 fIconFrom = kUnknownSource;
269 fBaseType = kUnknownNode;
270 fMimeType = "";
272 BEntry tmpEntry(newRef, traverse);
273 fStatus = tmpEntry.InitCheck();
274 if (fStatus != B_OK)
275 return fStatus;
277 if (traverse)
278 tmpEntry.GetRef(&fEntryRef);
279 else
280 fEntryRef = *newRef;
282 fStatus = tmpEntry.GetStat(&fStatBuf);
283 if (fStatus != B_OK)
284 return fStatus;
286 fStatus = OpenNode(writable);
287 if (!open)
288 CloseNode();
290 return fStatus;
294 status_t
295 Model::SetTo(const node_ref* dirNode, const node_ref* nodeRef,
296 const char* name, bool open, bool writable)
298 delete fNode;
299 fNode = NULL;
300 DeletePreferredAppVolumeNameLinkTo();
301 fIconFrom = kUnknownSource;
302 fBaseType = kUnknownNode;
303 fMimeType = "";
305 fStatBuf.st_dev = nodeRef->device;
306 fStatBuf.st_ino = nodeRef->node;
307 fEntryRef.device = dirNode->device;
308 fEntryRef.directory = dirNode->node;
309 fEntryRef.name = strdup(name);
311 BEntry tmpNode(&fEntryRef);
312 fStatus = tmpNode.InitCheck();
313 if (fStatus != B_OK)
314 return fStatus;
316 fStatus = tmpNode.GetStat(&fStatBuf);
317 if (fStatus != B_OK)
318 return fStatus;
320 fStatus = OpenNode(writable);
322 if (!open)
323 CloseNode();
325 return fStatus;
329 status_t
330 Model::InitCheck() const
332 return fStatus;
337 Model::CompareFolderNamesFirst(const Model* compareModel) const
339 if (compareModel == NULL)
340 return -1;
342 const Model* resolvedCompareModel = compareModel->ResolveIfLink();
343 const Model* resolvedMe = ResolveIfLink();
345 bool meIsDirOrVolume = resolvedMe->IsDirectory() || resolvedMe->IsVolume()
346 || resolvedMe->IsVirtualDirectory();
347 bool otherIsDirOrVolume = resolvedCompareModel->IsDirectory()
348 || resolvedCompareModel->IsVolume()
349 || resolvedCompareModel->IsVirtualDirectory();
351 if (meIsDirOrVolume) {
352 if (!otherIsDirOrVolume)
353 return -1;
354 } else if (otherIsDirOrVolume)
355 return 1;
357 return NaturalCompare(Name(), compareModel->Name());
361 const char*
362 Model::Name() const
364 static const char* kRootNodeName = B_TRANSLATE_MARK(B_DISKS_DIR_NAME);
365 static const char* kTrashNodeName = B_TRANSLATE_MARK(B_TRASH_DIR_NAME);
366 static const char* kDesktopNodeName = B_TRANSLATE_MARK(B_DESKTOP_DIR_NAME);
368 switch (fBaseType) {
369 case kRootNode:
370 return B_TRANSLATE_NOCOLLECT(kRootNodeName);
372 case kVolumeNode:
373 if (fVolumeName != NULL)
374 return fVolumeName;
375 break;
377 case kTrashNode:
378 return B_TRANSLATE_NOCOLLECT(kTrashNodeName);
380 case kDesktopNode:
381 return B_TRANSLATE_NOCOLLECT(kDesktopNodeName);
383 default:
384 break;
387 if (fHasLocalizedName && gLocalizedNamePreferred)
388 return fLocalizedName.String();
389 else
390 return fEntryRef.name;
394 status_t
395 Model::OpenNode(bool writable)
397 if (IsNodeOpen() && (writable == IsNodeOpenForWriting()))
398 return B_OK;
400 OpenNodeCommon(writable);
402 return fStatus;
406 status_t
407 Model::UpdateStatAndOpenNode(bool writable)
409 if (IsNodeOpen() && (writable == IsNodeOpenForWriting()))
410 return B_OK;
412 // try reading the stat structure again
413 BEntry tmpEntry(&fEntryRef);
414 fStatus = tmpEntry.InitCheck();
415 if (fStatus != B_OK)
416 return fStatus;
418 fStatus = tmpEntry.GetStat(&fStatBuf);
419 if (fStatus != B_OK)
420 return fStatus;
422 OpenNodeCommon(writable);
424 return fStatus;
428 status_t
429 Model::OpenNodeCommon(bool writable)
431 #if xDEBUG
432 PRINT(("opening node for %s\n", Name()));
433 #endif
435 #ifdef CHECK_OPEN_MODEL_LEAKS
436 if (writableOpenModelList != NULL)
437 writableOpenModelList->RemoveItem(this);
439 if (readOnlyOpenModelList != NULL)
440 readOnlyOpenModelList->RemoveItem(this);
441 #endif
443 if (fBaseType == kUnknownNode)
444 SetupBaseType();
446 switch (fBaseType) {
447 case kPlainNode:
448 case kExecutableNode:
449 case kQueryNode:
450 case kQueryTemplateNode:
451 case kVirtualDirectoryNode:
452 // open or reopen
453 delete fNode;
454 fNode = new BFile(&fEntryRef,
455 (uint32)(writable ? O_RDWR : O_RDONLY));
456 break;
458 case kDirectoryNode:
459 case kVolumeNode:
460 case kRootNode:
461 case kTrashNode:
462 case kDesktopNode:
463 if (!IsNodeOpen())
464 fNode = new BDirectory(&fEntryRef);
466 if (fBaseType == kDirectoryNode
467 && static_cast<BDirectory*>(fNode)->IsRootDirectory()) {
468 // promote from directory to volume
469 fBaseType = kVolumeNode;
471 break;
473 case kLinkNode:
474 if (!IsNodeOpen()) {
475 BEntry entry(&fEntryRef);
476 fNode = new BSymLink(&entry);
478 break;
480 default:
481 #if DEBUG
482 PrintToStream();
483 #endif
484 TRESPASS();
485 // this can only happen if GetStat failed before,
486 // in which case we shouldn't be here
488 // ToDo: Obviously, we can also be here if the type could not
489 // be determined, for example for block devices (so the TRESPASS()
490 // macro shouldn't be used here)!
491 return fStatus = B_ERROR;
494 fStatus = fNode->InitCheck();
495 if (fStatus != B_OK) {
496 delete fNode;
497 fNode = NULL;
498 // original code snoozed an error here and returned B_OK
499 return fStatus;
502 fWritable = writable;
504 if (fMimeType.Length() <= 0)
505 FinishSettingUpType();
507 #ifdef CHECK_OPEN_MODEL_LEAKS
508 if (fWritable) {
509 if (!writableOpenModelList) {
510 TRACE();
511 writableOpenModelList = new BObjectList<Model>(100);
513 writableOpenModelList->AddItem(this);
514 } else {
515 if (!readOnlyOpenModelList) {
516 TRACE();
517 readOnlyOpenModelList = new BObjectList<Model>(100);
519 readOnlyOpenModelList->AddItem(this);
521 #endif
523 if (gLocalizedNamePreferred)
524 CacheLocalizedName();
526 return fStatus;
530 void
531 Model::CloseNode()
533 #if xDEBUG
534 PRINT(("closing node for %s\n", Name()));
535 #endif
537 #ifdef CHECK_OPEN_MODEL_LEAKS
538 if (writableOpenModelList != NULL)
539 writableOpenModelList->RemoveItem(this);
541 if (readOnlyOpenModelList != NULL)
542 readOnlyOpenModelList->RemoveItem(this);
543 #endif
545 delete fNode;
546 fNode = NULL;
550 bool
551 Model::IsNodeOpen() const
553 return fNode != NULL;
558 bool
559 Model::IsNodeOpenForWriting() const
561 return fNode != NULL && fWritable;
565 void
566 Model::SetupBaseType()
568 switch (fStatBuf.st_mode & S_IFMT) {
569 case S_IFDIR:
570 // folder
571 fBaseType = kDirectoryNode;
572 break;
574 case S_IFREG:
575 // regular file
576 if ((fStatBuf.st_mode & S_IXUSR) != 0) {
577 // executable
578 fBaseType = kExecutableNode;
579 } else {
580 // non-executable
581 fBaseType = kPlainNode;
583 break;
585 case S_IFLNK:
586 // symlink
587 fBaseType = kLinkNode;
588 break;
590 default:
591 fBaseType = kUnknownNode;
592 break;
597 void
598 Model::CacheLocalizedName()
600 if (!fLocalizedNameIsCached) {
601 fLocalizedNameIsCached = true;
602 if (BLocaleRoster::Default()->GetLocalizedFileName(
603 fLocalizedName, fEntryRef, true) == B_OK)
604 fHasLocalizedName = true;
605 else
606 fHasLocalizedName = false;
611 void
612 Model::FinishSettingUpType()
614 char mimeString[B_MIME_TYPE_LENGTH];
615 BEntry entry;
617 // While we are reading the node, do a little snooping to see if it even
618 // makes sense to look for a node-based icon. This serves as a hint to the
619 // icon cache, allowing it to not hit the disk again for models that do not
620 // have an icon defined by the node.
621 if (IsNodeOpen() && fBaseType != kLinkNode && !CheckNodeIconHint(fNode))
622 fIconFrom = kUnknownNotFromNode;
624 if (fBaseType != kDirectoryNode
625 && fBaseType != kVolumeNode
626 && fBaseType != kLinkNode
627 && IsNodeOpen()) {
628 BNodeInfo info(fNode);
630 // check if a specific mime type is set
631 if (info.GetType(mimeString) == B_OK) {
632 // node has a specific mime type
633 fMimeType = mimeString;
634 if (strcmp(mimeString, B_QUERY_MIMETYPE) == 0)
635 fBaseType = kQueryNode;
636 else if (strcmp(mimeString, B_QUERY_TEMPLATE_MIMETYPE) == 0)
637 fBaseType = kQueryTemplateNode;
638 else if (strcmp(mimeString, kVirtualDirectoryMimeType) == 0)
639 fBaseType = kVirtualDirectoryNode;
641 if (info.GetPreferredApp(mimeString) == B_OK) {
642 if (fPreferredAppName)
643 DeletePreferredAppVolumeNameLinkTo();
645 if (mimeString[0])
646 fPreferredAppName = strdup(mimeString);
651 switch (fBaseType) {
652 case kDirectoryNode:
653 entry.SetTo(&fEntryRef);
654 if (entry.InitCheck() == B_OK) {
655 if (FSIsTrashDir(&entry))
656 fBaseType = kTrashNode;
657 else if (FSIsDeskDir(&entry))
658 fBaseType = kDesktopNode;
661 fMimeType = B_DIR_MIMETYPE;
662 // should use a shared string here
663 if (IsNodeOpen()) {
664 BNodeInfo info(fNode);
665 if (info.GetType(mimeString) == B_OK)
666 fMimeType = mimeString;
668 if (fIconFrom == kUnknownNotFromNode
669 && WellKnowEntryList::Match(NodeRef())
670 > (directory_which)-1) {
671 // one of home, beos, system, boot, etc.
672 fIconFrom = kTrackerSupplied;
675 break;
677 case kVolumeNode:
679 if (NodeRef()->node == fEntryRef.directory
680 && NodeRef()->device == fEntryRef.device) {
681 // promote from volume to file system root
682 fBaseType = kRootNode;
683 fMimeType = B_ROOT_MIMETYPE;
684 break;
687 // volumes have to have a B_VOLUME_MIMETYPE type
688 fMimeType = B_VOLUME_MIMETYPE;
689 if (fIconFrom == kUnknownNotFromNode) {
690 if (WellKnowEntryList::Match(NodeRef()) > (directory_which)-1)
691 fIconFrom = kTrackerSupplied;
692 else
693 fIconFrom = kVolume;
696 char name[B_FILE_NAME_LENGTH];
697 BVolume volume(NodeRef()->device);
698 if (volume.InitCheck() == B_OK && volume.GetName(name) == B_OK) {
699 if (fVolumeName != NULL)
700 DeletePreferredAppVolumeNameLinkTo();
702 fVolumeName = strdup(name);
704 #if DEBUG
705 else
706 PRINT(("get volume name failed for %s\n", fEntryRef.name));
707 #endif
708 break;
711 case kLinkNode:
712 fMimeType = B_LINK_MIMETYPE;
713 // should use a shared string here
714 break;
716 case kExecutableNode:
717 if (IsNodeOpen()) {
718 char signature[B_MIME_TYPE_LENGTH];
719 if (GetAppSignatureFromAttr(dynamic_cast<BFile*>(fNode),
720 signature) == B_OK) {
721 if (fPreferredAppName)
722 DeletePreferredAppVolumeNameLinkTo();
724 if (signature[0])
725 fPreferredAppName = strdup(signature);
728 if (fMimeType.Length() <= 0)
729 fMimeType = B_APP_MIME_TYPE;
730 // should use a shared string here
731 break;
733 default:
734 if (fMimeType.Length() <= 0)
735 fMimeType = B_FILE_MIMETYPE;
736 break;
741 void
742 Model::ResetIconFrom()
744 BModelOpener opener(this);
746 if (InitCheck() != B_OK)
747 return;
749 // mirror the logic from FinishSettingUpType
750 if ((fBaseType == kDirectoryNode || fBaseType == kVolumeNode
751 || fBaseType == kTrashNode || fBaseType == kDesktopNode)
752 && !CheckNodeIconHint(fNode)) {
753 BDirectory* directory = dynamic_cast<BDirectory*>(fNode);
754 if (WellKnowEntryList::Match(NodeRef()) > (directory_which)-1) {
755 fIconFrom = kTrackerSupplied;
756 return;
757 } else if (directory != NULL && directory->IsRootDirectory()) {
758 fIconFrom = kVolume;
759 return;
762 fIconFrom = kUnknownSource;
766 const char*
767 Model::PreferredAppSignature() const
769 if (IsVolume() || IsSymLink())
770 return "";
772 return fPreferredAppName ? fPreferredAppName : "";
776 void
777 Model::SetPreferredAppSignature(const char* signature)
779 ASSERT(!IsVolume() && !IsSymLink());
780 ASSERT(signature != fPreferredAppName);
781 // self assignment should not be an option
783 free(fPreferredAppName);
784 if (signature)
785 fPreferredAppName = strdup(signature);
786 else
787 fPreferredAppName = NULL;
791 const Model*
792 Model::ResolveIfLink() const
794 if (!IsSymLink())
795 return this;
797 if (!fLinkTo)
798 return this;
800 return fLinkTo;
804 Model*
805 Model::ResolveIfLink()
807 if (!IsSymLink())
808 return this;
810 if (!fLinkTo)
811 return this;
813 return fLinkTo;
817 void
818 Model::SetLinkTo(Model* model)
820 ASSERT(IsSymLink());
821 ASSERT(!fLinkTo || (fLinkTo != model));
823 delete fLinkTo;
824 fLinkTo = model;
828 void
829 Model::GetPreferredAppForBrokenSymLink(BString &result)
831 if (!IsSymLink() || LinkTo()) {
832 result = "";
833 return;
836 BModelOpener opener(this);
837 BNodeInfo info(fNode);
838 status_t error
839 = info.GetPreferredApp(result.LockBuffer(B_MIME_TYPE_LENGTH));
840 result.UnlockBuffer();
842 if (error != B_OK) {
843 // Tracker will have to do
844 result = kTrackerSignature;
849 // #pragma mark - Node monitor updating methods
852 void
853 Model::UpdateEntryRef(const node_ref* dirNode, const char* name)
855 if (IsVolume()) {
856 if (fVolumeName != NULL)
857 DeletePreferredAppVolumeNameLinkTo();
859 fVolumeName = strdup(name);
862 fEntryRef.device = dirNode->device;
863 fEntryRef.directory = dirNode->node;
865 if (fEntryRef.name != NULL && strcmp(fEntryRef.name, name) == 0)
866 return;
868 fEntryRef.set_name(name);
872 status_t
873 Model::WatchVolumeAndMountPoint(uint32 , BHandler* target)
875 ASSERT(IsVolume());
877 if (fEntryRef.name != NULL && fVolumeName != NULL
878 && strcmp(fEntryRef.name, "boot") == 0) {
879 // watch mount point for boot volume
880 BString bootMountPoint("/");
881 bootMountPoint += fVolumeName;
882 BEntry mountPointEntry(bootMountPoint.String());
883 Model mountPointModel(&mountPointEntry);
885 TTracker::WatchNode(mountPointModel.NodeRef(),
886 B_WATCH_NAME | B_WATCH_STAT | B_WATCH_ATTR, target);
889 return TTracker::WatchNode(NodeRef(),
890 B_WATCH_NAME | B_WATCH_STAT | B_WATCH_ATTR, target);
894 bool
895 Model::AttrChanged(const char* attrName)
897 // called on an attribute changed node monitor
898 // sync up cached values of mime type and preferred app and
899 // return true if icon needs updating
901 ASSERT(IsNodeOpen());
902 if (attrName != NULL
903 && (strcmp(attrName, kAttrIcon) == 0
904 || strcmp(attrName, kAttrMiniIcon) == 0
905 || strcmp(attrName, kAttrLargeIcon) == 0)) {
906 return true;
909 if (attrName == NULL
910 || strcmp(attrName, kAttrMIMEType) == 0
911 || strcmp(attrName, kAttrPreferredApp) == 0) {
912 char mimeString[B_MIME_TYPE_LENGTH];
913 BNodeInfo info(fNode);
914 if (info.GetType(mimeString) != B_OK)
915 fMimeType = "";
916 else {
917 // node has a specific mime type
918 fMimeType = mimeString;
919 if (!IsVolume() && !IsSymLink()
920 && info.GetPreferredApp(mimeString) == B_OK) {
921 SetPreferredAppSignature(mimeString);
925 #if xDEBUG
926 if (fIconFrom != kNode) {
927 PRINT(("%s, %s:updating icon because file type changed\n",
928 Name(), attrName != NULL ? attrName : ""));
929 } else {
930 PRINT(("Not updating icon even though type changed "
931 "because icon is from node.\n"));
933 #endif
935 return fIconFrom != kNode;
936 // update icon unless it is coming from a node
939 return attrName == NULL;
943 bool
944 Model::StatChanged()
946 ASSERT(IsNodeOpen());
947 mode_t oldMode = fStatBuf.st_mode;
948 fStatus = fNode->GetStat(&fStatBuf);
950 if (oldMode != fStatBuf.st_mode) {
951 bool forWriting = IsNodeOpenForWriting();
952 CloseNode();
953 //SetupBaseType();
954 // the node type can't change with a stat update...
955 OpenNodeCommon(forWriting);
956 return true;
959 return false;
963 // #pragma mark - Mime handling methods
966 bool
967 Model::IsDropTarget(const Model* forDocument, bool traverse) const
969 switch (CanHandleDrops()) {
970 case kCanHandle:
971 return true;
973 case kCannotHandle:
974 return false;
976 default:
977 break;
980 if (forDocument == NULL)
981 return true;
983 if (traverse) {
984 BEntry entry(forDocument->EntryRef(), true);
985 if (entry.InitCheck() != B_OK)
986 return false;
988 BFile file(&entry, O_RDONLY);
989 BNodeInfo mime(&file);
991 if (mime.InitCheck() != B_OK)
992 return false;
994 char mimeType[B_MIME_TYPE_LENGTH];
995 mime.GetType(mimeType);
997 return SupportsMimeType(mimeType, 0) != kDoesNotSupportType;
1000 // do some mime-based matching
1001 const char* documentMimeType = forDocument->MimeType();
1002 if (documentMimeType == NULL)
1003 return false;
1005 return SupportsMimeType(documentMimeType, 0) != kDoesNotSupportType;
1009 Model::CanHandleResult
1010 Model::CanHandleDrops() const
1012 if (IsDirectory()) {
1013 // directories take anything
1014 // resolve permissions here
1015 return kCanHandle;
1018 if (IsSymLink()) {
1019 // descend into symlink and try again on it's target
1021 BEntry entry(&fEntryRef, true);
1022 if (entry.InitCheck() != B_OK)
1023 return kCannotHandle;
1025 if (entry == BEntry(EntryRef()))
1026 // self-referencing link, avoid infinite recursion
1027 return kCannotHandle;
1029 Model model(&entry);
1030 if (model.InitCheck() != B_OK)
1031 return kCannotHandle;
1033 return model.CanHandleDrops();
1036 if (IsExecutable())
1037 return kNeedToCheckType;
1039 return kCannotHandle;
1043 inline bool
1044 IsSuperHandlerSignature(const char* signature)
1046 return strcasecmp(signature, B_FILE_MIMETYPE) == 0;
1050 enum {
1051 kDontMatch = 0,
1052 kMatchSupertype,
1053 kMatch
1057 static int32
1058 MatchMimeTypeString(/*const */BString* documentType, const char* handlerType)
1060 // perform a mime type wildcard match
1061 // handler types of the form "text"
1062 // handle every handled type with same supertype,
1063 // for everything else a full string match is used
1065 int32 supertypeOnlyLength = 0;
1066 const char* tmp = strstr(handlerType, "/");
1068 if (tmp == NULL) {
1069 // no subtype - supertype string only
1070 supertypeOnlyLength = (int32)strlen(handlerType);
1073 if (supertypeOnlyLength) {
1074 // compare just the supertype
1075 tmp = strstr(documentType->String(), "/");
1076 if (tmp && (tmp - documentType->String() == supertypeOnlyLength)) {
1077 if (documentType->ICompare(handlerType, supertypeOnlyLength) == 0)
1078 return kMatchSupertype;
1079 else
1080 return kDontMatch;
1084 if (documentType->ICompare(handlerType) == 0)
1085 return kMatch;
1087 return kDontMatch;
1091 int32
1092 Model::SupportsMimeType(const char* type, const BObjectList<BString>* list,
1093 bool exactReason) const
1095 ASSERT((type == 0) != (list == 0));
1096 // pass in one or the other
1098 int32 result = kDoesNotSupportType;
1100 BFile file(EntryRef(), O_RDONLY);
1101 BAppFileInfo handlerInfo(&file);
1103 BMessage message;
1104 if (handlerInfo.GetSupportedTypes(&message) != B_OK)
1105 return kDoesNotSupportType;
1107 for (int32 index = 0; ; index++) {
1108 // check if this model lists the type of dropped document as supported
1110 const char* mimeSignature;
1111 ssize_t bufferLength;
1113 if (message.FindData("types", 'CSTR', index,
1114 (const void**)&mimeSignature, &bufferLength)) {
1115 return result;
1118 if (IsSuperHandlerSignature(mimeSignature)) {
1119 if (!exactReason)
1120 return kSuperhandlerModel;
1122 if (result == kDoesNotSupportType)
1123 result = kSuperhandlerModel;
1126 int32 match;
1128 if (type != NULL || (list != NULL && list->IsEmpty())) {
1129 BString typeString(type);
1130 match = MatchMimeTypeString(&typeString, mimeSignature);
1131 } else {
1132 match = WhileEachListItem(const_cast<BObjectList<BString>*>(list),
1133 MatchMimeTypeString, mimeSignature);
1134 // const_cast shouldnt be here, have to have it until
1135 // MW cleans up
1137 if (match == kMatch)
1138 // supports the actual type, it can't get any better
1139 return kModelSupportsType;
1140 else if (match == kMatchSupertype) {
1141 if (!exactReason)
1142 return kModelSupportsSupertype;
1144 // we already know this model supports the file as a supertype,
1145 // now find out if it matches the type
1146 result = kModelSupportsSupertype;
1150 return result;
1154 bool
1155 Model::IsDropTargetForList(const BObjectList<BString>* list) const
1157 switch (CanHandleDrops()) {
1158 case kCanHandle:
1159 return true;
1161 case kCannotHandle:
1162 return false;
1164 default:
1165 break;
1168 return SupportsMimeType(0, list) != kDoesNotSupportType;
1172 bool
1173 Model::IsSuperHandler() const
1175 ASSERT(CanHandleDrops() == kNeedToCheckType);
1177 BFile file(EntryRef(), O_RDONLY);
1178 BAppFileInfo handlerInfo(&file);
1180 BMessage message;
1181 if (handlerInfo.GetSupportedTypes(&message) != B_OK)
1182 return false;
1184 for (int32 index = 0; ; index++) {
1185 const char* mimeSignature;
1186 ssize_t bufferLength;
1188 if (message.FindData("types", 'CSTR', index,
1189 (const void**)&mimeSignature, &bufferLength)) {
1190 return false;
1193 if (IsSuperHandlerSignature(mimeSignature))
1194 return true;
1196 return false;
1200 void
1201 Model::GetEntry(BEntry* entry) const
1203 entry->SetTo(EntryRef());
1207 void
1208 Model::GetPath(BPath* path) const
1210 BEntry entry(EntryRef());
1211 entry.GetPath(path);
1215 bool
1216 Model::Mimeset(bool force)
1218 BString oldType = MimeType();
1219 BPath path;
1220 GetPath(&path);
1222 update_mime_info(path.Path(), 0, 1, force ? 2 : 0);
1223 ModelNodeLazyOpener opener(this);
1224 opener.OpenNode();
1225 AttrChanged(NULL);
1227 return !oldType.ICompare(MimeType());
1231 ssize_t
1232 Model::WriteAttr(const char* attr, type_code type, off_t offset,
1233 const void* buffer, size_t length)
1235 BModelWriteOpener opener(this);
1236 if (!fNode)
1237 return 0;
1239 ssize_t result = fNode->WriteAttr(attr, type, offset, buffer, length);
1240 return result;
1244 ssize_t
1245 Model::WriteAttrKillForeign(const char* attr, const char* foreignAttr,
1246 type_code type, off_t offset, const void* buffer, size_t length)
1248 BModelWriteOpener opener(this);
1249 if (!fNode)
1250 return 0;
1252 ssize_t result = fNode->WriteAttr(attr, type, offset, buffer, length);
1253 if (result == (ssize_t)length)
1254 // nuke attribute in opposite endianness
1255 fNode->RemoveAttr(foreignAttr);
1256 return result;
1260 status_t
1261 Model::GetLongVersionString(BString &result, version_kind kind)
1263 BFile file(EntryRef(), O_RDONLY);
1264 status_t error = file.InitCheck();
1265 if (error != B_OK)
1266 return error;
1268 BAppFileInfo info(&file);
1269 error = info.InitCheck();
1270 if (error != B_OK)
1271 return error;
1273 version_info version;
1274 error = info.GetVersionInfo(&version, kind);
1275 if (error != B_OK)
1276 return error;
1278 result = version.long_info;
1279 return B_OK;
1282 status_t
1283 Model::GetVersionString(BString &result, version_kind kind)
1285 BFile file(EntryRef(), O_RDONLY);
1286 status_t error = file.InitCheck();
1287 if (error != B_OK)
1288 return error;
1290 BAppFileInfo info(&file);
1291 error = info.InitCheck();
1292 if (error != B_OK)
1293 return error;
1295 version_info version;
1296 error = info.GetVersionInfo(&version, kind);
1297 if (error != B_OK)
1298 return error;
1300 result.SetToFormat("%" B_PRId32 ".%" B_PRId32 ".%" B_PRId32, version.major,
1301 version.middle, version.minor);
1303 return B_OK;
1306 #if DEBUG
1308 void
1309 Model::PrintToStream(int32 level, bool deep)
1311 PRINT(("model name %s, entry name %s, inode %" B_PRIdINO ", dev %"
1312 B_PRIdDEV ", directory inode %" B_PRIdINO "\n",
1313 Name() ? Name() : "**empty name**",
1314 EntryRef()->name ? EntryRef()->name : "**empty ref name**",
1315 NodeRef()->node,
1316 NodeRef()->device,
1317 EntryRef()->directory));
1318 PRINT(("type %s \n", MimeType()));
1320 PRINT(("model type: "));
1321 switch (fBaseType) {
1322 case kPlainNode:
1323 PRINT(("plain\n"));
1324 break;
1326 case kQueryNode:
1327 PRINT(("query\n"));
1328 break;
1330 case kQueryTemplateNode:
1331 PRINT(("query template\n"));
1332 break;
1334 case kExecutableNode:
1335 PRINT(("exe\n"));
1336 break;
1338 case kDirectoryNode:
1339 case kTrashNode:
1340 case kDesktopNode:
1341 PRINT(("dir\n"));
1342 break;
1344 case kLinkNode:
1345 PRINT(("link\n"));
1346 break;
1348 case kRootNode:
1349 PRINT(("root\n"));
1350 break;
1352 case kVolumeNode:
1353 PRINT(("volume, name %s\n", fVolumeName ? fVolumeName : ""));
1354 break;
1356 case kVirtualDirectoryNode:
1357 PRINT(("virtual directory\n"));
1358 break;
1360 default:
1361 PRINT(("unknown\n"));
1362 break;
1365 if (level < 1)
1366 return;
1368 if (!IsVolume()) {
1369 PRINT(("preferred app %s\n",
1370 fPreferredAppName ? fPreferredAppName : ""));
1373 PRINT(("icon from: "));
1374 switch (IconFrom()) {
1375 case kUnknownSource:
1376 PRINT(("unknown\n"));
1377 break;
1379 case kUnknownNotFromNode:
1380 PRINT(("unknown but not from a node\n"));
1381 break;
1383 case kTrackerDefault:
1384 PRINT(("tracker default\n"));
1385 break;
1387 case kTrackerSupplied:
1388 PRINT(("tracker supplied\n"));
1389 break;
1391 case kMetaMime:
1392 PRINT(("metamime\n"));
1393 break;
1395 case kPreferredAppForType:
1396 PRINT(("preferred app for type\n"));
1397 break;
1399 case kPreferredAppForNode:
1400 PRINT(("preferred app for node\n"));
1401 break;
1403 case kNode:
1404 PRINT(("node\n"));
1405 break;
1407 case kVolume:
1408 PRINT(("volume\n"));
1409 break;
1411 default:
1412 break;
1415 PRINT(("model %s opened %s \n", !IsNodeOpen() ? "not " : "",
1416 IsNodeOpenForWriting() ? "for writing" : ""));
1418 if (IsNodeOpen()) {
1419 node_ref nodeRef;
1420 fNode->GetNodeRef(&nodeRef);
1421 PRINT(("node ref of open Node %" B_PRIdINO " %" B_PRIdDEV "\n",
1422 nodeRef.node, nodeRef.device));
1425 if (deep && IsSymLink()) {
1426 BEntry tmpEntry(EntryRef(), true);
1427 Model tmp(&tmpEntry);
1428 PRINT(("symlink to:\n"));
1429 tmp.PrintToStream();
1431 TrackIconSource(B_MINI_ICON);
1432 TrackIconSource(B_LARGE_ICON);
1436 void
1437 Model::TrackIconSource(icon_size size)
1439 PRINT(("tracking %s icon\n", size == B_LARGE_ICON ? "large" : "small"));
1440 BRect rect;
1441 if (size == B_MINI_ICON)
1442 rect.Set(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1);
1443 else
1444 rect.Set(0, 0, B_LARGE_ICON - 1, B_LARGE_ICON - 1);
1446 BBitmap bitmap(rect, B_CMAP8);
1448 BModelOpener opener(this);
1450 if (Node() == NULL) {
1451 PRINT(("track icon error - no node\n"));
1452 return;
1455 if (IsSymLink()) {
1456 PRINT(("tracking symlink icon\n"));
1457 if (fLinkTo) {
1458 fLinkTo->TrackIconSource(size);
1459 return;
1463 if (fBaseType == kVolumeNode) {
1464 BVolume volume(NodeRef()->device);
1465 status_t result = volume.GetIcon(&bitmap, size);
1466 PRINT(("getting icon from volume %s\n", strerror(result)));
1467 } else {
1468 BNodeInfo nodeInfo(Node());
1470 status_t err = nodeInfo.GetIcon(&bitmap, size);
1471 if (err == B_OK) {
1472 // file knew which icon to use, we are done
1473 PRINT(("track icon - got icon from file\n"));
1474 return;
1477 char preferredApp[B_MIME_TYPE_LENGTH];
1478 err = nodeInfo.GetPreferredApp(preferredApp);
1479 if (err == B_OK && preferredApp[0]) {
1480 BMimeType preferredAppType(preferredApp);
1481 err = preferredAppType.GetIconForType(MimeType(), &bitmap, size);
1482 if (err == B_OK) {
1483 PRINT(
1484 ("track icon - got icon for type %s from preferred "
1485 "app %s for file\n", MimeType(), preferredApp));
1486 return;
1490 BMimeType mimeType(MimeType());
1491 err = mimeType.GetIcon(&bitmap, size);
1492 if (err == B_OK) {
1493 // the system knew what icon to use for the type, we are done
1494 PRINT(("track icon - signature %s, got icon from system\n",
1495 MimeType()));
1496 return;
1499 err = mimeType.GetPreferredApp(preferredApp);
1500 if (err != B_OK) {
1501 // no preferred App for document, give up
1502 PRINT(("track icon - signature %s, no prefered app, error %s\n",
1503 MimeType(), strerror(err)));
1504 return;
1507 BMimeType preferredAppType(preferredApp);
1508 err = preferredAppType.GetIconForType(MimeType(), &bitmap, size);
1509 if (err == B_OK) {
1510 // the preferred app knew icon to use for the type, we are done
1511 PRINT(
1512 ("track icon - signature %s, got icon from preferred "
1513 "app %s\n", MimeType(), preferredApp));
1514 return;
1516 PRINT(
1517 ("track icon - signature %s, preferred app %s, no icon, "
1518 "error %s\n", MimeType(), preferredApp, strerror(err)));
1522 #endif // DEBUG
1524 #ifdef CHECK_OPEN_MODEL_LEAKS
1526 namespace BPrivate {
1528 #include <stdio.h>
1530 void
1531 DumpOpenModels(bool extensive)
1533 if (readOnlyOpenModelList) {
1534 int32 count = readOnlyOpenModelList->CountItems();
1535 printf("%ld models open read-only:\n", count);
1536 printf("==========================\n");
1537 for (int32 index = 0; index < count; index++) {
1538 if (extensive) {
1539 printf("---------------------------\n");
1540 readOnlyOpenModelList->ItemAt(index)->PrintToStream();
1541 } else
1542 printf("%s\n", readOnlyOpenModelList->ItemAt(index)->Name());
1546 if (writableOpenModelList) {
1547 int32 count = writableOpenModelList->CountItems();
1548 printf("%ld models open writable:\n", count);
1549 printf("models open writable:\n");
1550 printf("======================\n");
1551 for (int32 index = 0; index < count; index++) {
1552 if (extensive) {
1553 printf("---------------------------\n");
1554 writableOpenModelList->ItemAt(index)->PrintToStream();
1555 } else
1556 printf("%s\n", writableOpenModelList->ItemAt(index)->Name());
1562 void
1563 InitOpenModelDumping()
1565 readOnlyOpenModelList = 0;
1566 writableOpenModelList = 0;
1569 } // namespace BPrivate
1571 #endif // CHECK_OPEN_MODEL_LEAKS