X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=lib%2Fsilcutil%2Fsilcmime.c;h=839eb354b9ea6841c129d104e765f184588a6b69;hb=e7b6c157b80152bf9fb9266e6bdd93f9fb0db776;hp=d8b8c8f756015bac57ee549fb06476dd58067b79;hpb=2914f2d33fbb901ffd6b7ee108ca2d629a5c289d;p=silc.git diff --git a/lib/silcutil/silcmime.c b/lib/silcutil/silcmime.c index d8b8c8f7..839eb354 100644 --- a/lib/silcutil/silcmime.c +++ b/lib/silcutil/silcmime.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 2005 - 2006 Pekka Riikonen + Copyright (C) 2005 - 2007 Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,6 +19,14 @@ #include "silc.h" +/************************** Types and definitions ***************************/ + +/* MIME fragment ID context */ +typedef struct { + char *id; + SilcInt64 starttime; +} SilcMimeFragmentIdStruct, *SilcMimeFragmentId; + /************************ Static utility functions **************************/ /* MIME fields destructor */ @@ -34,11 +42,16 @@ static void silc_mime_field_dest(void *key, void *context, void *user_context) 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) @@ -46,6 +59,23 @@ static void silc_mime_assemble_dest(void *key, void *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 *********************************/ @@ -59,8 +89,8 @@ SilcMime silc_mime_alloc(void) 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); @@ -102,8 +132,8 @@ SilcMimeAssembler silc_mime_assembler_alloc(void) 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); @@ -121,6 +151,31 @@ void silc_mime_assembler_free(SilcMimeAssembler 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, @@ -132,8 +187,10 @@ 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(); @@ -149,26 +206,34 @@ SilcMime silc_mime_decode(SilcMime mime, const unsigned char *data, 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; @@ -198,6 +263,7 @@ SilcMime silc_mime_decode(SilcMime mime, const unsigned char *data, if (field && strstr(field, "multipart")) { char b[1024]; SilcMime p; + unsigned int len; mime->multiparts = silc_dlist_init(); if (!mime->multiparts) @@ -205,15 +271,24 @@ SilcMime silc_mime_decode(SilcMime mime, const unsigned char *data, /* 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)); - strncat(b, value, strchr(field, ';') - value); + len = (unsigned int)(strchr(field, ';') - value); + if (len > sizeof(b) - 1) { + silc_set_errno(SILC_ERR_OVERFLOW); + goto err; + } + strncpy(b, value, len); if (strchr(b, '"')) *strchr(b, '"') = '\0'; mime->multitype = silc_memdup(b, strlen(b)); @@ -226,14 +301,14 @@ SilcMime silc_mime_decode(SilcMime mime, const unsigned char *data, SILC_LOG_DEBUG(("Boundary '%s'", value)); memset(b, 0, sizeof(b)); - line = strdup(value); + line = silc_strdup(value); if (strrchr(line, '"')) { *strrchr(line, '"') = '\0'; - snprintf(b, sizeof(b) - 1, "--%s", line + 1); - mime->boundary = strdup(line + 1); + silc_snprintf(b, sizeof(b) - 1, "--%s", line + 1); + mime->boundary = silc_strdup(line + 1); } else { - snprintf(b, sizeof(b) - 1, "--%s", line); - mime->boundary = strdup(line); + silc_snprintf(b, sizeof(b) - 1, "--%s", line); + mime->boundary = silc_strdup(line); } silc_free(line); @@ -265,14 +340,14 @@ SilcMime silc_mime_decode(SilcMime mime, const unsigned char *data, 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; - SILC_LOG_HEXDUMP(("line %d", k - i), line, k - i); - /* Parse the part */ p = silc_mime_decode(NULL, line, k - i); if (!p) @@ -324,10 +399,10 @@ unsigned char *silc_mime_encode(SilcMime mime, SilcUInt32 *encoded_len) /* Encode the headers. Order doesn't matter */ i = 0; silc_hash_table_list(mime->fields, &htl); - while (silc_hash_table_get(&htl, (void **)&field, (void **)&value)) { + while (silc_hash_table_get(&htl, (void *)&field, (void *)&value)) { memset(tmp, 0, sizeof(tmp)); SILC_LOG_DEBUG(("Header %s: %s", field, value)); - snprintf(tmp, sizeof(tmp) - 1, "%s: %s\r\n", field, value); + silc_snprintf(tmp, sizeof(tmp) - 1, "%s: %s\r\n", field, value); silc_buffer_strformat(&buf, tmp, SILC_STRFMT_END); i++; } @@ -344,6 +419,7 @@ unsigned char *silc_mime_encode(SilcMime mime, SilcUInt32 *encoded_len) if (silc_buffer_len(&buf)) { silc_buffer_put(buffer, buf.head, silc_buffer_len(&buf)); silc_buffer_pull(buffer, silc_buffer_len(&buf)); + silc_buffer_purge(&buf); } /* Add data */ @@ -372,8 +448,8 @@ unsigned char *silc_mime_encode(SilcMime mime, SilcUInt32 *encoded_len) /* If fields are not present, add extra CRLF */ if (!silc_hash_table_count(part->fields)) - snprintf(tmp2, sizeof(tmp2) - 1, "\r\n"); - snprintf(tmp, sizeof(tmp) - 1, "%s--%s\r\n%s", + silc_snprintf(tmp2, sizeof(tmp2) - 1, "\r\n"); + silc_snprintf(tmp, sizeof(tmp) - 1, "%s--%s\r\n%s", i != 0 ? "\r\n" : "", mime->boundary, tmp2); i = 1; @@ -389,7 +465,7 @@ unsigned char *silc_mime_encode(SilcMime mime, SilcUInt32 *encoded_len) } memset(tmp, 0, sizeof(tmp)); - snprintf(tmp, sizeof(tmp) - 1, "\r\n--%s--\r\n", mime->boundary); + silc_snprintf(tmp, sizeof(tmp) - 1, "\r\n--%s--\r\n", mime->boundary); buffer = silc_buffer_realloc(buffer, silc_buffer_truelen(buffer) + strlen(tmp)); if (!buffer) @@ -409,6 +485,7 @@ unsigned char *silc_mime_encode(SilcMime mime, SilcUInt32 *encoded_len) 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; @@ -418,23 +495,31 @@ SilcMime silc_mime_assemble(SilcMimeAssembler assembler, SilcMime partial) 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, '"')) @@ -444,14 +529,18 @@ SilcMime silc_mime_assemble(SilcMimeAssembler assembler, SilcMime partial) /* 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); @@ -462,15 +551,23 @@ SilcMime silc_mime_assemble(SilcMimeAssembler assembler, SilcMime partial) SILC_LOG_DEBUG(("Fragment number %d", number)); /* Find fragments with this ID. */ - if (!silc_hash_table_find(assembler->fragments, (void *)id, - NULL, (void **)&f)) { + 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; } @@ -478,11 +575,13 @@ SilcMime silc_mime_assemble(SilcMimeAssembler assembler, SilcMime partial) 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); @@ -496,25 +595,30 @@ SilcMime silc_mime_assemble(SilcMimeAssembler assembler, SilcMime partial) /* 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. */ for (i = 1; i <= total; i++) { - if (!silc_hash_table_find(f, SILC_32_TO_PTR(i), NULL, (void **)&p)) + if (!silc_hash_table_find(f, SILC_32_TO_PTR(i), NULL, (void *)&p)) goto err; /* 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) { @@ -539,7 +643,7 @@ SilcMime silc_mime_assemble(SilcMimeAssembler assembler, SilcMime partial) 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); @@ -580,7 +684,7 @@ SilcDList silc_mime_encode_partial(SilcMime mime, int max_size) memset(type, 0, sizeof(type)); gethostname(type, sizeof(type) - 1); srand((time(NULL) + buf_len) ^ rand()); - snprintf(id, sizeof(id) - 1, "%X%X%X%s", + silc_snprintf(id, sizeof(id) - 1, "%X%X%X%s", (unsigned int)rand(), (unsigned int)time(NULL), (unsigned int)buf_len, type); @@ -592,7 +696,7 @@ SilcDList silc_mime_encode_partial(SilcMime mime, int max_size) silc_mime_add_field(partial, "MIME-Version", "1.0"); memset(type, 0, sizeof(type)); - snprintf(type, sizeof(type) - 1, + silc_snprintf(type, sizeof(type) - 1, "message/partial; id=\"%s\"; number=1", id); silc_mime_add_field(partial, "Content-Type", type); silc_mime_add_data(partial, buf, max_size); @@ -622,14 +726,14 @@ SilcDList silc_mime_encode_partial(SilcMime mime, int max_size) silc_mime_add_field(partial, "MIME-Version", "1.0"); if (len > max_size) { - snprintf(type, sizeof(type) - 1, + silc_snprintf(type, sizeof(type) - 1, "message/partial; id=\"%s\"; number=%d", id, num++); silc_mime_add_data(partial, buf + off, max_size); off += max_size; len -= max_size; } else { - snprintf(type, sizeof(type) - 1, + silc_snprintf(type, sizeof(type) - 1, "message/partial; id=\"%s\"; number=%d; total=%d", id, num, num); silc_mime_add_data(partial, buf + off, len); @@ -687,7 +791,7 @@ void silc_mime_add_field(SilcMime mime, const char *field, const char *value) 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 */ @@ -700,7 +804,7 @@ const char *silc_mime_get_field(SilcMime mime, const char *field) return NULL; if (!silc_hash_table_find(mime->fields, (void *)field, - NULL, (void **)&value)) + NULL, (void *)&value)) return NULL; return (const char *)value; @@ -779,10 +883,10 @@ void silc_mime_set_multipart(SilcMime mime, const char *type, return; memset(tmp, 0, sizeof(tmp)); - snprintf(tmp, sizeof(tmp) - 1, "multipart/%s; boundary=%s", type, boundary); + 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; @@ -793,8 +897,10 @@ void silc_mime_set_multipart(SilcMime mime, const char *type, 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; @@ -804,8 +910,10 @@ SilcBool silc_mime_add_multipart(SilcMime mime, SilcMime part) SilcBool silc_mime_is_multipart(SilcMime mime) { - if (!mime) + if (!mime) { + silc_set_errno(SILC_ERR_INVALID_ARGUMENT); return FALSE; + } return mime->multiparts != NULL; } @@ -814,8 +922,10 @@ SilcBool silc_mime_is_multipart(SilcMime mime) 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;