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