diff --git a/headers/private/storage/mime/database_support.h b/headers/private/storage/mime/database_support.h index d6a1e8aad0..0e54903270 100644 --- a/headers/private/storage/mime/database_support.h +++ b/headers/private/storage/mime/database_support.h @@ -7,12 +7,12 @@ #include +#include -#include class BNode; class BMessage; -class BString; +class BStringList; namespace BPrivate { @@ -20,8 +20,8 @@ namespace Storage { namespace Mime { // Database directory -const std::string get_database_directory(); -const std::string get_application_database_directory(); +const BStringList& get_database_directories(); +BString get_writable_database_directory(); // Attribute Prefixes extern const char *kMiniIconAttrPrefix; @@ -75,7 +75,7 @@ extern const char *kMetaMimeType; // Error codes (to be used only by BPrivate::Storage::Mime members) extern const status_t kMimeGuessFailureError; -std::string type_to_filename(const char *type); +BString type_to_writable_filename(const char *type); status_t open_type(const char *type, BNode *result); status_t open_or_create_type(const char *type, BNode *result, bool *didCreate); diff --git a/src/kits/storage/mime/database_support.cpp b/src/kits/storage/mime/database_support.cpp index 750a5c041e..09703f40da 100644 --- a/src/kits/storage/mime/database_support.cpp +++ b/src/kits/storage/mime/database_support.cpp @@ -4,6 +4,7 @@ * * Authors: * Tyler Dauwalder + * Ingo Weinhold */ /*! @@ -14,18 +15,23 @@ #include #include #include -#include +#include #include +#include #include #include #include #include +#include #include #include // For struct attr_info #include // For new(nothrow) #include #include +#include + +#include #include "mime/database_support.h" @@ -94,49 +100,187 @@ const char *kMetaMimeType = "application/x-vnd.Be-meta-mime"; // Error codes const status_t kMimeGuessFailureError = B_ERRORS_END+1; +static const directory_which kBaseDirectoryConstants[] = { + B_USER_SETTINGS_DIRECTORY, + B_USER_NONPACKAGED_DATA_DIRECTORY, + B_USER_DATA_DIRECTORY, + B_COMMON_NONPACKAGED_DATA_DIRECTORY, + B_COMMON_DATA_DIRECTORY, + B_SYSTEM_DATA_DIRECTORY +}; + static pthread_once_t sDatabaseDirectoryInitOnce = PTHREAD_ONCE_INIT; -static std::string sDatabaseDirectory; -static std::string sApplicationDatabaseDirectory; +static BStringList sDatabaseDirectories; static void init_database_directories() { - BPath path; - if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) - sDatabaseDirectory = path.Path(); + for (size_t i = 0; + i < sizeof(kBaseDirectoryConstants) + / sizeof(kBaseDirectoryConstants[0]); i++) { + BString directoryPath; + BPath path; + if (find_directory(kBaseDirectoryConstants[i], &path) == B_OK) + directoryPath = path.Path(); + else if (i == 0) + directoryPath = "/boot/home/config/settings"; + else + continue; + + directoryPath += "/mime_db"; + sDatabaseDirectories.Add(directoryPath); + } +} + + +const BStringList& +get_database_directories() +{ + pthread_once(&sDatabaseDirectoryInitOnce, &init_database_directories); + return sDatabaseDirectories; +} + + +BString +get_writable_database_directory() +{ + return get_database_directories().StringAt(0); +} + + +static BString +type_to_filename(const char* type, int32 index) +{ + BString path = get_database_directories().StringAt(index); + return path << '/' << BPrivate::Storage::to_lower(type).c_str(); +} + + +/*! Converts the given MIME type to an absolute path in the user writeable MIME + database directory. +*/ +BString +type_to_writable_filename(const char* type) +{ + return type_to_filename(type, 0); +} + + +static status_t +open_type(const char* type, BNode* _node, int32& _index) +{ + const BStringList& directories = get_database_directories(); + int32 count = directories.CountStrings(); + for (int32 i = 0; i < count; i++) { + status_t error = _node->SetTo(type_to_filename(type, i)); + attr_info attrInfo; + if (error == B_OK && _node->GetAttrInfo(kTypeAttr, &attrInfo) == B_OK) { + _index = i; + return B_OK; + } + } + + return B_ENTRY_NOT_FOUND; +} + + +static status_t +create_type_node(const char* type, BNode& _result) +{ + const char* slash = strchr(type, '/'); + BString superTypeName; + if (slash != NULL) + superTypeName.SetTo(type, slash - type); else - sDatabaseDirectory = "/boot/home/config/settings"; + superTypeName = type; + superTypeName.ToLower(); - sDatabaseDirectory += "/beos_mime"; - sApplicationDatabaseDirectory = sDatabaseDirectory + "/application"; + // open/create the directory for the supertype + BDirectory parent(get_writable_database_directory()); + status_t error = parent.InitCheck(); + if (error != B_OK) + return error; + + BDirectory superTypeDirectory; + if (BEntry(&parent, superTypeName).Exists()) + error = superTypeDirectory.SetTo(&parent, superTypeName); + else + error = parent.CreateDirectory(superTypeName, &superTypeDirectory); + if (error != B_OK) + return error; + + // create the subtype + BFile subTypeFile; + if (slash != NULL) { + error = superTypeDirectory.CreateFile(BString(slash + 1).ToLower(), + &subTypeFile); + if (error != B_OK) + return error; + } + + // assign the result + if (slash != NULL) + _result = subTypeFile; + else + _result = superTypeDirectory; + return _result.InitCheck(); } -const std::string -get_database_directory() +static status_t +copy_type_node(BNode& source, const char* type, BNode& _target) { - pthread_once(&sDatabaseDirectoryInitOnce, &init_database_directories); - return sDatabaseDirectory; + status_t error = create_type_node(type, _target); + if (error != B_OK) + return error; + + // copy the attributes + MemoryDeleter bufferDeleter; + size_t bufferSize = 0; + + source.RewindAttrs(); + char attribute[B_ATTR_NAME_LENGTH]; + while (source.GetNextAttrName(attribute) == B_OK) { + attr_info info; + error = source.GetAttrInfo(attribute, &info); + if (error != B_OK) { + syslog(LOG_ERR, "Failed to get info for attribute \"%s\" of MIME " + "type \"%s\": %s", attribute, type, strerror(error)); + continue; + } + + // resize our buffer, if necessary + if (info.size > bufferSize) { + bufferDeleter.SetTo(malloc(info.size)); + if (bufferDeleter.Get() == NULL) + return B_NO_MEMORY; + bufferSize = info.size; + } + + ssize_t bytesRead = source.ReadAttr(attribute, info.type, 0, + bufferDeleter.Get(), info.size); + if (bytesRead != info.size) { + syslog(LOG_ERR, "Failed to read attribute \"%s\" of MIME " + "type \"%s\": %s", attribute, type, + bytesRead < 0 ? strerror(bytesRead) : "short read"); + continue; + } + + ssize_t bytesWritten = _target.WriteAttr(attribute, info.type, 0, + bufferDeleter.Get(), info.size); + if (bytesWritten < 0) { + syslog(LOG_ERR, "Failed to write attribute \"%s\" of MIME " + "type \"%s\": %s", attribute, type, + bytesWritten < 0 ? strerror(bytesWritten) : "short write"); + continue; + } + } + + return B_OK; } -const std::string -get_application_database_directory() -{ - pthread_once(&sDatabaseDirectoryInitOnce, &init_database_directories); - return sApplicationDatabaseDirectory; -} - - -// type_to_filename -//! Converts the given MIME type to an absolute path in the MIME database. -std::string -type_to_filename(const char *type) -{ - return get_database_directory() + "/" + BPrivate::Storage::to_lower(type); -} - // open_type /*! \brief Opens a BNode on the given type, failing if the type has no corresponding file in the database. @@ -150,20 +294,8 @@ open_type(const char *type, BNode *result) if (type == NULL || result == NULL) return B_BAD_VALUE; - status_t status = result->SetTo(type_to_filename(type).c_str()); - - // TODO: this can be removed again later - we just didn't write this - // attribute is before at all... -#if 1 - if (status == B_OK) { - // check if the MIME:TYPE attribute exist, and create it if not - attr_info info; - if (result->GetAttrInfo(kTypeAttr, &info) != B_OK) - result->WriteAttr(kTypeAttr, B_STRING_TYPE, 0, type, strlen(type) + 1); - } -#endif - - return status; + int32 index; + return open_type(type, result, index); } // open_or_create_type @@ -180,44 +312,47 @@ open_or_create_type(const char *type, BNode *result, bool *didCreate) { if (didCreate) *didCreate = false; - std::string filename; - std::string typeLower = BPrivate::Storage::to_lower(type); - status_t err = (type && result ? B_OK : B_BAD_VALUE); - if (!err) { - filename = type_to_filename(type); - err = result->SetTo(filename.c_str()); - } - if (err == B_ENTRY_NOT_FOUND) { - // Figure out what type of node we need to create - // + Supertype == directory - // + Non-supertype == file - size_t pos = typeLower.find_first_of('/'); - if (pos == std::string::npos) { - // Supertype == directory - BDirectory parent(get_database_directory().c_str()); - err = parent.InitCheck(); - if (!err) - err = parent.CreateDirectory(typeLower.c_str(), NULL); - } else { - // Non-supertype == file - std::string super(typeLower, 0, pos); - std::string sub(typeLower, pos+1); - BDirectory parent((get_database_directory() + "/" + super).c_str()); - err = parent.InitCheck(); - if (!err) - err = parent.CreateFile(sub.c_str(), NULL); + + // See, if the type already exists. + int32 index; + status_t error = open_type(type, result, index); + if (error == B_OK) { + if (index == 0) + return B_OK; + + // The caller wants a editable node, but the node found is not in the + // user's settings directory. Copy the node. + BNode nodeToClone(*result); + if (nodeToClone.InitCheck() != B_OK) + return nodeToClone.InitCheck(); + + error = copy_type_node(nodeToClone, type, *result); + if (error != B_OK) { + result->Unset(); + return error; } - // Now try opening again - if (err == B_OK) - err = result->SetTo(filename.c_str()); - if (err == B_OK && didCreate) + + if (didCreate != NULL) *didCreate = true; - if (err == B_OK) { - // write META:TYPE attribute - result->WriteAttr(kTypeAttr, B_STRING_TYPE, 0, type, strlen(type) + 1); - } + return error; } - return err; + + // type doesn't exist yet -- create the respective node + error = create_type_node(type, *result); + if (error != B_OK) + return error; + + // write the type attribute + error = result->WriteAttr(kTypeAttr, B_STRING_TYPE, 0, type, + strlen(type) + 1); + if (error != B_OK) { + result->Unset(); + return error; + } + + if (didCreate != NULL) + *didCreate = true; + return B_OK; } // read_mime_attr diff --git a/src/servers/registrar/Jamfile b/src/servers/registrar/Jamfile index a2e77cc990..eba9ad50ff 100644 --- a/src/servers/registrar/Jamfile +++ b/src/servers/registrar/Jamfile @@ -43,6 +43,7 @@ Server registrar AssociatedTypes.cpp CreateAppMetaMimeThread.cpp Database.cpp + DatabaseDirectory.cpp InstalledTypes.cpp MimeSnifferAddon.cpp MimeSnifferAddonManager.cpp diff --git a/src/servers/registrar/mime/AssociatedTypes.cpp b/src/servers/registrar/mime/AssociatedTypes.cpp index 8865da9e81..8550839d35 100644 --- a/src/servers/registrar/mime/AssociatedTypes.cpp +++ b/src/servers/registrar/mime/AssociatedTypes.cpp @@ -8,7 +8,10 @@ */ #include "AssociatedTypes.h" -#include "MimeSnifferAddonManager.h" + +#include + +#include #include #include @@ -19,8 +22,9 @@ #include #include -#include -#include +#include "DatabaseDirectory.h" +#include "MimeSnifferAddonManager.h" + #define DBG(x) x //#define DBG(x) @@ -330,8 +334,8 @@ AssociatedTypes::BuildAssociatedTypesTable() fFileExtensions.clear(); fAssociatedTypes.clear(); - BDirectory root; - status_t err = root.SetTo(get_database_directory().c_str()); + DatabaseDirectory root; + status_t err = root.Init(); if (!err) { root.Rewind(); while (true) { @@ -354,8 +358,8 @@ AssociatedTypes::BuildAssociatedTypesTable() // First, iterate through this supertype directory and process // all of its subtypes - BDirectory dir; - if (dir.SetTo(&entry) == B_OK) { + DatabaseDirectory dir; + if (dir.Init(supertype) == B_OK) { dir.Rewind(); while (true) { BEntry subEntry; @@ -392,8 +396,7 @@ AssociatedTypes::BuildAssociatedTypesTable() } } else { DBG(OUT("Mime::AssociatedTypes::BuildAssociatedTypesTable(): " - "Failed opening mime database directory '%s'\n", - get_database_directory().c_str())); + "Failed opening mime database directory\n")); } if (!err) { fHaveDoneFullBuild = true; diff --git a/src/servers/registrar/mime/Database.cpp b/src/servers/registrar/mime/Database.cpp index 13532a67e1..96fb8822e2 100644 --- a/src/servers/registrar/mime/Database.cpp +++ b/src/servers/registrar/mime/Database.cpp @@ -65,9 +65,9 @@ Database::Database() fDeferredInstallNotificationsLocker("deferred install notifications"), fDeferredInstallNotifications() { - // Do some really minor error checking - BEntry entry(get_database_directory().c_str()); - fStatus = entry.Exists() ? B_OK : B_BAD_VALUE; + // make sure the user's MIME DB directory exists + fStatus = create_directory(get_writable_database_directory(), + S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); } // destructor @@ -107,7 +107,7 @@ Database::Install(const char *type) return B_BAD_VALUE; BEntry entry; - status_t err = entry.SetTo(type_to_filename(type).c_str()); + status_t err = entry.SetTo(type_to_writable_filename(type)); if (!err) { if (entry.Exists()) err = B_FILE_EXISTS; @@ -139,7 +139,7 @@ Database::Delete(const char *type) // Open the type BEntry entry; - status_t status = entry.SetTo(type_to_filename(type).c_str()); + status_t status = entry.SetTo(type_to_writable_filename(type)); if (status != B_OK) return status; diff --git a/src/servers/registrar/mime/DatabaseDirectory.cpp b/src/servers/registrar/mime/DatabaseDirectory.cpp new file mode 100644 index 0000000000..2c4699dd16 --- /dev/null +++ b/src/servers/registrar/mime/DatabaseDirectory.cpp @@ -0,0 +1,69 @@ +/* + * Copyright 2013, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ingo Weinhold + */ + + +#include "DatabaseDirectory.h" + +#include +#include +#include + +#include + + +DatabaseDirectory::DatabaseDirectory() + : + BMergedDirectory(B_COMPARE) +{ +} + + +DatabaseDirectory::~DatabaseDirectory() +{ +} + + +status_t +DatabaseDirectory::Init(const char* superType) +{ + status_t error = BMergedDirectory::Init(); + if (error != B_OK) + return error; + + const BStringList& directories + = BPrivate::Storage::Mime::get_database_directories(); + int32 count = directories.CountStrings(); + for (int32 i = 0; i < count; i++) { + BString directory = directories.StringAt(i); + if (superType != NULL) + directory << '/' << superType; + + AddDirectory(directory); + } + + return B_OK; +} + + +bool +DatabaseDirectory::ShallPreferFirstEntry(const entry_ref& entry1, int32 index1, + const entry_ref& entry2, int32 index2) +{ + return _IsValidMimeTypeEntry(entry1) || !_IsValidMimeTypeEntry(entry2); +} + + +bool +DatabaseDirectory::_IsValidMimeTypeEntry(const entry_ref& entry) +{ + // check whether the MIME:TYPE attribute exists + BNode node; + attr_info info; + return node.SetTo(&entry) == B_OK + && node.GetAttrInfo(BPrivate::Storage::Mime::kTypeAttr, &info) == B_OK; +} diff --git a/src/servers/registrar/mime/DatabaseDirectory.h b/src/servers/registrar/mime/DatabaseDirectory.h new file mode 100644 index 0000000000..6bfcfb0008 --- /dev/null +++ b/src/servers/registrar/mime/DatabaseDirectory.h @@ -0,0 +1,32 @@ +/* + * Copyright 2013, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ingo Weinhold + */ +#ifndef DATABASE_DIRECTORY_H +#define DATABASE_DIRECTORY_H + + +#include + + +class DatabaseDirectory : public BMergedDirectory { +public: + DatabaseDirectory(); + virtual ~DatabaseDirectory(); + + status_t Init(const char* superType = NULL); + +protected: + virtual bool ShallPreferFirstEntry(const entry_ref& entry1, + int32 index1, const entry_ref& entry2, + int32 index2); + +private: + bool _IsValidMimeTypeEntry(const entry_ref& entry); +}; + + +#endif // DATABASE_DIRECTORY_H diff --git a/src/servers/registrar/mime/InstalledTypes.cpp b/src/servers/registrar/mime/InstalledTypes.cpp index 12b0c20ba0..0bd602ad50 100644 --- a/src/servers/registrar/mime/InstalledTypes.cpp +++ b/src/servers/registrar/mime/InstalledTypes.cpp @@ -11,8 +11,9 @@ #include "InstalledTypes.h" -#include -#include +#include + +#include #include #include @@ -20,8 +21,11 @@ #include #include -#include -#include +#include +#include + +#include "DatabaseDirectory.h" + #define DBG(x) x //#define DBG(x) @@ -380,9 +384,9 @@ InstalledTypes::_BuildInstalledTypesList() err = B_NO_MEMORY; } - BDirectory root; + DatabaseDirectory root; if (!err) - err = root.SetTo(get_database_directory().c_str()); + err = root.Init(); if (!err) { root.Rewind(); while (true) { @@ -413,8 +417,8 @@ InstalledTypes::_BuildInstalledTypesList() // Now iterate through this supertype directory and add // all of its subtypes - BDirectory dir; - if (dir.SetTo(&entry) == B_OK) { + DatabaseDirectory dir; + if (dir.Init(supertype) == B_OK) { dir.Rewind(); while (true) { BEntry subEntry; @@ -453,8 +457,7 @@ InstalledTypes::_BuildInstalledTypesList() } } else { DBG(OUT("Mime::InstalledTypes::BuildInstalledTypesList(): " - "Failed opening mime database directory '%s'\n", - get_database_directory().c_str())); + "Failed opening mime database directory.\n")); } fHaveDoneFullBuild = true; return err; diff --git a/src/servers/registrar/mime/SnifferRules.cpp b/src/servers/registrar/mime/SnifferRules.cpp index e61885f31d..b71857364f 100644 --- a/src/servers/registrar/mime/SnifferRules.cpp +++ b/src/servers/registrar/mime/SnifferRules.cpp @@ -28,6 +28,7 @@ #include #include +#include "DatabaseDirectory.h" #include "MimeSnifferAddonManager.h" #define DBG(x) x @@ -332,9 +333,9 @@ SnifferRules::BuildRuleList() ssize_t maxBytesNeeded = 0; ssize_t bytesNeeded = 0; - BDirectory root; + DatabaseDirectory root; - status_t err = root.SetTo(get_database_directory().c_str()); + status_t err = root.Init(); if (!err) { root.Rewind(); while (true) { @@ -357,8 +358,8 @@ SnifferRules::BuildRuleList() // First, iterate through this supertype directory and process // all of its subtypes - BDirectory dir; - if (dir.SetTo(&entry) == B_OK) { + DatabaseDirectory dir; + if (dir.Init(supertype) == B_OK) { dir.Rewind(); while (true) { BEntry subEntry; @@ -399,8 +400,7 @@ SnifferRules::BuildRuleList() } } else { DBG(OUT("Mime::SnifferRules::BuildRuleList(): " - "Failed opening mime database directory '%s'\n", - get_database_directory().c_str())); + "Failed opening mime database directory.\n")); } if (!err) { diff --git a/src/servers/registrar/mime/SupportingApps.cpp b/src/servers/registrar/mime/SupportingApps.cpp index 566580e851..4caf6f5e91 100644 --- a/src/servers/registrar/mime/SupportingApps.cpp +++ b/src/servers/registrar/mime/SupportingApps.cpp @@ -10,9 +10,11 @@ #include "SupportingApps.h" -#include -#include +#include + +#include +#include #include #include @@ -20,9 +22,11 @@ #include #include -#include -#include -#include +#include +#include + +#include "DatabaseDirectory.h" + #define DBG(x) x //#define DBG(x) @@ -262,8 +266,8 @@ SupportingApps::BuildSupportingAppsTable() fSupportingApps.clear(); fStrandedTypes.clear(); - BDirectory dir; - status_t status = dir.SetTo(get_application_database_directory().c_str()); + DatabaseDirectory dir; + status_t status = dir.Init("application"); // Build the supporting apps table based on the mime database if (status == B_OK) {