#include <mime/DatabaseLocation.h>
#include <stdlib.h>
#include <syslog.h>
#include <new>
#include <Bitmap.h>
#include <DataIO.h>
#include <Directory.h>
#include <File.h>
#include <fs_attr.h>
#include <IconUtils.h>
#include <Message.h>
#include <Node.h>
#include <AutoDeleter.h>
#include <mime/database_support.h>
namespace BPrivate {
namespace Storage {
namespace Mime {
DatabaseLocation::DatabaseLocation()
:
fDirectories()
{
}
DatabaseLocation::~DatabaseLocation()
{
}
bool
DatabaseLocation::AddDirectory(const BString& directory)
{
return !directory.IsEmpty() && fDirectories.Add(directory);
}
status_t
DatabaseLocation::OpenType(const char* type, BNode& _node) const
{
if (type == NULL)
return B_BAD_VALUE;
int32 index;
return _OpenType(type, _node, index);
}
status_t
DatabaseLocation::OpenWritableType(const char* type, BNode& _node, bool create,
bool* _didCreate) const
{
if (_didCreate)
*_didCreate = false;
int32 index;
status_t result = _OpenType(type, _node, index);
if (result == B_OK) {
if (index == 0)
return B_OK;
else if (!create)
return B_ENTRY_NOT_FOUND;
BNode nodeToClone(_node);
if (nodeToClone.InitCheck() != B_OK)
return nodeToClone.InitCheck();
result = _CopyTypeNode(nodeToClone, type, _node);
if (result != B_OK) {
_node.Unset();
return result;
}
if (_didCreate != NULL)
*_didCreate = true;
return result;
} else if (!create)
return B_ENTRY_NOT_FOUND;
result = _CreateTypeNode(type, _node);
if (result != B_OK)
return result;
size_t toWrite = strlen(type) + 1;
ssize_t bytesWritten = _node.WriteAttr(kTypeAttr, B_STRING_TYPE, 0, type,
toWrite);
if (bytesWritten < 0)
result = bytesWritten;
else if ((size_t)bytesWritten != toWrite)
result = B_FILE_ERROR;
if (result != B_OK) {
_node.Unset();
return result;
}
if (_didCreate != NULL)
*_didCreate = true;
return B_OK;
}
ssize_t
DatabaseLocation::ReadAttribute(const char* type, const char* attribute,
void* data, size_t length, type_code datatype) const
{
if (type == NULL || attribute == NULL || data == NULL)
return B_BAD_VALUE;
BNode node;
status_t result = OpenType(type, node);
if (result != B_OK)
return result;
return node.ReadAttr(attribute, datatype, 0, data, length);
}
status_t
DatabaseLocation::ReadMessageAttribute(const char* type, const char* attribute,
BMessage& _message) const
{
if (type == NULL || attribute == NULL)
return B_BAD_VALUE;
BNode node;
attr_info info;
status_t result = OpenType(type, node);
if (result != B_OK)
return result;
result = node.GetAttrInfo(attribute, &info);
if (result != B_OK)
return result;
if (info.type != B_MESSAGE_TYPE)
return B_BAD_VALUE;
void* buffer = malloc(info.size);
if (buffer == NULL)
return B_NO_MEMORY;
MemoryDeleter bufferDeleter(buffer);
ssize_t bytesRead = node.ReadAttr(attribute, B_MESSAGE_TYPE, 0, buffer,
info.size);
if (bytesRead != info.size)
return bytesRead < 0 ? (status_t)bytesRead : (status_t)B_FILE_ERROR;
return _message.Unflatten((const char*)buffer);
}
status_t
DatabaseLocation::ReadStringAttribute(const char* type, const char* attribute,
BString& _string) const
{
if (type == NULL || attribute == NULL)
return B_BAD_VALUE;
BNode node;
status_t result = OpenType(type, node);
if (result != B_OK)
return result;
return node.ReadAttrString(attribute, &_string);
}
status_t
DatabaseLocation::WriteAttribute(const char* type, const char* attribute,
const void* data, size_t length, type_code datatype, bool* _didCreate) const
{
if (type == NULL || attribute == NULL || data == NULL)
return B_BAD_VALUE;
BNode node;
status_t result = OpenWritableType(type, node, true, _didCreate);
if (result != B_OK)
return result;
ssize_t bytesWritten = node.WriteAttr(attribute, datatype, 0, data, length);
if (bytesWritten < 0)
return bytesWritten;
return bytesWritten == (ssize_t)length
? (status_t)B_OK : (status_t)B_FILE_ERROR;
}
status_t
DatabaseLocation::WriteMessageAttribute(const char* type, const char* attribute,
const BMessage& message, bool* _didCreate) const
{
BMallocIO data;
status_t result = data.SetSize(message.FlattenedSize());
if (result != B_OK)
return result;
ssize_t bytes;
result = message.Flatten(&data, &bytes);
if (result != B_OK)
return result;
return WriteAttribute(type, attribute, data.Buffer(), data.BufferLength(),
B_MESSAGE_TYPE, _didCreate);
}
status_t
DatabaseLocation::DeleteAttribute(const char* type, const char* attribute) const
{
if (type == NULL || attribute == NULL)
return B_BAD_VALUE;
BNode node;
status_t result = OpenWritableType(type, node, false);
if (result != B_OK)
return result;
return node.RemoveAttr(attribute);
}
status_t
DatabaseLocation::GetAppHint(const char* type, entry_ref& _ref)
{
if (type == NULL)
return B_BAD_VALUE;
char path[B_PATH_NAME_LENGTH];
BEntry entry;
ssize_t status = ReadAttribute(type, kAppHintAttr, path, B_PATH_NAME_LENGTH,
kAppHintType);
if (status >= B_OK)
status = entry.SetTo(path);
if (status == B_OK)
status = entry.GetRef(&_ref);
return status;
}
status_t
DatabaseLocation::GetAttributesInfo(const char* type, BMessage& _info)
{
status_t result = ReadMessageAttribute(type, kAttrInfoAttr, _info);
if (result == B_ENTRY_NOT_FOUND) {
_info.MakeEmpty();
result = B_OK;
}
if (result == B_OK) {
_info.what = 233;
result = _info.AddString("type", type);
}
return result;
}
status_t
DatabaseLocation::GetShortDescription(const char* type, char* description)
{
ssize_t result = ReadAttribute(type, kShortDescriptionAttr, description,
B_MIME_TYPE_LENGTH, kShortDescriptionType);
return result >= 0 ? B_OK : result;
}
status_t
DatabaseLocation::GetLongDescription(const char* type, char* description)
{
ssize_t result = ReadAttribute(type, kLongDescriptionAttr, description,
B_MIME_TYPE_LENGTH, kLongDescriptionType);
return result >= 0 ? B_OK : result;
}
status_t
DatabaseLocation::GetFileExtensions(const char* type, BMessage& _extensions)
{
status_t result = ReadMessageAttribute(type, kFileExtensionsAttr, _extensions);
if (result == B_ENTRY_NOT_FOUND) {
_extensions.MakeEmpty();
result = B_OK;
}
if (result == B_OK) {
_extensions.what = 234;
result = _extensions.AddString("type", type);
}
return result;
}
status_t
DatabaseLocation::GetIcon(const char* type, BBitmap& _icon, icon_size size)
{
return GetIconForType(type, NULL, _icon, size);
}
status_t
DatabaseLocation::GetIcon(const char* type, uint8*& _data, size_t& _size)
{
return GetIconForType(type, NULL, _data, _size);
}
status_t
DatabaseLocation::GetIconForType(const char* type, const char* fileType,
BBitmap& _icon, icon_size which)
{
if (type == NULL)
return B_BAD_VALUE;
BNode node;
status_t result = OpenType(type, node);
if (result != B_OK)
return result;
BString vectorIconAttrName;
BString smallIconAttrName;
BString largeIconAttrName;
if (fileType != NULL) {
BString lowerCaseFileType(fileType);
lowerCaseFileType.ToLower();
vectorIconAttrName << kIconAttrPrefix << lowerCaseFileType;
smallIconAttrName << kMiniIconAttrPrefix << lowerCaseFileType;
largeIconAttrName << kLargeIconAttrPrefix << lowerCaseFileType;
} else {
vectorIconAttrName = kIconAttr;
smallIconAttrName = kMiniIconAttr;
largeIconAttrName = kLargeIconAttr;
}
return BIconUtils::GetIcon(&node, vectorIconAttrName, smallIconAttrName,
largeIconAttrName, which, &_icon);
}
status_t
DatabaseLocation::GetIconForType(const char* type, const char* fileType,
uint8*& _data, size_t& _size)
{
if (type == NULL)
return B_BAD_VALUE;
BNode node;
status_t result = OpenType(type, node);
if (result != B_OK)
return result;
BString iconAttrName;
if (fileType != NULL)
iconAttrName << kIconAttrPrefix << BString(fileType).ToLower();
else
iconAttrName = kIconAttr;
attr_info info;
if (result == B_OK)
result = node.GetAttrInfo(iconAttrName, &info);
if (result == B_OK)
result = (info.type == B_VECTOR_ICON_TYPE) ? B_OK : B_BAD_VALUE;
if (result == B_OK) {
uint8* buffer = new(std::nothrow) uint8[info.size];
if (buffer == NULL)
result = B_NO_MEMORY;
ssize_t bytesRead = -1;
if (result == B_OK) {
bytesRead = node.ReadAttr(iconAttrName, B_VECTOR_ICON_TYPE, 0, buffer,
info.size);
}
if (bytesRead >= 0)
result = bytesRead == info.size ? B_OK : B_FILE_ERROR;
if (result == B_OK) {
_data = buffer;
_size = info.size;
} else
delete[] buffer;
}
return result;
}
status_t
DatabaseLocation::GetPreferredApp(const char* type, char* signature,
app_verb verb)
{
ssize_t result = ReadAttribute(type, kPreferredAppAttr, signature,
B_MIME_TYPE_LENGTH, kPreferredAppType);
return result >= 0 ? B_OK : result;
}
status_t
DatabaseLocation::GetSnifferRule(const char* type, BString& _result)
{
return ReadStringAttribute(type, kSnifferRuleAttr, _result);
}
status_t
DatabaseLocation::GetSupportedTypes(const char* type, BMessage& _types)
{
status_t result = ReadMessageAttribute(type, kSupportedTypesAttr, _types);
if (result == B_ENTRY_NOT_FOUND) {
_types.MakeEmpty();
result = B_OK;
}
if (result == B_OK) {
_types.what = 0;
result = _types.AddString("type", type);
}
return result;
}
bool
DatabaseLocation::IsInstalled(const char* type)
{
BNode node;
return OpenType(type, node) == B_OK;
}
BString
DatabaseLocation::_TypeToFilename(const char* type, int32 index) const
{
BString path = fDirectories.StringAt(index);
return path << '/' << BString(type).ToLower();
}
status_t
DatabaseLocation::_OpenType(const char* type, BNode& _node, int32& _index) const
{
int32 count = fDirectories.CountStrings();
for (int32 i = 0; i < count; i++) {
status_t result = _node.SetTo(_TypeToFilename(type, i));
attr_info attrInfo;
if (result == B_OK && _node.GetAttrInfo(kTypeAttr, &attrInfo) == B_OK) {
_index = i;
return B_OK;
}
}
return B_ENTRY_NOT_FOUND;
}
status_t
DatabaseLocation::_CreateTypeNode(const char* type, BNode& _node) const
{
const char* slash = strchr(type, '/');
BString superTypeName;
if (slash != NULL)
superTypeName.SetTo(type, slash - type);
else
superTypeName = type;
superTypeName.ToLower();
BDirectory parent(WritableDirectory());
status_t result = parent.InitCheck();
if (result != B_OK)
return result;
BDirectory superTypeDirectory;
if (BEntry(&parent, superTypeName).Exists())
result = superTypeDirectory.SetTo(&parent, superTypeName);
else
result = parent.CreateDirectory(superTypeName, &superTypeDirectory);
if (result != B_OK)
return result;
BFile subTypeFile;
if (slash != NULL) {
result = superTypeDirectory.CreateFile(BString(slash + 1).ToLower(),
&subTypeFile);
if (result != B_OK)
return result;
}
if (slash != NULL)
_node = subTypeFile;
else
_node = superTypeDirectory;
return _node.InitCheck();
}
status_t
DatabaseLocation::_CopyTypeNode(BNode& source, const char* type, BNode& _target)
const
{
status_t result = _CreateTypeNode(type, _target);
if (result != B_OK)
return result;
MemoryDeleter bufferDeleter;
size_t bufferSize = 0;
source.RewindAttrs();
char attribute[B_ATTR_NAME_LENGTH];
while (source.GetNextAttrName(attribute) == B_OK) {
attr_info info;
result = source.GetAttrInfo(attribute, &info);
if (result != B_OK) {
syslog(LOG_ERR, "Failed to get info for attribute \"%s\" of MIME "
"type \"%s\": %s", attribute, type, strerror(result));
continue;
}
if (info.size > (off_t)bufferSize) {
bufferDeleter.SetTo(malloc(info.size));
if (!bufferDeleter.IsSet())
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;
}
}
}
}