xref: /haiku/src/servers/registrar/MIMEManager.cpp (revision e81a954787e50e56a7f06f72705b7859b6ab06d1)
1 /*
2  * Copyright 2002-2013, Haiku Inc.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Ingo Weinhold (bonefish@users.sf.net)
7  *		Tyler Dauwalder
8  */
9 
10 
11 #include "MIMEManager.h"
12 
13 #include <stdio.h>
14 #include <string>
15 
16 #include <Bitmap.h>
17 #include <Message.h>
18 #include <Messenger.h>
19 #include <Path.h>
20 #include <RegistrarDefs.h>
21 #include <String.h>
22 #include <TypeConstants.h>
23 
24 #include <mime/AppMetaMimeCreator.h>
25 #include <mime/database_support.h>
26 #include <mime/MimeSnifferAddonManager.h>
27 #include <mime/TextSnifferAddon.h>
28 
29 #include "CreateAppMetaMimeThread.h"
30 #include "MessageDeliverer.h"
31 #include "UpdateMimeInfoThread.h"
32 
33 
34 using namespace std;
35 using namespace BPrivate;
36 using BPrivate::Storage::Mime::MimeSnifferAddonManager;
37 using BPrivate::Storage::Mime::TextSnifferAddon;
38 
39 
40 /*!	\class MIMEManager
41 	\brief MIMEManager handles communication between BMimeType and the system-wide
42 	MimeDatabase object for BMimeType's write and non-atomic read functions.
43 
44 */
45 
46 
47 static MimeSnifferAddonManager*
48 init_mime_sniffer_add_on_manager()
49 {
50 	if (MimeSnifferAddonManager::CreateDefault() != B_OK)
51 		return NULL;
52 
53 	MimeSnifferAddonManager* manager = MimeSnifferAddonManager::Default();
54 	manager->AddMimeSnifferAddon(new(nothrow) TextSnifferAddon(
55 		BPrivate::Storage::Mime::default_database_location()));
56 	return manager;
57 }
58 
59 
60 class MIMEManager::DatabaseLocker
61 	: public BPrivate::Storage::Mime::MimeEntryProcessor::DatabaseLocker {
62 public:
63 	DatabaseLocker(MIMEManager* manager)
64 		:
65 		fManager(manager)
66 	{
67 	}
68 
69 	virtual bool Lock()
70 	{
71 		return fManager->Lock();
72 	}
73 
74 	virtual void Unlock()
75 	{
76 		fManager->Unlock();
77 	}
78 
79 private:
80 	MIMEManager*	fManager;
81 };
82 
83 
84 /*!	\brief Creates and initializes a MIMEManager.
85 */
86 MIMEManager::MIMEManager()
87 	:
88 	BLooper("main_mime"),
89 	fDatabase(BPrivate::Storage::Mime::default_database_location(),
90 		init_mime_sniffer_add_on_manager(), this),
91 	fDatabaseLocker(new(std::nothrow) DatabaseLocker(this)),
92 	fThreadManager()
93 {
94 	AddHandler(&fThreadManager);
95 }
96 
97 
98 /*!	\brief Frees all resources associate with this object.
99 */
100 MIMEManager::~MIMEManager()
101 {
102 }
103 
104 
105 /*!	\brief Overrides the super class version to handle the MIME specific
106 		   messages.
107 	\param message The message to be handled
108 */
109 void
110 MIMEManager::MessageReceived(BMessage *message)
111 {
112 	BMessage reply;
113 	status_t err;
114 
115 	switch (message->what) {
116 		case B_REG_MIME_SET_PARAM:
117 			HandleSetParam(message);
118 			break;
119 
120 		case B_REG_MIME_DELETE_PARAM:
121 			HandleDeleteParam(message);
122 			break;
123 
124 		case B_REG_MIME_START_WATCHING:
125 		case B_REG_MIME_STOP_WATCHING:
126 		{
127 			BMessenger messenger;
128 			err = message->FindMessenger("target", &messenger);
129 			if (!err) {
130 				err = message->what == B_REG_MIME_START_WATCHING
131 					? fDatabase.StartWatching(messenger)
132 					: fDatabase.StopWatching(messenger);
133 			}
134 
135 			reply.what = B_REG_RESULT;
136 			reply.AddInt32("result", err);
137 			message->SendReply(&reply, this);
138 			break;
139 		}
140 
141 		case B_REG_MIME_INSTALL:
142 		case B_REG_MIME_DELETE:
143 		{
144 			const char *type;
145 			err = message->FindString("type", &type);
146 			if (!err)
147 				err = message->what == B_REG_MIME_INSTALL
148 					? fDatabase.Install(type) : fDatabase.Delete(type);
149 
150 			reply.what = B_REG_RESULT;
151 			reply.AddInt32("result", err);
152 			message->SendReply(&reply, this);
153 			break;
154 		}
155 
156 		case B_REG_MIME_GET_INSTALLED_TYPES:
157 		{
158 			const char *supertype;
159 			err = message->FindString("supertype", &supertype);
160 			if (err == B_NAME_NOT_FOUND)
161 				err = fDatabase.GetInstalledTypes(&reply);
162 			else if (!err)
163 				err = fDatabase.GetInstalledTypes(supertype, &reply);
164 
165 			reply.what = B_REG_RESULT;
166 			reply.AddInt32("result", err);
167 			message->SendReply(&reply, this);
168 			break;
169 		}
170 
171 		case B_REG_MIME_GET_INSTALLED_SUPERTYPES:
172 		{
173 			err = fDatabase.GetInstalledSupertypes(&reply);
174 
175 			reply.what = B_REG_RESULT;
176 			reply.AddInt32("result", err);
177 			message->SendReply(&reply, this);
178 			break;
179 		}
180 
181 		case B_REG_MIME_GET_SUPPORTING_APPS:
182 		{
183 			const char *type;
184 			err = message->FindString("type", &type);
185 			if (!err)
186 				err = fDatabase.GetSupportingApps(type, &reply);
187 
188 			reply.what = B_REG_RESULT;
189 			reply.AddInt32("result", err);
190 			message->SendReply(&reply, this);
191 			break;
192 		}
193 
194 		case B_REG_MIME_GET_ASSOCIATED_TYPES:
195 		{
196 			const char *extension;
197 			err = message->FindString("extension", &extension);
198 			if (!err)
199 				err = fDatabase.GetAssociatedTypes(extension, &reply);
200 
201 			reply.what = B_REG_RESULT;
202 			reply.AddInt32("result", err);
203 			message->SendReply(&reply, this);
204 			break;
205 		}
206 
207 		case B_REG_MIME_SNIFF:
208 		{
209 			BString str;
210 			entry_ref ref;
211 			const char *filename;
212 			err = message->FindString("filename", &filename);
213 			if (!err)
214 				err = fDatabase.GuessMimeType(filename, &str);
215 			else if (err == B_NAME_NOT_FOUND) {
216 				err = message->FindRef("file ref", &ref);
217 				if (!err)
218 					err = fDatabase.GuessMimeType(&ref, &str);
219 				else if (err == B_NAME_NOT_FOUND) {
220 					const void *data;
221 					ssize_t dataSize;
222 					err = message->FindData("data", B_RAW_TYPE, &data,
223 						&dataSize);
224 					if (!err)
225 						err = fDatabase.GuessMimeType(data, dataSize, &str);
226 				}
227 			}
228 			if (!err)
229 				err = reply.AddString("mime type", str);
230 
231 			reply.what = B_REG_RESULT;
232 			reply.AddInt32("result", err);
233 			message->SendReply(&reply, this);
234 			break;
235 		}
236 
237 		case B_REG_MIME_CREATE_APP_META_MIME:
238 		case B_REG_MIME_UPDATE_MIME_INFO:
239 		{
240 			using BPrivate::Storage::Mime::MimeUpdateThread;
241 			using BPrivate::Storage::Mime::CreateAppMetaMimeThread;
242 			using BPrivate::Storage::Mime::UpdateMimeInfoThread;
243 
244 			entry_ref root;
245 			bool recursive;
246 			bool synchronous = false;
247 			int32 force;
248 
249 			MimeUpdateThread *thread = NULL;
250 
251 			status_t threadStatus = B_NO_INIT;
252 			bool messageIsDetached = false;
253 			bool stillOwnsThread = true;
254 
255 			// Gather our arguments
256 			err = message->FindRef("entry", &root);
257 			if (!err)
258 				err = message->FindBool("recursive", &recursive);
259 			if (!err)
260 				err = message->FindBool("synchronous", &synchronous);
261 			if (!err)
262 				err = message->FindInt32("force", &force);
263 
264 			// Detach the message for synchronous calls
265 			if (!err && synchronous) {
266 				DetachCurrentMessage();
267 				messageIsDetached = true;
268 			}
269 
270 			// Create the appropriate flavor of mime update thread
271 			if (!err) {
272 				switch (message->what) {
273 					case B_REG_MIME_CREATE_APP_META_MIME:
274 						thread = new(nothrow) CreateAppMetaMimeThread(
275 							synchronous ? "create_app_meta_mime (s)"
276 								: "create_app_meta_mime (a)",
277 							B_NORMAL_PRIORITY + 1, &fDatabase, fDatabaseLocker,
278 							BMessenger(&fThreadManager), &root, recursive,
279 							force, synchronous ? message : NULL);
280 						break;
281 
282 					case B_REG_MIME_UPDATE_MIME_INFO:
283 						thread = new(nothrow) UpdateMimeInfoThread(synchronous
284 								? "update_mime_info (s)"
285 								: "update_mime_info (a)",
286 							B_NORMAL_PRIORITY + 1, &fDatabase, fDatabaseLocker,
287 							BMessenger(&fThreadManager), &root, recursive,
288 							force, synchronous ? message : NULL);
289 						break;
290 
291 					default:
292 						err = B_BAD_VALUE;
293 						break;
294 				}
295 			}
296 			if (!err)
297 				err = thread ? B_OK : B_NO_MEMORY;
298 			if (!err)
299 				err = threadStatus = thread->InitCheck();
300 
301 			// Launch the thread
302 			if (!err) {
303 				err = fThreadManager.LaunchThread(thread);
304 				if (!err) {
305 					stillOwnsThread = false;
306 				}
307 			}
308 
309 			// If something went wrong, we need to notify the sender regardless. However,
310 			// if this is a synchronous call, we've already detached the message, and must
311 			// be careful that it gets deleted once and only once. Thus, if the MimeUpdateThread
312 			// object was created successfully, we don't need to delete the message, as that
313 			// object has assumed control of it. Otherwise, we are still responsible.
314 			if (err || !synchronous) {
315 				// Send the reply
316 				reply.what = B_REG_RESULT;
317 				reply.AddInt32("result", err);
318 				message->SendReply(&reply, this);
319 			}
320 			// Delete the message if necessary
321 			if (messageIsDetached && threadStatus != B_OK)
322 				delete message;
323 			// Delete the thread if necessary
324 			if (stillOwnsThread)
325 				delete thread;
326 			break;
327 		}
328 
329 		default:
330 			printf("MIMEMan: msg->what == %" B_PRIx32 " (%.4s)\n",
331 				message->what, (char*)&(message->what));
332 			BLooper::MessageReceived(message);
333 			break;
334 	}
335 }
336 
337 
338 status_t
339 MIMEManager::Notify(BMessage* message, const BMessenger& target)
340 {
341 	return MessageDeliverer::Default()->DeliverMessage(message, target);
342 }
343 
344 
345 //! Handles all B_REG_MIME_SET_PARAM messages
346 void
347 MIMEManager::HandleSetParam(BMessage *message)
348 {
349 	status_t err;
350 	int32 which;
351 	const char *type;
352 
353 	err = message->FindString("type", &type);
354 	if (!err)
355 		err = message->FindInt32("which", &which);
356 	if (!err) {
357 		switch (which) {
358 			case B_REG_MIME_APP_HINT:
359 			{
360 				entry_ref ref;
361 				err = message->FindRef("app hint", &ref);
362 				if (!err)
363 					err = fDatabase.SetAppHint(type, &ref);
364 				break;
365 			}
366 
367 			case B_REG_MIME_ATTR_INFO:
368 			{
369 				BMessage info;
370 				err = message->FindMessage("attr info", &info);
371 				if (!err)
372 					err = fDatabase.SetAttrInfo(type, &info);
373 				break;
374 			}
375 
376 			case B_REG_MIME_DESCRIPTION:
377 			{
378 				bool isLong;
379 				const char *description;
380 				err = message->FindBool("long", &isLong);
381 				if (!err)
382 					err = message->FindString("description", &description);
383 				if (!err) {
384 					err = isLong
385 						? fDatabase.SetLongDescription(type, description)
386 						: fDatabase.SetShortDescription(type, description);
387 				}
388 				break;
389 			}
390 
391 			case B_REG_MIME_FILE_EXTENSIONS:
392 			{
393 				BMessage extensions;
394 				err = message->FindMessage("extensions", &extensions);
395 				if (!err)
396 					err = fDatabase.SetFileExtensions(type, &extensions);
397 				break;
398 			}
399 
400 			case B_REG_MIME_ICON:
401 			case B_REG_MIME_ICON_FOR_TYPE:
402 			{
403 				const void *data;
404 				ssize_t dataSize;
405 				int32 size;
406 				err = message->FindData("icon data", B_RAW_TYPE, &data,
407 					&dataSize);
408 				if (!err)
409 					err = message->FindInt32("icon size", &size);
410 				if (which == B_REG_MIME_ICON_FOR_TYPE) {
411 					const char *fileType;
412 					if (!err)
413 						err = message->FindString("file type", &fileType);
414 					if (!err) {
415 						err = size == -1
416 							? fDatabase.SetIconForType(type, fileType, data,
417 								dataSize)
418 							: fDatabase.SetIconForType(type, fileType, data,
419 								dataSize, (icon_size)size);
420 					}
421 				} else {
422 					if (!err) {
423 						err = size == -1
424 							? fDatabase.SetIcon(type, data, dataSize)
425 							: fDatabase.SetIcon(type, data, dataSize,
426 								(icon_size)size);
427 					}
428 				}
429 				break;
430 				// End temporary fix code
431 			}
432 
433 			case B_REG_MIME_PREFERRED_APP:
434 			{
435 				const char *signature;
436 				int32 verb;
437 				err = message->FindString("signature", &signature);
438 				if (!err)
439 					err = message->FindInt32("app verb", &verb);
440 				if (!err) {
441 					err = fDatabase.SetPreferredApp(type, signature,
442 						(app_verb)verb);
443 				}
444 				break;
445 			}
446 
447 			case B_REG_MIME_SNIFFER_RULE:
448 			{
449 				const char *rule;
450 				err = message->FindString("sniffer rule", &rule);
451 				if (!err)
452 					err = fDatabase.SetSnifferRule(type, rule);
453 				break;
454 			}
455 
456 			case B_REG_MIME_SUPPORTED_TYPES:
457 			{
458 				BMessage types;
459 				bool fullSync = true;
460 				err = message->FindMessage("types", &types);
461 				if (!err)
462 					err = message->FindBool("full sync", &fullSync);
463 				if (!err)
464 					err = fDatabase.SetSupportedTypes(type, &types, fullSync);
465 				break;
466 			}
467 
468 			default:
469 				err = B_BAD_VALUE;
470 				break;
471 		}
472 	}
473 
474 	BMessage reply(B_REG_RESULT);
475 	reply.AddInt32("result", err);
476 	message->SendReply(&reply, this);
477 }
478 
479 
480 //! Handles all B_REG_MIME_SET_PARAM messages
481 void
482 MIMEManager::HandleDeleteParam(BMessage *message)
483 {
484 	status_t err;
485 	int32 which;
486 	const char *type;
487 
488 	err = message->FindString("type", &type);
489 	if (!err)
490 		err = message->FindInt32("which", &which);
491 	if (!err) {
492 		switch (which) {
493 			case B_REG_MIME_APP_HINT:
494 				err = fDatabase.DeleteAppHint(type);
495 				break;
496 
497 			case B_REG_MIME_ATTR_INFO:
498 				err = fDatabase.DeleteAttrInfo(type);
499 				break;
500 
501 			case B_REG_MIME_DESCRIPTION:
502 			{
503 				bool isLong;
504 				err = message->FindBool("long", &isLong);
505 				if (!err) {
506 					err = isLong
507 						? fDatabase.DeleteLongDescription(type)
508 						: fDatabase.DeleteShortDescription(type);
509 				}
510 				break;
511 			}
512 
513 			case B_REG_MIME_FILE_EXTENSIONS:
514 				err = fDatabase.DeleteFileExtensions(type);
515 				break;
516 
517 			case B_REG_MIME_ICON:
518 			case B_REG_MIME_ICON_FOR_TYPE:
519 			{
520 				int32 size;
521 				err = message->FindInt32("icon size", &size);
522 				if (which == B_REG_MIME_ICON_FOR_TYPE) {
523 					const char *fileType;
524 					if (!err)
525 						err = message->FindString("file type", &fileType);
526 					if (!err) {
527 						err = size == -1
528 							? fDatabase.DeleteIconForType(type, fileType)
529 							: fDatabase.DeleteIconForType(type, fileType,
530 								(icon_size)size);
531 					}
532 				} else {
533 					if (!err) {
534 						err = size == -1
535 							? fDatabase.DeleteIcon(type)
536 							: fDatabase.DeleteIcon(type, (icon_size)size);
537 					}
538 				}
539 				break;
540 			}
541 
542 			case B_REG_MIME_PREFERRED_APP:
543 			{
544 				int32 verb;
545 				err = message->FindInt32("app verb", &verb);
546 				if (!err)
547 					err = fDatabase.DeletePreferredApp(type, (app_verb)verb);
548 				break;
549 			}
550 
551 			case B_REG_MIME_SNIFFER_RULE:
552 				err = fDatabase.DeleteSnifferRule(type);
553 				break;
554 
555 			case B_REG_MIME_SUPPORTED_TYPES:
556 			{
557 				bool fullSync;
558 				err = message->FindBool("full sync", &fullSync);
559 				if (!err)
560 					err = fDatabase.DeleteSupportedTypes(type, fullSync);
561 				break;
562 			}
563 
564 			default:
565 				err = B_BAD_VALUE;
566 				break;
567 		}
568 	}
569 
570 	BMessage reply(B_REG_RESULT);
571 	reply.AddInt32("result", err);
572 	message->SendReply(&reply, this);
573 }
574 
575