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