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