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