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