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 // #pragma mark - Node monitor updating methods 829 830 831 void 832 Model::UpdateEntryRef(const node_ref* dirNode, const char* name) 833 { 834 if (IsVolume()) { 835 if (fVolumeName != NULL) 836 DeletePreferredAppVolumeNameLinkTo(); 837 838 fVolumeName = strdup(name); 839 } 840 841 fEntryRef.device = dirNode->device; 842 fEntryRef.directory = dirNode->node; 843 844 if (fEntryRef.name != NULL && strcmp(fEntryRef.name, name) == 0) 845 return; 846 847 fEntryRef.set_name(name); 848 } 849 850 851 status_t 852 Model::WatchVolumeAndMountPoint(uint32 , BHandler* target) 853 { 854 ASSERT(IsVolume()); 855 856 if (fEntryRef.name != NULL && fVolumeName != NULL 857 && strcmp(fEntryRef.name, "boot") == 0) { 858 // watch mount point for boot volume 859 BString bootMountPoint("/"); 860 bootMountPoint += fVolumeName; 861 BEntry mountPointEntry(bootMountPoint.String()); 862 Model mountPointModel(&mountPointEntry); 863 864 TTracker::WatchNode(mountPointModel.NodeRef(), 865 B_WATCH_NAME | B_WATCH_STAT | B_WATCH_ATTR, target); 866 } 867 868 return TTracker::WatchNode(NodeRef(), 869 B_WATCH_NAME | B_WATCH_STAT | B_WATCH_ATTR, target); 870 } 871 872 873 bool 874 Model::AttrChanged(const char* attrName) 875 { 876 // called on an attribute changed node monitor 877 // sync up cached values of mime type and preferred app and 878 // return true if icon needs updating 879 880 ASSERT(IsNodeOpen()); 881 if (attrName != NULL 882 && (strcmp(attrName, kAttrIcon) == 0 883 || strcmp(attrName, kAttrMiniIcon) == 0 884 || strcmp(attrName, kAttrLargeIcon) == 0)) { 885 return true; 886 } 887 888 if (attrName == NULL 889 || strcmp(attrName, kAttrMIMEType) == 0 890 || strcmp(attrName, kAttrPreferredApp) == 0) { 891 char mimeString[B_MIME_TYPE_LENGTH]; 892 BNodeInfo info(fNode); 893 if (info.GetType(mimeString) != B_OK) 894 fMimeType = ""; 895 else { 896 // node has a specific mime type 897 fMimeType = mimeString; 898 if (!IsVolume() && !IsSymLink() 899 && info.GetPreferredApp(mimeString) == B_OK) { 900 SetPreferredAppSignature(mimeString); 901 } 902 } 903 904 #if xDEBUG 905 if (fIconFrom != kNode) { 906 PRINT(("%s, %s:updating icon because file type changed\n", 907 Name(), attrName != NULL ? attrName : "")); 908 } else { 909 PRINT(("Not updating icon even though type changed " 910 "because icon is from node.\n")); 911 } 912 #endif 913 914 return fIconFrom != kNode; 915 // update icon unless it is coming from a node 916 } 917 918 return attrName == NULL; 919 } 920 921 922 bool 923 Model::StatChanged() 924 { 925 ASSERT(IsNodeOpen()); 926 mode_t oldMode = fStatBuf.st_mode; 927 fStatus = fNode->GetStat(&fStatBuf); 928 929 if (oldMode != fStatBuf.st_mode) { 930 bool forWriting = IsNodeOpenForWriting(); 931 CloseNode(); 932 //SetupBaseType(); 933 // the node type can't change with a stat update... 934 OpenNodeCommon(forWriting); 935 return true; 936 } 937 938 return false; 939 } 940 941 942 // #pragma mark - Mime handling methods 943 944 945 bool 946 Model::IsDropTarget(const Model* forDocument, bool traverse) const 947 { 948 switch (CanHandleDrops()) { 949 case kCanHandle: 950 return true; 951 952 case kCannotHandle: 953 return false; 954 955 default: 956 break; 957 } 958 959 if (forDocument == NULL) 960 return true; 961 962 if (traverse) { 963 BEntry entry(forDocument->EntryRef(), true); 964 if (entry.InitCheck() != B_OK) 965 return false; 966 967 BFile file(&entry, O_RDONLY); 968 BNodeInfo mime(&file); 969 970 if (mime.InitCheck() != B_OK) 971 return false; 972 973 char mimeType[B_MIME_TYPE_LENGTH]; 974 mime.GetType(mimeType); 975 976 return SupportsMimeType(mimeType, 0) != kDoesNotSupportType; 977 } 978 979 // do some mime-based matching 980 const char* documentMimeType = forDocument->MimeType(); 981 if (documentMimeType == NULL) 982 return false; 983 984 return SupportsMimeType(documentMimeType, 0) != kDoesNotSupportType; 985 } 986 987 988 Model::CanHandleResult 989 Model::CanHandleDrops() const 990 { 991 if (IsDirectory()) { 992 // directories take anything 993 // resolve permissions here 994 return kCanHandle; 995 } 996 997 if (IsSymLink()) { 998 // descend into symlink and try again on it's target 999 1000 BEntry entry(&fEntryRef, true); 1001 if (entry.InitCheck() != B_OK) 1002 return kCannotHandle; 1003 1004 if (entry == BEntry(EntryRef())) 1005 // self-referencing link, avoid infinite recursion 1006 return kCannotHandle; 1007 1008 Model model(&entry); 1009 if (model.InitCheck() != B_OK) 1010 return kCannotHandle; 1011 1012 return model.CanHandleDrops(); 1013 } 1014 1015 if (IsExecutable()) 1016 return kNeedToCheckType; 1017 1018 return kCannotHandle; 1019 } 1020 1021 1022 inline bool 1023 IsSuperHandlerSignature(const char* signature) 1024 { 1025 return strcasecmp(signature, B_FILE_MIMETYPE) == 0; 1026 } 1027 1028 1029 enum { 1030 kDontMatch = 0, 1031 kMatchSupertype, 1032 kMatch 1033 }; 1034 1035 1036 static int32 1037 MatchMimeTypeString(/*const */BString* documentType, const char* handlerType) 1038 { 1039 // perform a mime type wildcard match 1040 // handler types of the form "text" 1041 // handle every handled type with same supertype, 1042 // for everything else a full string match is used 1043 1044 int32 supertypeOnlyLength = 0; 1045 const char* tmp = strstr(handlerType, "/"); 1046 1047 if (tmp == NULL) { 1048 // no subtype - supertype string only 1049 supertypeOnlyLength = (int32)strlen(handlerType); 1050 } 1051 1052 if (supertypeOnlyLength) { 1053 // compare just the supertype 1054 tmp = strstr(documentType->String(), "/"); 1055 if (tmp && (tmp - documentType->String() == supertypeOnlyLength)) { 1056 if (documentType->ICompare(handlerType, supertypeOnlyLength) == 0) 1057 return kMatchSupertype; 1058 else 1059 return kDontMatch; 1060 } 1061 } 1062 1063 if (documentType->ICompare(handlerType) == 0) 1064 return kMatch; 1065 1066 return kDontMatch; 1067 } 1068 1069 1070 int32 1071 Model::SupportsMimeType(const char* type, const BObjectList<BString>* list, 1072 bool exactReason) const 1073 { 1074 ASSERT((type == 0) != (list == 0)); 1075 // pass in one or the other 1076 1077 int32 result = kDoesNotSupportType; 1078 1079 BFile file(EntryRef(), O_RDONLY); 1080 BAppFileInfo handlerInfo(&file); 1081 1082 BMessage message; 1083 if (handlerInfo.GetSupportedTypes(&message) != B_OK) 1084 return kDoesNotSupportType; 1085 1086 for (int32 index = 0; ; index++) { 1087 // check if this model lists the type of dropped document as supported 1088 1089 const char* mimeSignature; 1090 ssize_t bufferLength; 1091 1092 if (message.FindData("types", 'CSTR', index, 1093 (const void**)&mimeSignature, &bufferLength)) { 1094 return result; 1095 } 1096 1097 if (IsSuperHandlerSignature(mimeSignature)) { 1098 if (!exactReason) 1099 return kSuperhandlerModel; 1100 1101 if (result == kDoesNotSupportType) 1102 result = kSuperhandlerModel; 1103 } 1104 1105 int32 match; 1106 1107 if (type != NULL || (list != NULL && list->IsEmpty())) { 1108 BString typeString(type); 1109 match = MatchMimeTypeString(&typeString, mimeSignature); 1110 } else { 1111 match = WhileEachListItem(const_cast<BObjectList<BString>*>(list), 1112 MatchMimeTypeString, mimeSignature); 1113 // const_cast shouldnt be here, have to have it until 1114 // MW cleans up 1115 } 1116 if (match == kMatch) 1117 // supports the actual type, it can't get any better 1118 return kModelSupportsType; 1119 else if (match == kMatchSupertype) { 1120 if (!exactReason) 1121 return kModelSupportsSupertype; 1122 1123 // we already know this model supports the file as a supertype, 1124 // now find out if it matches the type 1125 result = kModelSupportsSupertype; 1126 } 1127 } 1128 1129 return result; 1130 } 1131 1132 1133 bool 1134 Model::IsDropTargetForList(const BObjectList<BString>* list) const 1135 { 1136 switch (CanHandleDrops()) { 1137 case kCanHandle: 1138 return true; 1139 1140 case kCannotHandle: 1141 return false; 1142 1143 default: 1144 break; 1145 } 1146 1147 return SupportsMimeType(0, list) != kDoesNotSupportType; 1148 } 1149 1150 1151 bool 1152 Model::IsSuperHandler() const 1153 { 1154 ASSERT(CanHandleDrops() == kNeedToCheckType); 1155 1156 BFile file(EntryRef(), O_RDONLY); 1157 BAppFileInfo handlerInfo(&file); 1158 1159 BMessage message; 1160 if (handlerInfo.GetSupportedTypes(&message) != B_OK) 1161 return false; 1162 1163 for (int32 index = 0; ; index++) { 1164 const char* mimeSignature; 1165 ssize_t bufferLength; 1166 1167 if (message.FindData("types", 'CSTR', index, 1168 (const void**)&mimeSignature, &bufferLength)) { 1169 return false; 1170 } 1171 1172 if (IsSuperHandlerSignature(mimeSignature)) 1173 return true; 1174 } 1175 return false; 1176 } 1177 1178 1179 void 1180 Model::GetEntry(BEntry* entry) const 1181 { 1182 entry->SetTo(EntryRef()); 1183 } 1184 1185 1186 void 1187 Model::GetPath(BPath* path) const 1188 { 1189 BEntry entry(EntryRef()); 1190 entry.GetPath(path); 1191 } 1192 1193 1194 bool 1195 Model::Mimeset(bool force) 1196 { 1197 BString oldType = MimeType(); 1198 BPath path; 1199 GetPath(&path); 1200 1201 update_mime_info(path.Path(), 0, 1, force ? 2 : 0); 1202 ModelNodeLazyOpener opener(this); 1203 opener.OpenNode(); 1204 AttrChanged(NULL); 1205 1206 return !oldType.ICompare(MimeType()); 1207 } 1208 1209 1210 ssize_t 1211 Model::WriteAttr(const char* attr, type_code type, off_t offset, 1212 const void* buffer, size_t length) 1213 { 1214 BModelWriteOpener opener(this); 1215 if (!fNode) 1216 return 0; 1217 1218 ssize_t result = fNode->WriteAttr(attr, type, offset, buffer, length); 1219 return result; 1220 } 1221 1222 1223 ssize_t 1224 Model::WriteAttrKillForeign(const char* attr, const char* foreignAttr, 1225 type_code type, off_t offset, const void* buffer, size_t length) 1226 { 1227 BModelWriteOpener opener(this); 1228 if (!fNode) 1229 return 0; 1230 1231 ssize_t result = fNode->WriteAttr(attr, type, offset, buffer, length); 1232 if (result == (ssize_t)length) 1233 // nuke attribute in opposite endianness 1234 fNode->RemoveAttr(foreignAttr); 1235 return result; 1236 } 1237 1238 1239 status_t 1240 Model::GetLongVersionString(BString &result, version_kind kind) 1241 { 1242 BFile file(EntryRef(), O_RDONLY); 1243 status_t error = file.InitCheck(); 1244 if (error != B_OK) 1245 return error; 1246 1247 BAppFileInfo info(&file); 1248 error = info.InitCheck(); 1249 if (error != B_OK) 1250 return error; 1251 1252 version_info version; 1253 error = info.GetVersionInfo(&version, kind); 1254 if (error != B_OK) 1255 return error; 1256 1257 result = version.long_info; 1258 return B_OK; 1259 } 1260 1261 status_t 1262 Model::GetVersionString(BString &result, version_kind kind) 1263 { 1264 BFile file(EntryRef(), O_RDONLY); 1265 status_t error = file.InitCheck(); 1266 if (error != B_OK) 1267 return error; 1268 1269 BAppFileInfo info(&file); 1270 error = info.InitCheck(); 1271 if (error != B_OK) 1272 return error; 1273 1274 version_info version; 1275 error = info.GetVersionInfo(&version, kind); 1276 if (error != B_OK) 1277 return error; 1278 1279 result.SetToFormat("%" B_PRId32 ".%" B_PRId32 ".%" B_PRId32, version.major, 1280 version.middle, version.minor); 1281 1282 return B_OK; 1283 } 1284 1285 #if DEBUG 1286 1287 void 1288 Model::PrintToStream(int32 level, bool deep) 1289 { 1290 PRINT(("model name %s, entry name %s, inode %" B_PRIdINO ", dev %" 1291 B_PRIdDEV ", directory inode %" B_PRIdINO "\n", 1292 Name() ? Name() : "**empty name**", 1293 EntryRef()->name ? EntryRef()->name : "**empty ref name**", 1294 NodeRef()->node, 1295 NodeRef()->device, 1296 EntryRef()->directory)); 1297 PRINT(("type %s \n", MimeType())); 1298 1299 PRINT(("model type: ")); 1300 switch (fBaseType) { 1301 case kPlainNode: 1302 PRINT(("plain\n")); 1303 break; 1304 1305 case kQueryNode: 1306 PRINT(("query\n")); 1307 break; 1308 1309 case kQueryTemplateNode: 1310 PRINT(("query template\n")); 1311 break; 1312 1313 case kExecutableNode: 1314 PRINT(("exe\n")); 1315 break; 1316 1317 case kDirectoryNode: 1318 case kTrashNode: 1319 case kDesktopNode: 1320 PRINT(("dir\n")); 1321 break; 1322 1323 case kLinkNode: 1324 PRINT(("link\n")); 1325 break; 1326 1327 case kRootNode: 1328 PRINT(("root\n")); 1329 break; 1330 1331 case kVolumeNode: 1332 PRINT(("volume, name %s\n", fVolumeName ? fVolumeName : "")); 1333 break; 1334 1335 case kVirtualDirectoryNode: 1336 PRINT(("virtual directory\n")); 1337 break; 1338 1339 default: 1340 PRINT(("unknown\n")); 1341 break; 1342 } 1343 1344 if (level < 1) 1345 return; 1346 1347 if (!IsVolume()) { 1348 PRINT(("preferred app %s\n", 1349 fPreferredAppName ? fPreferredAppName : "")); 1350 } 1351 1352 PRINT(("icon from: ")); 1353 switch (IconFrom()) { 1354 case kUnknownSource: 1355 PRINT(("unknown\n")); 1356 break; 1357 1358 case kUnknownNotFromNode: 1359 PRINT(("unknown but not from a node\n")); 1360 break; 1361 1362 case kTrackerDefault: 1363 PRINT(("tracker default\n")); 1364 break; 1365 1366 case kTrackerSupplied: 1367 PRINT(("tracker supplied\n")); 1368 break; 1369 1370 case kMetaMime: 1371 PRINT(("metamime\n")); 1372 break; 1373 1374 case kPreferredAppForType: 1375 PRINT(("preferred app for type\n")); 1376 break; 1377 1378 case kPreferredAppForNode: 1379 PRINT(("preferred app for node\n")); 1380 break; 1381 1382 case kNode: 1383 PRINT(("node\n")); 1384 break; 1385 1386 case kVolume: 1387 PRINT(("volume\n")); 1388 break; 1389 1390 default: 1391 break; 1392 } 1393 1394 PRINT(("model %s opened %s \n", !IsNodeOpen() ? "not " : "", 1395 IsNodeOpenForWriting() ? "for writing" : "")); 1396 1397 if (IsNodeOpen()) { 1398 node_ref nodeRef; 1399 fNode->GetNodeRef(&nodeRef); 1400 PRINT(("node ref of open Node %" B_PRIdINO " %" B_PRIdDEV "\n", 1401 nodeRef.node, nodeRef.device)); 1402 } 1403 1404 if (deep && IsSymLink()) { 1405 BEntry tmpEntry(EntryRef(), true); 1406 Model tmp(&tmpEntry); 1407 PRINT(("symlink to:\n")); 1408 tmp.PrintToStream(); 1409 } 1410 TrackIconSource(B_MINI_ICON); 1411 TrackIconSource(B_LARGE_ICON); 1412 } 1413 1414 1415 void 1416 Model::TrackIconSource(icon_size size) 1417 { 1418 PRINT(("tracking %s icon\n", size == B_LARGE_ICON ? "large" : "small")); 1419 BRect rect; 1420 if (size == B_MINI_ICON) 1421 rect.Set(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1); 1422 else 1423 rect.Set(0, 0, B_LARGE_ICON - 1, B_LARGE_ICON - 1); 1424 1425 BBitmap bitmap(rect, B_CMAP8); 1426 1427 BModelOpener opener(this); 1428 1429 if (Node() == NULL) { 1430 PRINT(("track icon error - no node\n")); 1431 return; 1432 } 1433 1434 if (IsSymLink()) { 1435 PRINT(("tracking symlink icon\n")); 1436 if (fLinkTo) { 1437 fLinkTo->TrackIconSource(size); 1438 return; 1439 } 1440 } 1441 1442 if (fBaseType == kVolumeNode) { 1443 BVolume volume(NodeRef()->device); 1444 status_t result = volume.GetIcon(&bitmap, size); 1445 PRINT(("getting icon from volume %s\n", strerror(result))); 1446 } else { 1447 BNodeInfo nodeInfo(Node()); 1448 1449 status_t err = nodeInfo.GetIcon(&bitmap, size); 1450 if (err == B_OK) { 1451 // file knew which icon to use, we are done 1452 PRINT(("track icon - got icon from file\n")); 1453 return; 1454 } 1455 1456 char preferredApp[B_MIME_TYPE_LENGTH]; 1457 err = nodeInfo.GetPreferredApp(preferredApp); 1458 if (err == B_OK && preferredApp[0]) { 1459 BMimeType preferredAppType(preferredApp); 1460 err = preferredAppType.GetIconForType(MimeType(), &bitmap, size); 1461 if (err == B_OK) { 1462 PRINT( 1463 ("track icon - got icon for type %s from preferred " 1464 "app %s for file\n", MimeType(), preferredApp)); 1465 return; 1466 } 1467 } 1468 1469 BMimeType mimeType(MimeType()); 1470 err = mimeType.GetIcon(&bitmap, size); 1471 if (err == B_OK) { 1472 // the system knew what icon to use for the type, we are done 1473 PRINT(("track icon - signature %s, got icon from system\n", 1474 MimeType())); 1475 return; 1476 } 1477 1478 err = mimeType.GetPreferredApp(preferredApp); 1479 if (err != B_OK) { 1480 // no preferred App for document, give up 1481 PRINT(("track icon - signature %s, no prefered app, error %s\n", 1482 MimeType(), strerror(err))); 1483 return; 1484 } 1485 1486 BMimeType preferredAppType(preferredApp); 1487 err = preferredAppType.GetIconForType(MimeType(), &bitmap, size); 1488 if (err == B_OK) { 1489 // the preferred app knew icon to use for the type, we are done 1490 PRINT( 1491 ("track icon - signature %s, got icon from preferred " 1492 "app %s\n", MimeType(), preferredApp)); 1493 return; 1494 } 1495 PRINT( 1496 ("track icon - signature %s, preferred app %s, no icon, " 1497 "error %s\n", MimeType(), preferredApp, strerror(err))); 1498 } 1499 } 1500 1501 #endif // DEBUG 1502 1503 #ifdef CHECK_OPEN_MODEL_LEAKS 1504 1505 namespace BPrivate { 1506 1507 #include <stdio.h> 1508 1509 void 1510 DumpOpenModels(bool extensive) 1511 { 1512 if (readOnlyOpenModelList) { 1513 int32 count = readOnlyOpenModelList->CountItems(); 1514 printf("%ld models open read-only:\n", count); 1515 printf("==========================\n"); 1516 for (int32 index = 0; index < count; index++) { 1517 if (extensive) { 1518 printf("---------------------------\n"); 1519 readOnlyOpenModelList->ItemAt(index)->PrintToStream(); 1520 } else 1521 printf("%s\n", readOnlyOpenModelList->ItemAt(index)->Name()); 1522 } 1523 } 1524 1525 if (writableOpenModelList) { 1526 int32 count = writableOpenModelList->CountItems(); 1527 printf("%ld models open writable:\n", count); 1528 printf("models open writable:\n"); 1529 printf("======================\n"); 1530 for (int32 index = 0; index < count; index++) { 1531 if (extensive) { 1532 printf("---------------------------\n"); 1533 writableOpenModelList->ItemAt(index)->PrintToStream(); 1534 } else 1535 printf("%s\n", writableOpenModelList->ItemAt(index)->Name()); 1536 } 1537 } 1538 } 1539 1540 1541 void 1542 InitOpenModelDumping() 1543 { 1544 readOnlyOpenModelList = 0; 1545 writableOpenModelList = 0; 1546 } 1547 1548 } // namespace BPrivate 1549 1550 #endif // CHECK_OPEN_MODEL_LEAKS 1551