#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <locale.h>
#include <cups/string.h>
#include "mime.h"
#include <cups/debug.h>
static int compare(mime_type_t **, mime_type_t **);
static int checkrules(const char *, FILE *, mime_magic_t *);
static int patmatch(const char *, const char *);
mime_type_t *
mimeAddType(mime_t *mime,
const char *super,
const char *type)
{
mime_type_t *temp,
**types;
if (mime == NULL || super == NULL || type == NULL)
return (NULL);
if (strlen(super) > (MIME_MAX_SUPER - 1) ||
strlen(type) > (MIME_MAX_TYPE - 1))
return (NULL);
if ((temp = mimeType(mime, super, type)) != NULL)
return (temp);
if ((temp = calloc(1, sizeof(mime_type_t))) == NULL)
return (NULL);
if (mime->num_types == 0)
types = (mime_type_t **)malloc(sizeof(mime_type_t *));
else
types = (mime_type_t **)realloc(mime->types, sizeof(mime_type_t *) * (mime->num_types + 1));
if (types == NULL)
{
free(temp);
return (NULL);
}
mime->types = types;
types += mime->num_types;
mime->num_types ++;
*types = temp;
strlcpy(temp->super, super, sizeof(temp->super));
if ((temp->type = strdup(type)) == NULL)
{
mime->num_types --;
return (NULL);
}
if (mime->num_types > 1)
qsort(mime->types, mime->num_types, sizeof(mime_type_t *),
(int (*)(const void *, const void *))compare);
return (temp);
}
int
mimeAddTypeRule(mime_type_t *mt,
const char *rule)
{
int num_values,
op,
logic,
invert;
char name[255],
value[3][255],
*ptr,
quote;
int length[3];
mime_magic_t *temp,
*current;
if (mt == NULL || rule == NULL)
return (-1);
for (current = mt->rules; current != NULL; current = current->next)
if (current->next == NULL)
break;
logic = MIME_MAGIC_NOP;
invert = 0;
DEBUG_printf(("%s/%s: %s\n", mt->super, mt->type, rule));
while (*rule != '\0')
{
while (isspace(*rule))
rule ++;
if (*rule == '(')
{
DEBUG_puts("new parenthesis group");
logic = MIME_MAGIC_NOP;
rule ++;
}
else if (*rule == ')')
{
DEBUG_puts("close paren...");
if (current == NULL || current->parent == NULL)
return (-1);
current = current->parent;
if (current->parent == NULL)
logic = MIME_MAGIC_OR;
else
logic = current->parent->op;
rule ++;
}
else if (*rule == '+' && current != NULL)
{
if (logic != MIME_MAGIC_AND &&
current != NULL && current->prev != NULL && current->prev->prev != NULL)
{
if ((temp = calloc(1, sizeof(mime_magic_t))) == NULL)
return (-1);
temp->op = MIME_MAGIC_AND;
temp->child = current;
temp->parent = current->parent;
current->prev->next = temp;
temp->prev = current->prev;
current->prev = NULL;
current->parent = temp;
DEBUG_printf(("creating new AND group %p...\n", temp));
}
else
{
DEBUG_printf(("setting group %p op to AND...\n", current->parent));
current->parent->op = MIME_MAGIC_AND;
}
logic = MIME_MAGIC_AND;
rule ++;
}
else if (*rule == ',')
{
if (logic != MIME_MAGIC_OR && current != NULL)
{
if (current->parent == NULL)
{
if ((temp = calloc(1, sizeof(mime_magic_t))) == NULL)
return (-1);
DEBUG_printf(("creating new AND group %p inside OR group\n", temp));
while (current->prev != NULL)
{
current->parent = temp;
current = current->prev;
}
current->parent = temp;
temp->op = MIME_MAGIC_AND;
temp->child = current;
mt->rules = current = temp;
}
else
{
DEBUG_puts("going up one level");
current = current->parent;
}
}
logic = MIME_MAGIC_OR;
rule ++;
}
else if (*rule == '!')
{
DEBUG_puts("NOT");
invert = 1;
rule ++;
}
else if (isalnum(*rule))
{
for (ptr = name; isalnum(*rule) && (ptr - name) < (sizeof(name) - 1);)
*ptr++ = *rule++;
*ptr = '\0';
num_values = 0;
if (*rule == '(')
{
rule ++;
for (num_values = 0;
num_values < (sizeof(value) / sizeof(value[0]));
num_values ++)
{
ptr = value[num_values];
while ((ptr - value[num_values]) < (sizeof(value[0]) - 1) &&
*rule != '\0' && *rule != ',' && *rule != ')')
{
if (isspace(*rule))
{
rule ++;
continue;
}
else if (*rule == '\"' || *rule == '\'')
{
quote = *rule++;
while (*rule != '\0' && *rule != quote &&
(ptr - value[num_values]) < (sizeof(value[0]) - 1))
*ptr++ = *rule++;
if (*rule == quote)
rule ++;
else
return (-1);
}
else if (*rule == '<')
{
rule ++;
while (*rule != '>' && *rule != '\0' &&
(ptr - value[num_values]) < (sizeof(value[0]) - 1))
{
if (isxdigit(rule[0]) && isxdigit(rule[1]))
{
if (isdigit(*rule))
*ptr = (*rule++ - '0') << 4;
else
*ptr = (tolower(*rule++) - 'a' + 10) << 4;
if (isdigit(*rule))
*ptr++ |= *rule++ - '0';
else
*ptr++ |= tolower(*rule++) - 'a' + 10;
}
else
return (-1);
}
if (*rule == '>')
rule ++;
else
return (-1);
}
else
*ptr++ = *rule++;
}
*ptr = '\0';
length[num_values] = ptr - value[num_values];
if (*rule != ',')
break;
rule ++;
}
if (*rule != ')')
return (-1);
rule ++;
if (strcmp(name, "match") == 0)
op = MIME_MAGIC_MATCH;
else if (strcmp(name, "ascii") == 0)
op = MIME_MAGIC_ASCII;
else if (strcmp(name, "printable") == 0)
op = MIME_MAGIC_PRINTABLE;
else if (strcmp(name, "string") == 0)
op = MIME_MAGIC_STRING;
else if (strcmp(name, "char") == 0)
op = MIME_MAGIC_CHAR;
else if (strcmp(name, "short") == 0)
op = MIME_MAGIC_SHORT;
else if (strcmp(name, "int") == 0)
op = MIME_MAGIC_INT;
else if (strcmp(name, "locale") == 0)
op = MIME_MAGIC_LOCALE;
else if (strcmp(name, "contains") == 0)
op = MIME_MAGIC_CONTAINS;
else
return (-1);
}
else
{
snprintf(value[0], sizeof(value[0]), "*.%s", name);
length[0] = strlen(value[0]);
num_values = 1;
op = MIME_MAGIC_MATCH;
}
if ((temp = calloc(1, sizeof(mime_magic_t))) == NULL)
return (-1);
temp->invert = invert;
if (current != NULL)
{
temp->parent = current->parent;
current->next = temp;
}
else
mt->rules = temp;
temp->prev = current;
if (logic == MIME_MAGIC_NOP)
{
DEBUG_printf(("making new OR group %p for parenthesis...\n", temp));
temp->op = MIME_MAGIC_OR;
if ((temp->child = calloc(1, sizeof(mime_magic_t))) == NULL)
return (-1);
temp->child->parent = temp;
temp = temp->child;
logic = MIME_MAGIC_OR;
}
DEBUG_printf(("adding %p: %s, op = %d, logic = %d, invert = %d\n",
temp, name, op, logic, invert));
current = temp;
temp->op = op;
invert = 0;
switch (op)
{
case MIME_MAGIC_MATCH :
if (length[0] > (sizeof(temp->value.matchv) - 1))
return (-1);
strcpy(temp->value.matchv, value[0]);
break;
case MIME_MAGIC_ASCII :
case MIME_MAGIC_PRINTABLE :
temp->offset = strtol(value[0], NULL, 0);
temp->length = strtol(value[1], NULL, 0);
if (temp->length > MIME_MAX_BUFFER)
temp->length = MIME_MAX_BUFFER;
break;
case MIME_MAGIC_STRING :
temp->offset = strtol(value[0], NULL, 0);
if (length[1] > sizeof(temp->value.stringv))
return (-1);
temp->length = length[1];
memcpy(temp->value.stringv, value[1], length[1]);
break;
case MIME_MAGIC_CHAR :
temp->offset = strtol(value[0], NULL, 0);
if (length[1] == 1)
temp->value.charv = value[1][0];
else
temp->value.charv = strtol(value[1], NULL, 0);
break;
case MIME_MAGIC_SHORT :
temp->offset = strtol(value[0], NULL, 0);
temp->value.shortv = strtol(value[1], NULL, 0);
break;
case MIME_MAGIC_INT :
temp->offset = strtol(value[0], NULL, 0);
temp->value.intv = strtol(value[1], NULL, 0);
break;
case MIME_MAGIC_LOCALE :
if (length[0] > (sizeof(temp->value.localev) - 1))
return (-1);
strcpy(temp->value.localev, value[0]);
break;
case MIME_MAGIC_CONTAINS :
temp->offset = strtol(value[0], NULL, 0);
temp->region = strtol(value[1], NULL, 0);
if (length[2] > sizeof(temp->value.stringv))
return (-1);
temp->length = length[2];
memcpy(temp->value.stringv, value[2], length[2]);
break;
}
}
else
break;
}
return (0);
}
mime_type_t *
mimeFileType(mime_t *mime,
const char *pathname)
{
int i;
FILE *fp;
mime_type_t **types;
const char *filename;
if (mime == NULL || pathname == NULL)
return (NULL);
if ((fp = fopen(pathname, "r")) == NULL)
return (NULL);
if ((filename = strrchr(pathname, '/')) != NULL)
filename ++;
else
filename = pathname;
for (i = mime->num_types, types = mime->types; i > 0; i --, types ++)
if (checkrules(filename, fp, (*types)->rules))
break;
fclose(fp);
if (i > 0)
return (*types);
else
return (NULL);
}
mime_type_t *
mimeType(mime_t *mime,
const char *super,
const char *type)
{
mime_type_t key,
*keyptr,
**match;
if (mime == NULL || super == NULL || type == NULL)
return (NULL);
if (strlen(super) > (MIME_MAX_SUPER - 1) ||
strlen(type) > (MIME_MAX_TYPE - 1))
return (NULL);
if (mime->num_types == 0)
return (NULL);
strlcpy(key.super, super, sizeof(key.super));
key.type = (char *)type;
keyptr = &key;
match = (mime_type_t **)bsearch(&keyptr, mime->types, mime->num_types,
sizeof(mime_type_t *),
(int (*)(const void *, const void *))compare);
if (match == NULL)
return (NULL);
else
return (*match);
}
static int
compare(mime_type_t **t0,
mime_type_t **t1)
{
int i;
if ((i = strcasecmp((*t0)->super, (*t1)->super)) == 0)
i = strcasecmp((*t0)->type, (*t1)->type);
return (i);
}
static int
checkrules(const char *filename,
FILE *fp,
mime_magic_t *rules)
{
int n;
int region;
int logic,
result,
intv;
short shortv;
unsigned char buffer[MIME_MAX_BUFFER],
*bufptr;
int bufoffset,
buflength;
if (rules == NULL)
return (0);
if (rules->parent == NULL)
logic = MIME_MAGIC_OR;
else
logic = rules->parent->op;
bufoffset = -1;
buflength = 0;
result = 0;
while (rules != NULL)
{
switch (rules->op)
{
case MIME_MAGIC_MATCH :
result = patmatch(filename, rules->value.matchv);
break;
case MIME_MAGIC_ASCII :
if (bufoffset < 0 || rules->offset < bufoffset ||
(rules->offset + rules->length) > (bufoffset + buflength))
{
fseek(fp, rules->offset, SEEK_SET);
buflength = fread(buffer, 1, sizeof(buffer), fp);
bufoffset = rules->offset;
}
if ((rules->offset + rules->length) > (bufoffset + buflength))
n = bufoffset + buflength - rules->offset;
else
n = rules->length;
bufptr = buffer + rules->offset - bufoffset;
while (n > 0)
if ((*bufptr >= 32 && *bufptr <= 126) ||
(*bufptr >= 8 && *bufptr <= 13) ||
*bufptr == 26 || *bufptr == 27)
{
n --;
bufptr ++;
}
else
break;
result = (n == 0);
break;
case MIME_MAGIC_PRINTABLE :
if (bufoffset < 0 || rules->offset < bufoffset ||
(rules->offset + rules->length) > (bufoffset + buflength))
{
fseek(fp, rules->offset, SEEK_SET);
buflength = fread(buffer, 1, sizeof(buffer), fp);
bufoffset = rules->offset;
}
if ((rules->offset + rules->length) > (bufoffset + buflength))
n = bufoffset + buflength - rules->offset;
else
n = rules->length;
bufptr = buffer + rules->offset - bufoffset;
while (n > 0)
if (*bufptr >= 128 ||
(*bufptr >= 32 && *bufptr <= 126) ||
(*bufptr >= 8 && *bufptr <= 13) ||
*bufptr == 26 || *bufptr == 27)
{
n --;
bufptr ++;
}
else
break;
result = (n == 0);
break;
case MIME_MAGIC_STRING :
if (bufoffset < 0 || rules->offset < bufoffset ||
(rules->offset + rules->length) > (bufoffset + buflength))
{
fseek(fp, rules->offset, SEEK_SET);
buflength = fread(buffer, 1, sizeof(buffer), fp);
bufoffset = rules->offset;
}
if ((rules->offset + rules->length) > (bufoffset + buflength))
result = 0;
else
result = (memcmp(buffer + rules->offset - bufoffset,
rules->value.stringv, rules->length) == 0);
break;
case MIME_MAGIC_CHAR :
if (bufoffset < 0 || rules->offset < bufoffset)
{
fseek(fp, rules->offset, SEEK_SET);
buflength = fread(buffer, 1, sizeof(buffer), fp);
bufoffset = rules->offset;
}
if (buflength < 1)
result = 0;
else
result = (buffer[rules->offset - bufoffset] == rules->value.charv);
break;
case MIME_MAGIC_SHORT :
if (bufoffset < 0 || rules->offset < bufoffset ||
(rules->offset + 2) > (bufoffset + buflength))
{
fseek(fp, rules->offset, SEEK_SET);
buflength = fread(buffer, 1, sizeof(buffer), fp);
bufoffset = rules->offset;
}
if (buflength < 2)
result = 0;
else
{
bufptr = buffer + rules->offset - bufoffset;
shortv = (bufptr[0] << 8) | bufptr[1];
result = (shortv == rules->value.shortv);
}
break;
case MIME_MAGIC_INT :
if (bufoffset < 0 || rules->offset < bufoffset ||
(rules->offset + 4) > (bufoffset + buflength))
{
fseek(fp, rules->offset, SEEK_SET);
buflength = fread(buffer, 1, sizeof(buffer), fp);
bufoffset = rules->offset;
}
if (buflength < 4)
result = 0;
else
{
bufptr = buffer + rules->offset - bufoffset;
intv = (((((bufptr[0] << 8) | bufptr[1]) << 8) | bufptr[2]) << 8) |
bufptr[3];;
result = (intv == rules->value.intv);
}
break;
case MIME_MAGIC_LOCALE :
#ifdef __APPLE__
result = (strcmp(rules->value.localev, setlocale(LC_ALL, NULL)) == 0);
#else
result = (strcmp(rules->value.localev, setlocale(LC_MESSAGES, NULL)) == 0);
#endif
break;
case MIME_MAGIC_CONTAINS :
if (bufoffset < 0 || rules->offset < bufoffset ||
(rules->offset + rules->region) > (bufoffset + buflength))
{
fseek(fp, rules->offset, SEEK_SET);
buflength = fread(buffer, 1, sizeof(buffer), fp);
bufoffset = rules->offset;
}
if ((rules->offset + rules->length) > (bufoffset + buflength))
result = 0;
else
{
if (buflength > rules->region)
region = rules->region - rules->length;
else
region = buflength - rules->length;
for (n = 0; n < region; n ++)
if ((result = (memcmp(buffer + rules->offset - bufoffset + n,
rules->value.stringv, rules->length) == 0)) != 0)
break;
}
break;
default :
if (rules->child != NULL)
result = checkrules(filename, fp, rules->child);
else
result = 0;
break;
}
if (rules->invert)
result = !result;
DEBUG_printf(("result of test %p is %d\n", rules, result));
if ((result && logic == MIME_MAGIC_OR) ||
(!result && logic == MIME_MAGIC_AND))
return (result);
rules = rules->next;
}
return (result);
}
static int
patmatch(const char *s,
const char *pat)
{
if (s == NULL || pat == NULL)
return (0);
while (*s != '\0' && *pat != '\0')
{
if (*pat == '*')
{
pat ++;
if (*pat == '\0')
return (1);
while (*s != '\0')
{
if (patmatch(s, pat))
return (1);
s ++;
}
}
else if (*pat == '?')
{
pat ++;
s ++;
continue;
}
else if (*pat == '[')
{
pat ++;
while (*pat != ']' && *pat != '\0')
if (*s == *pat)
break;
else
pat ++;
if (*pat == ']' || *pat == '\0')
return (0);
while (*pat != ']' && *pat != '\0')
pat ++;
if (*pat == ']')
pat ++;
continue;
}
else if (*pat == '\\')
{
pat ++;
}
if (*pat++ != *s++)
return (0);
}
return (*s == *pat);
}