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 <string.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(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 226 status_t InitCheck() { return fStatus; } 227 228 void Dump(); 229 void SyncWith(TUserArgs& args) throw(Error); 230 void StoreInto(BMessage* target); 231 const char* UserArgValue(TUserArgs& map, const char* name); 232 233 bool IsPrintableChar(char c) 234 { return c >= ' ' && c < 127 && c != '\'' && c != '\\'; } 235 }; 236 237 238 MimeAttribute::MimeAttribute(BMessage& msg, int32 index) 239 : 240 fStatus(B_NO_INIT), 241 fType('CSTR'), 242 fViewable(true), 243 fEditable(false), 244 fExtra(false), 245 fWidth(0), 246 fAlignment(0) 247 { 248 BString rawPublicName; 249 struct attrEntry { 250 const char* name; 251 type_code type; 252 bool required; 253 void* data; 254 } attrEntries[] = { 255 { "attr:name", B_STRING_TYPE, true, &fName }, 256 { "attr:public_name", B_STRING_TYPE, true, &rawPublicName }, 257 { "attr:type", B_INT32_TYPE, true, &fType }, 258 { "attr:viewable", B_BOOL_TYPE, false, &fViewable }, 259 { "attr:editable", B_BOOL_TYPE, false, &fEditable }, 260 { "attr:extra", B_BOOL_TYPE, false, &fExtra }, 261 { "attr:width", B_INT32_TYPE, false, &fWidth }, 262 { "attr:alignment", B_INT32_TYPE, false, &fAlignment } 263 }; 264 265 for (size_t i = 0; i < sizeof(attrEntries) / sizeof(attrEntries[0]); i++) { 266 switch (attrEntries[i].type) { 267 case B_STRING_TYPE: 268 fStatus = msg.FindString(attrEntries[i].name, index, 269 (BString*)attrEntries[i].data); 270 break; 271 case B_BOOL_TYPE: 272 fStatus = msg.FindBool(attrEntries[i].name, index, 273 (bool*)attrEntries[i].data); 274 break; 275 case B_INT32_TYPE: 276 fStatus = msg.FindInt32(attrEntries[i].name, index, 277 (int32*)attrEntries[i].data); 278 break; 279 } 280 281 if (attrEntries[i].required && fStatus != B_OK) 282 return; 283 } 284 285 fPublicName.CharacterEscape(rawPublicName, "\'", '\\'); 286 fStatus = B_OK; 287 } 288 289 290 MimeAttribute::MimeAttribute(TUserArgs& args) 291 { 292 SyncWith(args); 293 fStatus = B_OK; 294 } 295 296 297 void 298 MimeAttribute::SyncWith(TUserArgs& args) throw(Error) 299 { 300 const char* value = UserArgValue(args, kAttribute); 301 if (value != NULL) 302 fName.SetTo(value, B_MIME_TYPE_LENGTH); 303 304 value = UserArgValue(args, kAttrName); 305 if (value != NULL) 306 fPublicName.SetTo(value, B_MIME_TYPE_LENGTH); 307 308 value = UserArgValue(args, kAttrType); 309 if (value != NULL) { 310 fType = 0; 311 if (strlen(value) > 2 && value[0] == '0' && value[1] == 'x') { 312 stringstream ss; 313 ss << setbase(16) << value + 2; 314 ss >> fType; 315 } else if (strlen(value) == 4) { 316 for (int i = 0; i < 4 && value[i] != '\0'; i++) { 317 fType <<= 8; 318 fType |= (value[i] != '\0' ? value[i] : ' '); 319 } 320 321 } else 322 throw Error("Invalid data for %s", kAttrType); 323 324 fType = B_LENDIAN_TO_HOST_INT32(fType); 325 } 326 327 value = UserArgValue(args, kAttrWidth); 328 if (value != NULL) 329 fWidth = atoi(value); 330 331 value = UserArgValue(args, kAttrAlignment); 332 if (value != NULL) { 333 if (strcasecmp(value, "right") == 0) { 334 fAlignment = B_ALIGN_RIGHT; 335 } else if (strcasecmp(value, "left") == 0) { 336 fAlignment = B_ALIGN_LEFT; 337 } else if (strcasecmp(value, "center") == 0) { 338 fAlignment = B_ALIGN_CENTER; 339 } else 340 fAlignment = atoi(value); 341 } 342 343 value = UserArgValue(args, kAttrViewable); 344 if (value != NULL) 345 fViewable = atoi(value) != 0; 346 347 value = UserArgValue(args, kAttrEditable); 348 if (value != NULL) 349 fEditable = atoi(value) != 0; 350 351 value = UserArgValue(args, kAttrExtra); 352 if (value != NULL) 353 fExtra = atoi(value) != 0; 354 } 355 356 357 void 358 MimeAttribute::Dump() 359 { 360 uint32 type = B_HOST_TO_LENDIAN_INT32(fType); 361 const char* alignment = fAlignment == B_ALIGN_RIGHT ? "right" 362 : (fAlignment == B_ALIGN_LEFT ? "left" : "center"); 363 364 cout << " \\" << endl << "\t" << kAttribute << " \"" << fName << "\" " 365 << kAttrName << " \"" << fPublicName << "\""; 366 367 char c1 = (char)((type >> 24) & 0xFF); 368 char c2 = (char)((type >> 16) & 0xFF); 369 char c3 = (char)((type >> 8) & 0xFF); 370 char c4 = (char)(type & 0xFF); 371 372 ios::fmtflags flags = cout.flags(); 373 374 cout << " \\" << endl << "\t\t" << kAttrType; 375 if (IsPrintableChar(c1) && IsPrintableChar(c2) && 376 IsPrintableChar(c3) && IsPrintableChar(c4)) 377 cout << " '" << c1 << c2 << c3 << c4 << "' "; 378 else 379 cout << "0x" << hex << type; 380 381 cout << " " << kAttrWidth << " " << fWidth 382 << " " << kAttrAlignment << " " << alignment; 383 384 cout << " \\" << endl << "\t\t" << kAttrViewable << " " << fViewable 385 << " " << kAttrEditable << " " << fEditable 386 << " " << kAttrExtra << " " << fExtra; 387 388 cout.flags(flags); 389 } 390 391 392 void 393 MimeAttribute::StoreInto(BMessage* target) 394 { 395 struct attrEntry { 396 const char* name; 397 type_code type; 398 const void* data; 399 } attrEntries[] = { 400 { "attr:name", B_STRING_TYPE, fName.String() }, 401 { "attr:public_name", B_STRING_TYPE, fPublicName.String() }, 402 { "attr:type", B_INT32_TYPE, &fType }, 403 { "attr:viewable", B_BOOL_TYPE, &fViewable }, 404 { "attr:editable", B_BOOL_TYPE, &fEditable }, 405 { "attr:extra", B_BOOL_TYPE, &fExtra }, 406 { "attr:width", B_INT32_TYPE, &fWidth }, 407 { "attr:alignment", B_INT32_TYPE, &fAlignment } 408 }; 409 410 for (size_t i = 0; i < sizeof(attrEntries) / sizeof(attrEntries[0]); i++) { 411 switch (attrEntries[i].type) { 412 case B_STRING_TYPE: 413 fStatus = target->AddString(attrEntries[i].name, 414 (const char*)attrEntries[i].data); 415 break; 416 case B_BOOL_TYPE: 417 fStatus = target->AddBool(attrEntries[i].name, 418 (bool*)attrEntries[i].data); 419 break; 420 case B_INT32_TYPE: 421 fStatus = target->AddInt32(attrEntries[i].name, 422 *(int32*)attrEntries[i].data); 423 break; 424 } 425 426 if (fStatus != B_OK) 427 return; 428 } 429 } 430 431 432 const char* 433 MimeAttribute::UserArgValue(TUserArgs& map, const char* name) 434 { 435 TUserArgsI i = map.find(hash(name)); 436 if (i == map.end()) 437 return NULL; 438 return i->second != NULL ? i->second : ""; 439 } 440 441 442 // #pragma mark - 443 444 // the work-horse of the app - the class encapsulates extended info readed 445 // from the mime type and do all edit and dump operations 446 // 447 class MimeType : public BMimeType { 448 449 public: 450 MimeType(char** argv) throw (Error); 451 ~MimeType(); 452 453 void Process() throw (Error); 454 455 private: 456 status_t _InitCheck(); 457 void _SetTo(const char* mimetype) throw (Error); 458 void _PurgeProperties(); 459 void _Init(char** argv) throw (Error); 460 void _DumpIcon(uint8 *iconData, size_t iconSize); 461 void _Dump(const char* mimetype) throw (Error); 462 void _DoEdit() throw (Error); 463 void _SetIcon(const char* iconData, int32 iconSize); 464 465 const char* _UserArgValue(const char* name); 466 467 status_t fStatus; 468 const char* fToolName; 469 470 // configurable MimeType properties 471 BString fShort; 472 BString fLong; 473 BString fPrefApp; 474 BString fPrefAppSig; 475 BString fSniffRule; 476 BBitmap* fSmallIcon; 477 BBitmap* fBigIcon; 478 uint8* fVectorIcon; 479 size_t fVectorIconSize; 480 481 map<uint32, BString> fExtensions; 482 map<uint32, MimeAttribute> fAttributes; 483 484 // user provided arguments 485 TUserArgs fUserArguments; 486 TUserAttrs fUserAttributes; 487 488 // operation mode switches and flags 489 uint32 fOpMode; 490 bool fDumpNormal; 491 bool fDumpRule; 492 bool fDumpIcon; 493 bool fDumpAll; 494 bool fDoAdd; 495 bool fDoSet; 496 bool fDoForce; 497 bool fDoRemove; 498 bool fCheckSniffRule; 499 }; 500 501 502 MimeType::MimeType(char** argv) throw (Error) 503 : 504 fStatus(B_NO_INIT), 505 fToolName(argv[0]), 506 fSmallIcon(NULL), 507 fBigIcon(NULL), 508 fVectorIcon(NULL), 509 fVectorIconSize(0), 510 fOpMode(kOpModeUndefined), 511 fDumpNormal(false), 512 fDumpRule(false), 513 fDumpIcon(false), 514 fDumpAll(false), 515 fDoAdd(false), 516 fDoSet(false), 517 fDoForce(false), 518 fDoRemove(false), 519 fCheckSniffRule(false) 520 { 521 fToolName = strrchr(argv[0], '/'); 522 fToolName = fToolName == NULL ? argv[0] : fToolName + 1; 523 524 _Init(++argv); 525 } 526 527 528 MimeType::~MimeType() 529 { 530 delete fSmallIcon; 531 delete fBigIcon; 532 free(fVectorIcon); 533 } 534 535 536 void 537 MimeType::_Init(char** argv) throw (Error) 538 { 539 // fill the helper map of options - for quick lookup of arguments 540 map<uint32, const CmdOption*> cmdOptionsMap; 541 for (size_t i = 0; i < sizeof(gCmdOptions) / sizeof(gCmdOptions[0]); i++) 542 cmdOptionsMap.insert(pair<uint32, CmdOption*>( 543 hash(gCmdOptions[i].fName), &gCmdOptions[i])); 544 545 // parse the command line arguments 546 for (char** arg = argv; *arg; arg++) { 547 // non-option arguments are assumed as signature 548 if (**arg != '-') { 549 if (Type() != NULL) 550 throw Error("mime signature already specified: '%s'", Type()); 551 552 SetTo(*arg); 553 continue; 554 } 555 556 // check op.modes, options and attribs 557 uint32 key = hash(*arg); 558 559 map<uint32, const CmdOption*>::iterator I = cmdOptionsMap.find(key); 560 if (I == cmdOptionsMap.end()) 561 throw Error("unknown option '%s'", *arg); 562 563 switch (I->second->fType) { 564 case CmdOption::kHelp: 565 cerr << kUsageMessage; 566 throw Error(kHelpMessage); 567 568 case CmdOption::kMode: 569 // op.modes are exclusive - no simultaneous possible 570 if (fOpMode != kOpModeUndefined) 571 throw Error(kWrongModeMessage); 572 fOpMode = key; 573 574 if (hash(I->second->fName) != hash(kCheckSniffRule)) 575 break; 576 // else -> fallthrough, CheckRule works both as mode and Option 577 case CmdOption::kOption: 578 { 579 const char* name = *arg; 580 const char* param = NULL; 581 if (I->second->fNeedArg) { 582 if (!*++arg) 583 throw Error("argument required for '%s'", name); 584 param = *arg; 585 } 586 587 TUserArgsI A = fUserArguments.find(key); 588 if (A != fUserArguments.end() && !I->second->fNonExclusive) 589 throw Error("option '%s' already specified", name); 590 591 fUserArguments.insert( 592 pair<uint32, const char*>(key, param)); 593 } 594 break; 595 596 case CmdOption::kAttrRoot: 597 if (!*++arg) 598 throw Error("attribute name should be specified"); 599 600 fUserAttributes.resize(fUserAttributes.size() + 1); 601 fUserAttributes.back().insert( 602 pair<uint32, const char*>(key, *arg)); 603 break; 604 605 case CmdOption::kAttrib: 606 { 607 const char* name = *arg; 608 if (fUserAttributes.size() <= 0) 609 throw Error("'%s' allowed only after the '%s' <name>", 610 name, kAttribute); 611 612 if (!*++arg) 613 throw Error("'%s', argument should be specified", name); 614 615 TUserArgsI A = fUserAttributes.back().find(key); 616 if (A != fUserAttributes.back().end()) 617 throw Error("'%s' for attribute '%s' already specified", 618 name, A->second); 619 620 fUserAttributes.back().insert( 621 pair<uint32, const char*>(key, *arg)); 622 } 623 break; 624 625 default: 626 throw Error("internal error. wrong mode: %d", I->second->fType); 627 } 628 } 629 630 // check some mutual exclusive conditions 631 if (fOpMode == kOpModeUndefined) 632 throw Error(kNeedArgMessage); 633 634 if (Type() != NULL && InitCheck() != B_OK) 635 throw Error("error instantiating mime for '%s': %s", 636 Type(), strerror(InitCheck())); 637 638 fDoAdd = fOpMode == hash(kAdd); 639 fDoSet = fOpMode == hash(kSet); 640 fDoForce = fOpMode == hash(kForce); 641 fDoRemove = fOpMode == hash(kRemove); 642 fDumpNormal = fOpMode == hash(kDump); 643 fDumpRule = fOpMode == hash(kDumpSniffRule); 644 fDumpIcon = fOpMode == hash(kDumpIcon); 645 fDumpAll = fOpMode == hash(kDumpAll); 646 fCheckSniffRule = fOpMode == hash(kCheckSniffRule); 647 648 if (fDoAdd || fDoSet || fDoForce || fDoRemove) { 649 if (Type() == NULL) 650 throw Error("signature should be specified"); 651 652 if (!IsValid()) 653 throw Error("mime for '%s' is not valid", Type()); 654 655 } else if (fDumpNormal || fDumpRule || fDumpIcon || fDumpAll) { 656 if (Type() != NULL) { 657 if (!IsValid()) 658 throw Error("mime for '%s' is not valid", Type()); 659 660 if (!IsInstalled()) 661 throw Error("mime for '%s' is not installed", Type()); 662 } 663 } 664 665 // finally force to load mime-specific fileds 666 _SetTo(Type()); 667 } 668 669 670 status_t 671 MimeType::_InitCheck() 672 { 673 return fStatus != B_OK ? fStatus : BMimeType::InitCheck(); 674 } 675 676 677 void 678 MimeType::_PurgeProperties() 679 { 680 fShort.Truncate(0); 681 fLong.Truncate(0); 682 fPrefApp.Truncate(0); 683 fPrefAppSig.Truncate(0); 684 fSniffRule.Truncate(0); 685 686 delete fSmallIcon; 687 fSmallIcon = NULL; 688 689 delete fBigIcon; 690 fBigIcon = NULL; 691 692 fVectorIcon = NULL; 693 free(fVectorIcon); 694 695 fExtensions.clear(); 696 fAttributes.clear(); 697 } 698 699 700 void 701 MimeType::_DumpIcon(uint8 *iconData, size_t iconSize) 702 { 703 // bitmap icons ASCII art :) 704 int lineLimit = iconSize == B_MINI_ICON * B_MINI_ICON 705 ? B_MINI_ICON : B_LARGE_ICON; 706 707 ios::fmtflags flags = cout.flags(); 708 709 for (size_t i = 0; i < iconSize; i++) { 710 if (i % lineLimit == 0 && i != iconSize - 1) 711 cout << "\\" << endl; 712 713 cout << hex << setfill('0') << setw(2) << (uint16) iconData[i]; 714 } 715 716 cout.flags(flags); 717 } 718 719 720 void 721 MimeType::_SetIcon(const char* iconData, int32 iconSize) 722 { 723 uint8* bits = NULL; 724 BRect rect(0, 0, iconSize - 1, iconSize - 1); 725 726 switch (iconSize) { 727 case B_MINI_ICON: 728 if (fSmallIcon == NULL) 729 fSmallIcon = new BBitmap(rect, B_COLOR_8_BIT); 730 bits = (uint8*) fSmallIcon->Bits(); 731 break; 732 case B_LARGE_ICON: 733 if (fBigIcon == NULL) 734 fBigIcon = new BBitmap(rect, B_COLOR_8_BIT); 735 bits = (uint8*) fBigIcon->Bits(); 736 break; 737 default: 738 if (iconSize >= 0) 739 break; 740 free(fVectorIcon); 741 fVectorIconSize = -iconSize; 742 bits = fVectorIcon = (uint8*) malloc(fVectorIconSize); 743 break; 744 } 745 746 if (bits == NULL) 747 throw Error("cannot create icon of size %d", iconSize); 748 749 size_t dataSize = iconSize < 0 ? -iconSize / 2 : iconSize * iconSize; 750 751 for (size_t i = 0; i < dataSize; i++) { 752 stringstream ss; 753 uint16 val; 754 ss << setbase(16) << iconData[i * 2] << iconData[i * 2 + 1]; 755 ss >> val; 756 bits[i] = uint8(val & 0xff); 757 } 758 759 if (iconSize < 0) 760 SetIcon(fVectorIcon, dataSize); 761 else 762 SetIcon(iconSize == B_MINI_ICON ? fSmallIcon : fBigIcon, 763 (icon_size) iconSize); 764 } 765 766 767 void 768 MimeType::_SetTo(const char* mimetype) throw (Error) 769 { 770 if (mimetype == NULL) 771 return; // iterate all types - nothing to load ATM 772 773 if (BMimeType::SetTo(mimetype) != B_OK) 774 throw Error("failed to set mimetype to '%s'", mimetype); 775 776 _PurgeProperties(); 777 778 char buffer[B_MIME_TYPE_LENGTH] = { 0 }; 779 if (GetShortDescription(buffer) == B_OK) 780 fShort.SetTo(buffer, B_MIME_TYPE_LENGTH); 781 782 if (GetLongDescription(buffer) == B_OK) 783 fLong.SetTo(buffer, B_MIME_TYPE_LENGTH); 784 785 entry_ref ref; 786 if (GetAppHint(&ref) == B_OK) { 787 BPath path(&ref); 788 fPrefApp.SetTo(path.Path(), B_MIME_TYPE_LENGTH); 789 } 790 791 if (GetPreferredApp(buffer, B_OPEN) == B_OK) 792 fPrefAppSig.SetTo(buffer, B_MIME_TYPE_LENGTH); 793 794 BString rule; 795 if (GetSnifferRule(&rule) == B_OK) 796 fSniffRule.CharacterEscape(rule.String(), "\'", '\\'); 797 798 BMessage exts; 799 fExtensions.clear(); 800 if (GetFileExtensions(&exts) == B_OK) { 801 uint32 i = 0; 802 const char* ext = NULL; 803 while (exts.FindString("extensions", i++, &ext) == B_OK) 804 fExtensions.insert(pair<uint32, BString>(hash(ext), ext)); 805 } 806 807 BMessage attrs; 808 fAttributes.clear(); 809 if (GetAttrInfo(&attrs) == B_OK) { 810 for (int index = 0; ; index++) { 811 MimeAttribute attr(attrs, index); 812 if (attr.InitCheck() != B_OK) 813 break; 814 815 fAttributes.insert( 816 pair<uint32, MimeAttribute>(hash(attr.fName), attr)); 817 } 818 } 819 820 fSmallIcon = new BBitmap(BRect(0, 0, 15, 15), B_COLOR_8_BIT); 821 if (GetIcon(fSmallIcon, B_MINI_ICON) != B_OK) { 822 delete fSmallIcon; 823 fSmallIcon = NULL; 824 } 825 826 fBigIcon = new BBitmap(BRect(0, 0, 31, 31), B_COLOR_8_BIT); 827 if (GetIcon(fBigIcon, B_LARGE_ICON) != B_OK) { 828 delete fBigIcon; 829 fBigIcon = NULL; 830 } 831 832 if (GetIcon(&fVectorIcon, &fVectorIconSize) != B_OK) 833 fVectorIcon = NULL; 834 } 835 836 837 const char* 838 MimeType::_UserArgValue(const char* name) 839 { 840 TUserArgsI i = fUserArguments.find(hash(name)); 841 if (i == fUserArguments.end()) 842 return NULL; 843 844 return i->second != NULL ? i->second : ""; 845 } 846 847 848 void 849 MimeType::_Dump(const char* mimetype) throw (Error) 850 { 851 // _Dump can be called as part of all types iteration - so set to required 852 if (Type() == NULL || strcasecmp(Type(), mimetype) != 0) 853 _SetTo(mimetype); 854 855 // apps have themself as preferred app - use it to handle 856 // -includeApps option - do not dump applications info 857 if (!fPrefApp.IsEmpty() 858 && fPrefApp.ICompare(mimetype) == 0 859 && _UserArgValue(kIncludeApps) == NULL) 860 return; 861 862 if (fDumpIcon && fSmallIcon == NULL && fBigIcon == NULL) 863 return; 864 865 if (fDumpRule && fSniffRule.IsEmpty()) 866 return; 867 868 cout << fToolName << " -set " << mimetype; 869 870 if (fDumpNormal || fDumpAll) { 871 if (!fShort.IsEmpty()) 872 cout << " " << kShort << " \"" << fShort << "\""; 873 if (!fLong.IsEmpty()) 874 cout << " " << kLong << " \"" << fLong << "\""; 875 if (!fPrefApp.IsEmpty()) 876 cout << " " << kPreferredApp << " " << fPrefApp; 877 if (!fPrefAppSig.IsEmpty()) 878 cout << " " << kPreferredAppSig << " " << fPrefAppSig; 879 } 880 881 if (!fDumpIcon && !fSniffRule.IsEmpty()) 882 cout << " " << kSniffRule << " '" << fSniffRule << "'"; 883 884 if (fDumpNormal || fDumpAll) 885 for (map<uint32, BString>::iterator i = fExtensions.begin(); 886 i != fExtensions.end(); i++) 887 cout << " " << kExtension << " " << i->second; 888 889 if (fDumpAll) 890 for (map<uint32, MimeAttribute>::iterator i = fAttributes.begin(); 891 i != fAttributes.end(); i++) 892 i->second.Dump(); 893 894 if (fDumpIcon || fDumpAll) { 895 if (fSmallIcon != NULL && fSmallIcon->Bits() != NULL) { 896 cout << " \\" << endl << "\t" << kMiniIcon << " "; 897 _DumpIcon((uint8*) fSmallIcon->Bits(), fSmallIcon->BitsLength()); 898 } 899 900 if (fBigIcon != NULL && fBigIcon->Bits() != NULL) { 901 cout << " \\" << endl << "\t" << kLargeIcon << " "; 902 _DumpIcon((uint8*) fBigIcon->Bits(), fBigIcon->BitsLength()); 903 } 904 905 if (fVectorIcon != NULL && fVectorIcon != NULL) { 906 cout << " \\" << endl << "\t" << kVectorIcon << " "; 907 _DumpIcon((uint8*) fVectorIcon, fVectorIconSize); 908 } 909 } 910 911 cout << endl; 912 } 913 914 915 void 916 MimeType::_DoEdit() throw (Error) 917 { 918 if (fDoRemove || fDoForce) { 919 status_t result = Delete(); 920 if (result != B_OK) 921 throw Error(strerror(result), result); 922 923 if (fDoRemove) 924 return; 925 926 _PurgeProperties(); 927 } 928 929 if (!IsInstalled() && Install() != B_OK) 930 throw Error("could not install mimetype '%s'", Type()); 931 932 const char* value = _UserArgValue(kShort); 933 if (value != NULL && (!fDoAdd || fShort.IsEmpty())) 934 if (SetShortDescription(value) != B_OK) 935 throw Error("cannot set %s to %s for '%s'", kShort, value, Type()); 936 937 value = _UserArgValue(kLong); 938 if (value != NULL && (!fDoAdd || fLong.IsEmpty())) 939 if (SetLongDescription(value) != B_OK) 940 throw Error("cannot set %s to %s for '%s'", kLong, value, Type()); 941 942 value = _UserArgValue(kPreferredApp); 943 if (value != NULL && (!fDoAdd || fPrefApp.IsEmpty())) { 944 entry_ref appHint; 945 if (get_ref_for_path(value, &appHint) != B_OK) 946 throw Error("%s ref_entry for '%s' couldn't be found for '%s'", 947 kPreferredApp, value, Type()); 948 949 if (SetAppHint(&appHint) != B_OK) 950 throw Error("cannot set %s to %s for '%s'", 951 kPreferredApp, value, Type()); 952 } 953 954 value = _UserArgValue(kPreferredAppSig); 955 if (value != NULL && (!fDoAdd || fPrefAppSig.IsEmpty())) 956 if (SetPreferredApp(value) != B_OK) 957 throw Error("cannot set %s to %s for '%s'", 958 kPreferredAppSig, value, Type()); 959 960 value = _UserArgValue(kSniffRule); 961 if (value != NULL && (!fDoAdd || fSniffRule.IsEmpty())) 962 if (SetSnifferRule(value) != B_OK) 963 throw Error("cannot set %s to %s for '%s'", 964 kSniffRule, value, Type()); 965 966 value = _UserArgValue(kMiniIcon); 967 if (value != NULL && (!fDoAdd || fSmallIcon == NULL)) { 968 int32 iconSize = strlen(value); 969 if (iconSize / 2 != B_MINI_ICON * B_MINI_ICON) 970 throw Error("cannot set %s for '%s'. Hex data size %d is invalid", 971 kMiniIcon, Type(), iconSize); 972 973 _SetIcon(value, B_MINI_ICON); 974 } 975 976 value = _UserArgValue(kLargeIcon); 977 if (value != NULL && (!fDoAdd || fBigIcon == NULL)) { 978 int32 iconSize = strlen(value); 979 if (iconSize / 2 != B_LARGE_ICON * B_LARGE_ICON) 980 throw Error("cannot set %s for '%s'. Hex data size %d is invalid", 981 kLargeIcon, Type(), iconSize); 982 983 _SetIcon(value, B_LARGE_ICON); 984 } 985 986 value = _UserArgValue(kVectorIcon); 987 if (value != NULL && (!fDoAdd || fVectorIcon == NULL)) { 988 int32 iconSize = strlen(value); 989 if ((iconSize % 2) != 0) 990 throw Error("cannot set %s for '%s'. Hex data size %d is invalid", 991 kVectorIcon, Type(), iconSize); 992 993 // vector icon size is negative intended 994 _SetIcon(value, -iconSize); 995 } 996 997 // handle extensions update 998 pair<TUserArgsI, TUserArgsI> exts 999 = fUserArguments.equal_range(hash(kExtension)); 1000 for (TUserArgsI i = exts.first; i != exts.second; i++) { 1001 uint32 key = hash(i->second); 1002 if (fExtensions.find(key) == fExtensions.end()) 1003 fExtensions.insert(pair<uint32, BString>(key, i->second)); 1004 } 1005 1006 if (exts.first != exts.second) { 1007 BMessage msg; 1008 for (map<uint32, BString>::iterator i = fExtensions.begin(); 1009 i != fExtensions.end(); i++) 1010 if (msg.AddString("extensions", i->second.String()) != B_OK) 1011 throw Error("extension '%s' couldn't be added", 1012 i->second.String()); 1013 1014 if (SetFileExtensions(&msg) != B_OK) 1015 throw Error("set file extensions failed"); 1016 } 1017 1018 // take care about attribute trees 1019 for (TUserAttrsI userAttr = fUserAttributes.begin(); 1020 userAttr != fUserAttributes.end(); userAttr++ ) 1021 { 1022 // search for -attribute "name" in args map 1023 TUserArgsI attrArgs = userAttr->find(hash(kAttribute)); 1024 if (attrArgs == userAttr->end()) 1025 throw Error("internal error: %s arg not found", kAttribute); 1026 1027 // check if we already have this attribute cached 1028 map<uint32, MimeAttribute>::iterator 1029 attr = fAttributes.find(hash(attrArgs->second)); 1030 if (attr == fAttributes.end()) { 1031 // add new one 1032 MimeAttribute mimeAttr(*userAttr); 1033 fAttributes.insert( 1034 pair<uint32, MimeAttribute>(hash(mimeAttr.fName), mimeAttr)); 1035 } else if (!fDoAdd) 1036 attr->second.SyncWith(*userAttr); 1037 } 1038 1039 if (fAttributes.size() > 0) { 1040 BMessage msg; 1041 for (map<uint32, MimeAttribute>::iterator i = fAttributes.begin(); 1042 i != fAttributes.end(); i++) 1043 { 1044 i->second.StoreInto(&msg); 1045 if (i->second.InitCheck() != B_OK) 1046 throw Error("storing attributes in message failed"); 1047 } 1048 1049 if (SetAttrInfo(&msg) != B_OK) 1050 throw Error("set mimetype attributes failed"); 1051 } 1052 } 1053 1054 1055 void 1056 MimeType::Process() throw (Error) 1057 { 1058 if (fCheckSniffRule) { 1059 TUserArgsI I = fUserArguments.find(fOpMode); 1060 if (I == fUserArguments.end()) 1061 throw Error("Sniffer rule is empty"); 1062 1063 BString error; 1064 status_t result = BMimeType::CheckSnifferRule(I->second, &error); 1065 if (result == B_OK) 1066 cerr << I->second << endl << "Sniffer rule is correct" << endl; 1067 else 1068 cerr << error.String() << endl; 1069 1070 return; 1071 } 1072 1073 if (fDoAdd || fDoSet || fDoForce || fDoRemove) { 1074 _DoEdit(); 1075 return; 1076 } 1077 1078 if (fDumpNormal || fDumpRule || fDumpIcon || fDumpAll) { 1079 if (Type() != NULL) { 1080 _Dump(Type()); 1081 return; 1082 } 1083 1084 BMessage superTypes; 1085 int32 superCount = 0; 1086 type_code type = B_INT32_TYPE; 1087 if (BMimeType::GetInstalledSupertypes(&superTypes) != B_OK 1088 || superTypes.GetInfo("super_types", &type, &superCount) != B_OK) 1089 throw Error("super types enumeration failed"); 1090 1091 for (int32 si = 0; si < superCount; si++) { 1092 const char* superName = NULL; 1093 if (superTypes.FindString("super_types", si, &superName) != B_OK) 1094 throw Error("name for supertype #%d not found", si); 1095 1096 BMessage types; 1097 if (BMimeType::GetInstalledTypes(superName, &types) != B_OK) 1098 throw Error("mimetypes of supertype '%s' not found", superName); 1099 1100 int32 count = 0; 1101 if (types.GetInfo("types", &type, &count) != B_OK) 1102 continue; // no sub-types? 1103 1104 for (int32 i = 0; i < count; i++) { 1105 const char* name = NULL; 1106 if (types.FindString("types", i, &name) != B_OK) 1107 throw Error("name for type %s/#%d not found", superName, i); 1108 1109 _Dump(name); 1110 } 1111 } 1112 } 1113 } 1114 1115 1116 int 1117 main(int argc, char** argv) 1118 { 1119 // AppServer link is required to work with bitmaps 1120 BApplication app("application/x-vnd.haiku.setmime"); 1121 1122 try { 1123 1124 if (argc < 2) 1125 throw Error(kNeedArgMessage); 1126 1127 MimeType mimetype(argv); 1128 1129 mimetype.Process(); 1130 1131 } catch(exception& exc) { 1132 cerr << argv[0] << " : " << exc.what() << endl; 1133 cerr << kUsageMessage; 1134 return B_ERROR; 1135 } 1136 1137 return B_OK; 1138 } 1139 1140