root/src/kits/storage/AppFileInfo.cpp
/*
 * Copyright 2002-2014, Haiku, Inc.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Ingo Weinhold, ingo_weinhold@gmx.de
 */


#include <new>
#include <set>
#include <stdlib.h>
#include <string>

#include <AppFileInfo.h>
#include <Bitmap.h>
#include <File.h>
#include <fs_attr.h>
#include <IconUtils.h>
#include <MimeType.h>
#include <RegistrarDefs.h>
#include <Resources.h>
#include <Roster.h>
#include <String.h>


// debugging
//#define DBG(x) x
#define DBG(x)
#define OUT     printf


// type codes
enum {
        B_APP_FLAGS_TYPE        = 'APPF',
        B_VERSION_INFO_TYPE     = 'APPV',
};


// attributes
static const char* kTypeAttribute                               = "BEOS:TYPE";
static const char* kSignatureAttribute                  = "BEOS:APP_SIG";
static const char* kAppFlagsAttribute                   = "BEOS:APP_FLAGS";
static const char* kSupportedTypesAttribute             = "BEOS:FILE_TYPES";
static const char* kVersionInfoAttribute                = "BEOS:APP_VERSION";
static const char* kMiniIconAttribute                   = "BEOS:M:";
static const char* kLargeIconAttribute                  = "BEOS:L:";
static const char* kIconAttribute                               = "BEOS:";
static const char* kStandardIconType                    = "STD_ICON";
static const char* kIconType                                    = "ICON";
static const char* kCatalogEntryAttribute               = "SYS:NAME";

// resource IDs
static const int32 kTypeResourceID                              = 2;
static const int32 kSignatureResourceID                 = 1;
static const int32 kAppFlagsResourceID                  = 1;
static const int32 kSupportedTypesResourceID    = 1;
static const int32 kMiniIconResourceID                  = 101;
static const int32 kLargeIconResourceID                 = 101;
static const int32 kIconResourceID                              = 101;
static const int32 kVersionInfoResourceID               = 1;
static const int32 kMiniIconForTypeResourceID   = 0;
static const int32 kLargeIconForTypeResourceID  = 0;
static const int32 kIconForTypeResourceID               = 0;
static const int32 kCatalogEntryResourceID              = 1;

// R5 also exports these (Tracker is using them):
// (maybe we better want to drop them silently and declare
// the above in a public Haiku header - and use that one in
// Tracker when compiled for Haiku)
extern const uint32 MINI_ICON_TYPE, LARGE_ICON_TYPE;
const uint32 MINI_ICON_TYPE = 'MICN';
const uint32 LARGE_ICON_TYPE = 'ICON';


BAppFileInfo::BAppFileInfo()
        :
        fResources(NULL),
        fWhere(B_USE_BOTH_LOCATIONS)
{
}


BAppFileInfo::BAppFileInfo(BFile* file)
        :
        fResources(NULL),
        fWhere(B_USE_BOTH_LOCATIONS)
{
        SetTo(file);
}


BAppFileInfo::~BAppFileInfo()
{
        delete fResources;
}


status_t
BAppFileInfo::SetTo(BFile* file)
{
        // unset the old file
        BNodeInfo::SetTo(NULL);
        if (fResources) {
                delete fResources;
                fResources = NULL;
        }

        // check param
        status_t error
                = file != NULL && file->InitCheck() == B_OK ? B_OK : B_BAD_VALUE;

        info_location where = B_USE_BOTH_LOCATIONS;

        // create resources
        if (error == B_OK) {
                fResources = new(std::nothrow) BResources();
                if (fResources) {
                        error = fResources->SetTo(file);
                        if (error != B_OK) {
                                // no resources - this is no critical error, we'll just use
                                // attributes only, then
                                where = B_USE_ATTRIBUTES;
                                error = B_OK;
                        }
                } else
                        error = B_NO_MEMORY;
        }

        // set node info
        if (error == B_OK)
                error = BNodeInfo::SetTo(file);

        if (error != B_OK || (where & B_USE_RESOURCES) == 0) {
                delete fResources;
                fResources = NULL;
        }

        // clean up on error
        if (error != B_OK) {
                if (InitCheck() == B_OK)
                        BNodeInfo::SetTo(NULL);
        }

        // set data location
        if (error == B_OK)
                SetInfoLocation(where);

        // set error
        fCStatus = error;
        return error;
}


status_t
BAppFileInfo::GetType(char* type) const
{
        // check param and initialization
        status_t error = type != NULL ? B_OK : B_BAD_VALUE;
        if (error == B_OK && InitCheck() != B_OK)
                error = B_NO_INIT;
        // read the data
        size_t read = 0;
        if (error == B_OK) {
                error = _ReadData(kTypeAttribute, kTypeResourceID, B_MIME_STRING_TYPE,
                        type, B_MIME_TYPE_LENGTH, read);
        }
        // check the read data -- null terminate the string
        if (error == B_OK && type[read - 1] != '\0') {
                if (read == B_MIME_TYPE_LENGTH)
                        error = B_ERROR;
                else
                        type[read] = '\0';
        }
        return error;
}


status_t
BAppFileInfo::SetType(const char* type)
{
        // check initialization
        status_t error = B_OK;
        if (InitCheck() != B_OK)
                error = B_NO_INIT;
        if (error == B_OK) {
                if (type != NULL) {
                        // check param
                        size_t typeLen = strlen(type);
                        if (typeLen >= B_MIME_TYPE_LENGTH)
                                error = B_BAD_VALUE;
                        // write the data
                        if (error == B_OK) {
                                error = _WriteData(kTypeAttribute, kTypeResourceID,
                                        B_MIME_STRING_TYPE, type, typeLen + 1);
                        }
                } else
                        error = _RemoveData(kTypeAttribute, B_MIME_STRING_TYPE);
        }
        return error;
}


status_t
BAppFileInfo::GetSignature(char* signature) const
{
        // check param and initialization
        status_t error = (signature ? B_OK : B_BAD_VALUE);
        if (error == B_OK && InitCheck() != B_OK)
                error = B_NO_INIT;
        // read the data
        size_t read = 0;
        if (error == B_OK) {
                error = _ReadData(kSignatureAttribute, kSignatureResourceID,
                        B_MIME_STRING_TYPE, signature, B_MIME_TYPE_LENGTH, read);
        }
        // check the read data -- null terminate the string
        if (error == B_OK && signature[read - 1] != '\0') {
                if (read == B_MIME_TYPE_LENGTH)
                        error = B_ERROR;
                else
                        signature[read] = '\0';
        }
        return error;
}


status_t
BAppFileInfo::SetSignature(const char* signature)
{
        // check initialization
        status_t error = B_OK;
        if (InitCheck() != B_OK)
                error = B_NO_INIT;
        if (error == B_OK) {
                if (signature) {
                        // check param
                        size_t signatureLen = strlen(signature);
                        if (signatureLen >= B_MIME_TYPE_LENGTH)
                                error = B_BAD_VALUE;
                        // write the data
                        if (error == B_OK) {
                                error = _WriteData(kSignatureAttribute, kSignatureResourceID,
                                        B_MIME_STRING_TYPE, signature, signatureLen + 1);
                        }
                } else
                        error = _RemoveData(kSignatureAttribute, B_MIME_STRING_TYPE);
        }
        return error;
}


status_t
BAppFileInfo::GetCatalogEntry(char* catalogEntry) const
{
        if (catalogEntry == NULL)
                return B_BAD_VALUE;

        if (InitCheck() != B_OK)
                return B_NO_INIT;

        size_t read = 0;
        status_t error = _ReadData(kCatalogEntryAttribute, kCatalogEntryResourceID,
                B_STRING_TYPE, catalogEntry, B_MIME_TYPE_LENGTH * 3, read);

        if (error != B_OK)
                return error;

        if (read >= B_MIME_TYPE_LENGTH * 3)
                return B_ERROR;

        catalogEntry[read] = '\0';

        return B_OK;
}


status_t
BAppFileInfo::SetCatalogEntry(const char* catalogEntry)
{
        if (InitCheck() != B_OK)
                return B_NO_INIT;

        if (catalogEntry == NULL)
                return _RemoveData(kCatalogEntryAttribute, B_STRING_TYPE);

        size_t nameLength = strlen(catalogEntry);
        if (nameLength > B_MIME_TYPE_LENGTH * 3)
                return B_BAD_VALUE;

        return _WriteData(kCatalogEntryAttribute, kCatalogEntryResourceID,
                B_STRING_TYPE, catalogEntry, nameLength + 1);
}


status_t
BAppFileInfo::GetAppFlags(uint32* flags) const
{
        // check param and initialization
        status_t error = flags != NULL ? B_OK : B_BAD_VALUE;
        if (error == B_OK && InitCheck() != B_OK)
                error = B_NO_INIT;
        // read the data
        size_t read = 0;
        if (error == B_OK) {
                error = _ReadData(kAppFlagsAttribute, kAppFlagsResourceID,
                        B_APP_FLAGS_TYPE, flags, sizeof(uint32), read);
        }
        // check the read data
        if (error == B_OK && read != sizeof(uint32))
                error = B_ERROR;
        return error;
}


status_t
BAppFileInfo::SetAppFlags(uint32 flags)
{
        // check initialization
        status_t error = B_OK;
        if (InitCheck() != B_OK)
                error = B_NO_INIT;
        if (error == B_OK) {
                // write the data
                error = _WriteData(kAppFlagsAttribute, kAppFlagsResourceID,
                        B_APP_FLAGS_TYPE, &flags, sizeof(uint32));
        }
        return error;
}


status_t
BAppFileInfo::RemoveAppFlags()
{
        // check initialization
        status_t error = B_OK;
        if (InitCheck() != B_OK)
                error = B_NO_INIT;
        if (error == B_OK) {
                // remove the data
                error = _RemoveData(kAppFlagsAttribute, B_APP_FLAGS_TYPE);
        }
        return error;
}


status_t
BAppFileInfo::GetSupportedTypes(BMessage* types) const
{
        // check param and initialization
        status_t error = types != NULL ? B_OK : B_BAD_VALUE;
        if (error == B_OK && InitCheck() != B_OK)
                error = B_NO_INIT;
        // read the data
        size_t read = 0;
        void* buffer = NULL;
        if (error == B_OK) {
                error = _ReadData(kSupportedTypesAttribute, kSupportedTypesResourceID,
                        B_MESSAGE_TYPE, NULL, 0, read, &buffer);
        }
        // unflatten the buffer
        if (error == B_OK)
                error = types->Unflatten((const char*)buffer);
        // clean up
        free(buffer);
        return error;
}


status_t
BAppFileInfo::SetSupportedTypes(const BMessage* types, bool updateMimeDB,
        bool syncAll)
{
        // check initialization
        status_t error = B_OK;
        if (InitCheck() != B_OK)
                error = B_NO_INIT;

        BMimeType mimeType;
        if (error == B_OK)
                error = GetMetaMime(&mimeType);

        if (error == B_OK || error == B_ENTRY_NOT_FOUND) {
                error = B_OK;
                if (types) {
                        // check param -- supported types must be valid
                        const char* type;
                        for (int32 i = 0;
                                 error == B_OK && types->FindString("types", i, &type) == B_OK;
                                 i++) {
                                if (!BMimeType::IsValid(type))
                                        error = B_BAD_VALUE;
                        }

                        // get flattened size
                        ssize_t size = 0;
                        if (error == B_OK) {
                                size = types->FlattenedSize();
                                if (size < 0)
                                        error = size;
                        }

                        // allocate a buffer for the flattened data
                        char* buffer = NULL;
                        if (error == B_OK) {
                                buffer = new(std::nothrow) char[size];
                                if (!buffer)
                                        error = B_NO_MEMORY;
                        }

                        // flatten the message
                        if (error == B_OK)
                                error = types->Flatten(buffer, size);

                        // write the data
                        if (error == B_OK) {
                                error = _WriteData(kSupportedTypesAttribute,
                                        kSupportedTypesResourceID, B_MESSAGE_TYPE, buffer, size);
                        }

                        delete[] buffer;
                } else
                        error = _RemoveData(kSupportedTypesAttribute, B_MESSAGE_TYPE);

                // update the MIME database, if the app signature is installed
                if (updateMimeDB && error == B_OK && mimeType.IsInstalled())
                        error = mimeType.SetSupportedTypes(types, syncAll);
        }
        return error;
}


status_t
BAppFileInfo::SetSupportedTypes(const BMessage* types, bool syncAll)
{
        return SetSupportedTypes(types, true, syncAll);
}


status_t
BAppFileInfo::SetSupportedTypes(const BMessage* types)
{
        return SetSupportedTypes(types, true, false);
}


bool
BAppFileInfo::IsSupportedType(const char* type) const
{
        status_t error = type != NULL ? B_OK : B_BAD_VALUE;
        // get the supported types
        BMessage types;
        if (error == B_OK)
                error = GetSupportedTypes(&types);
        // turn type into a BMimeType
        BMimeType mimeType;
        if (error == B_OK)
                error = mimeType.SetTo(type);
        // iterate through the supported types
        bool found = false;
        if (error == B_OK) {
                const char* supportedType;
                for (int32 i = 0;
                         !found && types.FindString("types", i, &supportedType) == B_OK;
                         i++) {
                        found = strcmp(supportedType, "application/octet-stream") == 0
                                || BMimeType(supportedType).Contains(&mimeType);
                }
        }
        return found;
}


bool
BAppFileInfo::Supports(BMimeType* type) const
{
        status_t error
                = type != NULL && type->InitCheck() == B_OK ? B_OK : B_BAD_VALUE;
        // get the supported types
        BMessage types;
        if (error == B_OK)
                error = GetSupportedTypes(&types);
        // iterate through the supported types
        bool found = false;
        if (error == B_OK) {
                const char* supportedType;
                for (int32 i = 0;
                         !found && types.FindString("types", i, &supportedType) == B_OK;
                         i++) {
                        found = BMimeType(supportedType).Contains(type);
                }
        }
        return found;
}


status_t
BAppFileInfo::GetIcon(BBitmap* icon, icon_size which) const
{
        return GetIconForType(NULL, icon, which);
}


status_t
BAppFileInfo::GetIcon(uint8** data, size_t* size) const
{
        return GetIconForType(NULL, data, size);
}


status_t
BAppFileInfo::SetIcon(const BBitmap* icon, icon_size which, bool updateMimeDB)
{
        return SetIconForType(NULL, icon, which, updateMimeDB);
}


status_t
BAppFileInfo::SetIcon(const BBitmap* icon, icon_size which)
{
        return SetIconForType(NULL, icon, which, true);
}


status_t
BAppFileInfo::SetIcon(const uint8* data, size_t size, bool updateMimeDB)
{
        return SetIconForType(NULL, data, size, updateMimeDB);
}


status_t
BAppFileInfo::SetIcon(const uint8* data, size_t size)
{
        return SetIconForType(NULL, data, size, true);
}


status_t
BAppFileInfo::GetVersionInfo(version_info* info, version_kind kind) const
{
        // check params and initialization
        if (info == NULL)
                return B_BAD_VALUE;

        int32 index = 0;
        switch (kind) {
                case B_APP_VERSION_KIND:
                        index = 0;
                        break;
                case B_SYSTEM_VERSION_KIND:
                        index = 1;
                        break;
                default:
                        return B_BAD_VALUE;
        }

        if (InitCheck() != B_OK)
                return B_NO_INIT;

        // read the data
        size_t read = 0;
        version_info infos[2];
        status_t error = _ReadData(kVersionInfoAttribute, kVersionInfoResourceID,
                B_VERSION_INFO_TYPE, infos, 2 * sizeof(version_info), read);
        if (error != B_OK)
                return error;

        // check the read data
        if (read == sizeof(version_info)) {
                // only the app version info is there -- return a cleared system info
                if (index == 0)
                        *info = infos[index];
                else if (index == 1)
                        memset(info, 0, sizeof(version_info));
        } else if (read == 2 * sizeof(version_info)) {
                *info = infos[index];
        } else
                return B_ERROR;

        // return result
        return B_OK;
}


status_t
BAppFileInfo::SetVersionInfo(const version_info* info, version_kind kind)
{
        // check initialization
        status_t error = B_OK;
        if (InitCheck() != B_OK)
                error = B_NO_INIT;
        if (error == B_OK) {
                if (info != NULL) {
                        // check param
                        int32 index = 0;
                        if (error == B_OK) {
                                switch (kind) {
                                        case B_APP_VERSION_KIND:
                                                index = 0;
                                                break;
                                        case B_SYSTEM_VERSION_KIND:
                                                index = 1;
                                                break;
                                        default:
                                                error = B_BAD_VALUE;
                                                break;
                                }
                        }
                        // read both infos
                        version_info infos[2];
                        if (error == B_OK) {
                                size_t read;
                                if (_ReadData(kVersionInfoAttribute, kVersionInfoResourceID,
                                                B_VERSION_INFO_TYPE, infos, 2 * sizeof(version_info),
                                                read) == B_OK) {
                                        // clear the part that hasn't been read
                                        if (read < sizeof(infos))
                                                memset((char*)infos + read, 0, sizeof(infos) - read);
                                } else {
                                        // failed to read -- clear
                                        memset(infos, 0, sizeof(infos));
                                }
                        }
                        infos[index] = *info;
                        // write the data
                        if (error == B_OK) {
                                error = _WriteData(kVersionInfoAttribute,
                                        kVersionInfoResourceID, B_VERSION_INFO_TYPE, infos,
                                        2 * sizeof(version_info));
                        }
                } else
                        error = _RemoveData(kVersionInfoAttribute, B_VERSION_INFO_TYPE);
        }
        return error;
}


status_t
BAppFileInfo::GetIconForType(const char* type, BBitmap* icon, icon_size size)
        const
{
        if (InitCheck() != B_OK)
                return B_NO_INIT;

        if (icon == NULL || icon->InitCheck() != B_OK)
                return B_BAD_VALUE;

        // TODO: for consistency with attribute based icon reading, we
        // could also prefer B_CMAP8 icons here if the provided bitmap
        // is in that format. Right now, an existing B_CMAP8 icon resource
        // would be ignored as soon as a vector icon is present. On the other
        // hand, maybe this still results in a more consistent user interface,
        // since Tracker/Deskbar would surely show the vector icon.

        // try vector icon first
        BString vectorAttributeName(kIconAttribute);

        // check type param
        if (type != NULL) {
                if (BMimeType::IsValid(type))
                        vectorAttributeName += type;
                else
                        return B_BAD_VALUE;
        } else {
                vectorAttributeName += kIconType;
        }
        const char* attribute = vectorAttributeName.String();

        size_t bytesRead;
        void* allocatedBuffer;
        status_t error = _ReadData(attribute, -1, B_VECTOR_ICON_TYPE, NULL, 0,
                bytesRead, &allocatedBuffer);
        if (error == B_OK) {
                error = BIconUtils::GetVectorIcon((uint8*)allocatedBuffer,
                                                                                  bytesRead, icon);
                free(allocatedBuffer);
                return error;
        }

        // no vector icon if we got this far,
        // align size argument just in case
        if (size < B_LARGE_ICON)
                size = B_MINI_ICON;
        else
                size = B_LARGE_ICON;

        error = B_OK;
        // set some icon size related variables
        BString attributeString;
        BRect bounds;
        uint32 attrType = 0;
        size_t attrSize = 0;
        switch (size) {
                case B_MINI_ICON:
                        attributeString = kMiniIconAttribute;
                        bounds.Set(0, 0, 15, 15);
                        attrType = B_MINI_ICON_TYPE;
                        attrSize = 16 * 16;
                        break;
                case B_LARGE_ICON:
                        attributeString = kLargeIconAttribute;
                        bounds.Set(0, 0, 31, 31);
                        attrType = B_LARGE_ICON_TYPE;
                        attrSize = 32 * 32;
                        break;
                default:
                        return B_BAD_VALUE;
        }

        // compose attribute name
        attributeString += type != NULL ? type : kStandardIconType;
        attribute = attributeString.String();

        // check parameters
        // currently, scaling B_CMAP8 icons is not supported
        if (icon->ColorSpace() == B_CMAP8 && icon->Bounds() != bounds)
                return B_BAD_VALUE;

        // read the data
        if (error == B_OK) {
                bool tempBuffer
                        = icon->ColorSpace() != B_CMAP8 || icon->Bounds() != bounds;
                uint8* buffer = NULL;
                size_t read;
                if (tempBuffer) {
                        // other color space or bitmap size than stored in attribute
                        buffer = new(std::nothrow) uint8[attrSize];
                        if (!buffer) {
                                error = B_NO_MEMORY;
                        } else {
                                error = _ReadData(attribute, -1, attrType, buffer, attrSize,
                                        read);
                        }
                } else {
                        error = _ReadData(attribute, -1, attrType, icon->Bits(), attrSize,
                                read);
                }
                if (error == B_OK && read != attrSize)
                        error = B_ERROR;
                if (tempBuffer) {
                        // other color space than stored in attribute
                        if (error == B_OK) {
                                error = BIconUtils::ConvertFromCMAP8(buffer, (uint32)size,
                                        (uint32)size, (uint32)size, icon);
                        }
                        delete[] buffer;
                }
        }
        return error;
}


status_t
BAppFileInfo::GetIconForType(const char* type, uint8** data, size_t* size) const
{
        if (InitCheck() != B_OK)
                return B_NO_INIT;

        if (data == NULL || size == NULL)
                return B_BAD_VALUE;

        // get vector icon
        BString attributeName(kIconAttribute);

        // check type param
        if (type != NULL) {
                if (BMimeType::IsValid(type))
                        attributeName += type;
                else
                        return B_BAD_VALUE;
        } else
                attributeName += kIconType;

        void* allocatedBuffer = NULL;
        status_t ret = _ReadData(attributeName.String(), -1, B_VECTOR_ICON_TYPE,
                NULL, 0, *size, &allocatedBuffer);

        if (ret < B_OK)
                return ret;

        *data = (uint8*)allocatedBuffer;
        return B_OK;
}


status_t
BAppFileInfo::SetIconForType(const char* type, const BBitmap* icon,
        icon_size which, bool updateMimeDB)
{
        status_t error = B_OK;

        // set some icon size related variables
        BString attributeString;
        BRect bounds;
        uint32 attrType = 0;
        size_t attrSize = 0;
        int32 resourceID = 0;
        switch (which) {
                case B_MINI_ICON:
                        attributeString = kMiniIconAttribute;
                        bounds.Set(0, 0, 15, 15);
                        attrType = B_MINI_ICON_TYPE;
                        attrSize = 16 * 16;
                        resourceID = type != NULL
                                ? kMiniIconForTypeResourceID : kMiniIconResourceID;
                        break;
                case B_LARGE_ICON:
                        attributeString = kLargeIconAttribute;
                        bounds.Set(0, 0, 31, 31);
                        attrType = B_LARGE_ICON_TYPE;
                        attrSize = 32 * 32;
                        resourceID = type != NULL
                                ? kLargeIconForTypeResourceID : kLargeIconResourceID;
                        break;
                default:
                        error = B_BAD_VALUE;
                        break;
        }

        // check type param
        if (error == B_OK) {
                if (type != NULL) {
                        if (BMimeType::IsValid(type))
                                attributeString += type;
                        else
                                error = B_BAD_VALUE;
                } else
                        attributeString += kStandardIconType;
        }
        const char* attribute = attributeString.String();

        // check parameter and initialization
        if (error == B_OK && icon != NULL
                && (icon->InitCheck() != B_OK || icon->Bounds() != bounds)) {
                error = B_BAD_VALUE;
        }
        if (error == B_OK && InitCheck() != B_OK)
                error = B_NO_INIT;

        // write/remove the attribute
        if (error == B_OK) {
                if (icon != NULL) {
                        bool otherColorSpace = (icon->ColorSpace() != B_CMAP8);
                        if (otherColorSpace) {
                                BBitmap bitmap(bounds, B_BITMAP_NO_SERVER_LINK, B_CMAP8);
                                error = bitmap.InitCheck();
                                if (error == B_OK)
                                        error = bitmap.ImportBits(icon);
                                if (error == B_OK) {
                                        error = _WriteData(attribute, resourceID, attrType,
                                                bitmap.Bits(), attrSize, true);
                                }
                        } else {
                                error = _WriteData(attribute, resourceID, attrType,
                                        icon->Bits(), attrSize, true);
                        }
                } else  // no icon given => remove
                        error = _RemoveData(attribute, attrType);
        }

        // set the attribute on the MIME type, if the file has a signature
        BMimeType mimeType;
        if (updateMimeDB && error == B_OK && GetMetaMime(&mimeType) == B_OK) {
                if (!mimeType.IsInstalled())
                        error = mimeType.Install();
                if (error == B_OK)
                        error = mimeType.SetIconForType(type, icon, which);
        }
        return error;
}


status_t
BAppFileInfo::SetIconForType(const char* type, const BBitmap* icon,
        icon_size which)
{
        return SetIconForType(type, icon, which, true);
}


status_t
BAppFileInfo::SetIconForType(const char* type, const uint8* data, size_t size,
        bool updateMimeDB)
{
        if (InitCheck() != B_OK)
                return B_NO_INIT;

        // set some icon related variables
        BString attributeString = kIconAttribute;
        int32 resourceID = type ? kIconForTypeResourceID : kIconResourceID;
        uint32 attrType = B_VECTOR_ICON_TYPE;

        // check type param
        if (type != NULL) {
                if (BMimeType::IsValid(type))
                        attributeString += type;
                else
                        return B_BAD_VALUE;
        } else
                attributeString += kIconType;

        const char* attribute = attributeString.String();

        status_t error;
        // write/remove the attribute
        if (data != NULL)
                error = _WriteData(attribute, resourceID, attrType, data, size, true);
        else    // no icon given => remove
                error = _RemoveData(attribute, attrType);

        // set the attribute on the MIME type, if the file has a signature
        BMimeType mimeType;
        if (updateMimeDB && error == B_OK && GetMetaMime(&mimeType) == B_OK) {
                if (!mimeType.IsInstalled())
                        error = mimeType.Install();
                if (error == B_OK)
                        error = mimeType.SetIconForType(type, data, size);
        }
        return error;
}


status_t
BAppFileInfo::SetIconForType(const char* type, const uint8* data, size_t size)
{
        return SetIconForType(type, data, size, true);
}


void
BAppFileInfo::SetInfoLocation(info_location location)
{
        // if the resources failed to initialize, we must not use them
        if (fResources == NULL)
                location = info_location(location & ~B_USE_RESOURCES);

        fWhere = location;
}

bool
BAppFileInfo::IsUsingAttributes() const
{
        return (fWhere & B_USE_ATTRIBUTES) != 0;
}


bool
BAppFileInfo::IsUsingResources() const
{
        return (fWhere & B_USE_RESOURCES) != 0;
}


// FBC
void BAppFileInfo::_ReservedAppFileInfo1() {}
void BAppFileInfo::_ReservedAppFileInfo2() {}
void BAppFileInfo::_ReservedAppFileInfo3() {}


#ifdef __HAIKU_BEOS_COMPATIBLE
//!     Privatized assignment operator to prevent usage.
BAppFileInfo&
BAppFileInfo::operator=(const BAppFileInfo&)
{
        return *this;
}


//! Privatized copy constructor to prevent usage.
BAppFileInfo::BAppFileInfo(const BAppFileInfo&)
{
}
#endif


/*!     Initializes a BMimeType to the signature of the associated file.

        \warning The parameter \a meta is not checked.

        \param meta A pointer to a pre-allocated BMimeType that shall be
                   initialized to the signature of the associated file.

        \returns A status code.
        \retval B_OK Everything went fine.
        \retval B_BAD_VALUE \c NULL \a meta
        \retval B_ENTRY_NOT_FOUND The file has not signature or the signature is
                (not installed in the MIME database.) no valid MIME string.
*/
status_t
BAppFileInfo::GetMetaMime(BMimeType* meta) const
{
        char signature[B_MIME_TYPE_LENGTH];
        status_t error = GetSignature(signature);
        if (error == B_OK)
                error = meta->SetTo(signature);
        else if (error == B_BAD_VALUE)
                error = B_ENTRY_NOT_FOUND;
        if (error == B_OK && !meta->IsValid())
                error = B_BAD_VALUE;
        return error;
}


/*!     Reads data from an attribute or resource.

        \note The data is read from the location specified by \a fWhere.

        \warning The object must be properly initialized. The parameters are
                \b NOT checked.

        \param name The name of the attribute/resource to be read.
        \param id The resource ID of the resource to be read. It is ignored
                   when < 0.
        \param type The type of the attribute/resource to be read.
        \param buffer A pre-allocated buffer for the data to be read.
        \param bufferSize The size of the supplied buffer.
        \param bytesRead A reference parameter, set to the number of bytes
                   actually read.
        \param allocatedBuffer If not \c NULL, the method allocates a buffer
                   large enough too store the whole data and writes a pointer to it
                   into this variable. If \c NULL, the supplied buffer is used.

        \returns A status code.
        \retval B_OK Everything went fine.
        \retval B_ENTRY_NOT_FOUND The entry was not found.
        \retval B_NO_MEMORY Ran out of memory allocating the buffer.
        \retval B_BAD_VALUE \a type did not match.
*/
status_t
BAppFileInfo::_ReadData(const char* name, int32 id, type_code type,
        void* buffer, size_t bufferSize, size_t& bytesRead, void** allocatedBuffer)
        const
{
        status_t error = B_OK;

        if (allocatedBuffer)
                buffer = NULL;

        bool foundData = false;

        if (IsUsingAttributes()) {
                // get an attribute info
                attr_info info;
                if (error == B_OK)
                        error = fNode->GetAttrInfo(name, &info);

                // check type and size, allocate a buffer, if required
                if (error == B_OK && info.type != type)
                        error = B_BAD_VALUE;
                if (error == B_OK && allocatedBuffer != NULL) {
                        buffer = malloc(info.size);
                        if (buffer == NULL)
                                error = B_NO_MEMORY;
                        bufferSize = info.size;
                }
                if (error == B_OK && (off_t)bufferSize < info.size)
                        error = B_BAD_VALUE;

                // read the data
                if (error == B_OK) {
                        ssize_t read = fNode->ReadAttr(name, type, 0, buffer, info.size);
                        if (read < 0)
                                error = read;
                        else if (read != info.size)
                                error = B_ERROR;
                        else
                                bytesRead = read;
                }

                foundData = error == B_OK;

                // free the allocated buffer on error
                if (!foundData && allocatedBuffer != NULL && buffer != NULL) {
                        free(buffer);
                        buffer = NULL;
                }
        }

        if (!foundData && IsUsingResources()) {
                // get a resource info
                error = B_OK;
                int32 idFound;
                size_t sizeFound;
                if (error == B_OK) {
                        if (!fResources->GetResourceInfo(type, name, &idFound, &sizeFound))
                                error = B_ENTRY_NOT_FOUND;
                }

                // check id and size, allocate a buffer, if required
                if (error == B_OK && id >= 0 && idFound != id)
                        error = B_ENTRY_NOT_FOUND;
                if (error == B_OK && allocatedBuffer) {
                        buffer = malloc(sizeFound);
                        if (!buffer)
                                error = B_NO_MEMORY;
                        bufferSize = sizeFound;
                }
                if (error == B_OK && bufferSize < sizeFound)
                        error = B_BAD_VALUE;

                // load resource
                const void* resourceData = NULL;
                if (error == B_OK) {
                        resourceData = fResources->LoadResource(type, name, &bytesRead);
                        if (resourceData != NULL && sizeFound == bytesRead)
                                memcpy(buffer, resourceData, bytesRead);
                        else
                                error = B_ERROR;
                }
        } else if (!foundData)
                error = B_BAD_VALUE;

        // return the allocated buffer, or free it on error
        if (allocatedBuffer != NULL) {
                if (error == B_OK)
                        *allocatedBuffer = buffer;
                else
                        free(buffer);
        }

        return error;
}


/*!     Writes data to an attribute or resource.

        \note The data is written to the location(s) specified by \a fWhere.

        \warning The object must be properly initialized. The parameters are
                \b NOT checked.

        \param name The name of the attribute/resource to be written.
        \param id The resource ID of the resource to be written.
        \param type The type of the attribute/resource to be written.
        \param buffer A buffer containing the data to be written.
        \param bufferSize The size of the supplied buffer.
        \param findID If set to \c true use the ID that is already assigned to the
                   \a name / \a type pair or take the first unused ID >= \a id.
                   If \c false, \a id is used.

        \returns A status code.
        \retval B_OK Everything went fine.
        \retval B_ERROR An error occurred while trying to write the data.
*/
status_t
BAppFileInfo::_WriteData(const char* name, int32 id, type_code type,
        const void* buffer, size_t bufferSize, bool findID)
{
        if (!IsUsingAttributes() && !IsUsingResources())
                return B_NO_INIT;

        status_t error = B_OK;

        // write to attribute
        if (IsUsingAttributes()) {
                ssize_t written = fNode->WriteAttr(name, type, 0, buffer, bufferSize);
                if (written < 0)
                        error = written;
                else if (written != (ssize_t)bufferSize)
                        error = B_ERROR;
        }
        // write to resource
        if (IsUsingResources() && error == B_OK) {
                if (findID) {
                        // get the resource info
                        int32 idFound;
                        size_t sizeFound;
                        if (fResources->GetResourceInfo(type, name, &idFound, &sizeFound))
                                id = idFound;
                        else {
                                // type-name pair doesn't exist yet -- find unused ID
                                while (fResources->HasResource(type, id))
                                        id++;
                        }
                }
                error = fResources->AddResource(type, id, buffer, bufferSize, name);
        }
        return error;
}


/*!     Removes an attribute or resource.

        \note The removal location is specified by \a fWhere.

        \warning The object must be properly initialized. The parameters are
                \b NOT checked.

        \param name The name of the attribute/resource to be remove.
        \param type The type of the attribute/resource to be removed.

        \returns A status code.
        \retval B_OK Everything went fine.
        \retval B_NO_INIT Not using attributes and not using resources.
        \retval B_ENTRY_NOT_FOUND The attribute or resource was not found.
*/
status_t
BAppFileInfo::_RemoveData(const char* name, type_code type)
{
        if (!IsUsingAttributes() && !IsUsingResources())
                return B_NO_INIT;

        status_t error = B_OK;

        // remove the attribute
        if (IsUsingAttributes()) {
                error = fNode->RemoveAttr(name);
                // It's no error, if there has been no attribute.
                if (error == B_ENTRY_NOT_FOUND)
                        error = B_OK;
        }
        // remove the resource
        if (IsUsingResources() && error == B_OK) {
                // get a resource info
                int32 idFound;
                size_t sizeFound;
                if (fResources->GetResourceInfo(type, name, &idFound, &sizeFound))
                        error = fResources->RemoveResource(type, idFound);
        }
        return error;
}