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