1/* 2 * Copyright 2002-2006, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Tyler Dauwalder 7 * Ingo Weinhold, bonefish@users.sf.net 8 * Axel D��rfler, axeld@pinc-software.de 9 */ 10 11 12#include <mime/InstalledTypes.h> 13 14#include <stdio.h> 15 16#include <new> 17 18#include <Directory.h> 19#include <Entry.h> 20#include <Message.h> 21#include <MimeType.h> 22#include <String.h> 23 24#include <mime/database_support.h> 25#include <mime/DatabaseDirectory.h> 26#include <storage_support.h> 27 28 29#define DBG(x) x 30//#define DBG(x) 31#define OUT printf 32 33namespace BPrivate { 34namespace Storage { 35namespace Mime { 36 37/*! 38 \class InstalledTypes 39 \brief Installed types information for the entire database 40*/ 41 42//! Constructs a new InstalledTypes object 43InstalledTypes::InstalledTypes(DatabaseLocation* databaseLocation) 44 : 45 fDatabaseLocation(databaseLocation), 46 fCachedMessage(NULL), 47 fCachedSupertypesMessage(NULL), 48 fHaveDoneFullBuild(false) 49{ 50} 51 52 53//! Destroys the InstalledTypes object 54InstalledTypes::~InstalledTypes() 55{ 56 delete fCachedSupertypesMessage; 57 delete fCachedMessage; 58} 59 60 61/*! \brief Returns a list of all currently installed types in the 62 pre-allocated \c BMessage pointed to by \c types. 63 64 See \c BMimeType::GetInstalledTypes(BMessage*) for more information. 65*/ 66status_t 67InstalledTypes::GetInstalledTypes(BMessage *types) 68{ 69 status_t err = types ? B_OK : B_BAD_VALUE; 70 // See if we need to do our initial build still 71 if (!err && !fHaveDoneFullBuild) 72 err = _BuildInstalledTypesList(); 73 74 // See if we need to fill up a new message 75 if (!err && !fCachedMessage) 76 err = _CreateMessageWithTypes(&fCachedMessage); 77 78 // If we get this far, there a cached message waiting 79 if (!err) 80 *types = *fCachedMessage; 81 82 return err; 83} 84 85 86/*! \brief Returns a list of all currently installed types of the given 87 supertype in the pre-allocated \c BMessage pointed to by \c types. 88 89 See \c BMimeType::GetInstalledTypes(const char*, BMessage*) for more 90 information. 91*/ 92status_t 93InstalledTypes::GetInstalledTypes(const char *supertype, BMessage *types) 94{ 95 if (supertype == NULL || types == NULL) 96 return B_BAD_VALUE; 97 98 // Verify the supertype is valid *and* is a supertype 99 100 BMimeType mime; 101 BMimeType super; 102 // Make sure the supertype is valid 103 status_t err = mime.SetTo(supertype); 104 // Make sure it's really a supertype 105 if (!err && !mime.IsSupertypeOnly()) 106 err = B_BAD_VALUE; 107 // See if we need to do our initial build still 108 if (!err && !fHaveDoneFullBuild) 109 err = _BuildInstalledTypesList(); 110 111 // Ask the appropriate supertype for its list 112 if (!err) { 113 std::map<std::string, Supertype>::iterator i = fSupertypes.find(supertype); 114 if (i != fSupertypes.end()) 115 err = i->second.GetInstalledSubtypes(types); 116 else 117 err = B_NAME_NOT_FOUND; 118 } 119 return err; 120} 121 122 123/*! \brief Returns a list of all currently installed supertypes in the 124 pre-allocated \c BMessage pointed to by \c types. 125 126 See \c BMimeType::GetInstalledSupertypes() for more information. 127*/ 128status_t 129InstalledTypes::GetInstalledSupertypes(BMessage *types) 130{ 131 if (types == NULL) 132 return B_BAD_VALUE; 133 134 status_t err = B_OK; 135 136 // See if we need to do our initial build still 137 if (!fHaveDoneFullBuild) 138 err = _BuildInstalledTypesList(); 139 140 // See if we need to fill up a new message 141 if (!err && !fCachedSupertypesMessage) 142 err = _CreateMessageWithSupertypes(&fCachedSupertypesMessage); 143 144 // If we get this far, there's a cached message waiting 145 if (!err) 146 *types = *fCachedSupertypesMessage; 147 148 return err; 149} 150 151 152/*! \brief Adds the given type to the appropriate lists of installed types. 153 154 If cached messages exist, the type is simply appended to the end of 155 the current type list. 156*/ 157status_t 158InstalledTypes::AddType(const char *type) 159{ 160 if (!fHaveDoneFullBuild) 161 return B_OK; 162 163 BMimeType mime(type); 164 if (type == NULL || mime.InitCheck() != B_OK) 165 return B_BAD_VALUE; 166 167 // Find the / in the string, if one exists 168 uint i; 169 size_t len = strlen(type); 170 for (i = 0; i < len; i++) { 171 if (type[i] == '/') 172 break; 173 } 174 if (i == len) { 175 // Supertype only 176 std::map<std::string, Supertype>::iterator i; 177 return _AddSupertype(type, i); 178 } 179 180 // Copy the supertype 181 char super[B_PATH_NAME_LENGTH]; 182 strncpy(super, type, i); 183 super[i] = 0; 184 185 // Get a pointer to the subtype 186 const char *sub = &(type[i+1]); 187 188 // Add the subtype (which will add the supertype if necessary) 189 return _AddSubtype(super, sub); 190} 191 192 193/*! \brief Removes the given type from the appropriate installed types lists. 194 195 Any corresponding cached messages are invalidated. 196*/ 197status_t 198InstalledTypes::RemoveType(const char *type) 199{ 200 if (!fHaveDoneFullBuild) 201 return B_OK; 202 203 BMimeType mime(type); 204 if (type == NULL || mime.InitCheck() != B_OK) 205 return B_BAD_VALUE; 206 207 // Find the / in the string, if one exists 208 uint i; 209 size_t len = strlen(type); 210 for (i = 0; i < len; i++) { 211 if (type[i] == '/') 212 break; 213 } 214 if (i == len) { 215 // Supertype only 216 return _RemoveSupertype(type); 217 } 218 219 // Copy the supertype 220 char super[B_PATH_NAME_LENGTH]; 221 strncpy(super, type, i); 222 super[i] = 0; 223 224 // Get a pointer to the subtype 225 const char *sub = &(type[i+1]); 226 227 // Remove the subtype 228 return _RemoveSubtype(super, sub); 229} 230 231 232/*! \brief Adds the given supertype to the supertype map. 233 \return 234 - B_OK: success, even if the supertype already existed in the map 235 - "error code": failure 236*/ 237status_t 238InstalledTypes::_AddSupertype(const char *super, 239 std::map<std::string, Supertype>::iterator &i) 240{ 241 if (super == NULL) 242 return B_BAD_VALUE; 243 244 status_t err = B_OK; 245 246 i = fSupertypes.find(super); 247 if (i == fSupertypes.end()) { 248 Supertype &supertype = fSupertypes[super]; 249 supertype.SetName(super); 250 if (fCachedMessage) 251 err = fCachedMessage->AddString(kTypesField, super); 252 if (!err && fCachedSupertypesMessage) 253 err = fCachedSupertypesMessage->AddString(kSupertypesField, super); 254 } 255 256 return err; 257} 258 259 260/*! \brief Adds the given subtype to the given supertype's lists of installed types. 261 262 If the supertype does not yet exist, it is created. 263 264 \param super The supertype 265 \param sub The subtype (subtype only; no "supertype/subtype" types please) 266 \return 267 - B_OK: success 268 - B_NAME_IN_USE: The subtype already exists in the subtype list 269 - "error code": failure 270*/ 271status_t 272InstalledTypes::_AddSubtype(const char *super, const char *sub) 273{ 274 if (super == NULL || sub == NULL) 275 return B_BAD_VALUE; 276 277 std::map<std::string, Supertype>::iterator i; 278 status_t err = _AddSupertype(super, i); 279 if (!err) 280 err = _AddSubtype(i->second, sub); 281 282 return err; 283} 284 285 286/*! \brief Adds the given subtype to the given supertype's lists of installed types. 287 288 \param super The supertype object 289 \param sub The subtype (subtype only; no "supertype/subtype" types please) 290 \return 291 - B_OK: success 292 - B_NAME_IN_USE: The subtype already exists in the subtype list 293 - "error code": failure 294*/ 295status_t 296InstalledTypes::_AddSubtype(Supertype &super, const char *sub) 297{ 298 if (sub == NULL) 299 return B_BAD_VALUE; 300 301 status_t err = super.AddSubtype(sub); 302 if (!err && fCachedMessage) { 303 char type[B_PATH_NAME_LENGTH]; 304 sprintf(type, "%s/%s", super.GetName(), sub); 305 err = fCachedMessage->AddString("types", type); 306 } 307 return err; 308} 309 310 311/*! \brief Removes the given supertype and any corresponding subtypes. 312*/ 313status_t 314InstalledTypes::_RemoveSupertype(const char *super) 315{ 316 if (super == NULL) 317 return B_BAD_VALUE; 318 319 status_t err = fSupertypes.erase(super) == 1 ? B_OK : B_NAME_NOT_FOUND; 320 if (!err) 321 _ClearCachedMessages(); 322 return err; 323} 324 325 326/*! \brief Removes the given subtype from the given supertype. 327*/ 328status_t 329InstalledTypes::_RemoveSubtype(const char *super, const char *sub) 330{ 331 if (super == NULL || sub == NULL) 332 return B_BAD_VALUE; 333 334 status_t err = B_NAME_NOT_FOUND; 335 336 std::map<std::string, Supertype>::iterator i = fSupertypes.find(super); 337 if (i != fSupertypes.end()) { 338 err = i->second.RemoveSubtype(sub); 339 if (!err) 340 _ClearCachedMessages(); 341 } 342 343 return err; 344 345} 346 347 348//! Clears any cached messages and empties the supertype map 349void 350InstalledTypes::_Unset() 351{ 352 _ClearCachedMessages(); 353 fSupertypes.clear(); 354} 355 356 357//! Frees any cached messages and sets their pointers to NULL 358void 359InstalledTypes::_ClearCachedMessages() 360{ 361 delete fCachedSupertypesMessage; 362 delete fCachedMessage; 363 fCachedSupertypesMessage = NULL; 364 fCachedMessage = NULL; 365} 366 367 368/*! \brief Reads through the database and builds a complete set of installed types lists. 369 370 An initial set of cached messages are also created. 371*/ 372status_t 373InstalledTypes::_BuildInstalledTypesList() 374{ 375 status_t err = B_OK; 376 _Unset(); 377 378 // Create empty "cached messages" so proper messages 379 // will be built up as we add new types 380 try { 381 fCachedMessage = new BMessage(); 382 fCachedSupertypesMessage = new BMessage(); 383 } catch (std::bad_alloc&) { 384 err = B_NO_MEMORY; 385 } 386 387 DatabaseDirectory root; 388 if (!err) 389 err = root.Init(fDatabaseLocation); 390 if (!err) { 391 root.Rewind(); 392 while (true) { 393 BEntry entry; 394 err = root.GetNextEntry(&entry); 395 if (err) { 396 // If we've come to the end of list, it's not an error 397 if (err == B_ENTRY_NOT_FOUND) 398 err = B_OK; 399 break; 400 } else { 401 // Check that this entry is both a directory and a valid MIME string 402 char supertype[B_PATH_NAME_LENGTH]; 403 if (entry.IsDirectory() 404 && entry.GetName(supertype) == B_OK 405 && BMimeType::IsValid(supertype)) 406 { 407 // Make sure our string is all lowercase 408 BPrivate::Storage::to_lower(supertype); 409 410 // Add this supertype 411 std::map<std::string, Supertype>::iterator i; 412 if (_AddSupertype(supertype, i) != B_OK) 413 DBG(OUT("Mime::InstalledTypes::BuildInstalledTypesList()" 414 " -- Error adding supertype '%s': 0x%" B_PRIx32 "\n", 415 supertype, err)); 416 Supertype &supertypeRef = fSupertypes[supertype]; 417 418 // Now iterate through this supertype directory and add 419 // all of its subtypes 420 DatabaseDirectory dir; 421 if (dir.Init(fDatabaseLocation, supertype) == B_OK) { 422 dir.Rewind(); 423 while (true) { 424 BEntry subEntry; 425 err = dir.GetNextEntry(&subEntry); 426 if (err) { 427 // If we've come to the end of list, it's not an error 428 if (err == B_ENTRY_NOT_FOUND) 429 err = B_OK; 430 break; 431 } else { 432 // We need to preserve the case of the type name for 433 // queries, so we can't use the file name directly 434 BString type; 435 int32 subStart; 436 BNode node(&subEntry); 437 if (node.InitCheck() == B_OK 438 && node.ReadAttrString(kTypeAttr, &type) >= B_OK 439 && (subStart = type.FindFirst('/')) > 0) { 440 // Add the subtype 441 if (_AddSubtype(supertypeRef, type.String() 442 + subStart + 1) != B_OK) { 443 DBG(OUT("Mime::InstalledTypes::BuildInstalledTypesList()" 444 " -- Error adding subtype '%s/%s': 0x%" B_PRIx32 "\n", 445 supertype, type.String() + subStart + 1, err)); 446 } 447 } 448 } 449 } 450 } else { 451 DBG(OUT("Mime::InstalledTypes::BuildInstalledTypesList(): " 452 "Failed opening supertype directory '%s'\n", 453 supertype)); 454 } 455 } 456 } 457 } 458 } else { 459 DBG(OUT("Mime::InstalledTypes::BuildInstalledTypesList(): " 460 "Failed opening mime database directory.\n")); 461 } 462 fHaveDoneFullBuild = true; 463 return err; 464 465} 466 467 468/*! \brief Allocates a new BMessage into the BMessage pointer pointed to by \c result 469 and fills it with a complete list of installed types. 470*/ 471status_t 472InstalledTypes::_CreateMessageWithTypes(BMessage **_result) const 473{ 474 if (_result == NULL) 475 return B_BAD_VALUE; 476 477 status_t err = B_OK; 478 479 // Alloc the message 480 try { 481 *_result = new BMessage(); 482 } catch (std::bad_alloc&) { 483 err = B_NO_MEMORY; 484 } 485 486 // Fill with types 487 if (!err) { 488 BMessage &msg = **_result; 489 std::map<std::string, Supertype>::const_iterator i; 490 for (i = fSupertypes.begin(); i != fSupertypes.end() && !err; i++) { 491 err = msg.AddString(kTypesField, i->first.c_str()); 492 if (!err) 493 err = i->second.FillMessageWithTypes(msg); 494 } 495 } 496 return err; 497} 498 499 500/*! \brief Allocates a new BMessage into the BMessage pointer pointed to by \c result 501 and fills it with a complete list of installed supertypes. 502*/ 503status_t 504InstalledTypes::_CreateMessageWithSupertypes(BMessage **_result) const 505{ 506 if (_result == NULL) 507 return B_BAD_VALUE; 508 509 status_t err = B_OK; 510 511 // Alloc the message 512 try { 513 *_result = new BMessage(); 514 } catch (std::bad_alloc&) { 515 err = B_NO_MEMORY; 516 } 517 518 // Fill with types 519 if (!err) { 520 BMessage &msg = **_result; 521 std::map<std::string, Supertype>::const_iterator i; 522 for (i = fSupertypes.begin(); i != fSupertypes.end() && !err; i++) { 523 err = msg.AddString(kSupertypesField, i->first.c_str()); 524 } 525 } 526 return err; 527} 528 529} // namespace Mime 530} // namespace Storage 531} // namespace BPrivate 532 533