root/src/build/libbe/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 0
                if (updateMimeDB && error == B_OK && mimeType.IsInstalled())
                        error = mimeType.SetSupportedTypes(types, syncAll);
#endif
        }
        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
#if 0
        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);
        }
#endif
        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
#if 0
        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);
        }
#endif
        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() {}


BAppFileInfo&
BAppFileInfo::operator=(const BAppFileInfo&)
{
        return *this;
}


BAppFileInfo::BAppFileInfo(const BAppFileInfo&)
{
}


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;
}


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;
}


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;
}


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;
}