5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2005 - 2006 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.
22 /************************ Static utility functions **************************/
24 /* MIME fields destructor */
26 static void silc_mime_field_dest(void *key, void *context, void *user_context)
32 /* Assembler fragment destructor */
34 static void silc_mime_assembler_dest(void *key, void *context,
38 silc_hash_table_free(context);
41 /* Assembler partial MIME destructor */
43 static void silc_mime_assemble_dest(void *key, void *context,
46 silc_mime_free(context);
50 /******************************* Public API *********************************/
52 /* Allocate MIME context */
54 SilcMime silc_mime_alloc(void)
58 mime = silc_calloc(1, sizeof(*mime));
62 mime->fields = silc_hash_table_alloc(0, silc_hash_string, mime,
63 silc_hash_string_compare, mime,
64 silc_mime_field_dest, mime, TRUE);
73 /* Free MIME context */
75 void silc_mime_free(SilcMime mime)
80 silc_hash_table_free(mime->fields);
82 if (mime->multiparts) {
83 silc_dlist_start(mime->multiparts);
84 while ((m = silc_dlist_get(mime->multiparts)) != SILC_LIST_END)
86 silc_dlist_uninit(mime->multiparts);
88 silc_free(mime->boundary);
89 silc_free(mime->multitype);
90 silc_free(mime->data);
94 /* Allocate MIME assembler */
96 SilcMimeAssembler silc_mime_assembler_alloc(void)
98 SilcMimeAssembler assembler;
100 assembler = silc_calloc(1, sizeof(*assembler));
104 assembler->fragments =
105 silc_hash_table_alloc(0, silc_hash_string, NULL,
106 silc_hash_string_compare, NULL,
107 silc_mime_assembler_dest, assembler, TRUE);
108 if (!assembler->fragments) {
109 silc_mime_assembler_free(assembler);
116 /* Free MIME assembler */
118 void silc_mime_assembler_free(SilcMimeAssembler assembler)
120 silc_hash_table_free(assembler->fragments);
121 silc_free(assembler);
124 /* Decode MIME message */
126 SilcMime silc_mime_decode(SilcMime mime, const unsigned char *data,
131 char *tmp, *field, *value, *line;
133 SILC_LOG_DEBUG(("Parsing MIME message"));
139 mime = silc_mime_alloc();
145 /* Parse the fields */
146 line = tmp = (char *)data;
147 for (i = 0; i < data_len; i++) {
149 if (data_len - i >= 2 && tmp[i] == '\r' && tmp[i + 1] == '\n') {
151 field = strchr(line, ':');
154 field = silc_memdup(line, field - line);
158 /* Get value. Remove whitespaces too. */
159 value = strchr(line, ':');
160 if ((tmp + i) - value < 2)
163 for (k = 0; k < (tmp + i) - value; k++) {
164 if (value[k] == '\r')
166 if (value[k] != ' ' && value[k] != '\t')
170 if ((tmp + i) - value < 1)
172 value = silc_memdup(value, (tmp + i) - value);
176 SILC_LOG_DEBUG(("Header '%s' '%s'", field, value));
178 /* Add field and value */
179 silc_mime_add_field(mime, field, value);
183 /* Mark start of next line */
184 line = (tmp + i) + 2;
187 /* Break if this is last header */
188 if (data_len - i >= 2 &&
189 tmp[i] == '\r' && tmp[i + 1] == '\n') {
196 /* Parse multiparts if present */
197 field = (char *)silc_mime_get_field(mime, "Content-Type");
198 if (field && strstr(field, "multipart")) {
202 mime->multiparts = silc_dlist_init();
203 if (!mime->multiparts)
206 /* Get multipart type */
207 value = strchr(field, '/');
211 if (strchr(field, '"'))
213 if (!strchr(field, ';'))
215 memset(b, 0, sizeof(b));
216 strncat(b, value, strchr(field, ';') - value);
218 *strchr(b, '"') = '\0';
219 mime->multitype = silc_memdup(b, strlen(b));
222 value = strrchr(field, '=');
223 if (value && strlen(value) > 1) {
226 SILC_LOG_DEBUG(("Boundary '%s'", value));
228 memset(b, 0, sizeof(b));
229 line = strdup(value);
230 if (strrchr(line, '"')) {
231 *strrchr(line, '"') = '\0';
232 snprintf(b, sizeof(b) - 1, "--%s", line + 1);
233 mime->boundary = strdup(line + 1);
235 snprintf(b, sizeof(b) - 1, "--%s", line);
236 mime->boundary = strdup(line);
240 for (i = i; i < data_len; i++) {
241 /* Get boundary data */
242 if (data_len - i >= strlen(b) &&
243 tmp[i] == '-' && tmp[i + 1] == '-') {
244 if (memcmp(tmp + i, b, strlen(b)))
249 if (data_len - i >= 4 &&
250 tmp[i ] == '\r' && tmp[i + 1] == '\n' &&
251 tmp[i + 2] == '\r' && tmp[i + 3] == '\n')
253 else if (data_len - i >= 2 &&
254 tmp[i] == '\r' && tmp[i + 1] == '\n')
256 else if (data_len - i >= 2 &&
257 tmp[i] == '-' && tmp[i + 1] == '-')
262 /* Find end of boundary */
263 for (k = i; k < data_len; k++)
264 if (data_len - k >= strlen(b) &&
265 tmp[k] == '-' && tmp[k + 1] == '-')
266 if (!memcmp(tmp + k, b, strlen(b)))
271 /* Remove preceding CRLF */
275 p = silc_mime_decode(NULL, line, k - i);
279 silc_dlist_add(mime->multiparts, p);
288 SILC_LOG_DEBUG(("Data len %d", data_len - i));
290 silc_mime_add_data(mime, tmp + i, data_len - i);
301 /* Encode MIME message */
303 unsigned char *silc_mime_encode(SilcMime mime, SilcUInt32 *encoded_len)
306 SilcHashTableList htl;
307 SilcBufferStruct buf;
309 char *field, *value, tmp[1024], tmp2[4];
313 SILC_LOG_DEBUG(("Encoding MIME message"));
318 memset(&buf, 0, sizeof(buf));
320 /* Encode the headers. Order doesn't matter */
322 silc_hash_table_list(mime->fields, &htl);
323 while (silc_hash_table_get(&htl, (void **)&field, (void **)&value)) {
324 memset(tmp, 0, sizeof(tmp));
325 SILC_LOG_DEBUG(("Header %s: %s", field, value));
326 snprintf(tmp, sizeof(tmp) - 1, "%s: %s\r\n", field, value);
327 silc_buffer_strformat(&buf, tmp, SILC_STRFMT_END);
330 silc_hash_table_list_reset(&htl);
332 silc_buffer_strformat(&buf, "\r\n", SILC_STRFMT_END);
334 /* Assemble the whole buffer */
335 buffer = silc_buffer_alloc_size(mime->data_len + silc_buffer_len(&buf));
340 if (silc_buffer_len(&buf)) {
341 silc_buffer_put(buffer, buf.head, silc_buffer_len(&buf));
342 silc_buffer_pull(buffer, silc_buffer_len(&buf));
347 SILC_LOG_DEBUG(("Data len %d", mime->data_len));
348 silc_buffer_put(buffer, mime->data, mime->data_len);
352 if (mime->multiparts) {
353 SILC_LOG_DEBUG(("Encoding multiparts"));
355 silc_dlist_start(mime->multiparts);
357 while ((part = silc_dlist_get(mime->multiparts)) != SILC_LIST_END) {
361 /* Recursive encoding */
362 pd = silc_mime_encode(part, &pd_len);
366 memset(tmp, 0, sizeof(tmp));
367 memset(tmp2, 0, sizeof(tmp2));
369 /* If fields are not present, add extra CRLF */
370 if (!silc_hash_table_count(part->fields))
371 snprintf(tmp2, sizeof(tmp2) - 1, "\r\n");
372 snprintf(tmp, sizeof(tmp) - 1, "%s--%s\r\n%s",
373 i != 0 ? "\r\n" : "", mime->boundary, tmp2);
376 buffer = silc_buffer_realloc(buffer, silc_buffer_truelen(buffer) +
377 pd_len + strlen(tmp));
380 silc_buffer_put_tail(buffer, tmp, strlen(tmp));
381 silc_buffer_pull_tail(buffer, strlen(tmp));
382 silc_buffer_put_tail(buffer, pd, pd_len);
383 silc_buffer_pull_tail(buffer, pd_len);
387 memset(tmp, 0, sizeof(tmp));
388 snprintf(tmp, sizeof(tmp) - 1, "\r\n--%s--\r\n", mime->boundary);
389 buffer = silc_buffer_realloc(buffer, silc_buffer_truelen(buffer) +
393 silc_buffer_put_tail(buffer, tmp, strlen(tmp));
394 silc_buffer_pull_tail(buffer, strlen(tmp));
397 ret = silc_buffer_steal(buffer, encoded_len);
398 silc_buffer_free(buffer);
403 /* Assembles MIME message from partial MIME messages */
405 SilcMime silc_mime_assemble(SilcMimeAssembler assembler, SilcMime partial)
407 char *type, *id = NULL, *tmp;
409 SilcMime p, complete;
410 int i, number, total = -1;
411 const unsigned char *data;
413 SilcBuffer compbuf = NULL;
415 SILC_LOG_DEBUG(("Assembling MIME fragments"));
417 if (!assembler || !partial)
420 type = (char *)silc_mime_get_field(partial, "Content-Type");
425 tmp = strstr(type, "id=");
428 if (strlen(tmp) <= 4)
435 *strchr(id, ';') = '\0';
436 if (strrchr(id, '"'))
437 *strrchr(id, '"') = '\0';
439 SILC_LOG_DEBUG(("Fragment ID %s", id));
441 /* Get fragment number */
442 tmp = strstr(type, "number=");
445 tmp = strchr(tmp, '=');
449 if (strchr(tmp, ';')) {
451 *strchr(tmp, ';') = '\0';
458 SILC_LOG_DEBUG(("Fragment number %d", number));
460 /* Find fragments with this ID. */
461 if (!silc_hash_table_find(assembler->fragments, (void *)id,
462 NULL, (void **)&f)) {
463 /* This is new fragment to new message. Add to hash table and return. */
464 f = silc_hash_table_alloc(0, silc_hash_uint, NULL, NULL, NULL,
465 silc_mime_assemble_dest, NULL, TRUE);
468 silc_hash_table_add(f, SILC_32_TO_PTR(number), partial);
469 silc_hash_table_add(assembler->fragments, id, f);
473 /* Try to get total number */
474 tmp = strstr(type, "total=");
476 tmp = strchr(tmp, '=');
480 if (strchr(tmp, ';')) {
482 *strchr(tmp, ';') = '\0';
489 SILC_LOG_DEBUG(("Fragment total %d", total));
492 /* If more fragments to come, add to hash table */
493 if (number != total) {
494 silc_hash_table_add(f, SILC_32_TO_PTR(number), partial);
498 silc_hash_table_add(f, SILC_32_TO_PTR(number), partial);
500 /* Verify that we really have all the fragments */
501 if (silc_hash_table_count(f) < total)
504 /* Assemble the complete MIME message now. We get them in order from
506 for (i = 1; i <= total; i++) {
507 if (!silc_hash_table_find(f, SILC_32_TO_PTR(i), NULL, (void **)&p))
510 /* The fragment is in the data portion of the partial message */
511 data = silc_mime_get_data(p, &data_len);
517 compbuf = silc_buffer_alloc_size(data_len);
520 silc_buffer_put(compbuf, data, data_len);
522 compbuf = silc_buffer_realloc(compbuf, silc_buffer_truelen(compbuf) +
526 silc_buffer_put_tail(compbuf, data, data_len);
527 silc_buffer_pull_tail(compbuf, data_len);
531 /* Now parse the complete MIME message and deliver it */
532 complete = silc_mime_decode(NULL, (const unsigned char *)compbuf->head,
533 silc_buffer_truelen(compbuf));
537 /* Delete the hash table entry. Destructors will free memory */
538 silc_hash_table_del(assembler->fragments, (void *)id);
540 silc_buffer_free(compbuf);
547 silc_buffer_free(compbuf);
548 silc_mime_free(partial);
552 /* Encodes partial MIME messages */
554 SilcDList silc_mime_encode_partial(SilcMime mime, int max_size)
556 unsigned char *buf, *tmp;
557 SilcUInt32 buf_len, len, tmp_len, off;
561 char type[128], id[64];
564 SILC_LOG_DEBUG(("Fragmenting MIME message"));
566 /* Encode as normal */
567 buf = silc_mime_encode(mime, &buf_len);
571 list = silc_dlist_init();
573 /* Fragment if it is too large */
574 if (buf_len > max_size) {
575 memset(id, 0, sizeof(id));
576 memset(type, 0, sizeof(type));
577 gethostname(type, sizeof(type) - 1);
578 srand((time(NULL) + buf_len) ^ rand());
579 snprintf(id, sizeof(id) - 1, "%X%X%X%s",
580 (unsigned int)rand(), (unsigned int)time(NULL),
581 (unsigned int)buf_len, type);
583 SILC_LOG_DEBUG(("Fragment ID %s", id));
585 partial = silc_mime_alloc();
589 silc_mime_add_field(partial, "MIME-Version", "1.0");
590 memset(type, 0, sizeof(type));
591 snprintf(type, sizeof(type) - 1,
592 "message/partial; id=\"%s\"; number=1", id);
593 silc_mime_add_field(partial, "Content-Type", type);
594 silc_mime_add_data(partial, buf, max_size);
596 tmp = silc_mime_encode(partial, &tmp_len);
599 silc_mime_free(partial);
602 buffer = silc_buffer_alloc_size(tmp_len);
605 silc_buffer_put(buffer, tmp, tmp_len);
606 silc_dlist_add(list, buffer);
609 len = buf_len - max_size;
613 partial = silc_mime_alloc();
617 memset(type, 0, sizeof(type));
618 silc_mime_add_field(partial, "MIME-Version", "1.0");
620 if (len > max_size) {
621 snprintf(type, sizeof(type) - 1,
622 "message/partial; id=\"%s\"; number=%d",
624 silc_mime_add_data(partial, buf + off, max_size);
628 snprintf(type, sizeof(type) - 1,
629 "message/partial; id=\"%s\"; number=%d; total=%d",
631 silc_mime_add_data(partial, buf + off, len);
635 silc_mime_add_field(partial, "Content-Type", type);
637 tmp = silc_mime_encode(partial, &tmp_len);
640 silc_mime_free(partial);
643 buffer = silc_buffer_alloc_size(tmp_len);
646 silc_buffer_put(buffer, tmp, tmp_len);
647 silc_dlist_add(list, buffer);
651 /* No need to fragment */
652 buffer = silc_buffer_alloc_size(buf_len);
655 silc_buffer_put(buffer, buf, buf_len);
656 silc_dlist_add(list, buffer);
664 /* Free partial MIME list */
666 void silc_mime_partial_free(SilcDList partials)
673 silc_dlist_start(partials);
674 while ((buf = silc_dlist_get(partials)) != SILC_LIST_END)
675 silc_buffer_free(buf);
676 silc_dlist_uninit(partials);
681 void silc_mime_add_field(SilcMime mime, const char *field, const char *value)
683 if (!mime || !field || !value)
686 silc_hash_table_add(mime->fields, strdup(field), strdup(value));
691 const char *silc_mime_get_field(SilcMime mime, const char *field)
698 if (!silc_hash_table_find(mime->fields, (void *)field,
699 NULL, (void **)&value))
702 return (const char *)value;
707 void silc_mime_add_data(SilcMime mime, const unsigned char *data,
714 silc_free(mime->data);
716 mime->data = silc_memdup(data, data_len);
717 mime->data_len = data_len;
722 const unsigned char *silc_mime_get_data(SilcMime mime, SilcUInt32 *data_len)
728 *data_len = mime->data_len;
735 unsigned char *silc_mime_steal_data(SilcMime mime, SilcUInt32 *data_len)
743 *data_len = mime->data_len;
753 /* Returns TRUE if partial message */
755 SilcBool silc_mime_is_partial(SilcMime mime)
757 const char *type = silc_mime_get_field(mime, "Content-Type");
761 if (!strstr(type, "message/partial"))
767 /* Set as multipart message */
769 void silc_mime_set_multipart(SilcMime mime, const char *type,
770 const char *boundary)
774 if (!mime || !type || !boundary)
777 memset(tmp, 0, sizeof(tmp));
778 snprintf(tmp, sizeof(tmp) - 1, "multipart/%s; boundary=%s", type, boundary);
779 silc_mime_add_field(mime, "Content-Type", tmp);
780 silc_free(mime->boundary);
781 mime->boundary = strdup(boundary);
783 if (mime->multiparts)
785 mime->multiparts = silc_dlist_init();
790 SilcBool silc_mime_add_multipart(SilcMime mime, SilcMime part)
792 if (!mime || !mime->multiparts || !part)
795 silc_dlist_add(mime->multiparts, part);
799 /* Return TRUE if has multiparts */
801 SilcBool silc_mime_is_multipart(SilcMime mime)
806 return mime->multiparts != NULL;
809 /* Returns multiparts */
811 SilcDList silc_mime_get_multiparts(SilcMime mime, const char **type)
817 *type = (const char *)mime->multitype;
819 return mime->multiparts;