xref: /haiku/src/kits/tracker/Model.cpp (revision b671e9bbdbd10268a042b4f4cc4317ccd03d105e)
1 /*
2 Open Tracker License
3 
4 Terms and Conditions
5 
6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7 
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:
14 
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.
17 
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.
24 
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.
28 
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.
33 */
34 
35 //	Dedicated to BModel
36 
37 // ToDo:
38 // Consider moving iconFrom logic to BPose
39 // use a more efficient way of storing file type and preferred app strings
40 
41 #include <stdlib.h>
42 #include <string.h>
43 
44 #include <fs_info.h>
45 #include <fs_attr.h>
46 
47 #include <AppDefs.h>
48 #include <Bitmap.h>
49 #include <Debug.h>
50 #include <Directory.h>
51 #include <Entry.h>
52 #include <File.h>
53 #include <NodeInfo.h>
54 #include <NodeMonitor.h>
55 #include <Path.h>
56 #include <SymLink.h>
57 #include <Query.h>
58 #include <Volume.h>
59 #include <VolumeRoster.h>
60 
61 #include "Model.h"
62 
63 #include "Attributes.h"
64 #include "Bitmaps.h"
65 #include "FindPanel.h"
66 #include "FSUtils.h"
67 #include "MimeTypes.h"
68 #include "IconCache.h"
69 #include "Tracker.h"
70 #include "Utilities.h"
71 
72 #ifdef CHECK_OPEN_MODEL_LEAKS
73 BObjectList<Model> *writableOpenModelList = NULL;
74 BObjectList<Model> *readOnlyOpenModelList = NULL;
75 #endif
76 
77 namespace BPrivate {
78 extern
79 #ifdef _IMPEXP_BE
80 _IMPEXP_BE
81 #endif
82 bool CheckNodeIconHintPrivate(const BNode *, bool);
83 }
84 
85 
86 Model::Model()
87 	:
88 	fPreferredAppName(NULL),
89 	fBaseType(kUnknownNode),
90 	fIconFrom(kUnknownSource),
91 	fWritable(false),
92 	fNode(NULL),
93 	fStatus(B_NO_INIT)
94 {
95 }
96 
97 
98 Model::Model(const Model &cloneThis)
99 	:
100 	fEntryRef(cloneThis.fEntryRef),
101 	fMimeType(cloneThis.fMimeType),
102 	fPreferredAppName(NULL),
103 	fBaseType(cloneThis.fBaseType),
104 	fIconFrom(cloneThis.fIconFrom),
105 	fWritable(false),
106 	fNode(NULL)
107 {
108 	fStatBuf.st_dev = cloneThis.NodeRef()->device;
109 	fStatBuf.st_ino = cloneThis.NodeRef()->node;
110 
111 	if (cloneThis.IsSymLink() && cloneThis.LinkTo())
112 		fLinkTo = new Model(*cloneThis.LinkTo());
113 
114 	fStatus = OpenNode(cloneThis.IsNodeOpenForWriting());
115 	if (fStatus == B_OK) {
116 		ASSERT(fNode);
117 		fNode->GetStat(&fStatBuf);
118 		ASSERT(fStatBuf.st_dev == cloneThis.NodeRef()->device);
119 		ASSERT(fStatBuf.st_ino == cloneThis.NodeRef()->node);
120 	}
121 	if (!cloneThis.IsNodeOpen())
122 		CloseNode();
123 }
124 
125 
126 Model::Model(const node_ref *dirNode, const node_ref *node, const char *name,
127 	bool open, bool writable)
128 	:
129 	fPreferredAppName(NULL),
130 	fWritable(false),
131 	fNode(NULL)
132 {
133 	SetTo(dirNode, node, name, open, writable);
134 }
135 
136 
137 Model::Model(const BEntry *entry, bool open, bool writable)
138 	:
139 	fPreferredAppName(NULL),
140 	fWritable(false),
141 	fNode(NULL)
142 {
143 	SetTo(entry, open, writable);
144 }
145 
146 
147 Model::Model(const entry_ref *ref, bool traverse, bool open, bool writable)
148 	:
149 	fPreferredAppName(NULL),
150 	fBaseType(kUnknownNode),
151 	fIconFrom(kUnknownSource),
152 	fWritable(false),
153 	fNode(NULL)
154 {
155 	BEntry entry(ref, traverse);
156 	fStatus = entry.InitCheck();
157 	if (fStatus == B_OK)
158 		SetTo(&entry, open, writable);
159 }
160 
161 
162 void
163 Model::DeletePreferredAppVolumeNameLinkTo()
164 {
165 	if (IsSymLink()) {
166 		Model *tmp = fLinkTo;
167 			// deal with link to link to self
168 		fLinkTo = NULL;
169 		delete tmp;
170 
171 	} else if (IsVolume())
172 		free(fVolumeName);
173 	else
174 		free(fPreferredAppName);
175 
176 	fPreferredAppName = NULL;
177 }
178 
179 
180 Model::~Model()
181 {
182 #ifdef CHECK_OPEN_MODEL_LEAKS
183 	if (writableOpenModelList)
184 		writableOpenModelList->RemoveItem(this);
185 	if (readOnlyOpenModelList)
186 		readOnlyOpenModelList->RemoveItem(this);
187 #endif
188 
189 	DeletePreferredAppVolumeNameLinkTo();
190 	if (IconCache::NeedsDeletionNotification((IconSource)fIconFrom))
191 		// this check allows us to use temporary Model in the IconCache
192 		// without the danger of a deadlock
193 		IconCache::sIconCache->Deleting(this);
194 #if xDEBUG
195 	if (fNode)
196 		PRINT(("destructor closing node for %s\n", Name()));
197 #endif
198 
199 	delete fNode;
200 }
201 
202 
203 status_t
204 Model::SetTo(const BEntry *entry, bool open, bool writable)
205 {
206 	delete fNode;
207 	fNode = NULL;
208 	DeletePreferredAppVolumeNameLinkTo();
209 	fIconFrom = kUnknownSource;
210 	fBaseType = kUnknownNode;
211 	fMimeType = "";
212 
213 	fStatus = entry->GetRef(&fEntryRef);
214 	if (fStatus != B_OK)
215 		return fStatus;
216 
217 	fStatus = entry->GetStat(&fStatBuf);
218 	if (fStatus != B_OK)
219 		return fStatus;
220 
221 	fStatus = OpenNode(writable);
222 	if (!open)
223 		CloseNode();
224 
225 	return fStatus;
226 }
227 
228 
229 status_t
230 Model::SetTo(const entry_ref *newRef, bool traverse, bool open, bool writable)
231 {
232 	delete fNode;
233 	fNode = NULL;
234 	DeletePreferredAppVolumeNameLinkTo();
235 	fIconFrom = kUnknownSource;
236 	fBaseType = kUnknownNode;
237 	fMimeType = "";
238 
239 	BEntry tmpEntry(newRef, traverse);
240 	fStatus = tmpEntry.InitCheck();
241 	if (fStatus != B_OK)
242 		return fStatus;
243 
244 	if (traverse)
245 		tmpEntry.GetRef(&fEntryRef);
246 	else
247 		fEntryRef = *newRef;
248 
249 	fStatus = tmpEntry.GetStat(&fStatBuf);
250 	if (fStatus != B_OK)
251 		return fStatus;
252 
253 	fStatus = OpenNode(writable);
254 	if (!open)
255 		CloseNode();
256 
257 	return fStatus;
258 }
259 
260 
261 status_t
262 Model::SetTo(const node_ref *dirNode, const node_ref *nodeRef, const char *name,
263 	bool open, bool writable)
264 {
265 	delete fNode;
266 	fNode = NULL;
267 	DeletePreferredAppVolumeNameLinkTo();
268 	fIconFrom = kUnknownSource;
269 	fBaseType = kUnknownNode;
270 	fMimeType = "";
271 
272 	fStatBuf.st_dev = nodeRef->device;
273 	fStatBuf.st_ino = nodeRef->node;
274 	fEntryRef.device = dirNode->device;
275 	fEntryRef.directory = dirNode->node;
276 	fEntryRef.name = strdup(name);
277 
278 	BEntry tmpNode(&fEntryRef);
279 	fStatus = tmpNode.InitCheck();
280 	if (fStatus != B_OK)
281 		return fStatus;
282 
283 	fStatus = tmpNode.GetStat(&fStatBuf);
284 	if (fStatus != B_OK)
285 		return fStatus;
286 
287 	fStatus = OpenNode(writable);
288 
289 	if (!open)
290 		CloseNode();
291 
292 	return fStatus;
293 }
294 
295 
296 status_t
297 Model::InitCheck() const
298 {
299 	return fStatus;
300 }
301 
302 
303 int
304 Model::CompareFolderNamesFirst(const Model *compareModel) const
305 {
306 	if (compareModel == NULL)
307 		return -1;
308 
309 	const Model *resolvedCompareModel = compareModel->ResolveIfLink();
310 	const Model *resolvedMe = ResolveIfLink();
311 
312 	if (resolvedMe->IsVolume()) {
313 		if (!resolvedCompareModel->IsVolume())
314 			return -1;
315 	} else if (resolvedCompareModel->IsVolume())
316 		return 1;
317 
318 	if (resolvedMe->IsDirectory()) {
319 		if (!resolvedCompareModel->IsDirectory())
320 			return -1;
321 	} else if (resolvedCompareModel->IsDirectory())
322 		return 1;
323 
324 	return NaturalCompare(Name(), compareModel->Name());
325 }
326 
327 
328 const char *
329 Model::Name() const
330 {
331 	switch (fBaseType) {
332 		case kRootNode:
333 			return "Disks";
334 		case kVolumeNode:
335 			if (fVolumeName)
336 				return fVolumeName;
337 			// fall thru
338 	}
339 	return fEntryRef.name;
340 }
341 
342 
343 status_t
344 Model::OpenNode(bool writable)
345 {
346 	if (IsNodeOpen() && (writable == IsNodeOpenForWriting()))
347 		return B_OK;
348 
349 	OpenNodeCommon(writable);
350 	return fStatus;
351 }
352 
353 
354 status_t
355 Model::UpdateStatAndOpenNode(bool writable)
356 {
357 	if (IsNodeOpen() && (writable == IsNodeOpenForWriting()))
358 		return B_OK;
359 
360 	// try reading the stat structure again
361 	BEntry tmpEntry(&fEntryRef);
362 	fStatus = tmpEntry.InitCheck();
363 	if (fStatus != B_OK)
364 		return fStatus;
365 
366 	fStatus = tmpEntry.GetStat(&fStatBuf);
367 	if (fStatus != B_OK)
368 		return fStatus;
369 
370 	OpenNodeCommon(writable);
371 	return fStatus;
372 }
373 
374 
375 status_t
376 Model::OpenNodeCommon(bool writable)
377 {
378 #if xDEBUG
379 	PRINT(("opening node for %s\n", Name()));
380 #endif
381 
382 #ifdef CHECK_OPEN_MODEL_LEAKS
383 	if (writableOpenModelList)
384 		writableOpenModelList->RemoveItem(this);
385 	if (readOnlyOpenModelList)
386 		readOnlyOpenModelList->RemoveItem(this);
387 #endif
388 
389 	if (fBaseType == kUnknownNode)
390 		SetupBaseType();
391 
392 	switch (fBaseType) {
393 		case kPlainNode:
394 		case kExecutableNode:
395 		case kQueryNode:
396 		case kQueryTemplateNode:
397 			// open or reopen
398 			delete fNode;
399 			fNode = new BFile(&fEntryRef, (uint32)(writable ? O_RDWR : O_RDONLY));
400 			break;
401 
402 		case kDirectoryNode:
403 		case kVolumeNode:
404 		case kRootNode:
405 			if (!IsNodeOpen())
406 				fNode = new BDirectory(&fEntryRef);
407 
408 			if (fBaseType == kDirectoryNode
409 				&& static_cast<BDirectory *>(fNode)->IsRootDirectory()) {
410 				// promote from directory to volume
411 				fBaseType = kVolumeNode;
412 			}
413 			break;
414 
415 		case kLinkNode:
416 			if (!IsNodeOpen()) {
417 				BEntry entry(&fEntryRef);
418 				fNode = new BSymLink(&entry);
419 			}
420 			break;
421 
422 		default:
423 #if DEBUG
424 			PrintToStream();
425 #endif
426 			TRESPASS();
427 				// this can only happen if GetStat failed before, in which case
428 				// we shouldn't be here
429 			// ToDo: Obviously, we can also be here if the type could not be determined,
430 			// for example for block devices (so the TRESPASS() macro shouldn't be
431 			// used here)!
432 			return fStatus = B_ERROR;
433 	}
434 
435 	fStatus = fNode->InitCheck();
436 	if (fStatus != B_OK) {
437 		delete fNode;
438 		fNode = NULL;
439 		// original code snoozed an error here and returned B_OK
440 		return fStatus;
441 	}
442 
443 	fWritable = writable;
444 
445 	if (!fMimeType.Length())
446 		FinishSettingUpType();
447 
448 #ifdef CHECK_OPEN_MODEL_LEAKS
449 	if (fWritable) {
450 		if (!writableOpenModelList) {
451 			TRACE();
452 			writableOpenModelList = new BObjectList<Model>(100);
453 		}
454 		writableOpenModelList->AddItem(this);
455 	} else {
456 		if (!readOnlyOpenModelList) {
457 			TRACE();
458 			readOnlyOpenModelList = new BObjectList<Model>(100);
459 		}
460 		readOnlyOpenModelList->AddItem(this);
461 	}
462 #endif
463 
464 	return fStatus;
465 }
466 
467 
468 void
469 Model::CloseNode()
470 {
471 #if xDEBUG
472 	PRINT(("closing node for %s\n", Name()));
473 #endif
474 
475 #ifdef CHECK_OPEN_MODEL_LEAKS
476 	if (writableOpenModelList)
477 		writableOpenModelList->RemoveItem(this);
478 	if (readOnlyOpenModelList)
479 		readOnlyOpenModelList->RemoveItem(this);
480 #endif
481 
482 	delete fNode;
483 	fNode = NULL;
484 }
485 
486 
487 bool
488 Model::IsNodeOpen() const
489 {
490 	return fNode != NULL;
491 }
492 
493 
494 
495 bool
496 Model::IsNodeOpenForWriting() const
497 {
498 	return fNode != NULL && fWritable;
499 }
500 
501 
502 void
503 Model::SetupBaseType()
504 {
505 	switch (fStatBuf.st_mode & S_IFMT) {
506 		case S_IFDIR:
507 			// folder
508 			fBaseType = kDirectoryNode;
509 			break;
510 
511 		case S_IFREG:
512 			// regular file
513 			if (fStatBuf.st_mode & S_IXUSR)
514 				// executable
515 				fBaseType = kExecutableNode;
516 			else
517 				// non-executable
518 				fBaseType = kPlainNode;
519 			break;
520 
521 		case S_IFLNK:
522 			// symlink
523 			fBaseType = kLinkNode;
524 			break;
525 
526 		default:
527 			fBaseType = kUnknownNode;
528 			break;
529 	}
530 }
531 
532 
533 static bool
534 HasVectorIconHint(BNode *node)
535 {
536 	attr_info info;
537 	return node->GetAttrInfo(kAttrIcon, &info) == B_OK;
538 }
539 
540 
541 void
542 Model::FinishSettingUpType()
543 {
544 	char mimeString[B_MIME_TYPE_LENGTH];
545 
546 	// while we are reading the node, do a little
547 	// snooping to see if it even makes sense to look for a node-based
548 	// icon
549 	// This serves as a hint to the icon cache, allowing it to not hit the
550 	// disk again for models that do not have an icon defined by the node
551 	if (IsNodeOpen()
552 		&& fBaseType != kLinkNode
553 		&& !CheckNodeIconHintPrivate(fNode, dynamic_cast<TTracker *>(be_app) == NULL)
554 		&& !HasVectorIconHint(fNode)) {
555 			// when checking for the node icon hint, if we are libtracker, only check
556 			// for small icons - checking for the large icons is a little more
557 			// work for the filesystem and this will speed up the test.
558 			// This makes node icons only work if there is a small and a large node
559 			// icon on a file - for libtracker that is not a problem though
560 		fIconFrom = kUnknownNotFromNode;
561 	}
562 
563 	if (fBaseType != kDirectoryNode
564 		&& fBaseType != kVolumeNode
565 		&& fBaseType != kLinkNode
566 		&& IsNodeOpen()) {
567 		BNodeInfo info(fNode);
568 
569 		// check if a specific mime type is set
570 		if (info.GetType(mimeString) == B_OK) {
571 			// node has a specific mime type
572 			fMimeType = mimeString;
573 			if (strcmp(mimeString, B_QUERY_MIMETYPE) == 0)
574 				fBaseType = kQueryNode;
575 			else if (strcmp(mimeString, B_QUERY_TEMPLATE_MIMETYPE) == 0)
576 				fBaseType = kQueryTemplateNode;
577 
578 			if (info.GetPreferredApp(mimeString) == B_OK) {
579 				if (fPreferredAppName)
580 					DeletePreferredAppVolumeNameLinkTo();
581 
582 				if (mimeString[0])
583 					fPreferredAppName = strdup(mimeString);
584 			}
585 		}
586 	}
587 
588 	switch (fBaseType) {
589 		case kDirectoryNode:
590 			fMimeType = B_DIR_MIMETYPE;	// should use a shared string here
591 			if (IsNodeOpen()) {
592 				BNodeInfo info(fNode);
593 				if (info.GetType(mimeString) == B_OK)
594 					fMimeType = mimeString;
595 
596 				if (fIconFrom == kUnknownNotFromNode
597 					&& WellKnowEntryList::Match(NodeRef()) > (directory_which)-1)
598 					// one of home, beos, system, boot, etc.
599 					fIconFrom = kTrackerSupplied;
600 			}
601 			break;
602 
603 		case kVolumeNode:
604 		{
605 			if (NodeRef()->node == fEntryRef.directory
606 				&& NodeRef()->device == fEntryRef.device) {
607 				// promote from volume to file system root
608 				fBaseType = kRootNode;
609 				fMimeType = B_ROOT_MIMETYPE;
610 				break;
611 			}
612 
613 			// volumes have to have a B_VOLUME_MIMETYPE type
614 			fMimeType = B_VOLUME_MIMETYPE;
615 			if (fIconFrom == kUnknownNotFromNode) {
616 				if (WellKnowEntryList::Match(NodeRef()) > (directory_which)-1)
617 					fIconFrom = kTrackerSupplied;
618 				else
619 					fIconFrom = kVolume;
620 			}
621 
622 			char name[B_FILE_NAME_LENGTH];
623 			BVolume	volume(NodeRef()->device);
624 			if (volume.InitCheck() == B_OK && volume.GetName(name) == B_OK) {
625 				if (fVolumeName)
626 					DeletePreferredAppVolumeNameLinkTo();
627 
628 				fVolumeName = strdup(name);
629 			}
630 #if DEBUG
631 			else
632 				PRINT(("get volume name failed for %s\n", fEntryRef.name));
633 #endif
634 			break;
635 		}
636 
637 		case kLinkNode:
638 			fMimeType = B_LINK_MIMETYPE;	// should use a shared string here
639 			break;
640 
641 		case kExecutableNode:
642 			if (IsNodeOpen()) {
643 				char signature[B_MIME_TYPE_LENGTH];
644 				if (GetAppSignatureFromAttr(dynamic_cast<BFile *>(fNode), signature)
645 					== B_OK) {
646 
647 					if (fPreferredAppName)
648 						DeletePreferredAppVolumeNameLinkTo();
649 
650 					if (signature[0])
651 						fPreferredAppName = strdup(signature);
652 				}
653 			}
654 			if (!fMimeType.Length())
655 				fMimeType = B_APP_MIME_TYPE;	// should use a shared string here
656 			break;
657 
658 		default:
659 			if (!fMimeType.Length())
660 				fMimeType = B_FILE_MIMETYPE;
661 			break;
662 	}
663 }
664 
665 
666 void
667 Model::ResetIconFrom()
668 {
669 	BModelOpener opener(this);
670 
671 	if (InitCheck() != B_OK)
672 		return;
673 
674 	// mirror the logic from FinishSettingUpType
675 	if ((fBaseType == kDirectoryNode || fBaseType == kVolumeNode)
676 		&& !CheckNodeIconHintPrivate(fNode, dynamic_cast<TTracker *>(be_app) == NULL)) {
677 		if (WellKnowEntryList::Match(NodeRef()) > (directory_which)-1) {
678 			fIconFrom = kTrackerSupplied;
679 			return;
680 		} else if (dynamic_cast<BDirectory *>(fNode)->IsRootDirectory()) {
681 			fIconFrom = kVolume;
682 			return;
683 		}
684 	}
685 	fIconFrom = kUnknownSource;
686 }
687 
688 
689 const char *
690 Model::PreferredAppSignature() const
691 {
692 	if (IsVolume() || IsSymLink())
693 		return "";
694 
695 	return fPreferredAppName ? fPreferredAppName : "";
696 }
697 
698 
699 void
700 Model::SetPreferredAppSignature(const char *signature)
701 {
702 	ASSERT(!IsVolume() && !IsSymLink());
703 	ASSERT(signature != fPreferredAppName);
704 		// self assignment should not be an option
705 
706 	free(fPreferredAppName);
707 	if (signature)
708 		fPreferredAppName = strdup(signature);
709 	else
710 		fPreferredAppName = NULL;
711 }
712 
713 
714 const Model *
715 Model::ResolveIfLink() const
716 {
717 	if (!IsSymLink())
718 		return this;
719 
720 	if (!fLinkTo)
721 		return this;
722 
723 	return fLinkTo;
724 }
725 
726 
727 Model *
728 Model::ResolveIfLink()
729 {
730 	if (!IsSymLink())
731 		return this;
732 
733 	if (!fLinkTo)
734 		return this;
735 
736 	return fLinkTo;
737 }
738 
739 
740 void
741 Model::SetLinkTo(Model *model)
742 {
743 	ASSERT(IsSymLink());
744 	ASSERT(!fLinkTo || (fLinkTo != model));
745 
746 	if (fLinkTo)
747 		delete fLinkTo;
748 	fLinkTo = model;
749 }
750 
751 
752 void
753 Model::GetPreferredAppForBrokenSymLink(BString &result)
754 {
755 	if (!IsSymLink() || LinkTo()) {
756 		result = "";
757 		return;
758 	}
759 
760 	BModelOpener opener(this);
761 	BNodeInfo info(fNode);
762 	status_t error = info.GetPreferredApp(result.LockBuffer(B_MIME_TYPE_LENGTH));
763 	result.UnlockBuffer();
764 
765 	if (error != B_OK)
766 		// Tracker will have to do
767 		result = kTrackerSignature;
768 }
769 
770 
771 // Node monitor updating stuff
772 
773 void
774 Model::UpdateEntryRef(const node_ref *dirNode, const char *name)
775 {
776 	if (IsVolume()) {
777 		if (fVolumeName)
778 			DeletePreferredAppVolumeNameLinkTo();
779 
780 		fVolumeName = strdup(name);
781 	}
782 
783 	fEntryRef.device = dirNode->device;
784 	fEntryRef.directory = dirNode->node;
785 
786 	if (fEntryRef.name && strcmp(fEntryRef.name, name) == 0)
787 		return;
788 
789 	fEntryRef.set_name(name);
790 }
791 
792 
793 status_t
794 Model::WatchVolumeAndMountPoint(uint32 , BHandler *target)
795 {
796 	ASSERT(IsVolume());
797 
798 	if (fEntryRef.name && fVolumeName
799 		&& strcmp(fEntryRef.name, "boot") == 0) {
800 		// watch mount point for boot volume
801 		BString bootMountPoint("/");
802 		bootMountPoint += fVolumeName;
803 		BEntry mountPointEntry(bootMountPoint.String());
804 		Model mountPointModel(&mountPointEntry);
805 
806 		TTracker::WatchNode(mountPointModel.NodeRef(), B_WATCH_NAME
807 			| B_WATCH_STAT | B_WATCH_ATTR, target);
808 	}
809 
810 	return TTracker::WatchNode(NodeRef(), B_WATCH_NAME | B_WATCH_STAT
811 		| B_WATCH_ATTR, target);
812 }
813 
814 
815 bool
816 Model::AttrChanged(const char *attrName)
817 {
818 	// called on an attribute changed node monitor
819 	// sync up cached values of mime type and preferred app and
820 	// return true if icon needs updating
821 
822 	ASSERT(IsNodeOpen());
823 	if (attrName
824 		&& (strcmp(attrName, kAttrIcon) == 0
825 			|| strcmp(attrName, kAttrMiniIcon) == 0
826 			|| strcmp(attrName, kAttrLargeIcon) == 0))
827 		return true;
828 
829 	if (!attrName
830 		|| strcmp(attrName, kAttrMIMEType) == 0
831 		|| strcmp(attrName, kAttrPreferredApp) == 0) {
832 		char mimeString[B_MIME_TYPE_LENGTH];
833 		BNodeInfo info(fNode);
834 		if (info.GetType(mimeString) != B_OK)
835 			fMimeType = "";
836 		else {
837 			// node has a specific mime type
838 			fMimeType = mimeString;
839 			if (!IsVolume()
840 				&& !IsSymLink()
841 				&& info.GetPreferredApp(mimeString) == B_OK)
842 				SetPreferredAppSignature(mimeString);
843 		}
844 
845 #if xDEBUG
846 		if (fIconFrom != kNode)
847 			PRINT(("%s, %s:updating icon because file type changed\n",
848 				Name(), attrName ? attrName : ""));
849 		else
850 			PRINT(("not updating icon even thoug type changed "
851 				"because icon from node\n"));
852 
853 #endif
854 
855 		return fIconFrom != kNode;
856 		// update icon unless it is comming from a node
857 	}
858 
859 	return attrName == NULL;
860 }
861 
862 
863 bool
864 Model::StatChanged()
865 {
866 	ASSERT(IsNodeOpen());
867 	mode_t oldMode = fStatBuf.st_mode;
868 	fStatus = fNode->GetStat(&fStatBuf);
869 	if (oldMode != fStatBuf.st_mode) {
870 		bool forWriting = IsNodeOpenForWriting();
871 		CloseNode();
872 		//SetupBaseType();
873 			// the node type can't change with a stat update...
874 		OpenNodeCommon(forWriting);
875 		return true;
876 	}
877 	return false;
878 }
879 
880 // Mime handling stuff
881 
882 bool
883 Model::IsDropTarget(const Model *forDocument, bool traverse) const
884 {
885 	switch (CanHandleDrops()) {
886 		case kCanHandle:
887 			return true;
888 
889 		case kCannotHandle:
890 			return false;
891 
892 		default:
893 			break;
894 	}
895 	if (!forDocument)
896 		return true;
897 
898 	if (traverse) {
899 		BEntry entry(forDocument->EntryRef(), true);
900 		if (entry.InitCheck() != B_OK)
901 			return false;
902 
903 		BFile file(&entry, O_RDONLY);
904 		BNodeInfo mime(&file);
905 
906 		if (mime.InitCheck() != B_OK)
907 			return false;
908 
909 		char mimeType[B_MIME_TYPE_LENGTH];
910 		mime.GetType(mimeType);
911 
912 		return SupportsMimeType(mimeType, 0) != kDoesNotSupportType;
913 	}
914 	// do some mime-based matching
915 	const char *documentMimeType = forDocument->MimeType();
916 	if (!documentMimeType)
917 		return false;
918 
919 	return SupportsMimeType(documentMimeType, 0) != kDoesNotSupportType;
920 }
921 
922 
923 Model::CanHandleResult
924 Model::CanHandleDrops() const
925 {
926 	if (IsDirectory())
927 		// directories take anything
928 		// resolve permissions here
929 		return kCanHandle;
930 
931 
932 	if (IsSymLink()) {
933 		// descend into symlink and try again on it's target
934 
935 		BEntry entry(&fEntryRef, true);
936 		if (entry.InitCheck() != B_OK)
937 			return kCannotHandle;
938 
939 		if (entry == BEntry(EntryRef()))
940 			// self-referencing link, avoid infinite recursion
941 			return kCannotHandle;
942 
943 		Model model(&entry);
944 		if (model.InitCheck() != B_OK)
945 			return kCannotHandle;
946 
947 		return model.CanHandleDrops();
948 	}
949 
950 	if (IsExecutable())
951 		return kNeedToCheckType;
952 
953 	return kCannotHandle;
954 }
955 
956 
957 inline bool
958 IsSuperHandlerSignature(const char *signature)
959 {
960 	return strcasecmp(signature, B_FILE_MIMETYPE) == 0;
961 }
962 
963 
964 enum {
965 	kDontMatch = 0,
966 	kMatchSupertype,
967 	kMatch
968 };
969 
970 static int32
971 MatchMimeTypeString(/*const */BString *documentType, const char *handlerType)
972 {
973 	// perform a mime type wildcard match
974 	// handler types of the form "text"
975 	// handle every handled type with same supertype,
976 	// for everything else a full string match is used
977 
978 	int32 supertypeOnlyLength = 0;
979 	const char *tmp = strstr(handlerType, "/");
980 
981 	if (!tmp)
982 		// no subtype - supertype string only
983 		supertypeOnlyLength = (int32)strlen(handlerType);
984 
985 	if (supertypeOnlyLength) {
986 		// compare just the supertype
987 		tmp = strstr(documentType->String(), "/");
988 		if (tmp && (tmp - documentType->String() == supertypeOnlyLength)) {
989 			if (documentType->ICompare(handlerType, supertypeOnlyLength) == 0)
990 				return kMatchSupertype;
991 			else
992 				return kDontMatch;
993 		}
994 	}
995 
996 	if (documentType->ICompare(handlerType) == 0)
997 		return kMatch;
998 
999 	return kDontMatch;
1000 }
1001 
1002 
1003 int32
1004 Model::SupportsMimeType(const char *type, const BObjectList<BString> *list,
1005 	bool exactReason) const
1006 {
1007 	ASSERT((type == 0) != (list == 0));
1008 		// pass in one or the other
1009 
1010 	int32 result = kDoesNotSupportType;
1011 
1012 	BFile file(EntryRef(), O_RDONLY);
1013 	BAppFileInfo handlerInfo(&file);
1014 
1015 	BMessage message;
1016 	if (handlerInfo.GetSupportedTypes(&message) != B_OK)
1017 		return kDoesNotSupportType;
1018 
1019 	for (int32 index = 0; ; index++) {
1020 
1021 		// check if this model lists the type of dropped document as supported
1022 		const char *mimeSignature;
1023 		int32 bufferLength;
1024 
1025 		if (message.FindData("types", 'CSTR', index, (const void **)&mimeSignature,
1026 			&bufferLength))
1027 			return result;
1028 
1029 		if (IsSuperHandlerSignature(mimeSignature)) {
1030 			if (!exactReason)
1031 				return kSuperhandlerModel;
1032 
1033 			if (result == kDoesNotSupportType)
1034 				result = kSuperhandlerModel;
1035 		}
1036 
1037 		int32 match;
1038 
1039 		if (type) {
1040 			BString typeString(type);
1041 			match = MatchMimeTypeString(&typeString, mimeSignature);
1042 		} else
1043 			match = WhileEachListItem(const_cast<BObjectList<BString> *>(list),
1044 				MatchMimeTypeString, mimeSignature);
1045 				// const_cast shouldnt be here, have to have it until MW cleans up
1046 
1047 		if (match == kMatch)
1048 			// supports the actual type, it can't get any better
1049 			return kModelSupportsType;
1050 		else if (match == kMatchSupertype) {
1051 			if (!exactReason)
1052 				return kModelSupportsSupertype;
1053 
1054 			// we already know this model supports the file as a supertype,
1055 			// now find out if it matches the type
1056 			result = kModelSupportsSupertype;
1057 		}
1058 	}
1059 
1060 	return result;
1061 }
1062 
1063 
1064 bool
1065 Model::IsDropTargetForList(const BObjectList<BString> *list) const
1066 {
1067 	switch (CanHandleDrops()) {
1068 		case kCanHandle:
1069 			return true;
1070 
1071 		case kCannotHandle:
1072 			return false;
1073 
1074 		default:
1075 			break;
1076 	}
1077 	return SupportsMimeType(0, list) != kDoesNotSupportType;
1078 }
1079 
1080 
1081 bool
1082 Model::IsSuperHandler() const
1083 {
1084 	ASSERT(CanHandleDrops() == kNeedToCheckType);
1085 
1086 	BFile file(EntryRef(), O_RDONLY);
1087 	BAppFileInfo handlerInfo(&file);
1088 
1089 	BMessage message;
1090 	if (handlerInfo.GetSupportedTypes(&message) != B_OK)
1091 		return false;
1092 
1093 	for (int32 index = 0; ; index++) {
1094 		const char *mimeSignature;
1095 		int32 bufferLength;
1096 
1097 		if (message.FindData("types", 'CSTR', index, (const void **)&mimeSignature,
1098 			&bufferLength))
1099 			return false;
1100 
1101 		if (IsSuperHandlerSignature(mimeSignature))
1102 			return true;
1103 	}
1104 	return false;
1105 }
1106 
1107 
1108 void
1109 Model::GetEntry(BEntry *entry) const
1110 {
1111 	entry->SetTo(EntryRef());
1112 }
1113 
1114 
1115 void
1116 Model::GetPath(BPath *path) const
1117 {
1118 	BEntry entry(EntryRef());
1119 	entry.GetPath(path);
1120 }
1121 
1122 
1123 bool
1124 Model::Mimeset(bool force)
1125 {
1126 	BString oldType = MimeType();
1127 	ModelNodeLazyOpener opener(this);
1128 	BPath path;
1129 	GetPath(&path);
1130 	if (force) {
1131 		if (opener.OpenNode(true) != B_OK)
1132 			return false;
1133 
1134 		Node()->RemoveAttr(kAttrMIMEType);
1135 		update_mime_info(path.Path(), 0, 1, 1);
1136 	} else
1137 		update_mime_info(path.Path(), 0, 1, 0);
1138 
1139 	AttrChanged(0);
1140 
1141 	return !oldType.ICompare(MimeType());
1142 }
1143 
1144 
1145 ssize_t
1146 Model::WriteAttr(const char *attr, type_code type, off_t offset,
1147 	const void *buffer, size_t length)
1148 {
1149 	BModelWriteOpener opener(this);
1150 	if (!fNode)
1151 		return 0;
1152 
1153 	ssize_t result = fNode->WriteAttr(attr, type, offset, buffer, length);
1154 	return result;
1155 }
1156 
1157 
1158 ssize_t
1159 Model::WriteAttrKillForegin(const char *attr, const char *foreignAttr,
1160 	type_code type, off_t offset, const void *buffer, size_t length)
1161 {
1162 	BModelWriteOpener opener(this);
1163 	if (!fNode)
1164 		return 0;
1165 
1166 	ssize_t result = fNode->WriteAttr(attr, type, offset, buffer, length);
1167 	if (result == (ssize_t)length)
1168 		// nuke attribute in opposite endianness
1169 		fNode->RemoveAttr(foreignAttr);
1170 	return result;
1171 }
1172 
1173 
1174 status_t
1175 Model::GetLongVersionString(BString &result, version_kind kind)
1176 {
1177 	BFile file(EntryRef(), O_RDONLY);
1178 	status_t error = file.InitCheck();
1179 	if (error != B_OK)
1180 		return error;
1181 
1182 	BAppFileInfo info(&file);
1183 	error = info.InitCheck();
1184 	if (error != B_OK)
1185 		return error;
1186 
1187 	version_info version;
1188 	error = info.GetVersionInfo(&version, kind);
1189 	if (error != B_OK)
1190 		return error;
1191 
1192 	result = version.long_info;
1193 	return B_OK;
1194 }
1195 
1196 status_t
1197 Model::GetVersionString(BString &result, version_kind kind)
1198 {
1199 	BFile file(EntryRef(), O_RDONLY);
1200 	status_t error = file.InitCheck();
1201 	if (error != B_OK)
1202 		return error;
1203 
1204 	BAppFileInfo info(&file);
1205 	error = info.InitCheck();
1206 	if (error != B_OK)
1207 		return error;
1208 
1209 	version_info version;
1210 	error = info.GetVersionInfo(&version, kind);
1211 	if (error != B_OK)
1212 		return error;
1213 
1214 	char vstr[32];
1215 	sprintf(vstr, "%ld.%ld.%ld", version.major, version.middle, version.minor);
1216 	result = vstr;
1217 	return B_OK;
1218 }
1219 
1220 #if DEBUG
1221 
1222 void
1223 Model::PrintToStream(int32 level, bool deep)
1224 {
1225 	PRINT(("model name %s, entry name %s, inode %Lx, dev %x, directory inode %Lx\n",
1226 		Name() ? Name() : "**empty name**",
1227 		EntryRef()->name ? EntryRef()->name : "**empty ref name**",
1228 		NodeRef()->node,
1229 		NodeRef()->device,
1230 		EntryRef()->directory));
1231 	PRINT(("type %s \n", MimeType()));
1232 
1233 	PRINT(("model type: "));
1234 	switch (fBaseType) {
1235 		case kPlainNode:
1236 			PRINT(("plain\n"));
1237 			break;
1238 
1239 		case kQueryNode:
1240 			PRINT(("query\n"));
1241 			break;
1242 
1243 		case kQueryTemplateNode:
1244 			PRINT(("query template\n"));
1245 			break;
1246 
1247 		case kExecutableNode:
1248 			PRINT(("exe\n"));
1249 			break;
1250 
1251 		case kDirectoryNode:
1252 			PRINT(("dir\n"));
1253 			break;
1254 
1255 		case kLinkNode:
1256 			PRINT(("link\n"));
1257 			break;
1258 
1259 		case kRootNode:
1260 			PRINT(("root\n"));
1261 			break;
1262 
1263 		case kVolumeNode:
1264 			PRINT(("volume, name %s\n", fVolumeName ? fVolumeName : ""));
1265 			break;
1266 
1267 		default:
1268 			PRINT(("unknown\n"));
1269 			break;
1270 	}
1271 
1272 	if (level < 1)
1273 		return;
1274 
1275 	if (!IsVolume())
1276 		PRINT(("preferred app %s\n", fPreferredAppName ? fPreferredAppName : ""));
1277 
1278 	PRINT(("icon from: "));
1279 	switch (IconFrom()) {
1280 		case kUnknownSource:
1281 			PRINT(("unknown\n"));
1282 			break;
1283 		case kUnknownNotFromNode:
1284 			PRINT(("unknown but not from a node\n"));
1285 			break;
1286 		case kTrackerDefault:
1287 			PRINT(("tracker default\n"));
1288 			break;
1289 		case kTrackerSupplied:
1290 			PRINT(("tracker supplied\n"));
1291 			break;
1292 		case kMetaMime:
1293 			PRINT(("metamime\n"));
1294 			break;
1295 		case kPreferredAppForType:
1296 			PRINT(("preferred app for type\n"));
1297 			break;
1298 		case kPreferredAppForNode:
1299 			PRINT(("preferred app for node\n"));
1300 			break;
1301 		case kNode:
1302 			PRINT(("node\n"));
1303 			break;
1304 		case kVolume:
1305 			PRINT(("volume\n"));
1306 			break;
1307 	}
1308 
1309 	PRINT(("model %s opened %s \n", !IsNodeOpen() ? "not " : "",
1310 		IsNodeOpenForWriting() ? "for writing" : ""));
1311 
1312 	if (IsNodeOpen()) {
1313 		node_ref nodeRef;
1314 		fNode->GetNodeRef(&nodeRef);
1315 		PRINT(("node ref of open Node %Lx %x\n", nodeRef.node, nodeRef.device));
1316 	}
1317 
1318 	if (deep && IsSymLink()) {
1319 		BEntry tmpEntry(EntryRef(), true);
1320 		Model tmp(&tmpEntry);
1321 		PRINT(("symlink to:\n"));
1322 		tmp.PrintToStream();
1323 	}
1324 	TrackIconSource(B_MINI_ICON);
1325 	TrackIconSource(B_LARGE_ICON);
1326 }
1327 
1328 
1329 void
1330 Model::TrackIconSource(icon_size size)
1331 {
1332 	PRINT(("tracking %s icon\n", size == B_LARGE_ICON ? "large" : "small"));
1333 	BRect rect;
1334 	if (size == B_MINI_ICON)
1335 		rect.Set(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1);
1336 	else
1337 		rect.Set(0, 0, B_LARGE_ICON - 1, B_LARGE_ICON - 1);
1338 
1339 	BBitmap bitmap(rect, B_CMAP8);
1340 
1341 	BModelOpener opener(this);
1342 
1343 	if (Node() == NULL) {
1344 		PRINT(("track icon error - no node\n"));
1345 		return;
1346 	}
1347 
1348 	if (IsSymLink()) {
1349 		PRINT(("tracking symlink icon\n"));
1350 		if (fLinkTo) {
1351 			fLinkTo->TrackIconSource(size);
1352 			return;
1353 		}
1354 	}
1355 
1356 	if (fBaseType == kVolumeNode) {
1357 		BVolume volume(NodeRef()->device);
1358 		status_t result = volume.GetIcon(&bitmap, size);
1359 		PRINT(("getting icon from volume %s\n", strerror(result)));
1360 	} else {
1361 		BNodeInfo nodeInfo(Node());
1362 
1363 		status_t err = nodeInfo.GetIcon(&bitmap, size);
1364 		if (err == B_OK) {
1365 			// file knew which icon to use, we are done
1366 			PRINT(("track icon - got icon from file\n"));
1367 			return;
1368 		}
1369 
1370 		char preferredApp[B_MIME_TYPE_LENGTH];
1371 		err = nodeInfo.GetPreferredApp(preferredApp);
1372 		if (err == B_OK && preferredApp[0]) {
1373 			BMimeType preferredAppType(preferredApp);
1374 			err = preferredAppType.GetIconForType(MimeType(), &bitmap, size);
1375 			if (err == B_OK) {
1376 				PRINT(("track icon - got icon for type %s from preferred app %s for file\n",
1377 					MimeType(), preferredApp));
1378 				return;
1379 			}
1380 		}
1381 
1382 		BMimeType mimeType(MimeType());
1383 		err = mimeType.GetIcon(&bitmap, size);
1384 		if (err == B_OK) {
1385 			// the system knew what icon to use for the type, we are done
1386 			PRINT(("track icon - signature %s, got icon from system\n",
1387 				MimeType()));
1388 			return;
1389 		}
1390 
1391 		err = mimeType.GetPreferredApp(preferredApp);
1392 		if (err != B_OK) {
1393 			// no preferred App for document, give up
1394 			PRINT(("track icon - signature %s, no prefered app, error %s\n",
1395 				MimeType(), strerror(err)));
1396 			return;
1397 		}
1398 
1399 		BMimeType preferredAppType(preferredApp);
1400 		err = preferredAppType.GetIconForType(MimeType(), &bitmap, size);
1401 		if (err == B_OK) {
1402 			// the preferred app knew icon to use for the type, we are done
1403 			PRINT(("track icon - signature %s, got icon from preferred app %s\n",
1404 				MimeType(), preferredApp));
1405 			return;
1406 		}
1407 		PRINT(("track icon - signature %s, preferred app %s, no icon, error %s\n",
1408 			MimeType(), preferredApp, strerror(err)));
1409 	}
1410 }
1411 
1412 #endif	// DEBUG
1413 
1414 #ifdef CHECK_OPEN_MODEL_LEAKS
1415 
1416 namespace BPrivate {
1417 #include <stdio.h>
1418 
1419 void
1420 DumpOpenModels(bool extensive)
1421 {
1422 	if (readOnlyOpenModelList) {
1423 		int32 count = readOnlyOpenModelList->CountItems();
1424 		printf("%ld models open read-only:\n", count);
1425 		printf("==========================\n");
1426 		for (int32 index = 0; index < count; index++) {
1427 			if (extensive) {
1428 				printf("---------------------------\n");
1429 				readOnlyOpenModelList->ItemAt(index)->PrintToStream();
1430 			} else
1431 				printf("%s\n", readOnlyOpenModelList->ItemAt(index)->Name());
1432 		}
1433 	}
1434 	if (writableOpenModelList) {
1435 		int32 count = writableOpenModelList->CountItems();
1436 		printf("%ld models open writable:\n", count);
1437 		printf("models open writable:\n");
1438 		printf("======================\n");
1439 		for (int32 index = 0; index < count; index++) {
1440 			if (extensive) {
1441 				printf("---------------------------\n");
1442 				writableOpenModelList->ItemAt(index)->PrintToStream();
1443 			} else
1444 				printf("%s\n", writableOpenModelList->ItemAt(index)->Name());
1445 		}
1446 	}
1447 }
1448 
1449 
1450 void
1451 InitOpenModelDumping()
1452 {
1453 	readOnlyOpenModelList = 0;
1454 	writableOpenModelList = 0;
1455 }
1456 
1457 }	// namespace BPrivate
1458 
1459 #endif	// CHECK_OPEN_MODEL_LEAKS
1460