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