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