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 static bool 86 CheckNodeIconHint(BNode* node) 87 { 88 if (node == NULL) 89 return false; 90 91 attr_info info; 92 if (node->GetAttrInfo(kAttrIcon, &info) == B_OK 93 // has a vector icon, or 94 || (node->GetAttrInfo(kAttrMiniIcon, &info) == B_OK 95 && node->GetAttrInfo(kAttrLargeIcon, &info) == B_OK)) { 96 // has a mini _and_ large icon 97 return true; 98 } 99 100 return false; 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 snooping to see if it even 618 // makes sense to look for a node-based icon. This serves as a hint to the 619 // icon cache, allowing it to not hit the disk again for models that do not 620 // have an icon defined by the node. 621 if (IsNodeOpen() && fBaseType != kLinkNode && !CheckNodeIconHint(fNode)) 622 fIconFrom = kUnknownNotFromNode; 623 624 if (fBaseType != kDirectoryNode 625 && fBaseType != kVolumeNode 626 && fBaseType != kLinkNode 627 && IsNodeOpen()) { 628 BNodeInfo info(fNode); 629 630 // check if a specific mime type is set 631 if (info.GetType(mimeString) == B_OK) { 632 // node has a specific mime type 633 fMimeType = mimeString; 634 if (strcmp(mimeString, B_QUERY_MIMETYPE) == 0) 635 fBaseType = kQueryNode; 636 else if (strcmp(mimeString, B_QUERY_TEMPLATE_MIMETYPE) == 0) 637 fBaseType = kQueryTemplateNode; 638 else if (strcmp(mimeString, kVirtualDirectoryMimeType) == 0) 639 fBaseType = kVirtualDirectoryNode; 640 641 if (info.GetPreferredApp(mimeString) == B_OK) { 642 if (fPreferredAppName) 643 DeletePreferredAppVolumeNameLinkTo(); 644 645 if (mimeString[0]) 646 fPreferredAppName = strdup(mimeString); 647 } 648 } 649 } 650 651 switch (fBaseType) { 652 case kDirectoryNode: 653 entry.SetTo(&fEntryRef); 654 if (entry.InitCheck() == B_OK) { 655 if (FSIsTrashDir(&entry)) 656 fBaseType = kTrashNode; 657 else if (FSIsDeskDir(&entry)) 658 fBaseType = kDesktopNode; 659 } 660 661 fMimeType = B_DIR_MIMETYPE; 662 // should use a shared string here 663 if (IsNodeOpen()) { 664 BNodeInfo info(fNode); 665 if (info.GetType(mimeString) == B_OK) 666 fMimeType = mimeString; 667 668 if (fIconFrom == kUnknownNotFromNode 669 && WellKnowEntryList::Match(NodeRef()) 670 > (directory_which)-1) { 671 // one of home, beos, system, boot, etc. 672 fIconFrom = kTrackerSupplied; 673 } 674 } 675 break; 676 677 case kVolumeNode: 678 { 679 if (NodeRef()->node == fEntryRef.directory 680 && NodeRef()->device == fEntryRef.device) { 681 // promote from volume to file system root 682 fBaseType = kRootNode; 683 fMimeType = B_ROOT_MIMETYPE; 684 break; 685 } 686 687 // volumes have to have a B_VOLUME_MIMETYPE type 688 fMimeType = B_VOLUME_MIMETYPE; 689 if (fIconFrom == kUnknownNotFromNode) { 690 if (WellKnowEntryList::Match(NodeRef()) > (directory_which)-1) 691 fIconFrom = kTrackerSupplied; 692 else 693 fIconFrom = kVolume; 694 } 695 696 char name[B_FILE_NAME_LENGTH]; 697 BVolume volume(NodeRef()->device); 698 if (volume.InitCheck() == B_OK && volume.GetName(name) == B_OK) { 699 if (fVolumeName != NULL) 700 DeletePreferredAppVolumeNameLinkTo(); 701 702 fVolumeName = strdup(name); 703 } 704 #if DEBUG 705 else 706 PRINT(("get volume name failed for %s\n", fEntryRef.name)); 707 #endif 708 break; 709 } 710 711 case kLinkNode: 712 fMimeType = B_LINK_MIMETYPE; 713 // should use a shared string here 714 break; 715 716 case kExecutableNode: 717 if (IsNodeOpen()) { 718 char signature[B_MIME_TYPE_LENGTH]; 719 if (GetAppSignatureFromAttr(dynamic_cast<BFile*>(fNode), 720 signature) == B_OK) { 721 if (fPreferredAppName) 722 DeletePreferredAppVolumeNameLinkTo(); 723 724 if (signature[0]) 725 fPreferredAppName = strdup(signature); 726 } 727 } 728 if (fMimeType.Length() <= 0) 729 fMimeType = B_APP_MIME_TYPE; 730 // should use a shared string here 731 break; 732 733 default: 734 if (fMimeType.Length() <= 0) 735 fMimeType = B_FILE_MIMETYPE; 736 break; 737 } 738 } 739 740 741 void 742 Model::ResetIconFrom() 743 { 744 BModelOpener opener(this); 745 746 if (InitCheck() != B_OK) 747 return; 748 749 // mirror the logic from FinishSettingUpType 750 if ((fBaseType == kDirectoryNode || fBaseType == kVolumeNode 751 || fBaseType == kTrashNode || fBaseType == kDesktopNode) 752 && !CheckNodeIconHint(fNode)) { 753 BDirectory* directory = dynamic_cast<BDirectory*>(fNode); 754 if (WellKnowEntryList::Match(NodeRef()) > (directory_which)-1) { 755 fIconFrom = kTrackerSupplied; 756 return; 757 } else if (directory != NULL && directory->IsRootDirectory()) { 758 fIconFrom = kVolume; 759 return; 760 } 761 } 762 fIconFrom = kUnknownSource; 763 } 764 765 766 const char* 767 Model::PreferredAppSignature() const 768 { 769 if (IsVolume() || IsSymLink()) 770 return ""; 771 772 return fPreferredAppName ? fPreferredAppName : ""; 773 } 774 775 776 void 777 Model::SetPreferredAppSignature(const char* signature) 778 { 779 ASSERT(!IsVolume() && !IsSymLink()); 780 ASSERT(signature != fPreferredAppName); 781 // self assignment should not be an option 782 783 free(fPreferredAppName); 784 if (signature) 785 fPreferredAppName = strdup(signature); 786 else 787 fPreferredAppName = NULL; 788 } 789 790 791 const Model* 792 Model::ResolveIfLink() const 793 { 794 if (!IsSymLink()) 795 return this; 796 797 if (!fLinkTo) 798 return this; 799 800 return fLinkTo; 801 } 802 803 804 Model* 805 Model::ResolveIfLink() 806 { 807 if (!IsSymLink()) 808 return this; 809 810 if (!fLinkTo) 811 return this; 812 813 return fLinkTo; 814 } 815 816 817 void 818 Model::SetLinkTo(Model* model) 819 { 820 ASSERT(IsSymLink()); 821 ASSERT(!fLinkTo || (fLinkTo != model)); 822 823 delete fLinkTo; 824 fLinkTo = model; 825 } 826 827 828 void 829 Model::GetPreferredAppForBrokenSymLink(BString &result) 830 { 831 if (!IsSymLink() || LinkTo()) { 832 result = ""; 833 return; 834 } 835 836 BModelOpener opener(this); 837 BNodeInfo info(fNode); 838 status_t error 839 = info.GetPreferredApp(result.LockBuffer(B_MIME_TYPE_LENGTH)); 840 result.UnlockBuffer(); 841 842 if (error != B_OK) { 843 // Tracker will have to do 844 result = kTrackerSignature; 845 } 846 } 847 848 849 // #pragma mark - Node monitor updating methods 850 851 852 void 853 Model::UpdateEntryRef(const node_ref* dirNode, const char* name) 854 { 855 if (IsVolume()) { 856 if (fVolumeName != NULL) 857 DeletePreferredAppVolumeNameLinkTo(); 858 859 fVolumeName = strdup(name); 860 } 861 862 fEntryRef.device = dirNode->device; 863 fEntryRef.directory = dirNode->node; 864 865 if (fEntryRef.name != NULL && strcmp(fEntryRef.name, name) == 0) 866 return; 867 868 fEntryRef.set_name(name); 869 } 870 871 872 status_t 873 Model::WatchVolumeAndMountPoint(uint32 , BHandler* target) 874 { 875 ASSERT(IsVolume()); 876 877 if (fEntryRef.name != NULL && fVolumeName != NULL 878 && strcmp(fEntryRef.name, "boot") == 0) { 879 // watch mount point for boot volume 880 BString bootMountPoint("/"); 881 bootMountPoint += fVolumeName; 882 BEntry mountPointEntry(bootMountPoint.String()); 883 Model mountPointModel(&mountPointEntry); 884 885 TTracker::WatchNode(mountPointModel.NodeRef(), 886 B_WATCH_NAME | B_WATCH_STAT | B_WATCH_ATTR, target); 887 } 888 889 return TTracker::WatchNode(NodeRef(), 890 B_WATCH_NAME | B_WATCH_STAT | B_WATCH_ATTR, target); 891 } 892 893 894 bool 895 Model::AttrChanged(const char* attrName) 896 { 897 // called on an attribute changed node monitor 898 // sync up cached values of mime type and preferred app and 899 // return true if icon needs updating 900 901 ASSERT(IsNodeOpen()); 902 if (attrName != NULL 903 && (strcmp(attrName, kAttrIcon) == 0 904 || strcmp(attrName, kAttrMiniIcon) == 0 905 || strcmp(attrName, kAttrLargeIcon) == 0)) { 906 return true; 907 } 908 909 if (attrName == NULL 910 || strcmp(attrName, kAttrMIMEType) == 0 911 || strcmp(attrName, kAttrPreferredApp) == 0) { 912 char mimeString[B_MIME_TYPE_LENGTH]; 913 BNodeInfo info(fNode); 914 if (info.GetType(mimeString) != B_OK) 915 fMimeType = ""; 916 else { 917 // node has a specific mime type 918 fMimeType = mimeString; 919 if (!IsVolume() && !IsSymLink() 920 && info.GetPreferredApp(mimeString) == B_OK) { 921 SetPreferredAppSignature(mimeString); 922 } 923 } 924 925 #if xDEBUG 926 if (fIconFrom != kNode) { 927 PRINT(("%s, %s:updating icon because file type changed\n", 928 Name(), attrName != NULL ? attrName : "")); 929 } else { 930 PRINT(("Not updating icon even though type changed " 931 "because icon is from node.\n")); 932 } 933 #endif 934 935 return fIconFrom != kNode; 936 // update icon unless it is coming from a node 937 } 938 939 return attrName == NULL; 940 } 941 942 943 bool 944 Model::StatChanged() 945 { 946 ASSERT(IsNodeOpen()); 947 mode_t oldMode = fStatBuf.st_mode; 948 fStatus = fNode->GetStat(&fStatBuf); 949 950 if (oldMode != fStatBuf.st_mode) { 951 bool forWriting = IsNodeOpenForWriting(); 952 CloseNode(); 953 //SetupBaseType(); 954 // the node type can't change with a stat update... 955 OpenNodeCommon(forWriting); 956 return true; 957 } 958 959 return false; 960 } 961 962 963 // #pragma mark - Mime handling methods 964 965 966 bool 967 Model::IsDropTarget(const Model* forDocument, bool traverse) const 968 { 969 switch (CanHandleDrops()) { 970 case kCanHandle: 971 return true; 972 973 case kCannotHandle: 974 return false; 975 976 default: 977 break; 978 } 979 980 if (forDocument == NULL) 981 return true; 982 983 if (traverse) { 984 BEntry entry(forDocument->EntryRef(), true); 985 if (entry.InitCheck() != B_OK) 986 return false; 987 988 BFile file(&entry, O_RDONLY); 989 BNodeInfo mime(&file); 990 991 if (mime.InitCheck() != B_OK) 992 return false; 993 994 char mimeType[B_MIME_TYPE_LENGTH]; 995 mime.GetType(mimeType); 996 997 return SupportsMimeType(mimeType, 0) != kDoesNotSupportType; 998 } 999 1000 // do some mime-based matching 1001 const char* documentMimeType = forDocument->MimeType(); 1002 if (documentMimeType == NULL) 1003 return false; 1004 1005 return SupportsMimeType(documentMimeType, 0) != kDoesNotSupportType; 1006 } 1007 1008 1009 Model::CanHandleResult 1010 Model::CanHandleDrops() const 1011 { 1012 if (IsDirectory()) { 1013 // directories take anything 1014 // resolve permissions here 1015 return kCanHandle; 1016 } 1017 1018 if (IsSymLink()) { 1019 // descend into symlink and try again on it's target 1020 1021 BEntry entry(&fEntryRef, true); 1022 if (entry.InitCheck() != B_OK) 1023 return kCannotHandle; 1024 1025 if (entry == BEntry(EntryRef())) 1026 // self-referencing link, avoid infinite recursion 1027 return kCannotHandle; 1028 1029 Model model(&entry); 1030 if (model.InitCheck() != B_OK) 1031 return kCannotHandle; 1032 1033 return model.CanHandleDrops(); 1034 } 1035 1036 if (IsExecutable()) 1037 return kNeedToCheckType; 1038 1039 return kCannotHandle; 1040 } 1041 1042 1043 inline bool 1044 IsSuperHandlerSignature(const char* signature) 1045 { 1046 return strcasecmp(signature, B_FILE_MIMETYPE) == 0; 1047 } 1048 1049 1050 enum { 1051 kDontMatch = 0, 1052 kMatchSupertype, 1053 kMatch 1054 }; 1055 1056 1057 static int32 1058 MatchMimeTypeString(/*const */BString* documentType, const char* handlerType) 1059 { 1060 // perform a mime type wildcard match 1061 // handler types of the form "text" 1062 // handle every handled type with same supertype, 1063 // for everything else a full string match is used 1064 1065 int32 supertypeOnlyLength = 0; 1066 const char* tmp = strstr(handlerType, "/"); 1067 1068 if (tmp == NULL) { 1069 // no subtype - supertype string only 1070 supertypeOnlyLength = (int32)strlen(handlerType); 1071 } 1072 1073 if (supertypeOnlyLength) { 1074 // compare just the supertype 1075 tmp = strstr(documentType->String(), "/"); 1076 if (tmp && (tmp - documentType->String() == supertypeOnlyLength)) { 1077 if (documentType->ICompare(handlerType, supertypeOnlyLength) == 0) 1078 return kMatchSupertype; 1079 else 1080 return kDontMatch; 1081 } 1082 } 1083 1084 if (documentType->ICompare(handlerType) == 0) 1085 return kMatch; 1086 1087 return kDontMatch; 1088 } 1089 1090 1091 int32 1092 Model::SupportsMimeType(const char* type, const BObjectList<BString>* list, 1093 bool exactReason) const 1094 { 1095 ASSERT((type == 0) != (list == 0)); 1096 // pass in one or the other 1097 1098 int32 result = kDoesNotSupportType; 1099 1100 BFile file(EntryRef(), O_RDONLY); 1101 BAppFileInfo handlerInfo(&file); 1102 1103 BMessage message; 1104 if (handlerInfo.GetSupportedTypes(&message) != B_OK) 1105 return kDoesNotSupportType; 1106 1107 for (int32 index = 0; ; index++) { 1108 // check if this model lists the type of dropped document as supported 1109 1110 const char* mimeSignature; 1111 ssize_t bufferLength; 1112 1113 if (message.FindData("types", 'CSTR', index, 1114 (const void**)&mimeSignature, &bufferLength)) { 1115 return result; 1116 } 1117 1118 if (IsSuperHandlerSignature(mimeSignature)) { 1119 if (!exactReason) 1120 return kSuperhandlerModel; 1121 1122 if (result == kDoesNotSupportType) 1123 result = kSuperhandlerModel; 1124 } 1125 1126 int32 match; 1127 1128 if (type != NULL || (list != NULL && list->IsEmpty())) { 1129 BString typeString(type); 1130 match = MatchMimeTypeString(&typeString, mimeSignature); 1131 } else { 1132 match = WhileEachListItem(const_cast<BObjectList<BString>*>(list), 1133 MatchMimeTypeString, mimeSignature); 1134 // const_cast shouldnt be here, have to have it until 1135 // MW cleans up 1136 } 1137 if (match == kMatch) 1138 // supports the actual type, it can't get any better 1139 return kModelSupportsType; 1140 else if (match == kMatchSupertype) { 1141 if (!exactReason) 1142 return kModelSupportsSupertype; 1143 1144 // we already know this model supports the file as a supertype, 1145 // now find out if it matches the type 1146 result = kModelSupportsSupertype; 1147 } 1148 } 1149 1150 return result; 1151 } 1152 1153 1154 bool 1155 Model::IsDropTargetForList(const BObjectList<BString>* list) const 1156 { 1157 switch (CanHandleDrops()) { 1158 case kCanHandle: 1159 return true; 1160 1161 case kCannotHandle: 1162 return false; 1163 1164 default: 1165 break; 1166 } 1167 1168 return SupportsMimeType(0, list) != kDoesNotSupportType; 1169 } 1170 1171 1172 bool 1173 Model::IsSuperHandler() const 1174 { 1175 ASSERT(CanHandleDrops() == kNeedToCheckType); 1176 1177 BFile file(EntryRef(), O_RDONLY); 1178 BAppFileInfo handlerInfo(&file); 1179 1180 BMessage message; 1181 if (handlerInfo.GetSupportedTypes(&message) != B_OK) 1182 return false; 1183 1184 for (int32 index = 0; ; index++) { 1185 const char* mimeSignature; 1186 ssize_t bufferLength; 1187 1188 if (message.FindData("types", 'CSTR', index, 1189 (const void**)&mimeSignature, &bufferLength)) { 1190 return false; 1191 } 1192 1193 if (IsSuperHandlerSignature(mimeSignature)) 1194 return true; 1195 } 1196 return false; 1197 } 1198 1199 1200 void 1201 Model::GetEntry(BEntry* entry) const 1202 { 1203 entry->SetTo(EntryRef()); 1204 } 1205 1206 1207 void 1208 Model::GetPath(BPath* path) const 1209 { 1210 BEntry entry(EntryRef()); 1211 entry.GetPath(path); 1212 } 1213 1214 1215 bool 1216 Model::Mimeset(bool force) 1217 { 1218 BString oldType = MimeType(); 1219 BPath path; 1220 GetPath(&path); 1221 1222 update_mime_info(path.Path(), 0, 1, force ? 2 : 0); 1223 ModelNodeLazyOpener opener(this); 1224 opener.OpenNode(); 1225 AttrChanged(NULL); 1226 1227 return !oldType.ICompare(MimeType()); 1228 } 1229 1230 1231 ssize_t 1232 Model::WriteAttr(const char* attr, type_code type, off_t offset, 1233 const void* buffer, size_t length) 1234 { 1235 BModelWriteOpener opener(this); 1236 if (!fNode) 1237 return 0; 1238 1239 ssize_t result = fNode->WriteAttr(attr, type, offset, buffer, length); 1240 return result; 1241 } 1242 1243 1244 ssize_t 1245 Model::WriteAttrKillForeign(const char* attr, const char* foreignAttr, 1246 type_code type, off_t offset, const void* buffer, size_t length) 1247 { 1248 BModelWriteOpener opener(this); 1249 if (!fNode) 1250 return 0; 1251 1252 ssize_t result = fNode->WriteAttr(attr, type, offset, buffer, length); 1253 if (result == (ssize_t)length) 1254 // nuke attribute in opposite endianness 1255 fNode->RemoveAttr(foreignAttr); 1256 return result; 1257 } 1258 1259 1260 status_t 1261 Model::GetLongVersionString(BString &result, version_kind kind) 1262 { 1263 BFile file(EntryRef(), O_RDONLY); 1264 status_t error = file.InitCheck(); 1265 if (error != B_OK) 1266 return error; 1267 1268 BAppFileInfo info(&file); 1269 error = info.InitCheck(); 1270 if (error != B_OK) 1271 return error; 1272 1273 version_info version; 1274 error = info.GetVersionInfo(&version, kind); 1275 if (error != B_OK) 1276 return error; 1277 1278 result = version.long_info; 1279 return B_OK; 1280 } 1281 1282 status_t 1283 Model::GetVersionString(BString &result, version_kind kind) 1284 { 1285 BFile file(EntryRef(), O_RDONLY); 1286 status_t error = file.InitCheck(); 1287 if (error != B_OK) 1288 return error; 1289 1290 BAppFileInfo info(&file); 1291 error = info.InitCheck(); 1292 if (error != B_OK) 1293 return error; 1294 1295 version_info version; 1296 error = info.GetVersionInfo(&version, kind); 1297 if (error != B_OK) 1298 return error; 1299 1300 result.SetToFormat("%" B_PRId32 ".%" B_PRId32 ".%" B_PRId32, version.major, 1301 version.middle, version.minor); 1302 1303 return B_OK; 1304 } 1305 1306 #if DEBUG 1307 1308 void 1309 Model::PrintToStream(int32 level, bool deep) 1310 { 1311 PRINT(("model name %s, entry name %s, inode %" B_PRIdINO ", dev %" 1312 B_PRIdDEV ", directory inode %" B_PRIdINO "\n", 1313 Name() ? Name() : "**empty name**", 1314 EntryRef()->name ? EntryRef()->name : "**empty ref name**", 1315 NodeRef()->node, 1316 NodeRef()->device, 1317 EntryRef()->directory)); 1318 PRINT(("type %s \n", MimeType())); 1319 1320 PRINT(("model type: ")); 1321 switch (fBaseType) { 1322 case kPlainNode: 1323 PRINT(("plain\n")); 1324 break; 1325 1326 case kQueryNode: 1327 PRINT(("query\n")); 1328 break; 1329 1330 case kQueryTemplateNode: 1331 PRINT(("query template\n")); 1332 break; 1333 1334 case kExecutableNode: 1335 PRINT(("exe\n")); 1336 break; 1337 1338 case kDirectoryNode: 1339 case kTrashNode: 1340 case kDesktopNode: 1341 PRINT(("dir\n")); 1342 break; 1343 1344 case kLinkNode: 1345 PRINT(("link\n")); 1346 break; 1347 1348 case kRootNode: 1349 PRINT(("root\n")); 1350 break; 1351 1352 case kVolumeNode: 1353 PRINT(("volume, name %s\n", fVolumeName ? fVolumeName : "")); 1354 break; 1355 1356 case kVirtualDirectoryNode: 1357 PRINT(("virtual directory\n")); 1358 break; 1359 1360 default: 1361 PRINT(("unknown\n")); 1362 break; 1363 } 1364 1365 if (level < 1) 1366 return; 1367 1368 if (!IsVolume()) { 1369 PRINT(("preferred app %s\n", 1370 fPreferredAppName ? fPreferredAppName : "")); 1371 } 1372 1373 PRINT(("icon from: ")); 1374 switch (IconFrom()) { 1375 case kUnknownSource: 1376 PRINT(("unknown\n")); 1377 break; 1378 1379 case kUnknownNotFromNode: 1380 PRINT(("unknown but not from a node\n")); 1381 break; 1382 1383 case kTrackerDefault: 1384 PRINT(("tracker default\n")); 1385 break; 1386 1387 case kTrackerSupplied: 1388 PRINT(("tracker supplied\n")); 1389 break; 1390 1391 case kMetaMime: 1392 PRINT(("metamime\n")); 1393 break; 1394 1395 case kPreferredAppForType: 1396 PRINT(("preferred app for type\n")); 1397 break; 1398 1399 case kPreferredAppForNode: 1400 PRINT(("preferred app for node\n")); 1401 break; 1402 1403 case kNode: 1404 PRINT(("node\n")); 1405 break; 1406 1407 case kVolume: 1408 PRINT(("volume\n")); 1409 break; 1410 1411 default: 1412 break; 1413 } 1414 1415 PRINT(("model %s opened %s \n", !IsNodeOpen() ? "not " : "", 1416 IsNodeOpenForWriting() ? "for writing" : "")); 1417 1418 if (IsNodeOpen()) { 1419 node_ref nodeRef; 1420 fNode->GetNodeRef(&nodeRef); 1421 PRINT(("node ref of open Node %" B_PRIdINO " %" B_PRIdDEV "\n", 1422 nodeRef.node, nodeRef.device)); 1423 } 1424 1425 if (deep && IsSymLink()) { 1426 BEntry tmpEntry(EntryRef(), true); 1427 Model tmp(&tmpEntry); 1428 PRINT(("symlink to:\n")); 1429 tmp.PrintToStream(); 1430 } 1431 TrackIconSource(B_MINI_ICON); 1432 TrackIconSource(B_LARGE_ICON); 1433 } 1434 1435 1436 void 1437 Model::TrackIconSource(icon_size size) 1438 { 1439 PRINT(("tracking %s icon\n", size == B_LARGE_ICON ? "large" : "small")); 1440 BRect rect; 1441 if (size == B_MINI_ICON) 1442 rect.Set(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1); 1443 else 1444 rect.Set(0, 0, B_LARGE_ICON - 1, B_LARGE_ICON - 1); 1445 1446 BBitmap bitmap(rect, B_CMAP8); 1447 1448 BModelOpener opener(this); 1449 1450 if (Node() == NULL) { 1451 PRINT(("track icon error - no node\n")); 1452 return; 1453 } 1454 1455 if (IsSymLink()) { 1456 PRINT(("tracking symlink icon\n")); 1457 if (fLinkTo) { 1458 fLinkTo->TrackIconSource(size); 1459 return; 1460 } 1461 } 1462 1463 if (fBaseType == kVolumeNode) { 1464 BVolume volume(NodeRef()->device); 1465 status_t result = volume.GetIcon(&bitmap, size); 1466 PRINT(("getting icon from volume %s\n", strerror(result))); 1467 } else { 1468 BNodeInfo nodeInfo(Node()); 1469 1470 status_t err = nodeInfo.GetIcon(&bitmap, size); 1471 if (err == B_OK) { 1472 // file knew which icon to use, we are done 1473 PRINT(("track icon - got icon from file\n")); 1474 return; 1475 } 1476 1477 char preferredApp[B_MIME_TYPE_LENGTH]; 1478 err = nodeInfo.GetPreferredApp(preferredApp); 1479 if (err == B_OK && preferredApp[0]) { 1480 BMimeType preferredAppType(preferredApp); 1481 err = preferredAppType.GetIconForType(MimeType(), &bitmap, size); 1482 if (err == B_OK) { 1483 PRINT( 1484 ("track icon - got icon for type %s from preferred " 1485 "app %s for file\n", MimeType(), preferredApp)); 1486 return; 1487 } 1488 } 1489 1490 BMimeType mimeType(MimeType()); 1491 err = mimeType.GetIcon(&bitmap, size); 1492 if (err == B_OK) { 1493 // the system knew what icon to use for the type, we are done 1494 PRINT(("track icon - signature %s, got icon from system\n", 1495 MimeType())); 1496 return; 1497 } 1498 1499 err = mimeType.GetPreferredApp(preferredApp); 1500 if (err != B_OK) { 1501 // no preferred App for document, give up 1502 PRINT(("track icon - signature %s, no prefered app, error %s\n", 1503 MimeType(), strerror(err))); 1504 return; 1505 } 1506 1507 BMimeType preferredAppType(preferredApp); 1508 err = preferredAppType.GetIconForType(MimeType(), &bitmap, size); 1509 if (err == B_OK) { 1510 // the preferred app knew icon to use for the type, we are done 1511 PRINT( 1512 ("track icon - signature %s, got icon from preferred " 1513 "app %s\n", MimeType(), preferredApp)); 1514 return; 1515 } 1516 PRINT( 1517 ("track icon - signature %s, preferred app %s, no icon, " 1518 "error %s\n", MimeType(), preferredApp, strerror(err))); 1519 } 1520 } 1521 1522 #endif // DEBUG 1523 1524 #ifdef CHECK_OPEN_MODEL_LEAKS 1525 1526 namespace BPrivate { 1527 1528 #include <stdio.h> 1529 1530 void 1531 DumpOpenModels(bool extensive) 1532 { 1533 if (readOnlyOpenModelList) { 1534 int32 count = readOnlyOpenModelList->CountItems(); 1535 printf("%ld models open read-only:\n", count); 1536 printf("==========================\n"); 1537 for (int32 index = 0; index < count; index++) { 1538 if (extensive) { 1539 printf("---------------------------\n"); 1540 readOnlyOpenModelList->ItemAt(index)->PrintToStream(); 1541 } else 1542 printf("%s\n", readOnlyOpenModelList->ItemAt(index)->Name()); 1543 } 1544 } 1545 1546 if (writableOpenModelList) { 1547 int32 count = writableOpenModelList->CountItems(); 1548 printf("%ld models open writable:\n", count); 1549 printf("models open writable:\n"); 1550 printf("======================\n"); 1551 for (int32 index = 0; index < count; index++) { 1552 if (extensive) { 1553 printf("---------------------------\n"); 1554 writableOpenModelList->ItemAt(index)->PrintToStream(); 1555 } else 1556 printf("%s\n", writableOpenModelList->ItemAt(index)->Name()); 1557 } 1558 } 1559 } 1560 1561 1562 void 1563 InitOpenModelDumping() 1564 { 1565 readOnlyOpenModelList = 0; 1566 writableOpenModelList = 0; 1567 } 1568 1569 } // namespace BPrivate 1570 1571 #endif // CHECK_OPEN_MODEL_LEAKS 1572