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