From: Pekka Riikonen Date: Sat, 19 Nov 2005 16:26:20 +0000 (+0000) Subject: Added SilcMime API. X-Git-Tag: silc.toolkit.1.0.1~10 X-Git-Url: http://git.silcnet.org/gitweb/?a=commitdiff_plain;h=ff464970b94f956326a114bb0422569043ddd363;p=silc.git Added SilcMime API. --- diff --git a/lib/silcutil/Makefile.ad b/lib/silcutil/Makefile.ad index 027525a6..115e0b26 100644 --- a/lib/silcutil/Makefile.ad +++ b/lib/silcutil/Makefile.ad @@ -60,6 +60,7 @@ libsilcutil_la_SOURCES = \ silcprotocol.c \ silcvcard.c \ silcapputil.c \ + silcmime.c \ silcutf8.c \ silcstringprep.c @@ -86,6 +87,7 @@ include_HEADERS = \ silcstrutil.h \ silcvcard.h \ silcapputil.h \ + silcmime.h \ silcutf8.h \ silcstringprep.h \ silctypes.h diff --git a/lib/silcutil/silcmime.c b/lib/silcutil/silcmime.c new file mode 100644 index 00000000..8ccc891f --- /dev/null +++ b/lib/silcutil/silcmime.c @@ -0,0 +1,748 @@ +/* + + silcmime.c + + Author: Pekka Riikonen + + Copyright (C) 2005 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 + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +#include "silcincludes.h" +#include "silcmime.h" + +struct SilcMimeStruct { + SilcHashTable fields; + unsigned char *data; + SilcUInt32 data_len; + SilcDList multiparts; + char *boundary; +}; + +struct SilcMimeAssemblerStruct { + SilcMimeComplete complete; + void *complete_context; + SilcHashTable fragments; +}; + +static void silc_mime_field_dest(void *key, void *context, void *user_context) +{ + silc_free(key); + silc_free(context); +} + +SilcMime silc_mime_alloc(void) +{ + SilcMime mime; + + mime = silc_calloc(1, sizeof(*mime)); + if (!mime) + return NULL; + + mime->fields = silc_hash_table_alloc(0, silc_hash_string, mime, + silc_hash_string_compare, mime, + silc_mime_field_dest, mime, TRUE); + if (!mime->fields) { + silc_mime_free(mime); + return NULL; + } + + return mime; +} + +void silc_mime_free(SilcMime mime) +{ + SilcMime m; + + if (mime->fields) + silc_hash_table_free(mime->fields); + + if (mime->multiparts) { + silc_dlist_start(mime->multiparts); + while ((m = silc_dlist_get(mime->multiparts)) != SILC_LIST_END) + silc_mime_free(m); + silc_dlist_uninit(mime->multiparts); + } + silc_free(mime->boundary); + silc_free(mime); +} + +static void silc_mime_assembler_dest(void *key, void *context, + void *user_context) +{ + silc_free(key); + silc_hash_table_free(context); +} + +SilcMimeAssembler silc_mime_assembler_alloc(SilcMimeComplete complete, + void *complete_context) +{ + SilcMimeAssembler assembler; + + assembler = silc_calloc(1, sizeof(*assembler)); + if (!assembler) + return NULL; + + assembler->complete = complete; + assembler->complete_context = complete_context; + assembler->fragments = + silc_hash_table_alloc(0, silc_hash_string, NULL, + silc_hash_string_compare, NULL, + silc_mime_assembler_dest, assembler, TRUE); + if (!assembler->fragments) { + silc_mime_assembler_free(assembler); + return NULL; + } + + return assembler; +} + +void silc_mime_assembler_free(SilcMimeAssembler assembler) +{ + silc_hash_table_free(assembler->fragments); + silc_free(assembler); +} + +SilcMime silc_mime_decode(const unsigned char *data, SilcUInt32 data_len) +{ + SilcMime mime; + int i, k; + char *tmp, *field, *value, *line; + + SILC_LOG_DEBUG(("Parsing MIME message")); + + if (!data) + return NULL; + + mime = silc_mime_alloc(); + if (!mime) + return NULL; + + /* Parse the fields */ + line = tmp = (char *)data; + for (i = 0; i < data_len; i++) { + /* Get field line */ + if (data_len - i >= 2 && tmp[i] == '\r' && tmp[i + 1] == '\n') { + /* Get field */ + field = strchr(line, ':'); + if (!field) + 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) + goto err; + value++; + for (k = 0; k < (tmp + i) - value; k++) { + if (value[k] == '\r') + goto err; + if (value[k] != ' ' && value[k] != '\t') + break; + } + value += k; + if ((tmp + i) - value < 1) + goto err; + value = silc_memdup(value, (tmp + i) - value); + if (!value) + goto err; + + SILC_LOG_DEBUG(("Header '%s' '%s'", field, value)); + + /* Add field and value */ + silc_mime_add_field(mime, field, value); + silc_free(field); + silc_free(value); + + /* Mark start of next line */ + line = (tmp + i) + 2; + i += 2; + + /* Break if this is last header */ + if (data_len - i >= 2 && + tmp[i] == '\r' && tmp[i + 1] == '\n') { + i += 2; + break; + } + } + } + + /* Parse multiparts if present */ + field = (char *)silc_mime_get_field(mime, "Content-Type"); + if (field && strstr(field, "multipart")) { + char b[1024]; + SilcMime p; + + mime->multiparts = silc_dlist_init(); + if (!mime->multiparts) + goto err; + + /* Get boundary */ + value = strrchr(field, '='); + if (value && strlen(value) > 1) { + value++; + + SILC_LOG_DEBUG(("Boundary '%s'", value)); + + memset(b, 0, sizeof(b)); + line = strdup(value); + if (strrchr(line, '"')) { + *strrchr(line, '"') = '\0'; + snprintf(b, sizeof(b) - 1, "--%s", line + 1); + mime->boundary = strdup(line + 1); + } else { + snprintf(b, sizeof(b) - 1, "--%s", line); + mime->boundary = strdup(line); + } + silc_free(line); + + for (i = i; i < data_len; i++) { + /* Get boundary data */ + if (data_len - i >= strlen(b) && + tmp[i] == '-' && tmp[i + 1] == '-') { + if (memcmp(tmp + i, b, strlen(b))) + continue; + + i += strlen(b); + + if (data_len - i >= 4 && + tmp[i ] == '\r' && tmp[i + 1] == '\n' && + tmp[i + 2] == '\r' && tmp[i + 3] == '\n') + i += 4; + else if (data_len - i >= 2 && + tmp[i] == '\r' && tmp[i + 1] == '\n') + i += 2; + else if (data_len - i >= 2 && + tmp[i] == '-' && tmp[i + 1] == '-') + break; + + line = tmp + i; + + /* Find end of boundary */ + for (k = i; k < data_len; k++) + if (data_len - k >= strlen(b) && + tmp[k] == '-' && tmp[k + 1] == '-') + if (!memcmp(tmp + k, b, strlen(b))) + break; + if (k >= data_len) + goto err; + + /* Remove preceding CRLF */ + k -= 2; + + /* Parse the part */ + p = silc_mime_decode(line, k - i); + if (!p) + goto err; + + silc_dlist_add(mime->multiparts, p); + i += (k - i); + } + } + } + } else { + /* Get data area */ + if (i >= data_len) + i = 0; + SILC_LOG_DEBUG(("Data len %d", data_len - i)); + silc_mime_add_data(mime, tmp + i, data_len - i); + } + + return mime; + + err: + silc_mime_free(mime); + return NULL; +} + +unsigned char *silc_mime_encode(SilcMime mime, SilcUInt32 *encoded_len) +{ + SilcMime part; + SilcHashTableList htl; + SilcBufferStruct buf; + SilcBuffer buffer; + char *field, *value, tmp[1024], tmp2[4]; + unsigned char *ret; + int i; + + SILC_LOG_DEBUG(("Encoding MIME message")); + + if (!mime) + return NULL; + + memset(&buf, 0, sizeof(buf)); + + /* 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)) { + 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_buffer_strformat(&buf, tmp, SILC_STRFMT_END); + i++; + } + silc_hash_table_list_reset(&htl); + if (i) + silc_buffer_strformat(&buf, "\r\n", SILC_STRFMT_END); + + /* Assemble the whole buffer */ + buffer = silc_buffer_alloc_size(mime->data_len + buf.len); + if (!buffer) + return NULL; + + /* Add headers */ + if (buf.len) { + silc_buffer_put(buffer, buf.head, buf.len); + silc_buffer_pull(buffer, buf.len); + } + + /* Add data */ + if (mime->data) { + SILC_LOG_DEBUG(("Data len %d", mime->data_len)); + silc_buffer_put(buffer, mime->data, mime->data_len); + } + + /* Add multiparts */ + if (mime->multiparts) { + SILC_LOG_DEBUG(("Encoding multiparts")); + + silc_dlist_start(mime->multiparts); + i = 0; + while ((part = silc_dlist_get(mime->multiparts)) != SILC_LIST_END) { + unsigned char *pd; + SilcUInt32 pd_len; + + /* Recursive encoding */ + pd = silc_mime_encode(part, &pd_len); + if (!pd) + return NULL; + + memset(tmp, 0, sizeof(tmp)); + memset(tmp2, 0, sizeof(tmp2)); + if (i == 0) { + /* 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\r\n%s", mime->boundary, tmp2); + i = 1; + } else { + /* 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, "\r\n--%s\r\n%s", mime->boundary, tmp2); + } + + buffer = silc_buffer_realloc(buffer, buffer->truelen + pd_len + + strlen(tmp)); + if (!buffer) + return NULL; + silc_buffer_put_tail(buffer, tmp, strlen(tmp)); + silc_buffer_pull_tail(buffer, strlen(tmp)); + silc_buffer_put_tail(buffer, pd, pd_len); + silc_buffer_pull_tail(buffer, pd_len); + silc_free(pd); + } + + memset(tmp, 0, sizeof(tmp)); + snprintf(tmp, sizeof(tmp) - 1, "\r\n--%s--\r\n", mime->boundary); + buffer = silc_buffer_realloc(buffer, buffer->truelen + strlen(tmp)); + if (!buffer) + return NULL; + silc_buffer_put_tail(buffer, tmp, strlen(tmp)); + silc_buffer_pull_tail(buffer, strlen(tmp)); + } + + ret = silc_buffer_steal(buffer, encoded_len); + silc_buffer_free(buffer); + + return ret; +} + +static void silc_mime_assemble_dest(void *key, void *context, + void *user_context) +{ + silc_mime_free(context); +} + +void silc_mime_assemble(SilcMimeAssembler assembler, SilcMime partial) +{ + char *type, *id = NULL, *tmp; + SilcHashTable f; + SilcMime p, complete; + int i, number, total = -1; + const unsigned char *data; + SilcUInt32 data_len; + SilcBuffer compbuf = NULL; + + SILC_LOG_DEBUG(("Assembling MIME fragments")); + + if (!assembler || !partial) + goto err; + + type = (char *)silc_mime_get_field(partial, "Content-Type"); + if (!type) + goto err; + + /* Get ID */ + tmp = strstr(type, "id="); + if (!tmp) + goto err; + if (strlen(tmp) <= 4) + goto err; + tmp += 3; + if (*tmp == '"') + tmp++; + id = strdup(tmp); + if (strchr(id, ';')) + *strchr(id, ';') = '\0'; + if (strrchr(id, '"')) + *strrchr(id, '"') = '\0'; + + SILC_LOG_DEBUG(("Fragment ID %s", id)); + + /* Get fragment number */ + tmp = strstr(type, "number="); + if (!tmp) + goto err; + tmp = strchr(tmp, '='); + if (strlen(tmp) < 2) + goto err; + tmp++; + if (strchr(tmp, ';')) { + tmp = strdup(tmp); + *strchr(tmp, ';') = '\0'; + number = atoi(tmp); + silc_free(tmp); + } else { + number = atoi(tmp); + } + + SILC_LOG_DEBUG(("Fragment number %d", number)); + + /* Find fragments with this ID. */ + if (!silc_hash_table_find(assembler->fragments, (void *)id, + 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, + silc_mime_assemble_dest, NULL, TRUE); + if (!f) + goto err; + silc_hash_table_add(f, SILC_32_TO_PTR(number), partial); + silc_hash_table_add(assembler->fragments, id, f); + return; + } + + /* Try to get total number */ + tmp = strstr(type, "total="); + if (tmp) { + tmp = strchr(tmp, '='); + if (strlen(tmp) < 2) + goto err; + tmp++; + if (strchr(tmp, ';')) { + tmp = strdup(tmp); + *strchr(tmp, ';') = '\0'; + total = atoi(tmp); + silc_free(tmp); + } else { + total = atoi(tmp); + } + + SILC_LOG_DEBUG(("Fragment total %d", total)); + } + + /* If more fragments to come, add to hash table */ + if (number != total) { + silc_hash_table_add(f, SILC_32_TO_PTR(number), partial); + return; + } + + 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) + return; + + /* 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)) + goto err; + + /* The fragment is in the data portion of the partial message */ + data = silc_mime_get_data(p, &data_len); + if (!data) + goto err; + + /* Assemble */ + if (!compbuf) { + compbuf = silc_buffer_alloc_size(data_len); + if (!compbuf) + goto err; + silc_buffer_put(compbuf, data, data_len); + } else { + compbuf = silc_buffer_realloc(compbuf, compbuf->truelen + data_len); + if (!compbuf) + goto err; + silc_buffer_put_tail(compbuf, data, data_len); + silc_buffer_pull_tail(compbuf, data_len); + } + } + + /* Now parse the complete MIME message and deliver it */ + complete = silc_mime_decode((const unsigned char *)compbuf->head, + compbuf->truelen); + if (!complete) + goto err; + + if (assembler->complete) + assembler->complete(complete, assembler->complete_context); + + /* Delete the hash table entry. Destructors will free memory */ + silc_hash_table_del(assembler->fragments, (void *)id); + + silc_free(id); + silc_buffer_free(compbuf); + return; + + err: + silc_free(id); + if (compbuf) + silc_buffer_free(compbuf); + silc_mime_free(partial); +} + +SilcDList silc_mime_encode_partial(SilcMime mime, int max_size) +{ + unsigned char *buf, *tmp; + SilcUInt32 buf_len, len, tmp_len, off; + SilcDList list; + SilcBuffer buffer; + SilcMime partial; + char type[128], id[64]; + int num; + + SILC_LOG_DEBUG(("Fragmenting MIME message")); + + /* Encode as normal */ + buf = silc_mime_encode(mime, &buf_len); + if (!buf) + return NULL; + + list = silc_dlist_init(); + + /* Fragment if it is too large */ + if (buf_len > max_size) { + memset(id, 0, sizeof(id)); + srand((time(NULL) + buf_len) ^ rand()); + snprintf(id, sizeof(id) - 1, "%X%X%X", + (unsigned int)rand(), (unsigned int)time(NULL), + (unsigned int)buf_len); + + SILC_LOG_DEBUG(("Fragment ID %s", id)); + + partial = silc_mime_alloc(); + if (!partial) + return NULL; + + silc_mime_add_field(partial, "MIME-Version", "1.0"); + memset(type, 0, sizeof(type)); + 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); + + tmp = silc_mime_encode(partial, &tmp_len); + if (!tmp) + return NULL; + silc_mime_free(partial); + + /* Add to list */ + buffer = silc_buffer_alloc_size(tmp_len); + if (!buffer) + return NULL; + silc_buffer_put(buffer, tmp, tmp_len); + silc_dlist_add(list, buffer); + silc_free(tmp); + + len = buf_len - max_size; + off = max_size; + num = 2; + while (len > 0) { + partial = silc_mime_alloc(); + if (!partial) + return NULL; + + memset(type, 0, sizeof(type)); + silc_mime_add_field(partial, "MIME-Version", "1.0"); + + if (len > max_size) { + 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, + "message/partial; id=\"%s\"; number=%d; total=%d", + id, num, num); + silc_mime_add_data(partial, buf + off, len); + len = 0; + } + + silc_mime_add_field(partial, "Content-Type", type); + + tmp = silc_mime_encode(partial, &tmp_len); + if (!tmp) + return NULL; + silc_mime_free(partial); + + /* Add to list */ + buffer = silc_buffer_alloc_size(tmp_len); + if (!buffer) + return NULL; + silc_buffer_put(buffer, tmp, tmp_len); + silc_dlist_add(list, buffer); + silc_free(tmp); + } + } else { + /* No need to fragment */ + buffer = silc_buffer_alloc_size(buf_len); + if (!buffer) + return NULL; + silc_buffer_put(buffer, buf, buf_len); + silc_dlist_add(list, buffer); + } + + silc_free(buf); + + return list; +} + +void silc_mime_partial_free(SilcDList partials) +{ + SilcBuffer buf; + + if (!partials) + return; + + silc_dlist_start(partials); + while ((buf = silc_dlist_get(partials)) != SILC_LIST_END) + silc_buffer_free(buf); + silc_dlist_uninit(partials); +} + +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)); +} + +const char *silc_mime_get_field(SilcMime mime, const char *field) +{ + char *value; + + if (!mime || !field) + return NULL; + + if (!silc_hash_table_find(mime->fields, (void *)field, + NULL, (void **)&value)) + return NULL; + + return (const char *)value; +} + +void silc_mime_add_data(SilcMime mime, const unsigned char *data, + SilcUInt32 data_len) +{ + if (!mime || !data) + return; + + if (mime->data) + silc_free(mime->data); + + mime->data = silc_memdup(data, data_len); + mime->data_len = data_len; +} + +const unsigned char *silc_mime_get_data(SilcMime mime, SilcUInt32 *data_len) +{ + if (!mime) + return NULL; + + if (data_len) + *data_len = mime->data_len; + + return mime->data; +} + +bool silc_mime_is_partial(SilcMime mime) +{ + const char *type = silc_mime_get_field(mime, "Content-Type"); + if (!type) + return FALSE; + + if (strstr(type, "message/partial")) + return FALSE; + + return TRUE; +} + +void silc_mime_set_multipart(SilcMime mime, const char *type, + const char *boundary) +{ + char tmp[1024]; + + if (!mime || !type || !boundary) + return; + + memset(tmp, 0, sizeof(tmp)); + 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); + + if (mime->multiparts) + return; + mime->multiparts = silc_dlist_init(); +} + +bool silc_mime_add_multipart(SilcMime mime, SilcMime part) +{ + if (!mime || !mime->multiparts || !part) + return FALSE; + + silc_dlist_add(mime->multiparts, part); + return TRUE; +} + +bool silc_mime_is_multipart(SilcMime mime) +{ + if (!mime) + return FALSE; + + return mime->multiparts != NULL; +} + +SilcDList silc_mime_get_multiparts(SilcMime mime) +{ + if (!mime) + return NULL; + + return mime->multiparts; +} diff --git a/lib/silcutil/silcmime.h b/lib/silcutil/silcmime.h new file mode 100644 index 00000000..10ede17e --- /dev/null +++ b/lib/silcutil/silcmime.h @@ -0,0 +1,393 @@ +/* + + silcmime.h + + Author: Pekka Riikonen + + Copyright (C) 2005 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 + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +/****h* silcutil/SILC MIME Interface + * + * DESCRIPTION + * + * Simple implementation of MIME. Supports creation and parsing of simple + * MIME messages, multipart MIME messages, including nested multiparts, and + * MIME fragmentation and defragmentation. + * + ***/ + +#ifndef SILCMIME_H +#define SILCMIME_H + +/****s* silcutil/SILCMIMEAPI/SilcMime + * + * NAME + * + * typedef struct SilcMimeStruct *SilcMime; + * + * DESCRIPTION + * + * This context is the actual MIME message and is allocated + * by silc_mime_alloc and given as argument to all silc_mime_* + * functions. It is freed by the silc_mime_free function. + * + ***/ +typedef struct SilcMimeStruct *SilcMime; + +/****s* silcutil/SILCMIMEAPI/SilcMimeAssembler + * + * NAME + * + * typedef struct SilcMimeAssemblerStruct *SilcMimeAssembler; + * + * DESCRIPTION + * + * This context is a SILC MIME Assembler that is used to assemble partial + * MIME messages (fgraments) into complete MIME messages. It is allocated + * by silc_mime_assembler_alloc and freed by silc_mime_assembler_free. + * + ***/ +typedef struct SilcMimeAssemblerStruct *SilcMimeAssembler; + +/****f* silcutil/SILCMIMEAPI/SilcMimeComplete + * + * SYNOPSIS + * + * typedef void (*SilcMimeComplete)(SilcMime mime, void *context); + * + * DESCRIPTION + * + * Callback function that is called by silc_mime_assemble function when + * all fragments has been received. The `mime' is the complete MIME + * message. It must be freed with silc_mime_free. + * + ***/ +typedef void (*SilcMimeComplete)(SilcMime mime, void *context); + +/****f* silcutil/SILCMIMEAPI/silc_mime_alloc + * + * SYNOPSIS + * + * SilcMime silc_mime_alloc(void) + * + * DESCRIPTION + * + * Allocates SILC Mime message context. + * + ***/ +SilcMime silc_mime_alloc(void); + +/****f* silcutil/SILCMIMEAPI/silc_mime_free + * + * SYNOPSIS + * + * void silc_mime_alloc(SilcMime mime) + * + * DESCRIPTION + * + * Frees `mime' context. + * + ***/ +void silc_mime_free(SilcMime mime); + +/****f* silcutil/SILCMIMEAPI/silc_mime_assembler_alloc + * + * SYNOPSIS + * + * SilcMimeAssembler silc_mime_assembler_alloc(SilcMimeComplete complete, + * void *complete_context); + * + * DESCRIPTION + * + * Allocates MIME fragment assembler. The `complete' callback will be + * whenever a MIME message has been assembled completely. It delivers + * the complete MIME message to the caller. + * + ***/ +SilcMimeAssembler silc_mime_assembler_alloc(SilcMimeComplete complete, + void *complete_context); + +/****f* silcutil/SILCMIMEAPI/silc_mime_assembler_free + * + * SYNOPSIS + * + * void silc_mime_assembler_free(SilcMimeAssembler assembler) + * + * DESCRIPTION + * + * Frees `assembler' context. + * + ***/ +void silc_mime_assembler_free(SilcMimeAssembler assembler); + +/****f* silcutil/SILCMIMEAPI/silc_mime_decode + * + * SYNOPSIS + * + * SilcMime silc_mime_parse(const unsigned char *data, + * SilcUInt32 data_len); + * + * DESCRIPTION + * + * Decodes a MIME message and returns the parsed message into newly + * allocated SilcMime context. + * + * EXAMPLE + * + * // Parse MIME message and get its content type + * mime = silc_mime_parse(data, data_len); + * type = silc_mime_get_field(mime, "Content-Type"); + * ... + * + * // Assemble received MIME fragment + * mime = silc_mime_parse(data, data_len); + * if (silc_mime_is_partial(mime) == TRUE) + * silc_mime_assmeble(assembler, mime); + * + ***/ +SilcMime silc_mime_decode(const unsigned char *data, SilcUInt32 data_len); + +/****f* silcutil/SILCMIMEAPI/silc_mime_encode + * + * SYNOPSIS + * + * unsigned char *silc_mime_encode(SilcMime mime, SilcUInt32 *encoded_len); + * + * DESCRIPTION + * + * Encodes the `mime' context into a raw MIME message (may be human + * readable). The caller must free the returned buffer. If the `mime' + * is multipart MIME message all parts will be automatically encoded + * as well. + * + * If you want to create fragmented MIME message use the function + * silc_mime_encode_partial. + * + ***/ +unsigned char *silc_mime_encode(SilcMime mime, SilcUInt32 *encoded_len); + +/****f* silcutil/SILCMIMEAPI/silc_mime_assemble + * + * SYNOPSIS + * + * void silc_mime_assemble(SilcMimeAssembler assembler, SilcMime partial); + * + * DESCRIPTION + * + * Processes and attempts to assemble the received MIME fragment `partial'. + * To check if a received MIME message is a fragment use the + * silc_mime_is_partial function. The callback that was given as argument + * to the function silc_mime_assembler_alloc will be called when all + * fragments has been received, to deliver the complete MIME message. + * Caller must not free the `partial'. + * + * EXAMPLE + * + * // Assemble received MIME fragment + * mime = silc_mime_parse(data, data_len); + * if (silc_mime_is_partial(mime) == TRUE) + * silc_mime_assmeble(assembler, mime); + * + ***/ +void silc_mime_assemble(SilcMimeAssembler assembler, SilcMime partial); + +/****f* silcutil/SILCMIMEAPI/silc_mime_encode_partial + * + * SYNOPSIS + * + * SilcDList silc_mime_encode_partial(SilcMime mime, int max_size); + * + * DESCRIPTION + * + * Same as silc_mime_encode except fragments the MIME message `mime' + * if it is larger than `max_size' in bytes. Returns the MIME fragments + * in SilcDList where each entry is SilcBuffer context. The caller must + * free the returned list and all SilcBuffer entries in it by calling + * silc_mime_partial_free function. + * + * To assemble the fragments into a complete MIME message the + * silc_mime_assemble can be used. + * + ***/ +SilcDList silc_mime_encode_partial(SilcMime mime, int max_size); + +/****f* silcutil/SILCMIMEAPI/silc_mime_partial_free + * + * SYNOPSIS + * + * void silc_mime_partial_free(SilcDList partials); + * + * DESCRIPTION + * + * This function must be called to free the list returned by the + * silc_mime_encode_partial function. + * + ***/ +void silc_mime_partial_free(SilcDList partials); + +/****f* silcutil/SILCMIMEAPI/silc_mime_add_field + * + * SYNOPSIS + * + * void silc_mime_add_field(SilcMime mime, + * const char *field, const char *value); + * + * DESCRIPTION + * + * Adds a field indicated by `field' to MIME message `mime'. The field + * value is `value'. + * + * EXAMPLE + * + * silc_mime_add_field(mime, "MIME-Version", "1.0"); + * silc_mime_add_field(mime, "Content-Type", "image/jpeg"); + * silc_mime_add_field(mime, "Content-Transfer-Encoding", "binary"); + * + ***/ +void silc_mime_add_field(SilcMime mime, const char *field, const char *value); + +/****f* silcutil/SILCMIMEAPI/silc_mime_get_field + * + * SYNOPSIS + * + * const char *silc_mime_get_field(SilcMime mime, const char *field); + * + * DESCRIPTION + * + * Returns the `field' value or NULL if such field does not exist in the + * MIME message `mime'. + * + ***/ +const char *silc_mime_get_field(SilcMime mime, const char *field); + +/****f* silcutil/SILCMIMEAPI/silc_mime_add_data + * + * SYNOPSIS + * + * void silc_mime_add_data(SilcMime mime, const unsigned char *data, + * SilcUInt32 data_len); + * + * DESCRIPTION + * + * Adds the actual MIME data to the `mime' message. + * + ***/ +void silc_mime_add_data(SilcMime mime, const unsigned char *data, + SilcUInt32 data_len); + +/****f* silcutil/SILCMIMEAPI/silc_mime_get_data + * + * SYNOPSIS + * + * const unsigned char * + * silc_mime_get_data(SilcMime mime, SilcUInt32 *data_len); + * + * DESCRIPTION + * + * Returns the MIME data from the `mime' message. + * + ***/ +const unsigned char *silc_mime_get_data(SilcMime mime, SilcUInt32 *data_len); + +/****f* silcutil/SILCMIMEAPI/silc_mime_is_partial + * + * SYNOPSIS + * + * bool silc_mime_is_partial(SilcMime mime); + * + * DESCRIPTION + * + * Returns TRUE if the MIME message `mime' is a partial MIME fragment. + * + ***/ +bool silc_mime_is_partial(SilcMime mime); + +/****f* silcutil/SILCMIMEAPI/silc_mime_set_multipart + * + * SYNOPSIS + * + * void silc_mime_set_multipart(SilcMime mime, const char *type, + * const char *boundary); + * + * DESCRIPTION + * + * Sets the `mime' to be a multipart MIME message. The `type' specifies + * the multipart type, usually "mixed", but can be something else too. + * The `boundary' specifies the multipart boundary. + * + ***/ +void silc_mime_set_multipart(SilcMime mime, const char *type, + const char *boundary); + +/****f* silcutil/SILCMIMEAPI/silc_mime_add_multipart + * + * SYNOPSIS + * + * bool silc_mime_add_multipart(SilcMime mime, SilcMime part); + * + * DESCRIPTION + * + * Adds a multipart `part` to MIME message `mime'. The `part' will be + * freed automatically when silc_mime_free is called for `mime'. Returns + * TRUE if `part' was added to `mime' and FALSE if `mime' is not marked + * as multipart MIME message. + * + * NOTES + * + * The silc_mime_set_multipart must be called for `mime' before parts + * can be added to it. Otherwise FALSE will be returned. + * + * EXAMPLE + * + * part = silc_mime_alloc(); + * silc_mime_add_field(part, "Content-Type", "image/jpeg"); + * silc_mime_add_data(part, data, data_len); + * + * silc_mime_set_multipart(mime, "mixed", "boundary1"); + * silc_mime_add_multipart(mime, part); + * + ***/ +bool silc_mime_add_multipart(SilcMime mime, SilcMime part); + +/****f* silcutil/SILCMIMEAPI/silc_mime_is_multipart + * + * SYNOPSIS + * + * bool silc_mime_is_multipart(SilcMime mime); + * + * DESCRIPTION + * + * Returns TRUE if the MIME message `mime' is a multipart MIME message. + * Its parts can be get by calling silc_mime_get_multiparts. + * + ***/ +bool silc_mime_is_multipart(SilcMime mime); + +/****f* silcutil/SILCMIMEAPI/silc_mime_get_multiparts + * + * SYNOPSIS + * + * SilcDList silc_mime_get_multiparts(SilcMime mime); + * + * DESCRIPTION + * + * Returns list of the parts from the MIME message `mime'. Each entry + * in the list is SilcMime context. The caller must not free the returned + * list or the SilcMime contexts in the list. Returns NULL if no parts + * exists in the MIME message. + * + ***/ +SilcDList silc_mime_get_multiparts(SilcMime mime); + +#endif /* SILCMIME_H */ diff --git a/lib/silcutil/silcutil.c b/lib/silcutil/silcutil.c index a3746f92..b1f09b7b 100644 --- a/lib/silcutil/silcutil.c +++ b/lib/silcutil/silcutil.c @@ -490,7 +490,7 @@ SilcUInt32 silc_hash_utf8_string(void *key, void *user_context) SilcUInt32 silc_hash_uint(void *key, void *user_context) { - return *(SilcUInt32 *)key; + return SILC_PTR_TO_32(key); } /* Basic hash funtion to hash pointers. May be used with the SilcHashTable. */ diff --git a/lib/silcutil/tests/Makefile.am b/lib/silcutil/tests/Makefile.am index 8462ffbd..96cebf4d 100644 --- a/lib/silcutil/tests/Makefile.am +++ b/lib/silcutil/tests/Makefile.am @@ -18,12 +18,13 @@ AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign bin_PROGRAMS = test_silcstrutil test_silcstringprep test_silchashtable \ - test_silclist + test_silclist test_silcmime test_silcstrutil_SOURCES = test_silcstrutil.c test_silcstringprep_SOURCES = test_silcstringprep.c test_silchashtable_SOURCES = test_silchashtable.c test_silclist_SOURCES = test_silclist.c +test_silcmime_SOURCES = test_silcmime.c LIBS = $(SILC_COMMON_LIBS) LDADD = -L.. -L../.. -lsilc diff --git a/lib/silcutil/tests/test_silchashtable.c b/lib/silcutil/tests/test_silchashtable.c index 47326c0d..bf75e7f5 100644 --- a/lib/silcutil/tests/test_silchashtable.c +++ b/lib/silcutil/tests/test_silchashtable.c @@ -211,13 +211,13 @@ int main(int argc, char **argv) int i; if (argc > 1 && !strcmp(argv[1], "-d")) { - silc_debug = 1; - silc_debug_hexdump = 1; + silc_log_debug(TRUE); + silc_log_debug_hexdump(TRUE); silc_log_set_debug_string("*table*"); } if (argc > 1 && !strcmp(argv[1], "-D")) { - silc_debug = 1; + silc_log_debug(TRUE); dump = TRUE; silc_log_set_debug_string("*table*"); } diff --git a/lib/silcutil/tests/test_silclist.c b/lib/silcutil/tests/test_silclist.c index 93aad06e..b873af1d 100644 --- a/lib/silcutil/tests/test_silclist.c +++ b/lib/silcutil/tests/test_silclist.c @@ -14,8 +14,8 @@ int main(int argc, char **argv) struct foo *f, *f1, *f2, *f3; if (argc > 1 && !strcmp(argv[1], "-d")) { - silc_debug = 1; - silc_debug_hexdump = 1; + silc_log_debug(TRUE); + silc_log_debug_hexdump(TRUE); silc_log_set_debug_string("*list*"); } diff --git a/lib/silcutil/tests/test_silcmime.c b/lib/silcutil/tests/test_silcmime.c new file mode 100644 index 00000000..01e7fdd1 --- /dev/null +++ b/lib/silcutil/tests/test_silcmime.c @@ -0,0 +1,246 @@ +/* SilcMime tests */ + +#include "silcincludes.h" +#include "silcmime.h" + +struct foo { + int i; + struct foo *next; +}; + +static void ass_complete(SilcMime mime, void *context) +{ + unsigned char *enc; + SilcUInt32 enc_len; + + SILC_LOG_DEBUG(("Defragmentation completed")); + SILC_LOG_DEBUG(("Encoding MIME context")); + enc = silc_mime_encode(mime, &enc_len); + if (!enc) + SILC_LOG_DEBUG(("Error encoding")); + SILC_LOG_DEBUG(("Encoded MIME message: \n%s", enc)); + silc_free(enc); + silc_mime_free(mime); +} + +int main(int argc, char **argv) +{ + bool success = FALSE; + SilcMime mime, part, part2; + SilcMimeAssembler ass; + int i; + char tmp[500]; + unsigned char *enc; + SilcUInt32 enc_len; + SilcDList frag; + SilcBuffer buf; + + if (argc > 1 && !strcmp(argv[1], "-d")) { + silc_log_debug(TRUE); + silc_log_debug_hexdump(TRUE); + silc_log_set_debug_string("*mime*"); + } + + /* Simple MIME test */ + SILC_LOG_DEBUG(("Allocating MIME message context")); + mime = silc_mime_alloc(); + if (!mime) + goto err; + SILC_LOG_DEBUG(("Adding MIME fields")); + SILC_LOG_DEBUG(("Adding MIME-Version: 1.0")); + silc_mime_add_field(mime, "MIME-Version", "1.0"); + SILC_LOG_DEBUG(("Adding Content-Type: foo/bar")); + silc_mime_add_field(mime, "Content-Type", "foo/bar"); + SILC_LOG_DEBUG(("Adding Content-Transfer-Encoding: binary")); + silc_mime_add_field(mime, "Content-Transfer-Encoding", "binary"); + SILC_LOG_DEBUG(("Adding FOO: BaR")); + silc_mime_add_field(mime, "FOO", "BaR"); + SILC_LOG_DEBUG(("Adding MIME data, 100 A's + 1 B")); + for (i = 0; i < 100; i++) + tmp[i] = 'A'; + tmp[100] = 'B'; + silc_mime_add_data(mime, tmp, 101); + SILC_LOG_DEBUG(("Encoding MIME context")); + enc = silc_mime_encode(mime, &enc_len); + if (!enc) + goto err; + SILC_LOG_DEBUG(("Encoded MIME message: \n%s", enc)); + silc_mime_free(mime); + SILC_LOG_DEBUG(("Decoding MIME message")); + mime = silc_mime_decode(enc, enc_len); + if (!mime) + goto err; + SILC_LOG_DEBUG(("Re-encoding MIME context")); + silc_free(enc); + enc = silc_mime_encode(mime, &enc_len); + if (!enc) + goto err; + SILC_LOG_DEBUG(("Re-encoded MIME message: \n%s", enc)); + silc_free(enc); + silc_mime_free(mime); + + /* Multipart test, with nesting */ + SILC_LOG_DEBUG(("Allocating MIME message context")); + mime = silc_mime_alloc(); + if (!mime) + goto err; + SILC_LOG_DEBUG(("Adding MIME-Version: 1.0")); + silc_mime_add_field(mime, "MIME-Version", "1.0"); + SILC_LOG_DEBUG(("Adding Content-Transfer-Encoding: binary")); + silc_mime_add_field(mime, "Content-Transfer-Encoding", "binary"); + SILC_LOG_DEBUG(("Marking as multipart MIME message")); + silc_mime_set_multipart(mime, "mixed", "boundary"); + SILC_LOG_DEBUG(("Adding FOO: BaR")); + silc_mime_add_field(mime, "FOO", "BaR"); + SILC_LOG_DEBUG(("Allocating part")); + part = silc_mime_alloc(); + if (!part) + goto err; + SILC_LOG_DEBUG(("Adding MIME fields")); + SILC_LOG_DEBUG(("Adding Content-Type: foo/bar")); + silc_mime_add_field(part, "Content-Type", "foo/bar"); + SILC_LOG_DEBUG(("Adding MIME data, 100 A's + 1 B")); + for (i = 0; i < 100; i++) + tmp[i] = 'A'; + tmp[100] = 'B'; + silc_mime_add_data(part, tmp, 101); + SILC_LOG_DEBUG(("Adding part to MIME message")); + if (!silc_mime_add_multipart(mime, part)) + goto err; + SILC_LOG_DEBUG(("Allocating part")); + part = silc_mime_alloc(); + if (!part) + goto err; + SILC_LOG_DEBUG(("Adding Content-Type: image/foobar")); + silc_mime_add_field(part, "Content-Type", "image/foobar"); + SILC_LOG_DEBUG(("Adding MIME data, 50 A's + 1 B")); + for (i = 0; i < 50; i++) + tmp[i] = 'A'; + tmp[50] = 'B'; + silc_mime_add_data(part, tmp, 51); + SILC_LOG_DEBUG(("Adding part to MIME message")); + if (!silc_mime_add_multipart(mime, part)) + goto err; + SILC_LOG_DEBUG(("Allocating part")); + part = silc_mime_alloc(); + if (!part) + goto err; + SILC_LOG_DEBUG(("Adding MIME data, 10 A's + 1 B")); + for (i = 0; i < 10; i++) + tmp[i] = 'A'; + tmp[10] = 'B'; + silc_mime_add_data(part, tmp, 11); + SILC_LOG_DEBUG(("Adding part to MIME message")); + if (!silc_mime_add_multipart(mime, part)) + goto err; + SILC_LOG_DEBUG(("Allocating part")); + part = silc_mime_alloc(); + if (!part) + goto err; + SILC_LOG_DEBUG(("Adding part to MIME message")); + if (!silc_mime_add_multipart(mime, part)) + goto err; + silc_mime_set_multipart(part, "mixed", "booooooooundary"); + SILC_LOG_DEBUG(("Allocating part for nested multipart")); + part2 = silc_mime_alloc(); + if (!part) + goto err; + SILC_LOG_DEBUG(("Adding Content-Type: foo/nested")); + silc_mime_add_field(part2, "Content-Type", "foo/nested"); + SILC_LOG_DEBUG(("Adding MIME data, 150 A's + 1 B")); + for (i = 0; i < 150; i++) + tmp[i] = 'A'; + tmp[150] = 'B'; + silc_mime_add_data(part2, tmp, 151); + SILC_LOG_DEBUG(("Adding part to another part message")); + if (!silc_mime_add_multipart(part, part2)) + goto err; + SILC_LOG_DEBUG(("Encoding MIME context")); + enc = silc_mime_encode(mime, &enc_len); + if (!enc) + goto err; + SILC_LOG_DEBUG(("Encoded MIME message: \n%s", enc)); + silc_mime_free(mime); + SILC_LOG_DEBUG(("Decoding MIME message")); + mime = silc_mime_decode(enc, enc_len); + if (!mime) + goto err; + SILC_LOG_DEBUG(("Re-encoding MIME context")); + silc_free(enc); + enc = silc_mime_encode(mime, &enc_len); + if (!enc) + goto err; + SILC_LOG_DEBUG(("Re-encoded MIME message: \n%s", enc)); + silc_free(enc); + SILC_LOG_DEBUG(("Get multiparts")); + frag = silc_mime_get_multiparts(mime); + if (!frag) + goto err; + silc_dlist_start(frag); + while ((part = silc_dlist_get(frag)) != SILC_LIST_END) { + SILC_LOG_DEBUG(("Encoding MIME part")); + enc = silc_mime_encode(part, &enc_len); + if (!enc) + goto err; + if (silc_mime_is_multipart(part)) + SILC_LOG_DEBUG(("Is multipart")); + SILC_LOG_DEBUG(("Encoded MIME part: \n%s", enc)); + silc_free(enc); + } + silc_mime_free(mime); + + /* Fragmentation test */ + SILC_LOG_DEBUG(("Allocating MIME assembler")); + ass = silc_mime_assembler_alloc(ass_complete, NULL); + if (!ass) + goto err; + SILC_LOG_DEBUG(("Allocating MIME message context")); + mime = silc_mime_alloc(); + if (!mime) + goto err; + SILC_LOG_DEBUG(("Adding MIME fields")); + SILC_LOG_DEBUG(("Adding MIME-Version: 1.0")); + silc_mime_add_field(mime, "MIME-Version", "1.0"); + SILC_LOG_DEBUG(("Adding Content-Type: foo/bar")); + silc_mime_add_field(mime, "Content-Type", "foo/bar"); + SILC_LOG_DEBUG(("Adding Content-Transfer-Encoding: binary")); + silc_mime_add_field(mime, "Content-Transfer-Encoding", "binary"); + SILC_LOG_DEBUG(("Adding FOO: BaR")); + silc_mime_add_field(mime, "FOO", "BaR"); + SILC_LOG_DEBUG(("Adding MIME data, 300 A's + 1 B")); + for (i = 0; i < 300; i++) + tmp[i] = 'A'; + tmp[300] = 'B'; + silc_mime_add_data(mime, tmp, 301); + SILC_LOG_DEBUG(("Encoding MIME context")); + enc = silc_mime_encode(mime, &enc_len); + if (!enc) + goto err; + SILC_LOG_DEBUG(("Encoded MIME message: \n%s", enc)); + silc_free(enc); + SILC_LOG_DEBUG(("Fragment MIME message in 100 byte chunks")); + frag = silc_mime_encode_partial(mime, 100); + if (!frag) + goto err; + silc_dlist_start(frag); + while ((buf = silc_dlist_get(frag)) != SILC_LIST_END) + SILC_LOG_DEBUG(("Fragment \n%s", buf->data, buf->len)); + SILC_LOG_DEBUG(("Defragment")); + silc_dlist_start(frag); + while ((buf = silc_dlist_get(frag)) != SILC_LIST_END) { + part = silc_mime_decode(buf->data, buf->len); + if (!silc_mime_is_partial(part)) + goto err; + silc_mime_assemble(ass, part); + } + silc_mime_partial_free(frag); + silc_mime_assembler_free(ass); + + success = TRUE; + + err: + SILC_LOG_DEBUG(("Testing was %s", success ? "SUCCESS" : "FAILURE")); + fprintf(stderr, "Testing was %s\n", success ? "SUCCESS" : "FAILURE"); + + return success; +} diff --git a/lib/silcutil/tests/test_silcstringprep.c b/lib/silcutil/tests/test_silcstringprep.c index 26ff2aaf..3c48c52e 100644 --- a/lib/silcutil/tests/test_silcstringprep.c +++ b/lib/silcutil/tests/test_silcstringprep.c @@ -77,8 +77,8 @@ int main(int argc, char **argv) SilcStringprepStatus ret; if (argc > 1 && !strcmp(argv[1], "-d")) { - silc_debug = 1; - silc_debug_hexdump = 1; + silc_log_debug(TRUE); + silc_log_debug_hexdump(TRUE); silc_log_set_debug_string("*stringprep*,*utf8*"); } diff --git a/lib/silcutil/tests/test_silcstrutil.c b/lib/silcutil/tests/test_silcstrutil.c index dcf94662..821bc898 100644 --- a/lib/silcutil/tests/test_silcstrutil.c +++ b/lib/silcutil/tests/test_silcstrutil.c @@ -65,8 +65,8 @@ int main(int argc, char **argv) exit(0); break; case 'd': - silc_debug = TRUE; - silc_debug_hexdump = TRUE; + silc_log_debug(TRUE); + silc_log_debug_hexdump(TRUE); if (optarg) silc_log_set_debug_string(optarg); else