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