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