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