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