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