#include "silc.h"
+/************************** Types and definitions ***************************/
+
+/* MIME fragment ID context */
+typedef struct {
+ char *id;
+ SilcInt64 starttime;
+} SilcMimeFragmentIdStruct, *SilcMimeFragmentId;
+
/************************ Static utility functions **************************/
/* MIME fields destructor */
static void silc_mime_assembler_dest(void *key, void *context,
void *user_context)
{
- silc_free(key);
+ SilcMimeFragmentId id = key;
+
+ silc_free(id->id);
+ silc_free(id);
+
+ /* Free all fragments */
silc_hash_table_free(context);
}
-/* Assembler partial MIME destructor */
+/* Assembler partial MIME fragmentn destructor */
static void silc_mime_assemble_dest(void *key, void *context,
void *user_context)
silc_mime_free(context);
}
+/* MIME fragment ID hashing */
+
+static SilcUInt32 silc_mime_hash_id(void *key, void *user_context)
+{
+ SilcMimeFragmentId id = key;
+ return silc_hash_string_case(id->id, user_context);
+}
+
+/* MIME fragment ID comparing */
+
+static SilcBool silc_mime_id_compare(void *key1, void *key2,
+ void *user_context)
+{
+ SilcMimeFragmentId id1 = key1, id2 = key2;
+ return silc_hash_string_case_compare(id1->id, id2->id, user_context);
+}
+
/******************************* Public API *********************************/
if (!mime)
return NULL;
- mime->fields = silc_hash_table_alloc(0, silc_hash_string, mime,
- silc_hash_string_compare, mime,
+ mime->fields = silc_hash_table_alloc(NULL, 0, silc_hash_string_case, mime,
+ silc_hash_string_case_compare, mime,
silc_mime_field_dest, mime, TRUE);
if (!mime->fields) {
silc_mime_free(mime);
return NULL;
assembler->fragments =
- silc_hash_table_alloc(0, silc_hash_string, NULL,
- silc_hash_string_compare, NULL,
+ silc_hash_table_alloc(NULL, 0, silc_mime_hash_id, NULL,
+ silc_mime_id_compare, NULL,
silc_mime_assembler_dest, assembler, TRUE);
if (!assembler->fragments) {
silc_mime_assembler_free(assembler);
silc_free(assembler);
}
+/* Purge assembler from old unfinished fragments */
+
+void silc_mime_assembler_purge(SilcMimeAssembler assembler,
+ SilcUInt32 purge_minutes)
+{
+ SilcMimeFragmentId id;
+ SilcHashTableList htl;
+ SilcInt64 curtime = silc_time();
+ SilcUInt32 timeout = purge_minutes ? purge_minutes * 60 : 5 * 60;
+
+ SILC_LOG_DEBUG(("Purge MIME assembler"));
+
+ silc_hash_table_list(assembler->fragments, &htl);
+ while (silc_hash_table_get(&htl, (void *)&id, NULL)) {
+ if (curtime - id->starttime <= timeout)
+ continue;
+
+ SILC_LOG_DEBUG(("Purge partial MIME id %s", id->id));
+
+ /* Purge */
+ silc_hash_table_del(assembler->fragments, id);
+ }
+ silc_hash_table_list_reset(&htl);
+}
+
/* Decode MIME message */
SilcMime silc_mime_decode(SilcMime mime, const unsigned char *data,
SILC_LOG_DEBUG(("Parsing MIME message"));
- if (!data)
+ if (!data) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
return NULL;
+ }
if (!mime) {
mime = silc_mime_alloc();
if (data_len - i >= 2 && tmp[i] == '\r' && tmp[i + 1] == '\n') {
/* Get field */
field = strchr(line, ':');
- if (!field)
+ if (!field) {
+ silc_set_errno(SILC_ERR_BAD_ENCODING);
goto err;
+ }
field = silc_memdup(line, field - line);
if (!field)
goto err;
/* Get value. Remove whitespaces too. */
value = strchr(line, ':');
- if ((tmp + i) - value < 2)
+ if ((tmp + i) - value < 2) {
+ silc_set_errno(SILC_ERR_OVERFLOW);
goto err;
+ }
value++;
for (k = 0; k < (tmp + i) - value; k++) {
- if (value[k] == '\r')
+ if (value[k] == '\r') {
+ silc_set_errno(SILC_ERR_BAD_ENCODING);
goto err;
+ }
if (value[k] != ' ' && value[k] != '\t')
break;
}
value += k;
- if ((tmp + i) - value < 1)
+ if ((tmp + i) - value < 1) {
+ silc_set_errno(SILC_ERR_OVERFLOW);
goto err;
+ }
value = silc_memdup(value, (tmp + i) - value);
if (!value)
goto err;
/* Get multipart type */
value = strchr(field, '/');
- if (!value)
+ if (!value) {
+ silc_set_errno(SILC_ERR_BAD_ENCODING);
goto err;
+ }
value++;
if (strchr(field, '"'))
value++;
- if (!strchr(field, ';'))
+ if (!strchr(field, ';')) {
+ silc_set_errno(SILC_ERR_BAD_ENCODING);
goto err;
+ }
memset(b, 0, sizeof(b));
len = (unsigned int)(strchr(field, ';') - value);
- if (len > sizeof(b) - 1)
+ if (len > sizeof(b) - 1) {
+ silc_set_errno(SILC_ERR_OVERFLOW);
goto err;
+ }
strncpy(b, value, len);
if (strchr(b, '"'))
*strchr(b, '"') = '\0';
SILC_LOG_DEBUG(("Boundary '%s'", value));
memset(b, 0, sizeof(b));
- line = strdup(value);
+ line = silc_strdup(value);
if (strrchr(line, '"')) {
*strrchr(line, '"') = '\0';
silc_snprintf(b, sizeof(b) - 1, "--%s", line + 1);
- mime->boundary = strdup(line + 1);
+ mime->boundary = silc_strdup(line + 1);
} else {
silc_snprintf(b, sizeof(b) - 1, "--%s", line);
- mime->boundary = strdup(line);
+ mime->boundary = silc_strdup(line);
}
silc_free(line);
tmp[k] == '-' && tmp[k + 1] == '-')
if (!memcmp(tmp + k, b, strlen(b)))
break;
- if (k >= data_len)
+ if (k >= data_len) {
+ silc_set_errno(SILC_ERR_OVERFLOW);
goto err;
+ }
/* Remove preceding CRLF */
k -= 2;
SilcMime silc_mime_assemble(SilcMimeAssembler assembler, SilcMime partial)
{
char *type, *id = NULL, *tmp;
+ SilcMimeFragmentIdStruct *fragid, query;
SilcHashTable f;
SilcMime p, complete;
int i, number, total = -1;
SILC_LOG_DEBUG(("Assembling MIME fragments"));
- if (!assembler || !partial)
+ if (!assembler || !partial) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
goto err;
+ }
type = (char *)silc_mime_get_field(partial, "Content-Type");
- if (!type)
+ if (!type) {
+ silc_set_errno(SILC_ERR_BAD_ENCODING);
goto err;
+ }
/* Get ID */
tmp = strstr(type, "id=");
- if (!tmp)
+ if (!tmp) {
+ silc_set_errno(SILC_ERR_BAD_ENCODING);
goto err;
- if (strlen(tmp) <= 4)
+ }
+ if (strlen(tmp) <= 4) {
+ silc_set_errno(SILC_ERR_OVERFLOW);
goto err;
+ }
tmp += 3;
if (*tmp == '"')
tmp++;
- id = strdup(tmp);
+ id = silc_strdup(tmp);
if (strchr(id, ';'))
*strchr(id, ';') = '\0';
if (strrchr(id, '"'))
/* Get fragment number */
tmp = strstr(type, "number=");
- if (!tmp)
+ if (!tmp) {
+ silc_set_errno(SILC_ERR_BAD_ENCODING);
goto err;
+ }
tmp = strchr(tmp, '=');
- if (strlen(tmp) < 2)
+ if (strlen(tmp) < 2) {
+ silc_set_errno(SILC_ERR_OVERFLOW);
goto err;
+ }
tmp++;
if (strchr(tmp, ';')) {
- tmp = strdup(tmp);
+ tmp = silc_strdup(tmp);
*strchr(tmp, ';') = '\0';
number = atoi(tmp);
silc_free(tmp);
SILC_LOG_DEBUG(("Fragment number %d", number));
/* Find fragments with this ID. */
- if (!silc_hash_table_find(assembler->fragments, (void *)id,
+ query.id = id;
+ if (!silc_hash_table_find(assembler->fragments, (void *)&query,
NULL, (void *)&f)) {
/* This is new fragment to new message. Add to hash table and return. */
- f = silc_hash_table_alloc(0, silc_hash_uint, NULL, NULL, NULL,
+ f = silc_hash_table_alloc(NULL, 0, silc_hash_uint, NULL, NULL, NULL,
silc_mime_assemble_dest, NULL, TRUE);
if (!f)
- goto err;
+ goto err;
+
+ fragid = silc_calloc(1, sizeof(*fragid));
+ if (!fragid)
+ goto err;
+ fragid->id = id;
+ fragid->starttime = silc_time();
+
silc_hash_table_add(f, SILC_32_TO_PTR(number), partial);
- silc_hash_table_add(assembler->fragments, id, f);
+ silc_hash_table_add(assembler->fragments, fragid, f);
return NULL;
}
tmp = strstr(type, "total=");
if (tmp) {
tmp = strchr(tmp, '=');
- if (strlen(tmp) < 2)
+ if (strlen(tmp) < 2) {
+ silc_set_errno(SILC_ERR_OVERFLOW);
goto err;
+ }
tmp++;
if (strchr(tmp, ';')) {
- tmp = strdup(tmp);
+ tmp = silc_strdup(tmp);
*strchr(tmp, ';') = '\0';
total = atoi(tmp);
silc_free(tmp);
/* If more fragments to come, add to hash table */
if (number != total) {
silc_hash_table_add(f, SILC_32_TO_PTR(number), partial);
+ silc_free(id);
return NULL;
}
silc_hash_table_add(f, SILC_32_TO_PTR(number), partial);
/* Verify that we really have all the fragments */
- if (silc_hash_table_count(f) < total)
+ if (silc_hash_table_count(f) < total) {
+ silc_free(id);
return NULL;
+ }
/* Assemble the complete MIME message now. We get them in order from
the hash table. */
/* The fragment is in the data portion of the partial message */
data = silc_mime_get_data(p, &data_len);
- if (!data)
+ if (!data) {
+ silc_set_errno(SILC_ERR_BAD_ENCODING);
goto err;
+ }
/* Assemble */
if (!compbuf) {
goto err;
/* Delete the hash table entry. Destructors will free memory */
- silc_hash_table_del(assembler->fragments, (void *)id);
+ silc_hash_table_del(assembler->fragments, (void *)&query);
silc_free(id);
silc_buffer_free(compbuf);
if (!mime || !field || !value)
return;
- silc_hash_table_add(mime->fields, strdup(field), strdup(value));
+ silc_hash_table_add(mime->fields, silc_strdup(field), silc_strdup(value));
}
/* Get field */
silc_snprintf(tmp, sizeof(tmp) - 1, "multipart/%s; boundary=%s", type, boundary);
silc_mime_add_field(mime, "Content-Type", tmp);
silc_free(mime->boundary);
- mime->boundary = strdup(boundary);
+ mime->boundary = silc_strdup(boundary);
if (mime->multiparts)
return;
SilcBool silc_mime_add_multipart(SilcMime mime, SilcMime part)
{
- if (!mime || !mime->multiparts || !part)
+ if (!mime || !mime->multiparts || !part) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
return FALSE;
+ }
silc_dlist_add(mime->multiparts, part);
return TRUE;
SilcBool silc_mime_is_multipart(SilcMime mime)
{
- if (!mime)
+ if (!mime) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
return FALSE;
+ }
return mime->multiparts != NULL;
}
SilcDList silc_mime_get_multiparts(SilcMime mime, const char **type)
{
- if (!mime)
+ if (!mime) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
return NULL;
+ }
if (type)
*type = (const char *)mime->multitype;