1 /* 2 * Copyright 2011 Aleksas Pantechovskis, <alexp.frl@gmail.com> 3 * Copyright 2011 Siarzhuk Zharski, <imker@gmx.li> 4 * All rights reserved. Distributed under the terms of the MIT License. 5 */ 6 7 #include <iomanip> 8 #include <iostream> 9 #include <map> 10 #include <math.h> 11 #include <set> 12 #include <sstream> 13 #include <stdio.h> 14 #include <stdlib.h> 15 #include <strings.h> 16 #include <vector> 17 18 #include <Application.h> 19 #include <Bitmap.h> 20 #include <Entry.h> 21 #include <InterfaceDefs.h> 22 #include <Message.h> 23 #include <Mime.h> 24 #include <Path.h> 25 #include <String.h> 26 27 28 using namespace std; 29 30 31 const char* kUsageMessage = "# setmime:\n" 32 "# usage: setmime ((-dump | -dumpSniffRule | -dumpIcon | -dumpAll) " 33 "[ <signatureString> ] )\n" 34 "# | (-remove <signatureString> )\n" 35 "# | ( (-set | -force | -add) <signatureString>\n" 36 "# [ -short <short description> ] [ -long <long description> ]\n" 37 "# [ -preferredApp <preferred app path> ]\n" 38 "# [ -preferredAppSig <preferred app signature> ]\n" 39 "# [ -sniffRule <sniffRule> ]\n" 40 "# [ -extension <file suffix> ]\n" 41 "# [ -attribute <internal name>\n" 42 "# [ -attrName <public name> ] [ -attrType <type code> ]\n" 43 "# [ -attrWidth <display width> ][ -attrAlignment <position>]\n" 44 "# [ -attrViewable <bool flag> ][ -attrEditable <bool flag> ]\n" 45 "# [ -attrExtra <bool flag> ] ]\n" 46 "# [ -miniIcon <256 hex bytes> ]\n" 47 "# [ -largeIcon <1024 hex bytes> ]\n" 48 "# [ -vectorIcon <icon hex bytes> ] ... )\n" 49 "# | (-checkSniffRule <sniffRule>\n" 50 "# | -includeApps)\n"; 51 52 const char* kHelpMessage = "# -dump prints a specified metamime\n" 53 "# -remove removes specified metamime\n" 54 "# -add adds specified metamime and specified metamime attributes\n" 55 "# that have not yet been defined\n" 56 "# -set adds specified metamime and specified metamime attributes, \n" 57 "# overwrites the existing values of specified metamime attributes\n" 58 "# -force adds specified metamime and specified metamime attributes\n" 59 "# after first erasing all the existing attributes\n" 60 "# -dumpSniffRule prints just the MIME sniffer rule of a " 61 "specified metamime\n" 62 "# -dumpIcon prints just the icon information of a specified metamime\n" 63 "# -dumpAll prints all the information, including icons of a " 64 "specified metamime\n" 65 "# -checkSniffRule parses a MIME sniffer rule and reports any errors\n" 66 "# -includeApps will include applications\n"; 67 68 const char* kNeedArgMessage = "you have to specify any of " 69 "-dump[All|Icon|SnifferRule], -add, -set, " 70 "-force or -remove"; 71 72 const char* kWrongModeMessage = "can only specify one of -dump, -dumpAll, " 73 "-dumpIcon, -dumpSnifferRule, -remove, " 74 "-add, -set, -force or -checkSnifferRule"; 75 76 const char* kHelpReq = "--help"; 77 const char* kDump = "-dump"; 78 const char* kDumpSniffRule = "-dumpSniffRule"; 79 const char* kDumpIcon = "-dumpIcon"; 80 const char* kDumpAll = "-dumpAll"; 81 const char* kAdd = "-add"; 82 const char* kSet = "-set"; 83 const char* kForce = "-force"; 84 const char* kRemove = "-remove"; 85 const char* kCheckSniffRule = "-checkSniffRule"; 86 const char* kShort = "-short"; 87 const char* kLong = "-long"; 88 const char* kPreferredApp = "-preferredApp"; 89 const char* kPreferredAppSig = "-preferredAppSig"; 90 const char* kSniffRule = "-sniffRule"; 91 const char* kMiniIcon = "-miniIcon"; 92 const char* kLargeIcon = "-largeIcon"; 93 const char* kVectorIcon = "-vectorIcon"; 94 const char* kIncludeApps = "-includeApps"; 95 const char* kExtension = "-extension"; 96 const char* kAttribute = "-attribute"; 97 const char* kAttrName = "-attrName"; 98 const char* kAttrType = "-attrType"; 99 const char* kAttrWidth = "-attrWidth"; 100 const char* kAttrAlignment = "-attrAlignment"; 101 const char* kAttrViewable = "-attrViewable"; 102 const char* kAttrEditable = "-attrEditable"; 103 const char* kAttrExtra = "-attrExtra"; 104 105 106 const uint32 hash_function(const char* str) 107 { 108 uint32 h = 0; 109 uint32 g = 0; 110 for (const char* p = str; *p; p++) { 111 h = (h << 4) + (*p & 0xFF); 112 g = h & 0xF0000000; 113 if (g != 0) { 114 h ^= g >> 24; 115 h ^= g; 116 } 117 } 118 return h; 119 } 120 121 122 // the list of all acceptable command-line options 123 struct CmdOption { 124 125 enum Type { 126 kMode, 127 kOption, 128 kAttrRoot, 129 kAttrib, 130 kHelp 131 }; 132 133 const char* fName; 134 Type fType; 135 bool fNeedArg; 136 bool fNonExclusive; 137 138 } gCmdOptions[] = { 139 140 { kHelpReq, CmdOption::kHelp }, 141 142 { kDump, CmdOption::kMode }, 143 { kDumpSniffRule, CmdOption::kMode }, 144 { kDumpIcon, CmdOption::kMode }, 145 { kDumpAll, CmdOption::kMode }, 146 { kAdd, CmdOption::kMode }, 147 { kSet, CmdOption::kMode }, 148 { kForce, CmdOption::kMode }, 149 { kRemove, CmdOption::kMode }, 150 { kCheckSniffRule, CmdOption::kMode, true }, 151 152 { kShort, CmdOption::kOption, true }, 153 { kLong, CmdOption::kOption, true }, 154 { kPreferredApp, CmdOption::kOption, true }, 155 { kPreferredAppSig, CmdOption::kOption, true }, 156 { kSniffRule, CmdOption::kOption, true }, 157 { kMiniIcon , CmdOption::kOption, true }, 158 { kLargeIcon, CmdOption::kOption, true }, 159 { kVectorIcon, CmdOption::kOption, true }, 160 { kIncludeApps, CmdOption::kOption, false }, 161 { kExtension, CmdOption::kOption, true, true }, 162 { kAttribute, CmdOption::kAttrRoot, true, true }, 163 164 { kAttrName, CmdOption::kAttrib, true }, 165 { kAttrType, CmdOption::kAttrib, true }, 166 { kAttrWidth, CmdOption::kAttrib, true }, 167 { kAttrAlignment, CmdOption::kAttrib, true }, 168 { kAttrViewable, CmdOption::kAttrib, true }, 169 { kAttrEditable, CmdOption::kAttrib, true }, 170 { kAttrExtra, CmdOption::kAttrib, true } 171 }; 172 173 // the 'hash -> value' map of arguments provided by user 174 typedef multimap<uint32, const char*> TUserArgs; 175 typedef multimap<uint32, const char*>::iterator TUserArgsI; 176 177 // user provided attributes are grouped separately in vector 178 typedef vector<TUserArgs> TUserAttrs; 179 typedef vector<TUserArgs>::iterator TUserAttrsI; 180 181 const uint32 kOpModeUndefined = 0; 182 183 184 // #pragma mark - 185 186 class Error : public std::exception 187 { 188 BString fWhat; 189 public: 190 Error(const char* what, ...); 191 virtual ~Error() throw() {} 192 virtual const char* what() const throw() { return fWhat.String(); } 193 }; 194 195 196 Error::Error(const char* what, ...) 197 { 198 const int size = 1024; 199 va_list args; 200 va_start(args, what); 201 vsnprintf(fWhat.LockBuffer(size), size, what, args); 202 fWhat.UnlockBuffer(); 203 va_end(args); 204 } 205 206 207 // #pragma mark - 208 209 // encapsulate the single attribute params 210 // 211 struct MimeAttribute 212 { 213 status_t fStatus; 214 BString fName; 215 BString fPublicName; 216 int32 fType; 217 bool fViewable; 218 bool fEditable; 219 bool fExtra; 220 int32 fWidth; 221 int32 fAlignment; 222 223 MimeAttribute(BMessage& msg, int32 index); 224 MimeAttribute(TUserArgs& args); 225 MimeAttribute(const MimeAttribute& src); 226 227 status_t InitCheck() { return fStatus; } 228 229 MimeAttribute& operator=(const MimeAttribute& src); 230 231 void Dump(); 232 void SyncWith(TUserArgs& args); 233 void StoreInto(BMessage* target); 234 const char* UserArgValue(TUserArgs& map, const char* name); 235 236 bool IsPrintableChar(char c) 237 { return c >= ' ' && c < 127 && c != '\'' && c != '\\'; } 238 }; 239 240 241 MimeAttribute::MimeAttribute(BMessage& msg, int32 index) 242 : 243 fStatus(B_NO_INIT), 244 fType('CSTR'), 245 fViewable(true), 246 fEditable(false), 247 fExtra(false), 248 fWidth(0), 249 fAlignment(0) 250 { 251 BString rawPublicName; 252 struct attrEntry { 253 const char* name; 254 type_code type; 255 bool required; 256 void* data; 257 } attrEntries[] = { 258 { "attr:name", B_STRING_TYPE, true, &fName }, 259 { "attr:public_name", B_STRING_TYPE, true, &rawPublicName }, 260 { "attr:type", B_INT32_TYPE, true, &fType }, 261 { "attr:viewable", B_BOOL_TYPE, false, &fViewable }, 262 { "attr:editable", B_BOOL_TYPE, false, &fEditable }, 263 { "attr:extra", B_BOOL_TYPE, false, &fExtra }, 264 { "attr:width", B_INT32_TYPE, false, &fWidth }, 265 { "attr:alignment", B_INT32_TYPE, false, &fAlignment } 266 }; 267 268 for (size_t i = 0; i < sizeof(attrEntries) / sizeof(attrEntries[0]); i++) { 269 switch (attrEntries[i].type) { 270 case B_STRING_TYPE: 271 fStatus = msg.FindString(attrEntries[i].name, index, 272 (BString*)attrEntries[i].data); 273 break; 274 case B_BOOL_TYPE: 275 fStatus = msg.FindBool(attrEntries[i].name, index, 276 (bool*)attrEntries[i].data); 277 break; 278 case B_INT32_TYPE: 279 fStatus = msg.FindInt32(attrEntries[i].name, index, 280 (int32*)attrEntries[i].data); 281 break; 282 } 283 284 if (attrEntries[i].required && fStatus != B_OK) 285 return; 286 } 287 288 fPublicName.CharacterEscape(rawPublicName, "\'", '\\'); 289 fStatus = B_OK; 290 } 291 292 293 MimeAttribute::MimeAttribute(TUserArgs& args) 294 : 295 fStatus(B_NO_INIT), 296 fType('CSTR'), 297 fViewable(true), 298 fEditable(false), 299 fExtra(false), 300 fWidth(0), 301 fAlignment(0) 302 { 303 SyncWith(args); 304 fStatus = B_OK; 305 } 306 307 308 MimeAttribute::MimeAttribute(const MimeAttribute& src) 309 { 310 *this = src; 311 } 312 313 314 MimeAttribute& 315 MimeAttribute::operator=(const MimeAttribute& src) 316 { 317 fStatus = src.fStatus; 318 fName = src.fName; 319 fPublicName = src.fPublicName; 320 fType = src.fType; 321 fViewable = src.fViewable; 322 fEditable = src.fEditable; 323 fExtra = src.fExtra; 324 fWidth = src.fWidth; 325 fAlignment = src.fAlignment; 326 327 return *this; 328 } 329 330 331 void 332 MimeAttribute::SyncWith(TUserArgs& args) 333 { 334 const char* value = UserArgValue(args, kAttribute); 335 if (value != NULL) 336 fName.SetTo(value, B_MIME_TYPE_LENGTH); 337 338 value = UserArgValue(args, kAttrName); 339 if (value != NULL) 340 fPublicName.SetTo(value, B_MIME_TYPE_LENGTH); 341 342 value = UserArgValue(args, kAttrType); 343 if (value != NULL) { 344 fType = 0; 345 if (strlen(value) > 2 && value[0] == '0' && value[1] == 'x') { 346 stringstream ss; 347 ss << setbase(16) << value + 2; 348 ss >> fType; 349 } else if (strlen(value) == 4) { 350 for (int i = 0; i < 4 && value[i] != '\0'; i++) { 351 fType <<= 8; 352 fType |= (value[i] != '\0' ? value[i] : ' '); 353 } 354 355 } else 356 throw Error("Invalid data for %s", kAttrType); 357 358 fType = B_LENDIAN_TO_HOST_INT32(fType); 359 } 360 361 value = UserArgValue(args, kAttrWidth); 362 if (value != NULL) 363 fWidth = atoi(value); 364 365 value = UserArgValue(args, kAttrAlignment); 366 if (value != NULL) { 367 if (strcasecmp(value, "right") == 0) { 368 fAlignment = B_ALIGN_RIGHT; 369 } else if (strcasecmp(value, "left") == 0) { 370 fAlignment = B_ALIGN_LEFT; 371 } else if (strcasecmp(value, "center") == 0) { 372 fAlignment = B_ALIGN_CENTER; 373 } else 374 fAlignment = atoi(value); 375 } 376 377 value = UserArgValue(args, kAttrViewable); 378 if (value != NULL) 379 fViewable = atoi(value) != 0; 380 381 value = UserArgValue(args, kAttrEditable); 382 if (value != NULL) 383 fEditable = atoi(value) != 0; 384 385 value = UserArgValue(args, kAttrExtra); 386 if (value != NULL) 387 fExtra = atoi(value) != 0; 388 } 389 390 391 void 392 MimeAttribute::Dump() 393 { 394 uint32 type = B_HOST_TO_LENDIAN_INT32(fType); 395 const char* alignment = fAlignment == B_ALIGN_RIGHT ? "right" 396 : (fAlignment == B_ALIGN_LEFT ? "left" : "center"); 397 398 cout << " \\" << endl << "\t" << kAttribute << " \"" << fName << "\" " 399 << kAttrName << " \"" << fPublicName << "\""; 400 401 char c1 = (char)((type >> 24) & 0xFF); 402 char c2 = (char)((type >> 16) & 0xFF); 403 char c3 = (char)((type >> 8) & 0xFF); 404 char c4 = (char)(type & 0xFF); 405 406 ios::fmtflags flags = cout.flags(); 407 408 cout << " \\" << endl << "\t\t" << kAttrType; 409 if (IsPrintableChar(c1) && IsPrintableChar(c2) && 410 IsPrintableChar(c3) && IsPrintableChar(c4)) 411 cout << " '" << c1 << c2 << c3 << c4 << "' "; 412 else 413 cout << "0x" << hex << type; 414 415 cout << " " << kAttrWidth << " " << fWidth 416 << " " << kAttrAlignment << " " << alignment; 417 418 cout << " \\" << endl << "\t\t" << kAttrViewable << " " << fViewable 419 << " " << kAttrEditable << " " << fEditable 420 << " " << kAttrExtra << " " << fExtra; 421 422 cout.flags(flags); 423 } 424 425 426 void 427 MimeAttribute::StoreInto(BMessage* target) 428 { 429 struct attrEntry { 430 const char* name; 431 type_code type; 432 const void* data; 433 } attrEntries[] = { 434 { "attr:name", B_STRING_TYPE, fName.String() }, 435 { "attr:public_name", B_STRING_TYPE, fPublicName.String() }, 436 { "attr:type", B_INT32_TYPE, &fType }, 437 { "attr:viewable", B_BOOL_TYPE, &fViewable }, 438 { "attr:editable", B_BOOL_TYPE, &fEditable }, 439 { "attr:extra", B_BOOL_TYPE, &fExtra }, 440 { "attr:width", B_INT32_TYPE, &fWidth }, 441 { "attr:alignment", B_INT32_TYPE, &fAlignment } 442 }; 443 444 for (size_t i = 0; i < sizeof(attrEntries) / sizeof(attrEntries[0]); i++) { 445 switch (attrEntries[i].type) { 446 case B_STRING_TYPE: 447 fStatus = target->AddString(attrEntries[i].name, 448 (const char*)attrEntries[i].data); 449 break; 450 case B_BOOL_TYPE: 451 fStatus = target->AddBool(attrEntries[i].name, 452 (bool*)attrEntries[i].data); 453 break; 454 case B_INT32_TYPE: 455 fStatus = target->AddInt32(attrEntries[i].name, 456 *(int32*)attrEntries[i].data); 457 break; 458 } 459 460 if (fStatus != B_OK) 461 return; 462 } 463 } 464 465 466 const char* 467 MimeAttribute::UserArgValue(TUserArgs& map, const char* name) 468 { 469 TUserArgsI i = map.find(hash_function(name)); 470 if (i == map.end()) 471 return NULL; 472 return i->second != NULL ? i->second : ""; 473 } 474 475 476 // #pragma mark - 477 478 // the work-horse of the app - the class encapsulates extended info readed 479 // from the mime type and do all edit and dump operations 480 // 481 class MimeType : public BMimeType { 482 483 public: 484 MimeType(char** argv); 485 ~MimeType(); 486 487 void Process(); 488 489 private: 490 status_t _InitCheck(); 491 void _SetTo(const char* mimetype); 492 void _PurgeProperties(); 493 void _Init(char** argv); 494 void _DumpIcon(uint8 *iconData, size_t iconSize); 495 void _Dump(const char* mimetype); 496 void _DoEdit(); 497 void _SetIcon(const char* iconData, int32 iconSize); 498 499 const char* _UserArgValue(const char* name); 500 501 status_t fStatus; 502 const char* fToolName; 503 504 // configurable MimeType properties 505 BString fShort; 506 BString fLong; 507 BString fPrefApp; 508 BString fPrefAppSig; 509 BString fSniffRule; 510 BBitmap* fSmallIcon; 511 BBitmap* fBigIcon; 512 uint8* fVectorIcon; 513 size_t fVectorIconSize; 514 515 map<uint32, BString> fExtensions; 516 map<uint32, MimeAttribute> fAttributes; 517 518 // user provided arguments 519 TUserArgs fUserArguments; 520 TUserAttrs fUserAttributes; 521 522 // operation mode switches and flags 523 uint32 fOpMode; 524 bool fDumpNormal; 525 bool fDumpRule; 526 bool fDumpIcon; 527 bool fDumpAll; 528 bool fDoAdd; 529 bool fDoSet; 530 bool fDoForce; 531 bool fDoRemove; 532 bool fCheckSniffRule; 533 }; 534 535 536 MimeType::MimeType(char** argv) 537 : 538 fStatus(B_NO_INIT), 539 fToolName(argv[0]), 540 fSmallIcon(NULL), 541 fBigIcon(NULL), 542 fVectorIcon(NULL), 543 fVectorIconSize(0), 544 fOpMode(kOpModeUndefined), 545 fDumpNormal(false), 546 fDumpRule(false), 547 fDumpIcon(false), 548 fDumpAll(false), 549 fDoAdd(false), 550 fDoSet(false), 551 fDoForce(false), 552 fDoRemove(false), 553 fCheckSniffRule(false) 554 { 555 fToolName = strrchr(argv[0], '/'); 556 fToolName = fToolName == NULL ? argv[0] : fToolName + 1; 557 558 _Init(++argv); 559 } 560 561 562 MimeType::~MimeType() 563 { 564 delete fSmallIcon; 565 delete fBigIcon; 566 free(fVectorIcon); 567 } 568 569 570 void 571 MimeType::_Init(char** argv) 572 { 573 // fill the helper map of options - for quick lookup of arguments 574 map<uint32, const CmdOption*> cmdOptionsMap; 575 for (size_t i = 0; i < sizeof(gCmdOptions) / sizeof(gCmdOptions[0]); i++) 576 cmdOptionsMap.insert(pair<uint32, CmdOption*>( 577 hash_function(gCmdOptions[i].fName), &gCmdOptions[i])); 578 579 // parse the command line arguments 580 for (char** arg = argv; *arg; arg++) { 581 // non-option arguments are assumed as signature 582 if (**arg != '-') { 583 if (Type() != NULL) 584 throw Error("mime signature already specified: '%s'", Type()); 585 586 SetTo(*arg); 587 continue; 588 } 589 590 // check op.modes, options and attribs 591 uint32 key = hash_function(*arg); 592 593 map<uint32, const CmdOption*>::iterator I = cmdOptionsMap.find(key); 594 if (I == cmdOptionsMap.end()) 595 throw Error("unknown option '%s'", *arg); 596 597 switch (I->second->fType) { 598 case CmdOption::kHelp: 599 cerr << kUsageMessage; 600 throw Error(kHelpMessage); 601 602 case CmdOption::kMode: 603 // op.modes are exclusive - no simultaneous possible 604 if (fOpMode != kOpModeUndefined) 605 throw Error(kWrongModeMessage); 606 fOpMode = key; 607 608 if (hash_function(I->second->fName) != hash_function(kCheckSniffRule)) 609 break; 610 // else -> fallthrough, CheckRule works both as mode and Option 611 case CmdOption::kOption: 612 { 613 const char* name = *arg; 614 const char* param = NULL; 615 if (I->second->fNeedArg) { 616 if (!*++arg) 617 throw Error("argument required for '%s'", name); 618 param = *arg; 619 } 620 621 TUserArgsI A = fUserArguments.find(key); 622 if (A != fUserArguments.end() && !I->second->fNonExclusive) 623 throw Error("option '%s' already specified", name); 624 625 fUserArguments.insert( 626 pair<uint32, const char*>(key, param)); 627 } 628 break; 629 630 case CmdOption::kAttrRoot: 631 if (!*++arg) 632 throw Error("attribute name should be specified"); 633 634 fUserAttributes.resize(fUserAttributes.size() + 1); 635 fUserAttributes.back().insert( 636 pair<uint32, const char*>(key, *arg)); 637 break; 638 639 case CmdOption::kAttrib: 640 { 641 const char* name = *arg; 642 if (fUserAttributes.size() <= 0) 643 throw Error("'%s' allowed only after the '%s' <name>", 644 name, kAttribute); 645 646 if (!*++arg || **arg == '-') 647 throw Error("'%s', argument should be specified", name); 648 649 TUserArgsI A = fUserAttributes.back().find(key); 650 if (A != fUserAttributes.back().end()) 651 throw Error("'%s' for attribute '%s' already specified", 652 name, A->second); 653 654 fUserAttributes.back().insert( 655 pair<uint32, const char*>(key, *arg)); 656 } 657 break; 658 659 default: 660 throw Error("internal error. wrong mode: %d", I->second->fType); 661 } 662 } 663 664 // check some mutual exclusive conditions 665 if (fOpMode == kOpModeUndefined) 666 throw Error(kNeedArgMessage); 667 668 if (Type() != NULL && InitCheck() != B_OK) 669 throw Error("error instantiating mime for '%s': %s", 670 Type(), strerror(InitCheck())); 671 672 fDoAdd = fOpMode == hash_function(kAdd); 673 fDoSet = fOpMode == hash_function(kSet); 674 fDoForce = fOpMode == hash_function(kForce); 675 fDoRemove = fOpMode == hash_function(kRemove); 676 fDumpNormal = fOpMode == hash_function(kDump); 677 fDumpRule = fOpMode == hash_function(kDumpSniffRule); 678 fDumpIcon = fOpMode == hash_function(kDumpIcon); 679 fDumpAll = fOpMode == hash_function(kDumpAll); 680 fCheckSniffRule = fOpMode == hash_function(kCheckSniffRule); 681 682 if (fDoAdd || fDoSet || fDoForce || fDoRemove) { 683 if (Type() == NULL) 684 throw Error("signature should be specified"); 685 686 if (!IsValid()) 687 throw Error("mime for '%s' is not valid", Type()); 688 689 } else if (fDumpNormal || fDumpRule || fDumpIcon || fDumpAll) { 690 if (Type() != NULL) { 691 if (!IsValid()) 692 throw Error("mime for '%s' is not valid", Type()); 693 694 if (!IsInstalled()) 695 throw Error("mime for '%s' is not installed", Type()); 696 } 697 } 698 699 // finally force to load mime-specific fileds 700 _SetTo(Type()); 701 } 702 703 704 status_t 705 MimeType::_InitCheck() 706 { 707 return fStatus != B_OK ? fStatus : BMimeType::InitCheck(); 708 } 709 710 711 void 712 MimeType::_PurgeProperties() 713 { 714 fShort.Truncate(0); 715 fLong.Truncate(0); 716 fPrefApp.Truncate(0); 717 fPrefAppSig.Truncate(0); 718 fSniffRule.Truncate(0); 719 720 delete fSmallIcon; 721 fSmallIcon = NULL; 722 723 delete fBigIcon; 724 fBigIcon = NULL; 725 726 free(fVectorIcon); 727 fVectorIcon = NULL; 728 729 fExtensions.clear(); 730 fAttributes.clear(); 731 } 732 733 734 void 735 MimeType::_DumpIcon(uint8 *iconData, size_t iconSize) 736 { 737 // bitmap icons ASCII art :) 738 int lineLimit = iconSize == B_MINI_ICON * B_MINI_ICON 739 ? B_MINI_ICON : B_LARGE_ICON; 740 741 ios::fmtflags flags = cout.flags(); 742 743 for (size_t i = 0; i < iconSize; i++) { 744 if (i % lineLimit == 0 && i != iconSize - 1) 745 cout << "\\" << endl; 746 747 cout << hex << setfill('0') << setw(2) << (uint16) iconData[i]; 748 } 749 750 cout.flags(flags); 751 } 752 753 754 void 755 MimeType::_SetIcon(const char* iconData, int32 iconSize) 756 { 757 uint8* bits = NULL; 758 BRect rect(0, 0, iconSize - 1, iconSize - 1); 759 760 switch (iconSize) { 761 case B_MINI_ICON: 762 if (fSmallIcon == NULL) 763 fSmallIcon = new BBitmap(rect, B_COLOR_8_BIT); 764 bits = (uint8*) fSmallIcon->Bits(); 765 break; 766 case B_LARGE_ICON: 767 if (fBigIcon == NULL) 768 fBigIcon = new BBitmap(rect, B_COLOR_8_BIT); 769 bits = (uint8*) fBigIcon->Bits(); 770 break; 771 default: 772 if (iconSize >= 0) 773 break; 774 free(fVectorIcon); 775 fVectorIconSize = -iconSize; 776 bits = fVectorIcon = (uint8*) malloc(fVectorIconSize); 777 break; 778 } 779 780 if (bits == NULL) 781 throw Error("cannot create icon of size %d", iconSize); 782 783 size_t dataSize = iconSize < 0 ? -iconSize / 2 : iconSize * iconSize; 784 785 for (size_t i = 0; i < dataSize; i++) { 786 stringstream ss; 787 uint16 val; 788 ss << setbase(16) << iconData[i * 2] << iconData[i * 2 + 1]; 789 ss >> val; 790 bits[i] = uint8(val & 0xff); 791 } 792 793 if (iconSize < 0) 794 SetIcon(fVectorIcon, dataSize); 795 else 796 SetIcon(iconSize == B_MINI_ICON ? fSmallIcon : fBigIcon, 797 (icon_size) iconSize); 798 } 799 800 801 void 802 MimeType::_SetTo(const char* mimetype) 803 { 804 if (mimetype == NULL) 805 return; // iterate all types - nothing to load ATM 806 807 if (BMimeType::SetTo(mimetype) != B_OK) 808 throw Error("failed to set mimetype to '%s'", mimetype); 809 810 _PurgeProperties(); 811 812 char buffer[B_MIME_TYPE_LENGTH] = { 0 }; 813 if (GetShortDescription(buffer) == B_OK) 814 fShort.SetTo(buffer, B_MIME_TYPE_LENGTH); 815 816 if (GetLongDescription(buffer) == B_OK) 817 fLong.SetTo(buffer, B_MIME_TYPE_LENGTH); 818 819 entry_ref ref; 820 if (GetAppHint(&ref) == B_OK) { 821 BPath path(&ref); 822 fPrefApp.SetTo(path.Path(), B_MIME_TYPE_LENGTH); 823 } 824 825 if (GetPreferredApp(buffer, B_OPEN) == B_OK) 826 fPrefAppSig.SetTo(buffer, B_MIME_TYPE_LENGTH); 827 828 BString rule; 829 if (GetSnifferRule(&rule) == B_OK) 830 fSniffRule.CharacterEscape(rule.String(), "\'", '\\'); 831 832 BMessage exts; 833 fExtensions.clear(); 834 if (GetFileExtensions(&exts) == B_OK) { 835 uint32 i = 0; 836 const char* ext = NULL; 837 while (exts.FindString("extensions", i++, &ext) == B_OK) 838 fExtensions.insert(pair<uint32, BString>(hash_function(ext), ext)); 839 } 840 841 BMessage attrs; 842 fAttributes.clear(); 843 if (GetAttrInfo(&attrs) == B_OK) { 844 for (int index = 0; ; index++) { 845 MimeAttribute attr(attrs, index); 846 if (attr.InitCheck() != B_OK) 847 break; 848 849 fAttributes.insert( 850 pair<uint32, MimeAttribute>(hash_function(attr.fName), attr)); 851 } 852 } 853 854 fSmallIcon = new BBitmap(BRect(0, 0, 15, 15), B_COLOR_8_BIT); 855 if (GetIcon(fSmallIcon, B_MINI_ICON) != B_OK) { 856 delete fSmallIcon; 857 fSmallIcon = NULL; 858 } 859 860 fBigIcon = new BBitmap(BRect(0, 0, 31, 31), B_COLOR_8_BIT); 861 if (GetIcon(fBigIcon, B_LARGE_ICON) != B_OK) { 862 delete fBigIcon; 863 fBigIcon = NULL; 864 } 865 866 if (GetIcon(&fVectorIcon, &fVectorIconSize) != B_OK) 867 fVectorIcon = NULL; 868 } 869 870 871 const char* 872 MimeType::_UserArgValue(const char* name) 873 { 874 TUserArgsI i = fUserArguments.find(hash_function(name)); 875 if (i == fUserArguments.end()) 876 return NULL; 877 878 return i->second != NULL ? i->second : ""; 879 } 880 881 882 void 883 MimeType::_Dump(const char* mimetype) 884 { 885 // _Dump can be called as part of all types iteration - so set to required 886 if (Type() == NULL || strcasecmp(Type(), mimetype) != 0) 887 _SetTo(mimetype); 888 889 // apps have themself as preferred app - use it to handle 890 // -includeApps option - do not dump applications info 891 if (!fPrefApp.IsEmpty() 892 && fPrefApp.ICompare(mimetype) == 0 893 && _UserArgValue(kIncludeApps) == NULL) 894 return; 895 896 if (fDumpIcon && fSmallIcon == NULL && fBigIcon == NULL) 897 return; 898 899 if (fDumpRule && fSniffRule.IsEmpty()) 900 return; 901 902 cout << fToolName << " -set " << mimetype; 903 904 if (fDumpNormal || fDumpAll) { 905 if (!fShort.IsEmpty()) 906 cout << " " << kShort << " \"" << fShort << "\""; 907 if (!fLong.IsEmpty()) 908 cout << " " << kLong << " \"" << fLong << "\""; 909 if (!fPrefApp.IsEmpty()) 910 cout << " " << kPreferredApp << " " << fPrefApp; 911 if (!fPrefAppSig.IsEmpty()) 912 cout << " " << kPreferredAppSig << " " << fPrefAppSig; 913 } 914 915 if (!fDumpIcon && !fSniffRule.IsEmpty()) 916 cout << " " << kSniffRule << " '" << fSniffRule << "'"; 917 918 if (fDumpNormal || fDumpAll) 919 for (map<uint32, BString>::iterator i = fExtensions.begin(); 920 i != fExtensions.end(); i++) 921 cout << " " << kExtension << " " << i->second; 922 923 if (fDumpAll) 924 for (map<uint32, MimeAttribute>::iterator i = fAttributes.begin(); 925 i != fAttributes.end(); i++) 926 i->second.Dump(); 927 928 if (fDumpIcon || fDumpAll) { 929 if (fSmallIcon != NULL && fSmallIcon->Bits() != NULL) { 930 cout << " \\" << endl << "\t" << kMiniIcon << " "; 931 _DumpIcon((uint8*) fSmallIcon->Bits(), fSmallIcon->BitsLength()); 932 } 933 934 if (fBigIcon != NULL && fBigIcon->Bits() != NULL) { 935 cout << " \\" << endl << "\t" << kLargeIcon << " "; 936 _DumpIcon((uint8*) fBigIcon->Bits(), fBigIcon->BitsLength()); 937 } 938 939 if (fVectorIcon != NULL && fVectorIconSize != 0) { 940 cout << " \\" << endl << "\t" << kVectorIcon << " "; 941 _DumpIcon((uint8*) fVectorIcon, fVectorIconSize); 942 } 943 } 944 945 cout << endl; 946 } 947 948 949 void 950 MimeType::_DoEdit() 951 { 952 if (fDoRemove || fDoForce) { 953 status_t result = Delete(); 954 if (result != B_OK) 955 throw Error(strerror(result), result); 956 957 if (fDoRemove) 958 return; 959 960 _PurgeProperties(); 961 } 962 963 if (!IsInstalled() && Install() != B_OK) 964 throw Error("could not install mimetype '%s'", Type()); 965 966 const char* value = _UserArgValue(kShort); 967 if (value != NULL && (!fDoAdd || fShort.IsEmpty())) 968 if (SetShortDescription(value) != B_OK) 969 throw Error("cannot set %s to %s for '%s'", kShort, value, Type()); 970 971 value = _UserArgValue(kLong); 972 if (value != NULL && (!fDoAdd || fLong.IsEmpty())) 973 if (SetLongDescription(value) != B_OK) 974 throw Error("cannot set %s to %s for '%s'", kLong, value, Type()); 975 976 value = _UserArgValue(kPreferredApp); 977 if (value != NULL && (!fDoAdd || fPrefApp.IsEmpty())) { 978 entry_ref appHint; 979 if (get_ref_for_path(value, &appHint) != B_OK) 980 throw Error("%s ref_entry for '%s' couldn't be found for '%s'", 981 kPreferredApp, value, Type()); 982 983 if (SetAppHint(&appHint) != B_OK) 984 throw Error("cannot set %s to %s for '%s'", 985 kPreferredApp, value, Type()); 986 } 987 988 value = _UserArgValue(kPreferredAppSig); 989 if (value != NULL && (!fDoAdd || fPrefAppSig.IsEmpty())) 990 if (SetPreferredApp(value) != B_OK) 991 throw Error("cannot set %s to %s for '%s'", 992 kPreferredAppSig, value, Type()); 993 994 value = _UserArgValue(kSniffRule); 995 if (value != NULL && (!fDoAdd || fSniffRule.IsEmpty())) 996 if (SetSnifferRule(value) != B_OK) 997 throw Error("cannot set %s to %s for '%s'", 998 kSniffRule, value, Type()); 999 1000 value = _UserArgValue(kMiniIcon); 1001 if (value != NULL && (!fDoAdd || fSmallIcon == NULL)) { 1002 int32 iconSize = strlen(value); 1003 if (iconSize / 2 != B_MINI_ICON * B_MINI_ICON) 1004 throw Error("cannot set %s for '%s'. Hex data size %d is invalid", 1005 kMiniIcon, Type(), iconSize); 1006 1007 _SetIcon(value, B_MINI_ICON); 1008 } 1009 1010 value = _UserArgValue(kLargeIcon); 1011 if (value != NULL && (!fDoAdd || fBigIcon == NULL)) { 1012 int32 iconSize = strlen(value); 1013 if (iconSize / 2 != B_LARGE_ICON * B_LARGE_ICON) 1014 throw Error("cannot set %s for '%s'. Hex data size %d is invalid", 1015 kLargeIcon, Type(), iconSize); 1016 1017 _SetIcon(value, B_LARGE_ICON); 1018 } 1019 1020 value = _UserArgValue(kVectorIcon); 1021 if (value != NULL && (!fDoAdd || fVectorIcon == NULL)) { 1022 int32 iconSize = strlen(value); 1023 if ((iconSize % 2) != 0) 1024 throw Error("cannot set %s for '%s'. Hex data size %d is invalid", 1025 kVectorIcon, Type(), iconSize); 1026 1027 // vector icon size is negative intended 1028 _SetIcon(value, -iconSize); 1029 } 1030 1031 // handle extensions update 1032 pair<TUserArgsI, TUserArgsI> exts 1033 = fUserArguments.equal_range(hash_function(kExtension)); 1034 for (TUserArgsI i = exts.first; i != exts.second; i++) { 1035 uint32 key = hash_function(i->second); 1036 if (fExtensions.find(key) == fExtensions.end()) 1037 fExtensions.insert(pair<uint32, BString>(key, i->second)); 1038 } 1039 1040 if (exts.first != exts.second) { 1041 BMessage msg; 1042 for (map<uint32, BString>::iterator i = fExtensions.begin(); 1043 i != fExtensions.end(); i++) 1044 if (msg.AddString("extensions", i->second.String()) != B_OK) 1045 throw Error("extension '%s' couldn't be added", 1046 i->second.String()); 1047 1048 if (SetFileExtensions(&msg) != B_OK) 1049 throw Error("set file extensions failed"); 1050 } 1051 1052 // take care about attribute trees 1053 for (TUserAttrsI userAttr = fUserAttributes.begin(); 1054 userAttr != fUserAttributes.end(); userAttr++ ) 1055 { 1056 // search for -attribute "name" in args map 1057 TUserArgsI attrArgs = userAttr->find(hash_function(kAttribute)); 1058 if (attrArgs == userAttr->end()) 1059 throw Error("internal error: %s arg not found", kAttribute); 1060 1061 // check if we already have this attribute cached 1062 map<uint32, MimeAttribute>::iterator 1063 attr = fAttributes.find(hash_function(attrArgs->second)); 1064 if (attr == fAttributes.end()) { 1065 // add new one 1066 MimeAttribute mimeAttr(*userAttr); 1067 fAttributes.insert( 1068 pair<uint32, MimeAttribute>(hash_function(mimeAttr.fName), mimeAttr)); 1069 } else if (!fDoAdd) 1070 attr->second.SyncWith(*userAttr); 1071 } 1072 1073 if (fAttributes.size() > 0) { 1074 BMessage msg; 1075 for (map<uint32, MimeAttribute>::iterator i = fAttributes.begin(); 1076 i != fAttributes.end(); i++) 1077 { 1078 i->second.StoreInto(&msg); 1079 if (i->second.InitCheck() != B_OK) 1080 throw Error("storing attributes in message failed"); 1081 } 1082 1083 if (SetAttrInfo(&msg) != B_OK) 1084 throw Error("set mimetype attributes failed"); 1085 } 1086 } 1087 1088 1089 void 1090 MimeType::Process() 1091 { 1092 if (fCheckSniffRule) { 1093 TUserArgsI I = fUserArguments.find(fOpMode); 1094 if (I == fUserArguments.end()) 1095 throw Error("Sniffer rule is empty"); 1096 1097 BString error; 1098 status_t result = BMimeType::CheckSnifferRule(I->second, &error); 1099 if (result == B_OK) 1100 cerr << I->second << endl << "Sniffer rule is correct" << endl; 1101 else 1102 cerr << error.String() << endl; 1103 1104 return; 1105 } 1106 1107 if (fDoAdd || fDoSet || fDoForce || fDoRemove) { 1108 _DoEdit(); 1109 return; 1110 } 1111 1112 if (fDumpNormal || fDumpRule || fDumpIcon || fDumpAll) { 1113 if (Type() != NULL) { 1114 _Dump(Type()); 1115 return; 1116 } 1117 1118 BMessage superTypes; 1119 int32 superCount = 0; 1120 type_code type = B_INT32_TYPE; 1121 if (BMimeType::GetInstalledSupertypes(&superTypes) != B_OK 1122 || superTypes.GetInfo("super_types", &type, &superCount) != B_OK) 1123 throw Error("super types enumeration failed"); 1124 1125 for (int32 si = 0; si < superCount; si++) { 1126 const char* superName = NULL; 1127 if (superTypes.FindString("super_types", si, &superName) != B_OK) 1128 throw Error("name for supertype #%d not found", si); 1129 1130 BMessage types; 1131 if (BMimeType::GetInstalledTypes(superName, &types) != B_OK) 1132 throw Error("mimetypes of supertype '%s' not found", superName); 1133 1134 int32 count = 0; 1135 if (types.GetInfo("types", &type, &count) != B_OK) 1136 continue; // no sub-types? 1137 1138 for (int32 i = 0; i < count; i++) { 1139 const char* name = NULL; 1140 if (types.FindString("types", i, &name) != B_OK) 1141 throw Error("name for type %s/#%d not found", superName, i); 1142 1143 _Dump(name); 1144 } 1145 } 1146 } 1147 } 1148 1149 1150 int 1151 main(int argc, char** argv) 1152 { 1153 // AppServer link is required to work with bitmaps 1154 BApplication app("application/x-vnd.haiku.setmime"); 1155 1156 try { 1157 1158 if (argc < 2) 1159 throw Error(kNeedArgMessage); 1160 1161 MimeType mimetype(argv); 1162 1163 mimetype.Process(); 1164 1165 } catch(exception& exc) { 1166 cerr << argv[0] << " : " << exc.what() << endl; 1167 cerr << kUsageMessage; 1168 return B_ERROR; 1169 } 1170 1171 return B_OK; 1172 } 1173 1174