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