#include <mime/AssociatedTypes.h>
#include <stdio.h>
#include <new>
#include <Directory.h>
#include <Entry.h>
#include <Message.h>
#include <mime/database_support.h>
#include <mime/DatabaseDirectory.h>
#include <mime/DatabaseLocation.h>
#include <mime/MimeSniffer.h>
#include <MimeType.h>
#include <Path.h>
#include <String.h>
#include <storage_support.h>
#define DBG(x) x
#define OUT printf
namespace BPrivate {
namespace Storage {
namespace Mime {
AssociatedTypes::AssociatedTypes(DatabaseLocation* databaseLocation,
MimeSniffer* mimeSniffer)
:
fDatabaseLocation(databaseLocation),
fMimeSniffer(mimeSniffer),
fHaveDoneFullBuild(false)
{
}
AssociatedTypes::~AssociatedTypes()
{
}
status_t
AssociatedTypes::GetAssociatedTypes(const char *extension, BMessage *types)
{
status_t err = extension && types ? B_OK : B_BAD_VALUE;
std::string extStr;
if (!err && !fHaveDoneFullBuild) {
err = BuildAssociatedTypesTable();
}
if (!err) {
extStr = PrepExtension(extension);
err = extStr.length() > 0 ? B_OK : B_BAD_VALUE;
}
if (!err) {
types->MakeEmpty();
std::set<std::string> &assTypes = fAssociatedTypes[extStr];
std::set<std::string>::const_iterator i;
for (i = assTypes.begin(); i != assTypes.end() && !err; i++) {
err = types->AddString(kTypesField, i->c_str());
}
}
return err;
}
status_t
AssociatedTypes::GuessMimeType(const char *filename, BString *result)
{
status_t err = filename && result ? B_OK : B_BAD_VALUE;
if (!err && !fHaveDoneFullBuild)
err = BuildAssociatedTypesTable();
if (!err && fMimeSniffer != NULL) {
BMimeType mimeType;
float priority = fMimeSniffer->GuessMimeType(filename, &mimeType);
if (priority >= 0) {
*result = mimeType.Type();
return B_OK;
}
}
if (!err) {
const char *rawExtension = strrchr(filename, '.');
if (rawExtension && rawExtension[1] != '\0') {
std::string extension = PrepExtension(rawExtension + 1);
std::set<std::string> &types = fAssociatedTypes[extension];
std::set<std::string>::const_iterator i = types.begin();
if (i != types.end())
result->SetTo(i->c_str());
else
err = kMimeGuessFailureError;
} else {
err = kMimeGuessFailureError;
}
}
return err;
}
status_t
AssociatedTypes::GuessMimeType(const entry_ref *ref, BString *result)
{
if (!ref)
return B_BAD_VALUE;
BPath path;
status_t err = path.SetTo(ref);
if (!err)
err = GuessMimeType(path.Path(), result);
return err;
}
status_t
AssociatedTypes::SetFileExtensions(const char *type, const BMessage *extensions)
{
status_t err = type && extensions ? B_OK : B_BAD_VALUE;
if (!fHaveDoneFullBuild)
return err;
std::set<std::string> oldExtensions;
std::set<std::string> &newExtensions = fFileExtensions[type];
if (!err) {
oldExtensions = newExtensions;
newExtensions.clear();
const char *extension;
for (int32 i = 0;
extensions->FindString(kTypesField, i, &extension) == B_OK;
i++)
{
newExtensions.insert(extension);
AddAssociatedType(extension, type);
}
for (std::set<std::string>::const_iterator i = newExtensions.begin();
i != newExtensions.end();
i++)
{
oldExtensions.erase(*i);
}
for (std::set<std::string>::const_iterator i = oldExtensions.begin();
i != oldExtensions.end();
i++)
{
RemoveAssociatedType(i->c_str(), type);
}
}
return err;
}
status_t
AssociatedTypes::DeleteFileExtensions(const char *type)
{
BMessage extensions;
return SetFileExtensions(type, &extensions);
}
void
AssociatedTypes::PrintToStream() const
{
printf("\n");
printf("-----------------\n");
printf("Associated Types:\n");
printf("-----------------\n");
for (std::map<std::string, std::set<std::string> >::const_iterator i = fAssociatedTypes.begin();
i != fAssociatedTypes.end();
i++)
{
printf("%s: ", i->first.c_str());
fflush(stdout);
bool first = true;
for (std::set<std::string>::const_iterator type = i->second.begin();
type != i->second.end();
type++)
{
if (first)
first = false;
else
printf(", ");
printf("%s", type->c_str());
fflush(stdout);
}
printf("\n");
}
}
status_t
AssociatedTypes::AddAssociatedType(const char *extension, const char *type)
{
status_t err = extension && type ? B_OK : B_BAD_VALUE;
std::string extStr;
if (!err) {
extStr = PrepExtension(extension);
err = extStr.length() > 0 ? B_OK : B_BAD_VALUE;
}
if (!err)
fAssociatedTypes[extStr].insert(type);
return err;
}
status_t
AssociatedTypes::RemoveAssociatedType(const char *extension, const char *type)
{
status_t err = extension && type ? B_OK : B_BAD_VALUE;
std::string extStr;
if (!err) {
extStr = PrepExtension(extension);
err = extStr.length() > 0 ? B_OK : B_BAD_VALUE;
}
if (!err)
fAssociatedTypes[extension].erase(type);
return err;
}
status_t
AssociatedTypes::BuildAssociatedTypesTable()
{
fFileExtensions.clear();
fAssociatedTypes.clear();
DatabaseDirectory root;
status_t err = root.Init(fDatabaseLocation);
if (!err) {
root.Rewind();
while (true) {
BEntry entry;
err = root.GetNextEntry(&entry);
if (err) {
if (err == B_ENTRY_NOT_FOUND)
err = B_OK;
break;
} else {
char supertype[B_PATH_NAME_LENGTH];
if (entry.IsDirectory()
&& entry.GetName(supertype) == B_OK
&& BMimeType::IsValid(supertype))
{
BPrivate::Storage::to_lower(supertype);
DatabaseDirectory dir;
if (dir.Init(fDatabaseLocation, supertype) == B_OK) {
dir.Rewind();
while (true) {
BEntry subEntry;
err = dir.GetNextEntry(&subEntry);
if (err) {
if (err == B_ENTRY_NOT_FOUND)
err = B_OK;
break;
} else {
char subtype[B_PATH_NAME_LENGTH];
if (subEntry.GetName(subtype) == B_OK) {
BPrivate::Storage::to_lower(subtype);
BString fulltype;
fulltype.SetToFormat("%s/%s", supertype, subtype);
ProcessType(fulltype);
}
}
}
} else {
DBG(OUT("Mime::AssociatedTypes::BuildAssociatedTypesTable(): "
"Failed opening supertype directory '%s'\n",
supertype));
}
ProcessType(supertype);
}
}
}
} else {
DBG(OUT("Mime::AssociatedTypes::BuildAssociatedTypesTable(): "
"Failed opening mime database directory\n"));
}
if (!err) {
fHaveDoneFullBuild = true;
} else {
DBG(OUT("Mime::AssociatedTypes::BuildAssociatedTypesTable() failed, "
"error code == 0x%" B_PRIx32 "\n", err));
}
return err;
}
status_t
AssociatedTypes::ProcessType(const char *type)
{
status_t err = type ? B_OK : B_BAD_VALUE;
if (!err) {
BMessage msg;
if (fDatabaseLocation->ReadMessageAttribute(type, kFileExtensionsAttr,
msg) == B_OK) {
const char *extension;
std::set<std::string> &fileExtensions = fFileExtensions[type];
for (int i = 0; msg.FindString(kExtensionsField, i, &extension) == B_OK; i++) {
std::string extStr = PrepExtension(extension);
if (extStr.length() > 0) {
fileExtensions.insert(extStr);
AddAssociatedType(extStr.c_str(), type);
}
}
}
}
return err;
}
std::string
AssociatedTypes::PrepExtension(const char *extension) const
{
if (extension) {
uint i = 0;
while (extension[i] == '.')
i++;
return BPrivate::Storage::to_lower(&(extension[i]));
} else {
return "";
}
}
}
}
}