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