root/src/kits/storage/MimeType.cpp
/*
 * Copyright 2002-2006 Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Axel Dörfler, axeld@pinc-software.de
 *              Tyler Dauwalder
 *              Ingo Weinhold, bonefish@users.sf.net
 */


#include "MimeType.h"

#include <Bitmap.h>
#include <mime/database_support.h>
#include <mime/DatabaseLocation.h>

#include <RegistrarDefs.h>
#include <RosterPrivate.h>

#include <ctype.h>
#include <new>
#include <stdio.h>
#include <strings.h>

#include "sniffer/Rule.h"
#include "sniffer/Parser.h"


using namespace BPrivate;

// Private helper functions
static bool isValidMimeChar(const char ch);

using namespace BPrivate::Storage::Mime;
using namespace std;

const char* B_PEF_APP_MIME_TYPE         = "application/x-be-executable";
const char* B_PE_APP_MIME_TYPE          = "application/x-vnd.Be-peexecutable";
const char* B_ELF_APP_MIME_TYPE         = "application/x-vnd.Be-elfexecutable";
const char* B_RESOURCE_MIME_TYPE        = "application/x-be-resource";
const char* B_FILE_MIME_TYPE            = "application/octet-stream";
// Might be defined platform depended, but ELF will certainly be the common
// format for all platforms anyway.
const char* B_APP_MIME_TYPE                     = B_ELF_APP_MIME_TYPE;


static bool
isValidMimeChar(const char ch)
{
        // Handles white space and most CTLs
        return ch > 32
                && ch != '/'
                && ch != '<'
                && ch != '>'
                && ch != '@'
                && ch != ','
                && ch != ';'
                && ch != ':'
                && ch != '"'
                && ch != '('
                && ch != ')'
                && ch != '['
                && ch != ']'
                && ch != '?'
                && ch != '='
                && ch != '\\'
                && ch != 127;   // DEL
}


//      #pragma mark -


// Creates an uninitialized BMimeType object.
BMimeType::BMimeType()
        :
        fType(NULL),
        fCStatus(B_NO_INIT)
{
}


// Creates a BMimeType object and initializes it to the supplied
// MIME type.
BMimeType::BMimeType(const char* mimeType)
        :
        fType(NULL),
        fCStatus(B_NO_INIT)
{
        SetTo(mimeType);
}


// Frees all resources associated with this object.
BMimeType::~BMimeType()
{
        Unset();
}


// Initializes this object to the supplied MIME type.
status_t
BMimeType::SetTo(const char* mimeType)
{
        if (mimeType == NULL) {
                Unset();
        } else if (!BMimeType::IsValid(mimeType)) {
                fCStatus = B_BAD_VALUE;
        } else {
                Unset();
                fType = new(std::nothrow) char[strlen(mimeType) + 1];
                if (fType) {
                        strlcpy(fType, mimeType, B_MIME_TYPE_LENGTH);
                        fCStatus = B_OK;
                } else {
                        fCStatus = B_NO_MEMORY;
                }
        }
        return fCStatus;
}


// Returns the object to an uninitialized state
void
BMimeType::Unset()
{
        delete [] fType;
        fType = NULL;
        fCStatus = B_NO_INIT;
}


// Returns the result of the most recent constructor or SetTo() call
status_t
BMimeType::InitCheck() const
{
        return fCStatus;
}


// Returns the MIME string represented by this object
const char*
BMimeType::Type() const
{
        return fType;
}


// Returns whether the object represents a valid MIME type
bool
BMimeType::IsValid() const
{
        return InitCheck() == B_OK && BMimeType::IsValid(Type());
}


// Returns whether this objects represents a supertype
bool
BMimeType::IsSupertypeOnly() const
{
        if (fCStatus == B_OK) {
                // We assume here fCStatus will be B_OK *only* if
                // the MIME string is valid
                size_t len = strlen(fType);
                for (size_t i = 0; i < len; i++) {
                        if (fType[i] == '/')
                                return false;
                }
                return true;
        } else
                return false;
}


// Returns whether or not this type is currently installed in the
// MIME database
bool
BMimeType::IsInstalled() const
{
        return InitCheck() == B_OK
                && default_database_location()->IsInstalled(Type());
}


// Gets the supertype of the MIME type represented by this object
status_t
BMimeType::GetSupertype(BMimeType* supertype) const
{
        if (supertype == NULL)
                return B_BAD_VALUE;

        supertype->Unset();
        status_t status = fCStatus == B_OK ? B_OK : B_BAD_VALUE;
        if (status == B_OK) {
                size_t len = strlen(fType);
                size_t i = 0;
                for (; i < len; i++) {
                        if (fType[i] == '/')
                                break;
                }
                if (i == len) {
                        // object is a supertype only
                        status = B_BAD_VALUE;
                } else {
                        char superMime[B_MIME_TYPE_LENGTH];
                        strncpy(superMime, fType, i);
                        superMime[i] = 0;
                        status = supertype->SetTo(superMime) == B_OK ? B_OK : B_BAD_VALUE;
                }
        }

        return status;
}


// Returns whether this and the supplied MIME type are equal
bool
BMimeType::operator==(const BMimeType &type) const
{
        if (InitCheck() == B_NO_INIT && type.InitCheck() == B_NO_INIT)
                return true;
        else if (InitCheck() == B_OK && type.InitCheck() == B_OK)
                return strcasecmp(Type(), type.Type()) == 0;

        return false;
}


// Returns whether this and the supplied MIME type are equal
bool
BMimeType::operator==(const char* type) const
{
        BMimeType mime;
        if (type)
                mime.SetTo(type);

        return (*this) == mime;
}


// Returns whether this MIME type is a supertype of or equals the
// supplied one
bool
BMimeType::Contains(const BMimeType* type) const
{
        if (type == NULL)
                return false;

        if (*this == *type)
                return true;

        BMimeType super;
        if (type->GetSupertype(&super) == B_OK && *this == super)
                return true;
        return false;
}


// Adds the MIME type to the MIME database
status_t
BMimeType::Install()
{
        status_t err = InitCheck();

        BMessage message(B_REG_MIME_INSTALL);
        BMessage reply;
        status_t result;

        // Build and send the message, read the reply
        if (err == B_OK)
                err = message.AddString("type", Type());

        if (err == B_OK)
                err = BRoster::Private().SendTo(&message, &reply, true);

        if (err == B_OK)
                err = (status_t)(reply.what == B_REG_RESULT ? B_OK : B_BAD_REPLY);

        if (err == B_OK)
                err = reply.FindInt32("result", &result);

        if (err == B_OK)
                err = result;

        return err;
}


// Removes the MIME type from the MIME database
status_t
BMimeType::Delete()
{
        status_t err = InitCheck();

        BMessage message(B_REG_MIME_DELETE);
        BMessage reply;
        status_t result;

        // Build and send the message, read the reply
        if (err == B_OK)
                err = message.AddString("type", Type());

        if (err == B_OK)
                err = BRoster::Private().SendTo(&message, &reply, true);

        if (err == B_OK)
                err = (status_t)(reply.what == B_REG_RESULT ? B_OK : B_BAD_REPLY);

        if (err == B_OK)
                err = reply.FindInt32("result", &result);

        if (err == B_OK)
                err = result;

        return err;
}


// Fetches the large or mini icon associated with the MIME type
status_t
BMimeType::GetIcon(BBitmap* icon, icon_size size) const
{
        if (icon == NULL)
                return B_BAD_VALUE;

        status_t err = InitCheck();
        if (err == B_OK)
                err = default_database_location()->GetIcon(Type(), *icon, size);

        return err;
}


//      Fetches the vector icon associated with the MIME type
status_t
BMimeType::GetIcon(uint8** data, size_t* size) const
{
        if (data == NULL || size == NULL)
                return B_BAD_VALUE;

        status_t err = InitCheck();
        if (err == B_OK)
                err = default_database_location()->GetIcon(Type(), *data, *size);

        return err;
}


// Fetches the signature of the MIME type's preferred application from the
// MIME database
status_t
BMimeType::GetPreferredApp(char* signature, app_verb verb) const
{
        status_t err = InitCheck();
        if (err == B_OK) {
                err = default_database_location()->GetPreferredApp(Type(), signature,
                        verb);
        }

        return err;
}


// Fetches from the MIME database a BMessage describing the attributes
// typically associated with files of the given MIME type
status_t
BMimeType::GetAttrInfo(BMessage* info) const
{
        if (info == NULL)
                return B_BAD_VALUE;

        status_t err = InitCheck();
        if (err == B_OK)
                err = default_database_location()->GetAttributesInfo(Type(), *info);

        return err;
}


// Fetches the MIME type's associated filename extensions from the MIME
// database
status_t
BMimeType::GetFileExtensions(BMessage* extensions) const
{
        if (extensions == NULL)
                return B_BAD_VALUE;

        status_t err = InitCheck();
        if (err == B_OK) {
                err = default_database_location()->GetFileExtensions(Type(),
                        *extensions);
        }

        return err;
}


// Fetches the MIME type's short description from the MIME database
status_t
BMimeType::GetShortDescription(char* description) const
{
        status_t err = InitCheck();
        if (err == B_OK) {
                err = default_database_location()->GetShortDescription(Type(),
                        description);
        }

        return err;
}


// Fetches the MIME type's long description from the MIME database
status_t
BMimeType::GetLongDescription(char* description) const
{
        status_t err = InitCheck();
        if (err == B_OK) {
                err = default_database_location()->GetLongDescription(Type(),
                        description);
        }

        return err;
}


// Fetches a \c BMessage containing a list of MIME signatures of
// applications that are able to handle files of this MIME type.
status_t
BMimeType::GetSupportingApps(BMessage* signatures) const
{
        if (signatures == NULL)
                return B_BAD_VALUE;

        BMessage message(B_REG_MIME_GET_SUPPORTING_APPS);
        status_t result;

        status_t err = InitCheck();
        if (err == B_OK)
                err = message.AddString("type", Type());
        if (err == B_OK)
                err = BRoster::Private().SendTo(&message, signatures, true);
        if (err == B_OK) {
                err = (status_t)(signatures->what == B_REG_RESULT ? B_OK
                        : B_BAD_REPLY);
        }
        if (err == B_OK)
                err = signatures->FindInt32("result", &result);
        if (err == B_OK)
                err = result;

        return err;
}


// Sets the large or mini icon for the MIME type
status_t
BMimeType::SetIcon(const BBitmap* icon, icon_size which)
{
        return SetIconForType(NULL, icon, which);
}


// Sets the vector icon for the MIME type
status_t
BMimeType::SetIcon(const uint8* data, size_t size)
{
        return SetIconForType(NULL, data, size);
}


// Sets the preferred application for the MIME type
status_t
BMimeType::SetPreferredApp(const char* signature, app_verb verb)
{
        status_t err = InitCheck();

        BMessage message(signature && signature[0]
                ? B_REG_MIME_SET_PARAM : B_REG_MIME_DELETE_PARAM);
        BMessage reply;
        status_t result;

        // Build and send the message, read the reply
        if (err == B_OK)
                err = message.AddString("type", Type());

        if (err == B_OK)
                err = message.AddInt32("which", B_REG_MIME_PREFERRED_APP);

        if (err == B_OK && signature != NULL)
                err = message.AddString("signature", signature);

        if (err == B_OK)
                err = message.AddInt32("app verb", verb);

        if (err == B_OK)
                err = BRoster::Private().SendTo(&message, &reply, true);

        if (err == B_OK)
                err = (status_t)(reply.what == B_REG_RESULT ? B_OK : B_BAD_REPLY);

        if (err == B_OK)
                err = reply.FindInt32("result", &result);

        if (err == B_OK)
                err = result;

        return err;
}


// Sets the description of the attributes typically associated with files
// of the given MIME type
status_t
BMimeType::SetAttrInfo(const BMessage* info)
{
        status_t err = InitCheck();

        BMessage message(info ? B_REG_MIME_SET_PARAM : B_REG_MIME_DELETE_PARAM);
        BMessage reply;
        status_t result;

        // Build and send the message, read the reply
        if (err == B_OK)
                err = message.AddString("type", Type());
        if (err == B_OK)
                err = message.AddInt32("which", B_REG_MIME_ATTR_INFO);
        if (err == B_OK && info != NULL)
                err = message.AddMessage("attr info", info);
        if (err == B_OK)
                err = BRoster::Private().SendTo(&message, &reply, true);
        if (err == B_OK)
                err = (status_t)(reply.what == B_REG_RESULT ? B_OK : B_BAD_REPLY);
        if (err == B_OK)
                err = reply.FindInt32("result", &result);
        if (err == B_OK)
                err = result;

        return err;
}


// Sets the list of filename extensions associated with the MIME type
status_t
BMimeType::SetFileExtensions(const BMessage* extensions)
{
        status_t err = InitCheck();

        BMessage message(extensions ? B_REG_MIME_SET_PARAM : B_REG_MIME_DELETE_PARAM);
        BMessage reply;
        status_t result;

        // Build and send the message, read the reply
        if (err == B_OK)
                err = message.AddString("type", Type());

        if (err == B_OK)
                err = message.AddInt32("which", B_REG_MIME_FILE_EXTENSIONS);

        if (err == B_OK && extensions != NULL)
                err = message.AddMessage("extensions", extensions);

        if (err == B_OK)
                err = BRoster::Private().SendTo(&message, &reply, true);

        if (err == B_OK)
                err = (status_t)(reply.what == B_REG_RESULT ? B_OK : B_BAD_REPLY);

        if (err == B_OK)
                err = reply.FindInt32("result", &result);

        if (err == B_OK)
                err = result;

        return err;
}


// Sets the short description field for the MIME type
status_t
BMimeType::SetShortDescription(const char* description)
{
        status_t err = InitCheck();

        BMessage message(description && description [0]
                ? B_REG_MIME_SET_PARAM : B_REG_MIME_DELETE_PARAM);
        BMessage reply;
        status_t result;

        // Build and send the message, read the reply
        if (err == B_OK)
                err = message.AddString("type", Type());

        if (err == B_OK)
                err = message.AddInt32("which", B_REG_MIME_DESCRIPTION);

        if (err == B_OK && description)
                err = message.AddString("description", description);

        if (err == B_OK)
                err = message.AddBool("long", false);

        if (err == B_OK)
                err = BRoster::Private().SendTo(&message, &reply, true);

        if (err == B_OK)
                err = (status_t)(reply.what == B_REG_RESULT ? B_OK : B_BAD_REPLY);

        if (err == B_OK)
                err = reply.FindInt32("result", &result);

        if (err == B_OK)
                err = result;

        return err;
}


// Sets the long description field for the MIME type
status_t
BMimeType::SetLongDescription(const char* description)
{
        status_t err = InitCheck();

        BMessage message(description && description[0]
                ? B_REG_MIME_SET_PARAM : B_REG_MIME_DELETE_PARAM);
        BMessage reply;
        status_t result;

        // Build and send the message, read the reply
        if (err == B_OK)
                err = message.AddString("type", Type());

        if (err == B_OK)
                err = message.AddInt32("which", B_REG_MIME_DESCRIPTION);

        if (err == B_OK && description)
                err = message.AddString("description", description);

        if (err == B_OK)
                err = message.AddBool("long", true);

        if (err == B_OK)
                err = BRoster::Private().SendTo(&message, &reply, true);

        if (err == B_OK)
                err = (status_t)(reply.what == B_REG_RESULT ? B_OK : B_BAD_REPLY);

        if (err == B_OK)
                err = reply.FindInt32("result", &result);

        if (err == B_OK)
                err = result;

        return err;
}


// Fetches a BMessage listing all the MIME supertypes currently
// installed in the MIME database.
/*static*/ status_t
BMimeType::GetInstalledSupertypes(BMessage* supertypes)
{
        if (supertypes == NULL)
                return B_BAD_VALUE;

        BMessage message(B_REG_MIME_GET_INSTALLED_SUPERTYPES);
        status_t result;

        status_t err = BRoster::Private().SendTo(&message, supertypes, true);
        if (err == B_OK) {
                err = (status_t)(supertypes->what == B_REG_RESULT ? B_OK
                        : B_BAD_REPLY);
        }
        if (err == B_OK)
                err = supertypes->FindInt32("result", &result);
        if (err == B_OK)
                err = result;

        return err;
}


// Fetches a BMessage listing all the MIME types currently installed
// in the MIME database.
status_t
BMimeType::GetInstalledTypes(BMessage* types)
{
        return GetInstalledTypes(NULL, types);
}


// Fetches a BMessage listing all the MIME subtypes of the given
// supertype currently installed in the MIME database.
/*static*/ status_t
BMimeType::GetInstalledTypes(const char* supertype, BMessage* types)
{
        if (types == NULL)
                return B_BAD_VALUE;

        status_t result;

        // Build and send the message, read the reply
        BMessage message(B_REG_MIME_GET_INSTALLED_TYPES);
        status_t err = B_OK;

        if (supertype != NULL)
                err = message.AddString("supertype", supertype);
        if (err == B_OK)
                err = BRoster::Private().SendTo(&message, types, true);
        if (err == B_OK)
                err = (status_t)(types->what == B_REG_RESULT ? B_OK : B_BAD_REPLY);
        if (err == B_OK)
                err = types->FindInt32("result", &result);
        if (err == B_OK)
                err = result;

        return err;
}


// Fetches a \c BMessage containing a list of MIME signatures of
// applications that are able to handle files of any type.
status_t
BMimeType::GetWildcardApps(BMessage* wild_ones)
{
        BMimeType mime;
        status_t err = mime.SetTo("application/octet-stream");
        if (err == B_OK)
                err = mime.GetSupportingApps(wild_ones);
        return err;
}


// Returns whether the given string represents a valid MIME type.
bool
BMimeType::IsValid(const char* string)
{
        if (string == NULL)
                return false;

        bool foundSlash = false;
        size_t len = strlen(string);
        if (len >= B_MIME_TYPE_LENGTH || len == 0)
                return false;

        for (size_t i = 0; i < len; i++) {
                char ch = string[i];
                if (ch == '/') {
                        if (foundSlash || i == 0 || i == len - 1)
                                return false;
                        else
                                foundSlash = true;
                } else if (!isValidMimeChar(ch)) {
                        return false;
                }
        }
        return true;
}


// Fetches an \c entry_ref that serves as a hint as to where the MIME type's
// preferred application might live
status_t
BMimeType::GetAppHint(entry_ref* ref) const
{
        if (ref == NULL)
                return B_BAD_VALUE;

        status_t err = InitCheck();
        if (err == B_OK)
                err = default_database_location()->GetAppHint(Type(), *ref);
        return err;
}


// Sets the app hint field for the MIME type
status_t
BMimeType::SetAppHint(const entry_ref* ref)
{
        status_t err = InitCheck();

        BMessage message(ref ? B_REG_MIME_SET_PARAM : B_REG_MIME_DELETE_PARAM);
        BMessage reply;
        status_t result;

        // Build and send the message, read the reply
        if (err == B_OK)
                err = message.AddString("type", Type());

        if (err == B_OK)
                err = message.AddInt32("which", B_REG_MIME_APP_HINT);

        if (err == B_OK && ref != NULL)
                err = message.AddRef("app hint", ref);

        if (err == B_OK)
                err = BRoster::Private().SendTo(&message, &reply, true);

        if (err == B_OK)
                err = (status_t)(reply.what == B_REG_RESULT ? B_OK : B_BAD_REPLY);

        if (err == B_OK)
                err = reply.FindInt32("result", &result);

        if (err == B_OK)
                err = result;

        return err;
}


// Fetches the large or mini icon used by an application of this type for
// files of the given type.
status_t
BMimeType::GetIconForType(const char* type, BBitmap* icon, icon_size which) const
{
        if (icon == NULL)
                return B_BAD_VALUE;

        // If type is NULL, this function works just like GetIcon(), othewise,
        // we need to make sure the give type is valid.
        status_t err;
        if (type) {
                err = BMimeType::IsValid(type) ? B_OK : B_BAD_VALUE;
                if (err == B_OK) {
                        err = default_database_location()->GetIconForType(Type(), type,
                                *icon, which);
                }
        } else
                err = GetIcon(icon, which);

        return err;
}


// Fetches the vector icon used by an application of this type for files of
// the given type.
status_t
BMimeType::GetIconForType(const char* type, uint8** _data, size_t* _size) const
{
        if (_data == NULL || _size == NULL)
                return B_BAD_VALUE;

        // If type is NULL, this function works just like GetIcon(), otherwise,
        // we need to make sure the give type is valid.
        if (type == NULL)
                return GetIcon(_data, _size);

        if (!BMimeType::IsValid(type))
                return B_BAD_VALUE;

        return default_database_location()->GetIconForType(Type(), type, *_data,
                *_size);
}


// Sets the large or mini icon used by an application of this type for
// files of the given type.
status_t
BMimeType::SetIconForType(const char* type, const BBitmap* icon, icon_size which)
{
        status_t err = InitCheck();

        BMessage message(icon ? B_REG_MIME_SET_PARAM : B_REG_MIME_DELETE_PARAM);
        BMessage reply;
        status_t result;

        void* data = NULL;
        int32 dataSize;

        // Build and send the message, read the reply
        if (err == B_OK)
                err = message.AddString("type", Type());

        if (err == B_OK) {
                err = message.AddInt32("which",
                        type ? B_REG_MIME_ICON_FOR_TYPE : B_REG_MIME_ICON);
        }

        if (icon != NULL) {
                if (err == B_OK)
                        err = get_icon_data(icon, which, &data, &dataSize);

                if (err == B_OK)
                        err = message.AddData("icon data", B_RAW_TYPE, data, dataSize);
        }

        if (err == B_OK)
                err = message.AddInt32("icon size", which);

        if (type != NULL) {
                if (err == B_OK)
                        err = BMimeType::IsValid(type) ? B_OK : B_BAD_VALUE;

                if (err == B_OK)
                        err = message.AddString("file type", type);
        }

        if (err == B_OK)
                err = BRoster::Private().SendTo(&message, &reply, true);

        if (err == B_OK)
                err = (status_t)(reply.what == B_REG_RESULT ? B_OK : B_BAD_REPLY);

        if (err == B_OK)
                err = reply.FindInt32("result", &result);

        if (err == B_OK)
                err = result;

        delete[] (int8*)data;

        return err;
}


// Sets the large or mini icon used by an application of this type for
// files of the given type.
status_t
BMimeType::SetIconForType(const char* type, const uint8* data, size_t dataSize)
{
        status_t err = InitCheck();

        BMessage message(data ? B_REG_MIME_SET_PARAM : B_REG_MIME_DELETE_PARAM);
        BMessage reply;
        status_t result;

        // Build and send the message, read the reply
        if (err == B_OK)
                err = message.AddString("type", Type());
        if (err == B_OK)
                err = message.AddInt32("which", (type ? B_REG_MIME_ICON_FOR_TYPE : B_REG_MIME_ICON));
        if (data) {
                if (err == B_OK)
                        err = message.AddData("icon data", B_RAW_TYPE, data, dataSize);
        }
        if (err == B_OK)
                err = message.AddInt32("icon size", -1);
                // -1 indicates size should be ignored (vector icon data)
        if (type) {
                if (err == B_OK)
                        err = BMimeType::IsValid(type) ? B_OK : B_BAD_VALUE;
                if (err == B_OK)
                        err = message.AddString("file type", type);
        }
        if (err == B_OK)
                err = BRoster::Private().SendTo(&message, &reply, true);
        if (err == B_OK)
                err = (status_t)(reply.what == B_REG_RESULT ? B_OK : B_BAD_REPLY);
        if (err == B_OK)
                err = reply.FindInt32("result", &result);
        if (err == B_OK)
                err = result;

        return err;
}


// Retrieves the MIME type's sniffer rule
status_t
BMimeType::GetSnifferRule(BString* result) const
{
        if (result == NULL)
                return B_BAD_VALUE;

        status_t err = InitCheck();
        if (err == B_OK)
                err = default_database_location()->GetSnifferRule(Type(), *result);

        return err;
}


// Sets the MIME type's sniffer rule
status_t
BMimeType::SetSnifferRule(const char* rule)
{
        status_t err = InitCheck();
        if (err == B_OK && rule != NULL && rule[0] != '\0')
                err = CheckSnifferRule(rule, NULL);

        if (err != B_OK)
                return err;

        BMessage message(rule && rule[0] ? B_REG_MIME_SET_PARAM
                : B_REG_MIME_DELETE_PARAM);
        BMessage reply;
        status_t result;

        // Build and send the message, read the reply
        err = message.AddString("type", Type());
        if (err == B_OK)
                err = message.AddInt32("which", B_REG_MIME_SNIFFER_RULE);

        if (err == B_OK && rule)
                err = message.AddString("sniffer rule", rule);

        if (err == B_OK)
                err = BRoster::Private().SendTo(&message, &reply, true);

        if (err == B_OK)
                err = (status_t)(reply.what == B_REG_RESULT ? B_OK : B_BAD_REPLY);

        if (err == B_OK)
                err = reply.FindInt32("result", &result);

        if (err == B_OK)
                err = result;

        return err;
}


// Checks whether a MIME sniffer rule is valid or not.
status_t
BMimeType::CheckSnifferRule(const char* rule, BString* parseError)
{
        BPrivate::Storage::Sniffer::Rule snifferRule;

        return BPrivate::Storage::Sniffer::parse(rule, &snifferRule, parseError);
}


// Guesses a MIME type for the entry referred to by the given
// entry_ref.
status_t
BMimeType::GuessMimeType(const entry_ref* file, BMimeType* type)
{
        status_t err = file && type ? B_OK : B_BAD_VALUE;

        BMessage message(B_REG_MIME_SNIFF);
        BMessage reply;
        status_t result;
        const char* str;

        // Build and send the message, read the reply
        if (err == B_OK)
                err = message.AddRef("file ref", file);

        if (err == B_OK)
                err = BRoster::Private().SendTo(&message, &reply, true);

        if (err == B_OK)
                err = (status_t)(reply.what == B_REG_RESULT ? B_OK : B_BAD_REPLY);

        if (err == B_OK)
                err = reply.FindInt32("result", &result);

        if (err == B_OK)
                err = result;

        if (err == B_OK)
                err = reply.FindString("mime type", &str);

        if (err == B_OK)
                err = type->SetTo(str);

        return err;
}


// Guesses a MIME type for the supplied chunk of data.
status_t
BMimeType::GuessMimeType(const void* buffer, int32 length, BMimeType* type)
{
        status_t err = buffer && type ? B_OK : B_BAD_VALUE;

        BMessage message(B_REG_MIME_SNIFF);
        BMessage reply;
        status_t result;
        const char* str;

        // Build and send the message, read the reply
        if (err == B_OK)
                err = message.AddData("data", B_RAW_TYPE, buffer, length);

        if (err == B_OK)
                err = BRoster::Private().SendTo(&message, &reply, true);

        if (err == B_OK)
                err = (status_t)(reply.what == B_REG_RESULT ? B_OK : B_BAD_REPLY);

        if (err == B_OK)
                err = reply.FindInt32("result", &result);

        if (err == B_OK)
                err = result;

        if (err == B_OK)
                err = reply.FindString("mime type", &str);

        if (err == B_OK)
                err = type->SetTo(str);

        return err;
}


// Guesses a MIME type for the given filename.
status_t
BMimeType::GuessMimeType(const char* filename, BMimeType* type)
{
        status_t err = filename && type ? B_OK : B_BAD_VALUE;

        BMessage message(B_REG_MIME_SNIFF);
        BMessage reply;
        status_t result;
        const char* str;

        // Build and send the message, read the reply
        if (err == B_OK)
                err = message.AddString("filename", filename);

        if (err == B_OK)
                err = BRoster::Private().SendTo(&message, &reply, true);

        if (err == B_OK)
                err = (status_t)(reply.what == B_REG_RESULT ? B_OK : B_BAD_REPLY);

        if (err == B_OK)
                err = reply.FindInt32("result", &result);

        if (err == B_OK)
                err = result;

        if (err == B_OK)
                err = reply.FindString("mime type", &str);

        if (err == B_OK)
                err = type->SetTo(str);

        return err;
}


// Starts monitoring the MIME database for a given target.
status_t
BMimeType::StartWatching(BMessenger target)
{
        BMessage message(B_REG_MIME_START_WATCHING);
        BMessage reply;
        status_t result;
        status_t err;

        // Build and send the message, read the reply
        err = message.AddMessenger("target", target);
        if (err == B_OK)
                err = BRoster::Private().SendTo(&message, &reply, true);

        if (err == B_OK)
                err = (status_t)(reply.what == B_REG_RESULT ? B_OK : B_BAD_REPLY);

        if (err == B_OK)
                err = reply.FindInt32("result", &result);

        if (err == B_OK)
                err = result;

        return err;
}


// Stops monitoring the MIME database for a given target
status_t
BMimeType::StopWatching(BMessenger target)
{
        BMessage message(B_REG_MIME_STOP_WATCHING);
        BMessage reply;
        status_t result;
        status_t err;

        // Build and send the message, read the reply
        err = message.AddMessenger("target", target);
        if (err == B_OK)
                err = BRoster::Private().SendTo(&message, &reply, true);

        if (err == B_OK)
                err = (status_t)(reply.what == B_REG_RESULT ? B_OK : B_BAD_REPLY);

        if (err == B_OK)
                err = reply.FindInt32("result", &result);

        if (err == B_OK)
                err = result;

        return err;
}


// Initializes this object to the supplied MIME type
status_t
BMimeType::SetType(const char* mimeType)
{
        return SetTo(mimeType);
}


void BMimeType::_ReservedMimeType1() {}
void BMimeType::_ReservedMimeType2() {}
void BMimeType::_ReservedMimeType3() {}


#ifdef __HAIKU_BEOS_COMPATIBLE
// assignment operator.
// Unimplemented
BMimeType&
BMimeType::operator=(const BMimeType &)
{
        return *this;
                // not implemented
}


// copy constructor
// Unimplemented
BMimeType::BMimeType(const BMimeType &)
{
}
#endif


status_t
BMimeType::GetSupportedTypes(BMessage* types)
{
        if (types == NULL)
                return B_BAD_VALUE;

        status_t err = InitCheck();
        if (err == B_OK)
                err = default_database_location()->GetSupportedTypes(Type(), *types);

        return err;
}


/*!     Sets the list of MIME types supported by the MIME type (which is
        assumed to be an application signature).

        If \a types is \c NULL the application's supported types are unset.

        The supported MIME types must be stored in a field "types" of type
        \c B_STRING_TYPE in \a types.

        For each supported type the result of BMimeType::GetSupportingApps() will
        afterwards include the signature of this application.

        \a fullSync specifies whether or not any types that are no longer
        listed as supported types as of this call to SetSupportedTypes() shall be
        updated as well, i.e. whether this application shall be removed from their
        lists of supporting applications.

        If \a fullSync is \c false, this application will not be removed from the
        previously supported types' supporting apps lists until the next call
        to BMimeType::SetSupportedTypes() or BMimeType::DeleteSupportedTypes()
        with a \c true \a fullSync parameter, the next call to BMimeType::Delete(),
        or the next reboot.

        \param types The supported types to be assigned to the file.
               May be \c NULL.
        \param fullSync \c true to also synchronize the previously supported
               types, \c false otherwise.

        \returns \c B_OK on success or another error code on failure.
*/
status_t
BMimeType::SetSupportedTypes(const BMessage* types, bool fullSync)
{
        status_t err = InitCheck();

        // Build and send the message, read the reply
        BMessage message(types ? B_REG_MIME_SET_PARAM : B_REG_MIME_DELETE_PARAM);
        BMessage reply;
        status_t result;

        if (err == B_OK)
                err = message.AddString("type", Type());

        if (err == B_OK)
                err = message.AddInt32("which", B_REG_MIME_SUPPORTED_TYPES);

        if (err != B_OK && types != NULL)
                err = message.AddMessage("types", types);

        if (err == B_OK)
                err = message.AddBool("full sync", fullSync);

        if (err == B_OK)
                err = BRoster::Private().SendTo(&message, &reply, true);

        if (err == B_OK)
                err = (status_t)(reply.what == B_REG_RESULT ? B_OK : B_BAD_REPLY);

        if (err == B_OK)
                err = reply.FindInt32("result", &result);

        if (err == B_OK)
                err = result;

        return err;
}


/*!     Returns a list of mime types associated with the given file extension

        The list of types is returned in the pre-allocated \c BMessage pointed to
        by \a types. The types are stored in the message's "types" field, which
        is an array of \c B_STRING_TYPE values.

        \param extension The file extension of interest
        \param types Pointer to a pre-allocated BMessage into which the result will
               be stored.

        \returns \c B_OK on success or another error code on failure.
*/
status_t
BMimeType::GetAssociatedTypes(const char* extension, BMessage* types)
{
        status_t err = extension && types ? B_OK : B_BAD_VALUE;

        BMessage message(B_REG_MIME_GET_ASSOCIATED_TYPES);
        BMessage &reply = *types;
        status_t result;

        // Build and send the message, read the reply
        if (err == B_OK)
                err = message.AddString("extension", extension);

        if (err == B_OK)
                err = BRoster::Private().SendTo(&message, &reply, true);

        if (err == B_OK)
                err = (status_t)(reply.what == B_REG_RESULT ? B_OK : B_BAD_REPLY);

        if (err == B_OK)
                err = reply.FindInt32("result", &result);

        if (err == B_OK)
                err = result;

        return err;
}