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
34using namespace std;
35using namespace BPrivate;
36using BPrivate::Storage::Mime::MimeSnifferAddonManager;
37using 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
47static MimeSnifferAddonManager*
48init_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
60class MIMEManager::DatabaseLocker
61	: public BPrivate::Storage::Mime::MimeEntryProcessor::DatabaseLocker {
62public:
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
79private:
80	MIMEManager*	fManager;
81};
82
83
84/*!	\brief Creates and initializes a MIMEManager.
85*/
86MIMEManager::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*/
100MIMEManager::~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*/
109void
110MIMEManager::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
338status_t
339MIMEManager::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
346void
347MIMEManager::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
481void
482MIMEManager::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