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