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