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