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