5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2005 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
20 #include "silcincludes.h"
23 struct SilcMimeStruct {
32 struct SilcMimeAssemblerStruct {
33 SilcHashTable fragments;
36 static void silc_mime_field_dest(void *key, void *context, void *user_context)
42 SilcMime silc_mime_alloc(void)
46 mime = silc_calloc(1, sizeof(*mime));
50 mime->fields = silc_hash_table_alloc(0, silc_hash_string, mime,
51 silc_hash_string_compare, mime,
52 silc_mime_field_dest, mime, TRUE);
61 void silc_mime_free(SilcMime mime)
66 silc_hash_table_free(mime->fields);
68 if (mime->multiparts) {
69 silc_dlist_start(mime->multiparts);
70 while ((m = silc_dlist_get(mime->multiparts)) != SILC_LIST_END)
72 silc_dlist_uninit(mime->multiparts);
74 silc_free(mime->boundary);
75 silc_free(mime->multitype);
76 silc_free(mime->data);
80 static void silc_mime_assembler_dest(void *key, void *context,
84 silc_hash_table_free(context);
87 SilcMimeAssembler silc_mime_assembler_alloc(void)
89 SilcMimeAssembler assembler;
91 assembler = silc_calloc(1, sizeof(*assembler));
95 assembler->fragments =
96 silc_hash_table_alloc(0, silc_hash_string, NULL,
97 silc_hash_string_compare, NULL,
98 silc_mime_assembler_dest, assembler, TRUE);
99 if (!assembler->fragments) {
100 silc_mime_assembler_free(assembler);
107 void silc_mime_assembler_free(SilcMimeAssembler assembler)
109 silc_hash_table_free(assembler->fragments);
110 silc_free(assembler);
113 SilcMime silc_mime_decode(const unsigned char *data, SilcUInt32 data_len)
117 char *tmp, *field, *value, *line;
119 SILC_LOG_DEBUG(("Parsing MIME message"));
124 mime = silc_mime_alloc();
128 /* Parse the fields */
129 line = tmp = (char *)data;
130 for (i = 0; i < data_len; i++) {
132 if (data_len - i >= 2 && tmp[i] == '\r' && tmp[i + 1] == '\n') {
134 field = strchr(line, ':');
137 field = silc_memdup(line, field - line);
141 /* Get value. Remove whitespaces too. */
142 value = strchr(line, ':');
143 if ((tmp + i) - value < 2)
146 for (k = 0; k < (tmp + i) - value; k++) {
147 if (value[k] == '\r')
149 if (value[k] != ' ' && value[k] != '\t')
153 if ((tmp + i) - value < 1)
155 value = silc_memdup(value, (tmp + i) - value);
159 SILC_LOG_DEBUG(("Header '%s' '%s'", field, value));
161 /* Add field and value */
162 silc_mime_add_field(mime, field, value);
166 /* Mark start of next line */
167 line = (tmp + i) + 2;
170 /* Break if this is last header */
171 if (data_len - i >= 2 &&
172 tmp[i] == '\r' && tmp[i + 1] == '\n') {
179 /* Parse multiparts if present */
180 field = (char *)silc_mime_get_field(mime, "Content-Type");
181 if (field && strstr(field, "multipart")) {
185 mime->multiparts = silc_dlist_init();
186 if (!mime->multiparts)
189 /* Get multipart type */
190 value = strchr(field, '/');
194 if (strchr(field, '"'))
196 if (!strchr(field, ';'))
198 memset(b, 0, sizeof(b));
199 strncat(b, value, strchr(field, ';') - value);
201 *strchr(b, '"') = '\0';
202 mime->multitype = silc_memdup(b, strlen(b));
205 value = strrchr(field, '=');
206 if (value && strlen(value) > 1) {
209 SILC_LOG_DEBUG(("Boundary '%s'", value));
211 memset(b, 0, sizeof(b));
212 line = strdup(value);
213 if (strrchr(line, '"')) {
214 *strrchr(line, '"') = '\0';
215 snprintf(b, sizeof(b) - 1, "--%s", line + 1);
216 mime->boundary = strdup(line + 1);
218 snprintf(b, sizeof(b) - 1, "--%s", line);
219 mime->boundary = strdup(line);
223 for (i = i; i < data_len; i++) {
224 /* Get boundary data */
225 if (data_len - i >= strlen(b) &&
226 tmp[i] == '-' && tmp[i + 1] == '-') {
227 if (memcmp(tmp + i, b, strlen(b)))
232 if (data_len - i >= 4 &&
233 tmp[i ] == '\r' && tmp[i + 1] == '\n' &&
234 tmp[i + 2] == '\r' && tmp[i + 3] == '\n')
236 else if (data_len - i >= 2 &&
237 tmp[i] == '\r' && tmp[i + 1] == '\n')
239 else if (data_len - i >= 2 &&
240 tmp[i] == '-' && tmp[i + 1] == '-')
245 /* Find end of boundary */
246 for (k = i; k < data_len; k++)
247 if (data_len - k >= strlen(b) &&
248 tmp[k] == '-' && tmp[k + 1] == '-')
249 if (!memcmp(tmp + k, b, strlen(b)))
254 /* Remove preceding CRLF */
258 p = silc_mime_decode(line, k - i);
262 silc_dlist_add(mime->multiparts, p);
271 SILC_LOG_DEBUG(("Data len %d", data_len - i));
272 silc_mime_add_data(mime, tmp + i, data_len - i);
278 silc_mime_free(mime);
282 unsigned char *silc_mime_encode(SilcMime mime, SilcUInt32 *encoded_len)
285 SilcHashTableList htl;
286 SilcBufferStruct buf;
288 char *field, *value, tmp[1024], tmp2[4];
292 SILC_LOG_DEBUG(("Encoding MIME message"));
297 memset(&buf, 0, sizeof(buf));
299 /* Encode the headers. Order doesn't matter */
301 silc_hash_table_list(mime->fields, &htl);
302 while (silc_hash_table_get(&htl, (void **)&field, (void **)&value)) {
303 memset(tmp, 0, sizeof(tmp));
304 SILC_LOG_DEBUG(("Header %s: %s", field, value));
305 snprintf(tmp, sizeof(tmp) - 1, "%s: %s\r\n", field, value);
306 silc_buffer_strformat(&buf, tmp, SILC_STRFMT_END);
309 silc_hash_table_list_reset(&htl);
311 silc_buffer_strformat(&buf, "\r\n", SILC_STRFMT_END);
313 /* Assemble the whole buffer */
314 buffer = silc_buffer_alloc_size(mime->data_len + silc_buffer_len(&buf));
319 if (silc_buffer_len(&buf)) {
320 silc_buffer_put(buffer, buf.head, silc_buffer_len(&buf));
321 silc_buffer_pull(buffer, silc_buffer_len(&buf));
326 SILC_LOG_DEBUG(("Data len %d", mime->data_len));
327 silc_buffer_put(buffer, mime->data, mime->data_len);
331 if (mime->multiparts) {
332 SILC_LOG_DEBUG(("Encoding multiparts"));
334 silc_dlist_start(mime->multiparts);
336 while ((part = silc_dlist_get(mime->multiparts)) != SILC_LIST_END) {
340 /* Recursive encoding */
341 pd = silc_mime_encode(part, &pd_len);
345 memset(tmp, 0, sizeof(tmp));
346 memset(tmp2, 0, sizeof(tmp2));
348 /* If fields are not present, add extra CRLF */
349 if (!silc_hash_table_count(part->fields))
350 snprintf(tmp2, sizeof(tmp2) - 1, "\r\n");
351 snprintf(tmp, sizeof(tmp) - 1, "%s--%s\r\n%s",
352 i != 0 ? "\r\n" : "", mime->boundary, tmp2);
355 buffer = silc_buffer_realloc(buffer, silc_buffer_truelen(buffer) +
356 pd_len + strlen(tmp));
359 silc_buffer_put_tail(buffer, tmp, strlen(tmp));
360 silc_buffer_pull_tail(buffer, strlen(tmp));
361 silc_buffer_put_tail(buffer, pd, pd_len);
362 silc_buffer_pull_tail(buffer, pd_len);
366 memset(tmp, 0, sizeof(tmp));
367 snprintf(tmp, sizeof(tmp) - 1, "\r\n--%s--\r\n", mime->boundary);
368 buffer = silc_buffer_realloc(buffer, silc_buffer_truelen(buffer) +
372 silc_buffer_put_tail(buffer, tmp, strlen(tmp));
373 silc_buffer_pull_tail(buffer, strlen(tmp));
376 ret = silc_buffer_steal(buffer, encoded_len);
377 silc_buffer_free(buffer);
382 static void silc_mime_assemble_dest(void *key, void *context,
385 silc_mime_free(context);
388 SilcMime silc_mime_assemble(SilcMimeAssembler assembler, SilcMime partial)
390 char *type, *id = NULL, *tmp;
392 SilcMime p, complete;
393 int i, number, total = -1;
394 const unsigned char *data;
396 SilcBuffer compbuf = NULL;
398 SILC_LOG_DEBUG(("Assembling MIME fragments"));
400 if (!assembler || !partial)
403 type = (char *)silc_mime_get_field(partial, "Content-Type");
408 tmp = strstr(type, "id=");
411 if (strlen(tmp) <= 4)
418 *strchr(id, ';') = '\0';
419 if (strrchr(id, '"'))
420 *strrchr(id, '"') = '\0';
422 SILC_LOG_DEBUG(("Fragment ID %s", id));
424 /* Get fragment number */
425 tmp = strstr(type, "number=");
428 tmp = strchr(tmp, '=');
432 if (strchr(tmp, ';')) {
434 *strchr(tmp, ';') = '\0';
441 SILC_LOG_DEBUG(("Fragment number %d", number));
443 /* Find fragments with this ID. */
444 if (!silc_hash_table_find(assembler->fragments, (void *)id,
445 NULL, (void **)&f)) {
446 /* This is new fragment to new message. Add to hash table and return. */
447 f = silc_hash_table_alloc(0, silc_hash_uint, NULL, NULL, NULL,
448 silc_mime_assemble_dest, NULL, TRUE);
451 silc_hash_table_add(f, SILC_32_TO_PTR(number), partial);
452 silc_hash_table_add(assembler->fragments, id, f);
456 /* Try to get total number */
457 tmp = strstr(type, "total=");
459 tmp = strchr(tmp, '=');
463 if (strchr(tmp, ';')) {
465 *strchr(tmp, ';') = '\0';
472 SILC_LOG_DEBUG(("Fragment total %d", total));
475 /* If more fragments to come, add to hash table */
476 if (number != total) {
477 silc_hash_table_add(f, SILC_32_TO_PTR(number), partial);
481 silc_hash_table_add(f, SILC_32_TO_PTR(number), partial);
483 /* Verify that we really have all the fragments */
484 if (silc_hash_table_count(f) < total)
487 /* Assemble the complete MIME message now. We get them in order from
489 for (i = 1; i <= total; i++) {
490 if (!silc_hash_table_find(f, SILC_32_TO_PTR(i), NULL, (void **)&p))
493 /* The fragment is in the data portion of the partial message */
494 data = silc_mime_get_data(p, &data_len);
500 compbuf = silc_buffer_alloc_size(data_len);
503 silc_buffer_put(compbuf, data, data_len);
505 compbuf = silc_buffer_realloc(compbuf, silc_buffer_truelen(compbuf) +
509 silc_buffer_put_tail(compbuf, data, data_len);
510 silc_buffer_pull_tail(compbuf, data_len);
514 /* Now parse the complete MIME message and deliver it */
515 complete = silc_mime_decode((const unsigned char *)compbuf->head,
516 silc_buffer_truelen(compbuf));
520 /* Delete the hash table entry. Destructors will free memory */
521 silc_hash_table_del(assembler->fragments, (void *)id);
523 silc_buffer_free(compbuf);
530 silc_buffer_free(compbuf);
531 silc_mime_free(partial);
535 SilcDList silc_mime_encode_partial(SilcMime mime, int max_size)
537 unsigned char *buf, *tmp;
538 SilcUInt32 buf_len, len, tmp_len, off;
542 char type[128], id[64];
545 SILC_LOG_DEBUG(("Fragmenting MIME message"));
547 /* Encode as normal */
548 buf = silc_mime_encode(mime, &buf_len);
552 list = silc_dlist_init();
554 /* Fragment if it is too large */
555 if (buf_len > max_size) {
556 memset(id, 0, sizeof(id));
557 memset(type, 0, sizeof(type));
558 gethostname(type, sizeof(type) - 1);
559 srand((time(NULL) + buf_len) ^ rand());
560 snprintf(id, sizeof(id) - 1, "%X%X%X%s",
561 (unsigned int)rand(), (unsigned int)time(NULL),
562 (unsigned int)buf_len, type);
564 SILC_LOG_DEBUG(("Fragment ID %s", id));
566 partial = silc_mime_alloc();
570 silc_mime_add_field(partial, "MIME-Version", "1.0");
571 memset(type, 0, sizeof(type));
572 snprintf(type, sizeof(type) - 1,
573 "message/partial; id=\"%s\"; number=1", id);
574 silc_mime_add_field(partial, "Content-Type", type);
575 silc_mime_add_data(partial, buf, max_size);
577 tmp = silc_mime_encode(partial, &tmp_len);
580 silc_mime_free(partial);
583 buffer = silc_buffer_alloc_size(tmp_len);
586 silc_buffer_put(buffer, tmp, tmp_len);
587 silc_dlist_add(list, buffer);
590 len = buf_len - max_size;
594 partial = silc_mime_alloc();
598 memset(type, 0, sizeof(type));
599 silc_mime_add_field(partial, "MIME-Version", "1.0");
601 if (len > max_size) {
602 snprintf(type, sizeof(type) - 1,
603 "message/partial; id=\"%s\"; number=%d",
605 silc_mime_add_data(partial, buf + off, max_size);
609 snprintf(type, sizeof(type) - 1,
610 "message/partial; id=\"%s\"; number=%d; total=%d",
612 silc_mime_add_data(partial, buf + off, len);
616 silc_mime_add_field(partial, "Content-Type", type);
618 tmp = silc_mime_encode(partial, &tmp_len);
621 silc_mime_free(partial);
624 buffer = silc_buffer_alloc_size(tmp_len);
627 silc_buffer_put(buffer, tmp, tmp_len);
628 silc_dlist_add(list, buffer);
632 /* No need to fragment */
633 buffer = silc_buffer_alloc_size(buf_len);
636 silc_buffer_put(buffer, buf, buf_len);
637 silc_dlist_add(list, buffer);
645 void silc_mime_partial_free(SilcDList partials)
652 silc_dlist_start(partials);
653 while ((buf = silc_dlist_get(partials)) != SILC_LIST_END)
654 silc_buffer_free(buf);
655 silc_dlist_uninit(partials);
658 void silc_mime_add_field(SilcMime mime, const char *field, const char *value)
660 if (!mime || !field || !value)
663 silc_hash_table_add(mime->fields, strdup(field), strdup(value));
666 const char *silc_mime_get_field(SilcMime mime, const char *field)
673 if (!silc_hash_table_find(mime->fields, (void *)field,
674 NULL, (void **)&value))
677 return (const char *)value;
680 void silc_mime_add_data(SilcMime mime, const unsigned char *data,
687 silc_free(mime->data);
689 mime->data = silc_memdup(data, data_len);
690 mime->data_len = data_len;
693 const unsigned char *silc_mime_get_data(SilcMime mime, SilcUInt32 *data_len)
699 *data_len = mime->data_len;
704 bool silc_mime_is_partial(SilcMime mime)
706 const char *type = silc_mime_get_field(mime, "Content-Type");
710 if (!strstr(type, "message/partial"))
716 void silc_mime_set_multipart(SilcMime mime, const char *type,
717 const char *boundary)
721 if (!mime || !type || !boundary)
724 memset(tmp, 0, sizeof(tmp));
725 snprintf(tmp, sizeof(tmp) - 1, "multipart/%s; boundary=%s", type, boundary);
726 silc_mime_add_field(mime, "Content-Type", tmp);
727 silc_free(mime->boundary);
728 mime->boundary = strdup(boundary);
730 if (mime->multiparts)
732 mime->multiparts = silc_dlist_init();
735 bool silc_mime_add_multipart(SilcMime mime, SilcMime part)
737 if (!mime || !mime->multiparts || !part)
740 silc_dlist_add(mime->multiparts, part);
744 bool silc_mime_is_multipart(SilcMime mime)
749 return mime->multiparts != NULL;
752 SilcDList silc_mime_get_multiparts(SilcMime mime, const char **type)
758 *type = (const char *)mime->multitype;
760 return mime->multiparts;