#include <cups/string-private.h>
#include <cups/debug-private.h>
#include <cups/dir.h>
#include "mime-private.h"
typedef struct _mime_fcache_s
{
char *name,
*path;
} _mime_fcache_t;
static const char *mime_add_fcache(cups_array_t *filtercache, const char *name,
const char *filterpath);
static int mime_compare_fcache(_mime_fcache_t *a, _mime_fcache_t *b);
static void mime_delete_fcache(cups_array_t *filtercache);
static void mime_delete_rules(mime_magic_t *rules);
static void mime_load_convs(mime_t *mime, const char *filename,
const char *filterpath,
cups_array_t *filtercache);
static void mime_load_types(mime_t *mime, const char *filename);
void
mimeDelete(mime_t *mime)
{
mime_type_t *type;
mime_filter_t *filter;
DEBUG_printf(("mimeDelete(mime=%p)", mime));
if (!mime)
return;
for (filter = (mime_filter_t *)cupsArrayFirst(mime->filters);
filter;
filter = (mime_filter_t *)cupsArrayNext(mime->filters))
mimeDeleteFilter(mime, filter);
for (type = (mime_type_t *)cupsArrayFirst(mime->types);
type;
type = (mime_type_t *)cupsArrayNext(mime->types))
mimeDeleteType(mime, type);
cupsArrayDelete(mime->types);
cupsArrayDelete(mime->filters);
cupsArrayDelete(mime->srcs);
free(mime);
}
void
mimeDeleteFilter(mime_t *mime,
mime_filter_t *filter)
{
DEBUG_printf(("mimeDeleteFilter(mime=%p, filter=%p(%s/%s->%s/%s, cost=%d, "
"maxsize=" CUPS_LLFMT "))", mime, filter,
filter ? filter->src->super : "???",
filter ? filter->src->type : "???",
filter ? filter->dst->super : "???",
filter ? filter->dst->super : "???",
filter ? filter->cost : -1,
filter ? CUPS_LLCAST filter->maxsize : CUPS_LLCAST -1));
if (!mime || !filter)
return;
#ifdef DEBUG
if (!cupsArrayFind(mime->filters, filter))
DEBUG_puts("1mimeDeleteFilter: Filter not in MIME database.");
#endif
cupsArrayRemove(mime->filters, filter);
free(filter);
if (mime->srcs)
{
DEBUG_puts("1mimeDeleteFilter: Deleting source lookup cache.");
cupsArrayDelete(mime->srcs);
mime->srcs = NULL;
}
}
void
mimeDeleteType(mime_t *mime,
mime_type_t *mt)
{
DEBUG_printf(("mimeDeleteType(mime=%p, mt=%p(%s/%s))", mime, mt,
mt ? mt->super : "???", mt ? mt->type : "???"));
if (!mime || !mt)
return;
#ifdef DEBUG
if (!cupsArrayFind(mime->types, mt))
DEBUG_puts("1mimeDeleteFilter: Type not in MIME database.");
#endif
cupsArrayRemove(mime->types, mt);
mime_delete_rules(mt->rules);
free(mt);
}
void
_mimeError(mime_t *mime,
const char *message,
...)
{
va_list ap;
char buffer[8192];
if (mime->error_cb)
{
va_start(ap, message);
vsnprintf(buffer, sizeof(buffer), message, ap);
va_end(ap);
(*mime->error_cb)(mime->error_ctx, buffer);
}
}
mime_filter_t *
mimeFirstFilter(mime_t *mime)
{
DEBUG_printf(("6mimeFirstFilter(mime=%p)", mime));
if (!mime)
{
DEBUG_puts("7mimeFirstFilter: Returning NULL.");
return (NULL);
}
else
{
mime_filter_t *first = (mime_filter_t *)cupsArrayFirst(mime->filters);
DEBUG_printf(("7mimeFirstFilter: Returning %p.", first));
return (first);
}
}
mime_type_t *
mimeFirstType(mime_t *mime)
{
DEBUG_printf(("6mimeFirstType(mime=%p)", mime));
if (!mime)
{
DEBUG_puts("7mimeFirstType: Returning NULL.");
return (NULL);
}
else
{
mime_type_t *first = (mime_type_t *)cupsArrayFirst(mime->types);
DEBUG_printf(("7mimeFirstType: Returning %p.", first));
return (first);
}
}
mime_t *
mimeLoad(const char *pathname,
const char *filterpath)
{
mime_t *mime;
DEBUG_printf(("mimeLoad(pathname=\"%s\", filterpath=\"%s\")", pathname,
filterpath));
mime = mimeLoadFilters(mimeLoadTypes(NULL, pathname), pathname, filterpath);
DEBUG_printf(("1mimeLoad: Returning %p.", mime));
return (mime);
}
mime_t *
mimeLoadFilters(mime_t *mime,
const char *pathname,
const char *filterpath)
{
cups_dir_t *dir;
cups_dentry_t *dent;
char filename[1024];
cups_array_t *filtercache;
DEBUG_printf(("mimeLoadFilters(mime=%p, pathname=\"%s\", filterpath=\"%s\")",
mime, pathname, filterpath));
if (!mime || !pathname || !filterpath)
{
DEBUG_puts("1mimeLoadFilters: Bad arguments.");
return (mime);
}
if ((dir = cupsDirOpen(pathname)) == NULL)
{
DEBUG_printf(("1mimeLoadFilters: Unable to open \"%s\": %s", pathname,
strerror(errno)));
_mimeError(mime, "Unable to open \"%s\": %s", pathname, strerror(errno));
return (mime);
}
filtercache = cupsArrayNew((cups_array_func_t)mime_compare_fcache, NULL);
while ((dent = cupsDirRead(dir)) != NULL)
{
if (strlen(dent->filename) > 6 &&
!strcmp(dent->filename + strlen(dent->filename) - 6, ".convs"))
{
snprintf(filename, sizeof(filename), "%s/%s", pathname, dent->filename);
DEBUG_printf(("1mimeLoadFilters: Loading \"%s\".", filename));
mime_load_convs(mime, filename, filterpath, filtercache);
}
}
mime_delete_fcache(filtercache);
cupsDirClose(dir);
return (mime);
}
mime_t *
mimeLoadTypes(mime_t *mime,
const char *pathname)
{
cups_dir_t *dir;
cups_dentry_t *dent;
char filename[1024];
DEBUG_printf(("mimeLoadTypes(mime=%p, pathname=\"%s\")", mime, pathname));
if ((dir = cupsDirOpen(pathname)) == NULL)
{
DEBUG_printf(("1mimeLoadTypes: Unable to open \"%s\": %s", pathname,
strerror(errno)));
DEBUG_printf(("1mimeLoadTypes: Returning %p.", mime));
_mimeError(mime, "Unable to open \"%s\": %s", pathname, strerror(errno));
return (mime);
}
if (!mime)
mime = mimeNew();
if (!mime)
{
cupsDirClose(dir);
DEBUG_puts("1mimeLoadTypes: Returning NULL.");
return (NULL);
}
while ((dent = cupsDirRead(dir)) != NULL)
{
if (strlen(dent->filename) > 6 &&
!strcmp(dent->filename + strlen(dent->filename) - 6, ".types"))
{
snprintf(filename, sizeof(filename), "%s/%s", pathname, dent->filename);
DEBUG_printf(("1mimeLoadTypes: Loading \"%s\".", filename));
mime_load_types(mime, filename);
}
}
cupsDirClose(dir);
DEBUG_printf(("1mimeLoadTypes: Returning %p.", mime));
return (mime);
}
mime_t *
mimeNew(void)
{
return ((mime_t *)calloc(1, sizeof(mime_t)));
}
mime_filter_t *
mimeNextFilter(mime_t *mime)
{
DEBUG_printf(("6mimeNextFilter(mime=%p)", mime));
if (!mime)
{
DEBUG_puts("7mimeNextFilter: Returning NULL.");
return (NULL);
}
else
{
mime_filter_t *next = (mime_filter_t *)cupsArrayNext(mime->filters);
DEBUG_printf(("7mimeNextFilter: Returning %p.", next));
return (next);
}
}
mime_type_t *
mimeNextType(mime_t *mime)
{
DEBUG_printf(("6mimeNextType(mime=%p)", mime));
if (!mime)
{
DEBUG_puts("7mimeNextType: Returning NULL.");
return (NULL);
}
else
{
mime_type_t *next = (mime_type_t *)cupsArrayNext(mime->types);
DEBUG_printf(("7mimeNextType: Returning %p.", next));
return (next);
}
}
int
mimeNumFilters(mime_t *mime)
{
DEBUG_printf(("mimeNumFilters(mime=%p)", mime));
if (!mime)
{
DEBUG_puts("1mimeNumFilters: Returning 0.");
return (0);
}
else
{
DEBUG_printf(("1mimeNumFilters: Returning %d.",
cupsArrayCount(mime->filters)));
return (cupsArrayCount(mime->filters));
}
}
int
mimeNumTypes(mime_t *mime)
{
DEBUG_printf(("mimeNumTypes(mime=%p)", mime));
if (!mime)
{
DEBUG_puts("1mimeNumTypes: Returning 0.");
return (0);
}
else
{
DEBUG_printf(("1mimeNumTypes: Returning %d.",
cupsArrayCount(mime->types)));
return (cupsArrayCount(mime->types));
}
}
void
mimeSetErrorCallback(
mime_t *mime,
mime_error_cb_t cb,
void *ctx)
{
if (mime)
{
mime->error_cb = cb;
mime->error_ctx = ctx;
}
}
static const char *
mime_add_fcache(
cups_array_t *filtercache,
const char *name,
const char *filterpath)
{
_mime_fcache_t key,
*temp;
char path[1024];
DEBUG_printf(("2mime_add_fcache(filtercache=%p, name=\"%s\", "
"filterpath=\"%s\")", filtercache, name, filterpath));
key.name = (char *)name;
if ((temp = (_mime_fcache_t *)cupsArrayFind(filtercache, &key)) != NULL)
{
DEBUG_printf(("3mime_add_fcache: Returning \"%s\".", temp->path));
return (temp->path);
}
if ((temp = calloc(1, sizeof(_mime_fcache_t))) == NULL)
{
DEBUG_puts("3mime_add_fcache: Returning NULL.");
return (NULL);
}
temp->name = strdup(name);
if (cupsFileFind(name, filterpath, 1, path, sizeof(path)))
temp->path = strdup(path);
cupsArrayAdd(filtercache, temp);
DEBUG_printf(("3mime_add_fcache: Returning \"%s\".", temp->path));
return (temp->path);
}
static int
mime_compare_fcache(_mime_fcache_t *a,
_mime_fcache_t *b)
{
return (strcmp(a->name, b->name));
}
static void
mime_delete_fcache(
cups_array_t *filtercache)
{
_mime_fcache_t *current;
DEBUG_printf(("2mime_delete_fcache(filtercache=%p)", filtercache));
for (current = (_mime_fcache_t *)cupsArrayFirst(filtercache);
current;
current = (_mime_fcache_t *)cupsArrayNext(filtercache))
{
free(current->name);
if (current->path)
free(current->path);
free(current);
}
cupsArrayDelete(filtercache);
}
static void
mime_delete_rules(mime_magic_t *rules)
{
mime_magic_t *next;
DEBUG_printf(("2mime_delete_rules(rules=%p)", rules));
while (rules != NULL)
{
next = rules->next;
if (rules->child != NULL)
mime_delete_rules(rules->child);
if (rules->op == MIME_MAGIC_REGEX)
regfree(&(rules->value.rev));
free(rules);
rules = next;
}
}
static void
mime_load_convs(
mime_t *mime,
const char *filename,
const char *filterpath,
cups_array_t *filtercache)
{
cups_file_t *fp;
char line[1024],
*lineptr,
super[MIME_MAX_SUPER],
type[MIME_MAX_TYPE],
*temp,
*filter;
mime_type_t *temptype,
*dsttype;
int cost;
DEBUG_printf(("2mime_load_convs(mime=%p, filename=\"%s\", filterpath=\"%s\", "
"filtercache=%p)", mime, filename, filterpath, filtercache));
if ((fp = cupsFileOpen(filename, "r")) == NULL)
{
DEBUG_printf(("3mime_load_convs: Unable to open \"%s\": %s", filename,
strerror(errno)));
_mimeError(mime, "Unable to open \"%s\": %s", filename, strerror(errno));
return;
}
while (cupsFileGets(fp, line, sizeof(line)) != NULL)
{
if (!line[0] || line[0] == '#')
continue;
for (lineptr = line + strlen(line) - 1;
lineptr >= line && isspace(*lineptr & 255);
lineptr --)
*lineptr = '\0';
lineptr = line;
while (*lineptr != ' ' && *lineptr != '\t' && *lineptr != '\0')
lineptr ++;
while (*lineptr == ' ' || *lineptr == '\t')
lineptr ++;
temp = super;
while (*lineptr != '/' && *lineptr != '\n' && *lineptr != '\0' &&
(temp - super + 1) < MIME_MAX_SUPER)
*temp++ = (char)tolower(*lineptr++ & 255);
*temp = '\0';
if (*lineptr != '/')
continue;
lineptr ++;
temp = type;
while (*lineptr != ' ' && *lineptr != '\t' && *lineptr != '\n' &&
*lineptr != '\0' && (temp - type + 1) < MIME_MAX_TYPE)
*temp++ = (char)tolower(*lineptr++ & 255);
*temp = '\0';
if (*lineptr == '\0' || *lineptr == '\n')
continue;
if ((dsttype = mimeType(mime, super, type)) == NULL)
{
DEBUG_printf(("3mime_load_convs: Destination type %s/%s not found.",
super, type));
continue;
}
while (*lineptr == ' ' || *lineptr == '\t')
lineptr ++;
if (*lineptr < '0' || *lineptr > '9')
continue;
cost = atoi(lineptr);
while (*lineptr != ' ' && *lineptr != '\t' && *lineptr != '\0')
lineptr ++;
while (*lineptr == ' ' || *lineptr == '\t')
lineptr ++;
if (*lineptr == '\0' || *lineptr == '\n')
continue;
filter = lineptr;
if (strcmp(filter, "-"))
{
if (!mime_add_fcache(filtercache, filter, filterpath))
{
DEBUG_printf(("mime_load_convs: Filter %s not found in %s.", filter,
filterpath));
_mimeError(mime, "Filter \"%s\" not found.", filter);
continue;
}
}
lineptr = line;
temp = super;
while (*lineptr != '/' && *lineptr != '\n' && *lineptr != '\0' &&
(temp - super + 1) < MIME_MAX_SUPER)
*temp++ = (char)tolower(*lineptr++ & 255);
*temp = '\0';
if (*lineptr != '/')
continue;
lineptr ++;
temp = type;
while (*lineptr != ' ' && *lineptr != '\t' && *lineptr != '\n' &&
*lineptr != '\0' && (temp - type + 1) < MIME_MAX_TYPE)
*temp++ = (char)tolower(*lineptr++ & 255);
*temp = '\0';
if (!strcmp(super, "*") && !strcmp(type, "*"))
{
strlcpy(super, "application", sizeof(super));
strlcpy(type, "octet-stream", sizeof(type));
}
for (temptype = (mime_type_t *)cupsArrayFirst(mime->types);
temptype;
temptype = (mime_type_t *)cupsArrayNext(mime->types))
if ((super[0] == '*' || !strcmp(temptype->super, super)) &&
(type[0] == '*' || !strcmp(temptype->type, type)))
mimeAddFilter(mime, temptype, dsttype, cost, filter);
}
cupsFileClose(fp);
}
static void
mime_load_types(mime_t *mime,
const char *filename)
{
cups_file_t *fp;
size_t linelen;
char line[32768],
*lineptr,
super[MIME_MAX_SUPER],
type[MIME_MAX_TYPE],
*temp;
mime_type_t *typeptr;
DEBUG_printf(("2mime_load_types(mime=%p, filename=\"%s\")", mime, filename));
if ((fp = cupsFileOpen(filename, "r")) == NULL)
{
DEBUG_printf(("3mime_load_types: Unable to open \"%s\": %s", filename,
strerror(errno)));
_mimeError(mime, "Unable to open \"%s\": %s", filename, strerror(errno));
return;
}
while (cupsFileGets(fp, line, sizeof(line)) != NULL)
{
if (!line[0] || line[0] == '#')
continue;
linelen = strlen(line);
while (line[linelen - 1] == '\\')
{
linelen --;
if (cupsFileGets(fp, line + linelen, sizeof(line) - linelen) == NULL)
line[linelen] = '\0';
else
linelen += strlen(line + linelen);
}
lineptr = line;
temp = super;
while (*lineptr != '/' && *lineptr != '\n' && *lineptr != '\0' &&
(temp - super + 1) < MIME_MAX_SUPER)
*temp++ = (char)tolower(*lineptr++ & 255);
*temp = '\0';
if (*lineptr != '/')
continue;
lineptr ++;
temp = type;
while (*lineptr != ' ' && *lineptr != '\t' && *lineptr != '\n' &&
*lineptr != '\0' && (temp - type + 1) < MIME_MAX_TYPE)
*temp++ = (char)tolower(*lineptr++ & 255);
*temp = '\0';
typeptr = mimeAddType(mime, super, type);
mimeAddTypeRule(typeptr, lineptr);
}
cupsFileClose(fp);
}