xref: /haiku/src/bin/setmime.cpp (revision 73ad2473e7874b3702cf5b0fdf4c81b747812ed9)
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 				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) throw(Error);
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) throw(Error)
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(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) throw (Error);
485 					~MimeType();
486 
487 	void			Process() throw (Error);
488 
489 private:
490 	status_t		_InitCheck();
491 	void			_SetTo(const char* mimetype) throw (Error);
492 	void			_PurgeProperties();
493 	void			_Init(char** argv) throw (Error);
494 	void			_DumpIcon(uint8 *iconData, size_t iconSize);
495 	void			_Dump(const char* mimetype) throw (Error);
496 	void			_DoEdit() throw (Error);
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) throw (Error)
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) throw (Error)
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(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(*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(I->second->fName) != hash(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(kAdd);
673 	fDoSet = fOpMode == hash(kSet);
674 	fDoForce = fOpMode == hash(kForce);
675 	fDoRemove = fOpMode == hash(kRemove);
676 	fDumpNormal = fOpMode == hash(kDump);
677 	fDumpRule = fOpMode == hash(kDumpSniffRule);
678 	fDumpIcon = fOpMode == hash(kDumpIcon);
679 	fDumpAll = fOpMode == hash(kDumpAll);
680 	fCheckSniffRule = fOpMode == hash(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 	fVectorIcon = NULL;
727 	free(fVectorIcon);
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) throw (Error)
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(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(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(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) throw (Error)
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() throw (Error)
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(kExtension));
1034 	for (TUserArgsI i = exts.first; i != exts.second; i++) {
1035 		uint32 key = hash(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(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(attrArgs->second));
1064 		if (attr == fAttributes.end()) {
1065 			// add new one
1066 			MimeAttribute mimeAttr(*userAttr);
1067 			fAttributes.insert(
1068 				pair<uint32, MimeAttribute>(hash(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() throw (Error)
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