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