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 {
31 struct SilcMimeAssemblerStruct {
32 SilcMimeComplete complete;
33 void *complete_context;
34 SilcHashTable fragments;
37 static void silc_mime_field_dest(void *key, void *context, void *user_context)
43 SilcMime silc_mime_alloc(void)
47 mime = silc_calloc(1, sizeof(*mime));
51 mime->fields = silc_hash_table_alloc(0, silc_hash_string, mime,
52 silc_hash_string_compare, mime,
53 silc_mime_field_dest, mime, TRUE);
62 void silc_mime_free(SilcMime mime)
67 silc_hash_table_free(mime->fields);
69 if (mime->multiparts) {
70 silc_dlist_start(mime->multiparts);
71 while ((m = silc_dlist_get(mime->multiparts)) != SILC_LIST_END)
73 silc_dlist_uninit(mime->multiparts);
75 silc_free(mime->boundary);
79 static void silc_mime_assembler_dest(void *key, void *context,
83 silc_hash_table_free(context);
86 SilcMimeAssembler silc_mime_assembler_alloc(SilcMimeComplete complete,
87 void *complete_context)
89 SilcMimeAssembler assembler;
91 assembler = silc_calloc(1, sizeof(*assembler));
95 assembler->complete = complete;
96 assembler->complete_context = complete_context;
97 assembler->fragments =
98 silc_hash_table_alloc(0, silc_hash_string, NULL,
99 silc_hash_string_compare, NULL,
100 silc_mime_assembler_dest, assembler, TRUE);
101 if (!assembler->fragments) {
102 silc_mime_assembler_free(assembler);
109 void silc_mime_assembler_free(SilcMimeAssembler assembler)
111 silc_hash_table_free(assembler->fragments);
112 silc_free(assembler);
115 SilcMime silc_mime_decode(const unsigned char *data, SilcUInt32 data_len)
119 char *tmp, *field, *value, *line;
121 SILC_LOG_DEBUG(("Parsing MIME message"));
126 mime = silc_mime_alloc();
130 /* Parse the fields */
131 line = tmp = (char *)data;
132 for (i = 0; i < data_len; i++) {
134 if (data_len - i >= 2 && tmp[i] == '\r' && tmp[i + 1] == '\n') {
136 field = strchr(line, ':');
139 field = silc_memdup(line, field - line);
143 /* Get value. Remove whitespaces too. */
144 value = strchr(line, ':');
145 if ((tmp + i) - value < 2)
148 for (k = 0; k < (tmp + i) - value; k++) {
149 if (value[k] == '\r')
151 if (value[k] != ' ' && value[k] != '\t')
155 if ((tmp + i) - value < 1)
157 value = silc_memdup(value, (tmp + i) - value);
161 SILC_LOG_DEBUG(("Header '%s' '%s'", field, value));
163 /* Add field and value */
164 silc_mime_add_field(mime, field, value);
168 /* Mark start of next line */
169 line = (tmp + i) + 2;
172 /* Break if this is last header */
173 if (data_len - i >= 2 &&
174 tmp[i] == '\r' && tmp[i + 1] == '\n') {
181 /* Parse multiparts if present */
182 field = (char *)silc_mime_get_field(mime, "Content-Type");
183 if (field && strstr(field, "multipart")) {
187 mime->multiparts = silc_dlist_init();
188 if (!mime->multiparts)
192 value = strrchr(field, '=');
193 if (value && strlen(value) > 1) {
196 SILC_LOG_DEBUG(("Boundary '%s'", value));
198 memset(b, 0, sizeof(b));
199 line = strdup(value);
200 if (strrchr(line, '"')) {
201 *strrchr(line, '"') = '\0';
202 snprintf(b, sizeof(b) - 1, "--%s", line + 1);
203 mime->boundary = strdup(line + 1);
205 snprintf(b, sizeof(b) - 1, "--%s", line);
206 mime->boundary = strdup(line);
210 for (i = i; i < data_len; i++) {
211 /* Get boundary data */
212 if (data_len - i >= strlen(b) &&
213 tmp[i] == '-' && tmp[i + 1] == '-') {
214 if (memcmp(tmp + i, b, strlen(b)))
219 if (data_len - i >= 4 &&
220 tmp[i ] == '\r' && tmp[i + 1] == '\n' &&
221 tmp[i + 2] == '\r' && tmp[i + 3] == '\n')
223 else if (data_len - i >= 2 &&
224 tmp[i] == '\r' && tmp[i + 1] == '\n')
226 else if (data_len - i >= 2 &&
227 tmp[i] == '-' && tmp[i + 1] == '-')
232 /* Find end of boundary */
233 for (k = i; k < data_len; k++)
234 if (data_len - k >= strlen(b) &&
235 tmp[k] == '-' && tmp[k + 1] == '-')
236 if (!memcmp(tmp + k, b, strlen(b)))
241 /* Remove preceding CRLF */
245 p = silc_mime_decode(line, k - i);
249 silc_dlist_add(mime->multiparts, p);
258 SILC_LOG_DEBUG(("Data len %d", data_len - i));
259 silc_mime_add_data(mime, tmp + i, data_len - i);
265 silc_mime_free(mime);
269 unsigned char *silc_mime_encode(SilcMime mime, SilcUInt32 *encoded_len)
272 SilcHashTableList htl;
273 SilcBufferStruct buf;
275 char *field, *value, tmp[1024], tmp2[4];
279 SILC_LOG_DEBUG(("Encoding MIME message"));
284 memset(&buf, 0, sizeof(buf));
286 /* Encode the headers. Order doesn't matter */
288 silc_hash_table_list(mime->fields, &htl);
289 while (silc_hash_table_get(&htl, (void **)&field, (void **)&value)) {
290 memset(tmp, 0, sizeof(tmp));
291 SILC_LOG_DEBUG(("Header %s: %s", field, value));
292 snprintf(tmp, sizeof(tmp) - 1, "%s: %s\r\n", field, value);
293 silc_buffer_strformat(&buf, tmp, SILC_STRFMT_END);
296 silc_hash_table_list_reset(&htl);
298 silc_buffer_strformat(&buf, "\r\n", SILC_STRFMT_END);
300 /* Assemble the whole buffer */
301 buffer = silc_buffer_alloc_size(mime->data_len + buf.len);
307 silc_buffer_put(buffer, buf.head, buf.len);
308 silc_buffer_pull(buffer, buf.len);
313 SILC_LOG_DEBUG(("Data len %d", mime->data_len));
314 silc_buffer_put(buffer, mime->data, mime->data_len);
318 if (mime->multiparts) {
319 SILC_LOG_DEBUG(("Encoding multiparts"));
321 silc_dlist_start(mime->multiparts);
323 while ((part = silc_dlist_get(mime->multiparts)) != SILC_LIST_END) {
327 /* Recursive encoding */
328 pd = silc_mime_encode(part, &pd_len);
332 memset(tmp, 0, sizeof(tmp));
333 memset(tmp2, 0, sizeof(tmp2));
335 /* If fields are not present, add extra CRLF */
336 if (!silc_hash_table_count(part->fields))
337 snprintf(tmp2, sizeof(tmp2) - 1, "\r\n");
338 snprintf(tmp, sizeof(tmp) - 1, "--%s\r\n%s", mime->boundary, tmp2);
341 /* If fields are not present, add extra CRLF */
342 if (!silc_hash_table_count(part->fields))
343 snprintf(tmp2, sizeof(tmp2) - 1, "\r\n");
344 snprintf(tmp, sizeof(tmp) - 1, "\r\n--%s\r\n%s", mime->boundary, tmp2);
347 buffer = silc_buffer_realloc(buffer, buffer->truelen + pd_len +
351 silc_buffer_put_tail(buffer, tmp, strlen(tmp));
352 silc_buffer_pull_tail(buffer, strlen(tmp));
353 silc_buffer_put_tail(buffer, pd, pd_len);
354 silc_buffer_pull_tail(buffer, pd_len);
358 memset(tmp, 0, sizeof(tmp));
359 snprintf(tmp, sizeof(tmp) - 1, "\r\n--%s--\r\n", mime->boundary);
360 buffer = silc_buffer_realloc(buffer, buffer->truelen + strlen(tmp));
363 silc_buffer_put_tail(buffer, tmp, strlen(tmp));
364 silc_buffer_pull_tail(buffer, strlen(tmp));
367 ret = silc_buffer_steal(buffer, encoded_len);
368 silc_buffer_free(buffer);
373 static void silc_mime_assemble_dest(void *key, void *context,
376 silc_mime_free(context);
379 void silc_mime_assemble(SilcMimeAssembler assembler, SilcMime partial)
381 char *type, *id = NULL, *tmp;
383 SilcMime p, complete;
384 int i, number, total = -1;
385 const unsigned char *data;
387 SilcBuffer compbuf = NULL;
389 SILC_LOG_DEBUG(("Assembling MIME fragments"));
391 if (!assembler || !partial)
394 type = (char *)silc_mime_get_field(partial, "Content-Type");
399 tmp = strstr(type, "id=");
402 if (strlen(tmp) <= 4)
409 *strchr(id, ';') = '\0';
410 if (strrchr(id, '"'))
411 *strrchr(id, '"') = '\0';
413 SILC_LOG_DEBUG(("Fragment ID %s", id));
415 /* Get fragment number */
416 tmp = strstr(type, "number=");
419 tmp = strchr(tmp, '=');
423 if (strchr(tmp, ';')) {
425 *strchr(tmp, ';') = '\0';
432 SILC_LOG_DEBUG(("Fragment number %d", number));
434 /* Find fragments with this ID. */
435 if (!silc_hash_table_find(assembler->fragments, (void *)id,
436 NULL, (void **)&f)) {
437 /* This is new fragment to new message. Add to hash table and return. */
438 f = silc_hash_table_alloc(0, silc_hash_uint, NULL, NULL, NULL,
439 silc_mime_assemble_dest, NULL, TRUE);
442 silc_hash_table_add(f, SILC_32_TO_PTR(number), partial);
443 silc_hash_table_add(assembler->fragments, id, f);
447 /* Try to get total number */
448 tmp = strstr(type, "total=");
450 tmp = strchr(tmp, '=');
454 if (strchr(tmp, ';')) {
456 *strchr(tmp, ';') = '\0';
463 SILC_LOG_DEBUG(("Fragment total %d", total));
466 /* If more fragments to come, add to hash table */
467 if (number != total) {
468 silc_hash_table_add(f, SILC_32_TO_PTR(number), partial);
472 silc_hash_table_add(f, SILC_32_TO_PTR(number), partial);
474 /* Verify that we really have all the fragments */
475 if (silc_hash_table_count(f) < total)
478 /* Assemble the complete MIME message now. We get them in order from
480 for (i = 1; i <= total; i++) {
481 if (!silc_hash_table_find(f, SILC_32_TO_PTR(i), NULL, (void **)&p))
484 /* The fragment is in the data portion of the partial message */
485 data = silc_mime_get_data(p, &data_len);
491 compbuf = silc_buffer_alloc_size(data_len);
494 silc_buffer_put(compbuf, data, data_len);
496 compbuf = silc_buffer_realloc(compbuf, compbuf->truelen + data_len);
499 silc_buffer_put_tail(compbuf, data, data_len);
500 silc_buffer_pull_tail(compbuf, data_len);
504 /* Now parse the complete MIME message and deliver it */
505 complete = silc_mime_decode((const unsigned char *)compbuf->head,
510 if (assembler->complete)
511 assembler->complete(complete, assembler->complete_context);
513 /* Delete the hash table entry. Destructors will free memory */
514 silc_hash_table_del(assembler->fragments, (void *)id);
517 silc_buffer_free(compbuf);
523 silc_buffer_free(compbuf);
524 silc_mime_free(partial);
527 SilcDList silc_mime_encode_partial(SilcMime mime, int max_size)
529 unsigned char *buf, *tmp;
530 SilcUInt32 buf_len, len, tmp_len, off;
534 char type[128], id[64];
537 SILC_LOG_DEBUG(("Fragmenting MIME message"));
539 /* Encode as normal */
540 buf = silc_mime_encode(mime, &buf_len);
544 list = silc_dlist_init();
546 /* Fragment if it is too large */
547 if (buf_len > max_size) {
548 memset(id, 0, sizeof(id));
549 srand((time(NULL) + buf_len) ^ rand());
550 snprintf(id, sizeof(id) - 1, "%X%X%X",
551 (unsigned int)rand(), (unsigned int)time(NULL),
552 (unsigned int)buf_len);
554 SILC_LOG_DEBUG(("Fragment ID %s", id));
556 partial = silc_mime_alloc();
560 silc_mime_add_field(partial, "MIME-Version", "1.0");
561 memset(type, 0, sizeof(type));
562 snprintf(type, sizeof(type) - 1,
563 "message/partial; id=\"%s\"; number=1", id);
564 silc_mime_add_field(partial, "Content-Type", type);
565 silc_mime_add_data(partial, buf, max_size);
567 tmp = silc_mime_encode(partial, &tmp_len);
570 silc_mime_free(partial);
573 buffer = silc_buffer_alloc_size(tmp_len);
576 silc_buffer_put(buffer, tmp, tmp_len);
577 silc_dlist_add(list, buffer);
580 len = buf_len - max_size;
584 partial = silc_mime_alloc();
588 memset(type, 0, sizeof(type));
589 silc_mime_add_field(partial, "MIME-Version", "1.0");
591 if (len > max_size) {
592 snprintf(type, sizeof(type) - 1,
593 "message/partial; id=\"%s\"; number=%d",
595 silc_mime_add_data(partial, buf + off, max_size);
599 snprintf(type, sizeof(type) - 1,
600 "message/partial; id=\"%s\"; number=%d; total=%d",
602 silc_mime_add_data(partial, buf + off, len);
606 silc_mime_add_field(partial, "Content-Type", type);
608 tmp = silc_mime_encode(partial, &tmp_len);
611 silc_mime_free(partial);
614 buffer = silc_buffer_alloc_size(tmp_len);
617 silc_buffer_put(buffer, tmp, tmp_len);
618 silc_dlist_add(list, buffer);
622 /* No need to fragment */
623 buffer = silc_buffer_alloc_size(buf_len);
626 silc_buffer_put(buffer, buf, buf_len);
627 silc_dlist_add(list, buffer);
635 void silc_mime_partial_free(SilcDList partials)
642 silc_dlist_start(partials);
643 while ((buf = silc_dlist_get(partials)) != SILC_LIST_END)
644 silc_buffer_free(buf);
645 silc_dlist_uninit(partials);
648 void silc_mime_add_field(SilcMime mime, const char *field, const char *value)
650 if (!mime || !field || !value)
653 silc_hash_table_add(mime->fields, strdup(field), strdup(value));
656 const char *silc_mime_get_field(SilcMime mime, const char *field)
663 if (!silc_hash_table_find(mime->fields, (void *)field,
664 NULL, (void **)&value))
667 return (const char *)value;
670 void silc_mime_add_data(SilcMime mime, const unsigned char *data,
677 silc_free(mime->data);
679 mime->data = silc_memdup(data, data_len);
680 mime->data_len = data_len;
683 const unsigned char *silc_mime_get_data(SilcMime mime, SilcUInt32 *data_len)
689 *data_len = mime->data_len;
694 bool silc_mime_is_partial(SilcMime mime)
696 const char *type = silc_mime_get_field(mime, "Content-Type");
700 if (strstr(type, "message/partial"))
706 void silc_mime_set_multipart(SilcMime mime, const char *type,
707 const char *boundary)
711 if (!mime || !type || !boundary)
714 memset(tmp, 0, sizeof(tmp));
715 snprintf(tmp, sizeof(tmp) - 1, "multipart/%s; boundary=%s", type, boundary);
716 silc_mime_add_field(mime, "Content-Type", tmp);
717 silc_free(mime->boundary);
718 mime->boundary = strdup(boundary);
720 if (mime->multiparts)
722 mime->multiparts = silc_dlist_init();
725 bool silc_mime_add_multipart(SilcMime mime, SilcMime part)
727 if (!mime || !mime->multiparts || !part)
730 silc_dlist_add(mime->multiparts, part);
734 bool silc_mime_is_multipart(SilcMime mime)
739 return mime->multiparts != NULL;
742 SilcDList silc_mime_get_multiparts(SilcMime mime)
747 return mime->multiparts;