1 /*
2 
3   silcmime.c
4 
5   Author: Pekka Riikonen <priikone@silcnet.org>
6 
7   Copyright (C) 2005 - 2007 Pekka Riikonen
8 
9   The contents of this file are subject to one of the Licenses specified
10   in the COPYING file;  You may not use this file except in compliance
11   with the License.
12 
13   The software distributed under the License is distributed on an "AS IS"
14   basis, in the hope that it will be useful, but WITHOUT WARRANTY OF ANY
15   KIND, either expressed or implied.  See the COPYING file for more
16   information.
17 
18 */
19 
20 #include "silc.h"
21 
22 /************************ Static utility functions **************************/
23 
24 /* MIME fields destructor */
25 
silc_mime_field_dest(void * key,void * context,void * user_context)26 static void silc_mime_field_dest(void *key, void *context, void *user_context)
27 {
28   silc_free(key);
29   silc_free(context);
30 }
31 
32 /* Assembler fragment destructor */
33 
silc_mime_assembler_dest(void * key,void * context,void * user_context)34 static void silc_mime_assembler_dest(void *key, void *context,
35 				     void *user_context)
36 {
37   silc_free(key);
38   silc_hash_table_free(context);
39 }
40 
41 /* Assembler partial MIME destructor */
42 
silc_mime_assemble_dest(void * key,void * context,void * user_context)43 static void silc_mime_assemble_dest(void *key, void *context,
44 				    void *user_context)
45 {
46   silc_mime_free(context);
47 }
48 
49 
50 /******************************* Public API *********************************/
51 
52 /* Allocate MIME context */
53 
silc_mime_alloc(void)54 SilcMime silc_mime_alloc(void)
55 {
56   SilcMime mime;
57 
58   mime = silc_calloc(1, sizeof(*mime));
59   if (!mime)
60     return NULL;
61 
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);
65   if (!mime->fields) {
66     silc_mime_free(mime);
67     return NULL;
68   }
69 
70   return mime;
71 }
72 
73 /* Free MIME context */
74 
silc_mime_free(SilcMime mime)75 void silc_mime_free(SilcMime mime)
76 {
77   SilcMime m;
78 
79   if (mime->fields)
80     silc_hash_table_free(mime->fields);
81 
82   if (mime->multiparts) {
83     silc_dlist_start(mime->multiparts);
84     while ((m = silc_dlist_get(mime->multiparts)) != SILC_LIST_END)
85       silc_mime_free(m);
86     silc_dlist_uninit(mime->multiparts);
87   }
88   silc_free(mime->boundary);
89   silc_free(mime->multitype);
90   silc_free(mime->data);
91   silc_free(mime);
92 }
93 
94 /* Allocate MIME assembler */
95 
silc_mime_assembler_alloc(void)96 SilcMimeAssembler silc_mime_assembler_alloc(void)
97 {
98   SilcMimeAssembler assembler;
99 
100   assembler = silc_calloc(1, sizeof(*assembler));
101   if (!assembler)
102     return NULL;
103 
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);
110     return NULL;
111   }
112 
113   return assembler;
114 }
115 
116 /* Free MIME assembler */
117 
silc_mime_assembler_free(SilcMimeAssembler assembler)118 void silc_mime_assembler_free(SilcMimeAssembler assembler)
119 {
120   silc_hash_table_free(assembler->fragments);
121   silc_free(assembler);
122 }
123 
124 /* Decode MIME message */
125 
silc_mime_decode(SilcMime mime,const unsigned char * data,SilcUInt32 data_len)126 SilcMime silc_mime_decode(SilcMime mime, const unsigned char *data,
127 			  SilcUInt32 data_len)
128 {
129   SilcMime m = NULL;
130   int i, k;
131   char *tmp, *field, *value, *line;
132 
133   SILC_LOG_DEBUG(("Parsing MIME message"));
134 
135   if (!data)
136     return NULL;
137 
138   if (!mime) {
139     mime = silc_mime_alloc();
140     if (!mime)
141       return NULL;
142     m = mime;
143   }
144 
145   /* Parse the fields */
146   line = tmp = (char *)data;
147   for (i = 0; i < data_len; i++) {
148     /* Get field line */
149     if (data_len - i >= 2 && tmp[i] == '\r' && tmp[i + 1] == '\n') {
150       /* Get field */
151       field = strchr(line, ':');
152       if (!field)
153 	goto err;
154       field = silc_memdup(line, field - line);
155       if (!field)
156 	goto err;
157 
158       /* Get value. Remove whitespaces too. */
159       value = strchr(line, ':');
160       if ((tmp + i) - value < 2)
161 	goto err;
162       value++;
163       for (k = 0; k < (tmp + i) - value; k++) {
164 	if (value[k] == '\r')
165 	  goto err;
166 	if (value[k] != ' ' && value[k] != '\t')
167 	  break;
168       }
169       value += k;
170       if ((tmp + i) - value < 1)
171 	goto err;
172       value = silc_memdup(value, (tmp + i) - value);
173       if (!value)
174 	goto err;
175 
176       SILC_LOG_DEBUG(("Header '%s' '%s'", field, value));
177 
178       /* Add field and value */
179       silc_mime_add_field(mime, field, value);
180       silc_free(field);
181       silc_free(value);
182 
183       /* Mark start of next line */
184       line = (tmp + i) + 2;
185       i += 2;
186 
187       /* Break if this is last header */
188       if (data_len - i >= 2 &&
189 	  tmp[i] == '\r' && tmp[i + 1] == '\n') {
190 	i += 2;
191 	break;
192       }
193     }
194   }
195 
196   /* Parse multiparts if present */
197   field = (char *)silc_mime_get_field(mime, "Content-Type");
198   if (field && strstr(field, "multipart")) {
199     char b[1024];
200     SilcMime p;
201     unsigned int len;
202 
203     mime->multiparts = silc_dlist_init();
204     if (!mime->multiparts)
205       goto err;
206 
207     /* Get multipart type */
208     value = strchr(field, '/');
209     if (!value)
210       goto err;
211     value++;
212     if (strchr(field, '"'))
213       value++;
214     if (!strchr(field, ';'))
215       goto err;
216     memset(b, 0, sizeof(b));
217     len = (unsigned int)(strchr(field, ';') - value);
218     if (len > sizeof(b) - 1)
219       goto err;
220     strncpy(b, value, len);
221     if (strchr(b, '"'))
222       *strchr(b, '"') = '\0';
223     mime->multitype = silc_memdup(b, strlen(b));
224 
225     /* Get boundary */
226     value = strrchr(field, '=');
227     if (value && strlen(value) > 1) {
228       value++;
229 
230       SILC_LOG_DEBUG(("Boundary '%s'", value));
231 
232       memset(b, 0, sizeof(b));
233       line = strdup(value);
234       if (strrchr(line, '"')) {
235 	*strrchr(line, '"') = '\0';
236 	silc_snprintf(b, sizeof(b) - 1, "--%s", line + 1);
237 	mime->boundary = strdup(line + 1);
238       } else {
239 	silc_snprintf(b, sizeof(b) - 1, "--%s", line);
240 	mime->boundary = strdup(line);
241       }
242       silc_free(line);
243 
244       for ( ; i < data_len; i++) {
245 	/* Get boundary data */
246 	if (data_len - i >= strlen(b) &&
247 	    tmp[i] == '-' && tmp[i + 1] == '-') {
248 	  if (memcmp(tmp + i, b, strlen(b)))
249 	    continue;
250 
251 	  i += strlen(b);
252 
253 	  if (data_len - i >= 4 &&
254 	      tmp[i    ] == '\r' && tmp[i + 1] == '\n' &&
255 	      tmp[i + 2] == '\r' && tmp[i + 3] == '\n')
256 	    i += 4;
257 	  else if (data_len - i >= 2 &&
258 		   tmp[i] == '\r' && tmp[i + 1] == '\n')
259 	    i += 2;
260 	  else if (data_len - i >= 2 &&
261 		   tmp[i] == '-' && tmp[i + 1] == '-')
262 	    break;
263 
264 	  line = tmp + i;
265 
266 	  /* Find end of boundary */
267 	  for (k = i; k < data_len; k++)
268 	    if (data_len - k >= strlen(b) &&
269 		tmp[k] == '-' && tmp[k + 1] == '-')
270 	      if (!memcmp(tmp + k, b, strlen(b)))
271 		break;
272 	  if (k >= data_len)
273 	    goto err;
274 
275 	  /* Remove preceding CRLF */
276 	  k -= 2;
277 
278 	  /* Parse the part */
279 	  p = silc_mime_decode(NULL, line, k - i);
280 	  if (!p)
281 	    goto err;
282 
283 	  silc_dlist_add(mime->multiparts, p);
284 	  i += (k - i);
285 	}
286       }
287     }
288   } else {
289     /* Get data area.  If we are at the end and we have fields present
290        there is no data area present, but, if fields are not present we
291        only have data area. */
292     if (i >= data_len && !silc_hash_table_count(mime->fields))
293       i = 0;
294     SILC_LOG_DEBUG(("Data len %d", data_len - i));
295     if (data_len - i)
296       silc_mime_add_data(mime, tmp + i, data_len - i);
297   }
298 
299   return mime;
300 
301  err:
302   if (m)
303     silc_mime_free(m);
304   return NULL;
305 }
306 
307 /* Encode MIME message */
308 
silc_mime_encode(SilcMime mime,SilcUInt32 * encoded_len)309 unsigned char *silc_mime_encode(SilcMime mime, SilcUInt32 *encoded_len)
310 {
311   SilcMime part;
312   SilcHashTableList htl;
313   SilcBufferStruct buf;
314   SilcBuffer buffer;
315   char *field, *value, tmp[1024], tmp2[4];
316   unsigned char *ret;
317   int i;
318 
319   SILC_LOG_DEBUG(("Encoding MIME message"));
320 
321   if (!mime)
322     return NULL;
323 
324   memset(&buf, 0, sizeof(buf));
325 
326   /* Encode the headers. Order doesn't matter */
327   i = 0;
328   silc_hash_table_list(mime->fields, &htl);
329   while (silc_hash_table_get(&htl, (void *)&field, (void *)&value)) {
330     memset(tmp, 0, sizeof(tmp));
331     SILC_LOG_DEBUG(("Header %s: %s", field, value));
332     silc_snprintf(tmp, sizeof(tmp) - 1, "%s: %s\r\n", field, value);
333     silc_buffer_strformat(&buf, tmp, SILC_STRFMT_END);
334     i++;
335   }
336   silc_hash_table_list_reset(&htl);
337   if (i)
338     silc_buffer_strformat(&buf, "\r\n", SILC_STRFMT_END);
339 
340   /* Assemble the whole buffer */
341   buffer = silc_buffer_alloc_size(mime->data_len + silc_buffer_len(&buf));
342   if (!buffer)
343     return NULL;
344 
345   /* Add headers */
346   if (silc_buffer_len(&buf)) {
347     silc_buffer_put(buffer, buf.head, silc_buffer_len(&buf));
348     silc_buffer_pull(buffer, silc_buffer_len(&buf));
349     silc_buffer_purge(&buf);
350   }
351 
352   /* Add data */
353   if (mime->data) {
354     SILC_LOG_DEBUG(("Data len %d", mime->data_len));
355     silc_buffer_put(buffer, mime->data, mime->data_len);
356   }
357 
358   /* Add multiparts */
359   if (mime->multiparts) {
360     SILC_LOG_DEBUG(("Encoding multiparts"));
361 
362     silc_dlist_start(mime->multiparts);
363     i = 0;
364     while ((part = silc_dlist_get(mime->multiparts)) != SILC_LIST_END) {
365       unsigned char *pd;
366       SilcUInt32 pd_len;
367 
368       /* Recursive encoding */
369       pd = silc_mime_encode(part, &pd_len);
370       if (!pd)
371 	return NULL;
372 
373       memset(tmp, 0, sizeof(tmp));
374       memset(tmp2, 0, sizeof(tmp2));
375 
376       /* If fields are not present, add extra CRLF */
377       if (!silc_hash_table_count(part->fields))
378 	silc_snprintf(tmp2, sizeof(tmp2) - 1, "\r\n");
379       silc_snprintf(tmp, sizeof(tmp) - 1, "%s--%s\r\n%s",
380 	       i != 0 ? "\r\n" : "", mime->boundary, tmp2);
381       i = 1;
382 
383       buffer = silc_buffer_realloc(buffer, silc_buffer_truelen(buffer) +
384 				   pd_len + strlen(tmp));
385       if (!buffer)
386 	return NULL;
387       silc_buffer_put_tail(buffer, tmp, strlen(tmp));
388       silc_buffer_pull_tail(buffer, strlen(tmp));
389       silc_buffer_put_tail(buffer, pd, pd_len);
390       silc_buffer_pull_tail(buffer, pd_len);
391       silc_free(pd);
392     }
393 
394     memset(tmp, 0, sizeof(tmp));
395     silc_snprintf(tmp, sizeof(tmp) - 1, "\r\n--%s--\r\n", mime->boundary);
396     buffer = silc_buffer_realloc(buffer, silc_buffer_truelen(buffer) +
397 				 strlen(tmp));
398     if (!buffer)
399       return NULL;
400     silc_buffer_put_tail(buffer, tmp, strlen(tmp));
401     silc_buffer_pull_tail(buffer, strlen(tmp));
402   }
403 
404   ret = silc_buffer_steal(buffer, encoded_len);
405   silc_buffer_free(buffer);
406 
407   return ret;
408 }
409 
410 /* Assembles MIME message from partial MIME messages */
411 
silc_mime_assemble(SilcMimeAssembler assembler,SilcMime partial)412 SilcMime silc_mime_assemble(SilcMimeAssembler assembler, SilcMime partial)
413 {
414   char *type, *id = NULL, *tmp;
415   SilcHashTable f;
416   SilcMime p, complete;
417   int i, number, total = -1;
418   const unsigned char *data;
419   SilcUInt32 data_len;
420   SilcBuffer compbuf = NULL;
421 
422   SILC_LOG_DEBUG(("Assembling MIME fragments"));
423 
424   if (!assembler || !partial)
425     goto err;
426 
427   type = (char *)silc_mime_get_field(partial, "Content-Type");
428   if (!type)
429     goto err;
430 
431   /* Get ID */
432   tmp = strstr(type, "id=");
433   if (!tmp)
434     goto err;
435   if (strlen(tmp) <= 4)
436     goto err;
437   tmp += 3;
438   if (*tmp == '"')
439     tmp++;
440   id = strdup(tmp);
441   if (strchr(id, ';'))
442     *strchr(id, ';') = '\0';
443   if (strrchr(id, '"'))
444     *strrchr(id, '"') = '\0';
445 
446   SILC_LOG_DEBUG(("Fragment ID %s", id));
447 
448   /* Get fragment number */
449   tmp = strstr(type, "number=");
450   if (!tmp)
451     goto err;
452   tmp = strchr(tmp, '=');
453   if (strlen(tmp) < 2)
454     goto err;
455   tmp++;
456   if (strchr(tmp, ';')) {
457     tmp = strdup(tmp);
458     *strchr(tmp, ';') = '\0';
459     number = atoi(tmp);
460     silc_free(tmp);
461   } else {
462     number = atoi(tmp);
463   }
464 
465   SILC_LOG_DEBUG(("Fragment number %d", number));
466 
467   /* Find fragments with this ID. */
468   if (!silc_hash_table_find(assembler->fragments, (void *)id,
469 			    NULL, (void *)&f)) {
470     /* This is new fragment to new message.  Add to hash table and return. */
471     f = silc_hash_table_alloc(0, silc_hash_uint, NULL, NULL, NULL,
472 			      silc_mime_assemble_dest, NULL, TRUE);
473     if (!f)
474 	 goto err;
475     silc_hash_table_add(f, SILC_32_TO_PTR(number), partial);
476     silc_hash_table_add(assembler->fragments, id, f);
477     return NULL;
478   }
479 
480   /* Try to get total number */
481   tmp = strstr(type, "total=");
482   if (tmp) {
483     tmp = strchr(tmp, '=');
484     if (strlen(tmp) < 2)
485       goto err;
486     tmp++;
487     if (strchr(tmp, ';')) {
488       tmp = strdup(tmp);
489       *strchr(tmp, ';') = '\0';
490       total = atoi(tmp);
491       silc_free(tmp);
492     } else {
493       total = atoi(tmp);
494     }
495 
496     SILC_LOG_DEBUG(("Fragment total %d", total));
497   }
498 
499   /* If more fragments to come, add to hash table */
500   if (number != total) {
501     silc_hash_table_add(f, SILC_32_TO_PTR(number), partial);
502     return NULL;
503   }
504 
505   silc_hash_table_add(f, SILC_32_TO_PTR(number), partial);
506 
507   /* Verify that we really have all the fragments */
508   if (silc_hash_table_count(f) < total)
509     return NULL;
510 
511   /* Assemble the complete MIME message now. We get them in order from
512      the hash table. */
513   for (i = 1; i <= total; i++) {
514     if (!silc_hash_table_find(f, SILC_32_TO_PTR(i), NULL, (void *)&p))
515       goto err;
516 
517     /* The fragment is in the data portion of the partial message */
518     data = silc_mime_get_data(p, &data_len);
519     if (!data)
520       goto err;
521 
522     /* Assemble */
523     if (!compbuf) {
524       compbuf = silc_buffer_alloc_size(data_len);
525       if (!compbuf)
526 	goto err;
527       silc_buffer_put(compbuf, data, data_len);
528     } else {
529       compbuf = silc_buffer_realloc(compbuf, silc_buffer_truelen(compbuf) +
530 				    data_len);
531       if (!compbuf)
532 	goto err;
533       silc_buffer_put_tail(compbuf, data, data_len);
534       silc_buffer_pull_tail(compbuf, data_len);
535     }
536   }
537 
538   if (!compbuf)
539     goto err;
540 
541   /* Now parse the complete MIME message and deliver it */
542   complete = silc_mime_decode(NULL, (const unsigned char *)compbuf->head,
543 			      silc_buffer_truelen(compbuf));
544   if (!complete)
545     goto err;
546 
547   /* Delete the hash table entry. Destructors will free memory */
548   silc_hash_table_del(assembler->fragments, (void *)id);
549   silc_free(id);
550   silc_buffer_free(compbuf);
551 
552   return complete;
553 
554  err:
555   silc_free(id);
556   if (compbuf)
557     silc_buffer_free(compbuf);
558   silc_mime_free(partial);
559   return NULL;
560 }
561 
562 /* Encodes partial MIME messages */
563 
silc_mime_encode_partial(SilcMime mime,int max_size)564 SilcDList silc_mime_encode_partial(SilcMime mime, int max_size)
565 {
566   unsigned char *buf, *tmp;
567   SilcUInt32 buf_len, len, tmp_len, off;
568   SilcDList list;
569   SilcBuffer buffer;
570   SilcMime partial;
571   char type[128], id[64];
572   int num;
573 
574   SILC_LOG_DEBUG(("Fragmenting MIME message"));
575 
576   /* Encode as normal */
577   buf = silc_mime_encode(mime, &buf_len);
578   if (!buf)
579     return NULL;
580 
581   list = silc_dlist_init();
582 
583   /* Fragment if it is too large */
584   if (buf_len > max_size) {
585     memset(id, 0, sizeof(id));
586     memset(type, 0, sizeof(type));
587     gethostname(type, sizeof(type) - 1);
588     srand((time(NULL) + buf_len) ^ rand());
589     silc_snprintf(id, sizeof(id) - 1, "%X%X%X%s",
590 	     (unsigned int)rand(), (unsigned int)time(NULL),
591 	     (unsigned int)buf_len, type);
592 
593     SILC_LOG_DEBUG(("Fragment ID %s", id));
594 
595     partial = silc_mime_alloc();
596     if (!partial)
597       return NULL;
598 
599     silc_mime_add_field(partial, "MIME-Version", "1.0");
600     memset(type, 0, sizeof(type));
601     silc_snprintf(type, sizeof(type) - 1,
602 	     "message/partial; id=\"%s\"; number=1", id);
603     silc_mime_add_field(partial, "Content-Type", type);
604     silc_mime_add_data(partial, buf, max_size);
605 
606     tmp = silc_mime_encode(partial, &tmp_len);
607     if (!tmp)
608       return NULL;
609     silc_mime_free(partial);
610 
611     /* Add to list */
612     buffer = silc_buffer_alloc_size(tmp_len);
613     if (!buffer)
614       return NULL;
615     silc_buffer_put(buffer, tmp, tmp_len);
616     silc_dlist_add(list, buffer);
617     silc_free(tmp);
618 
619     len = buf_len - max_size;
620     off = max_size;
621     num = 2;
622     while (len > 0) {
623       partial = silc_mime_alloc();
624       if (!partial)
625 	return NULL;
626 
627       memset(type, 0, sizeof(type));
628       silc_mime_add_field(partial, "MIME-Version", "1.0");
629 
630       if (len > max_size) {
631 	silc_snprintf(type, sizeof(type) - 1,
632 		 "message/partial; id=\"%s\"; number=%d",
633 		 id, num++);
634 	silc_mime_add_data(partial, buf + off, max_size);
635 	off += max_size;
636 	len -= max_size;
637       } else {
638 	silc_snprintf(type, sizeof(type) - 1,
639 		 "message/partial; id=\"%s\"; number=%d; total=%d",
640 		 id, num, num);
641 	silc_mime_add_data(partial, buf + off, len);
642 	len = 0;
643       }
644 
645       silc_mime_add_field(partial, "Content-Type", type);
646 
647       tmp = silc_mime_encode(partial, &tmp_len);
648       if (!tmp)
649 	return NULL;
650       silc_mime_free(partial);
651 
652       /* Add to list */
653       buffer = silc_buffer_alloc_size(tmp_len);
654       if (!buffer)
655 	return NULL;
656       silc_buffer_put(buffer, tmp, tmp_len);
657       silc_dlist_add(list, buffer);
658       silc_free(tmp);
659     }
660   } else {
661     /* No need to fragment */
662     buffer = silc_buffer_alloc_size(buf_len);
663     if (!buffer)
664       return NULL;
665     silc_buffer_put(buffer, buf, buf_len);
666     silc_dlist_add(list, buffer);
667   }
668 
669   silc_free(buf);
670 
671   return list;
672 }
673 
674 /* Free partial MIME list */
675 
silc_mime_partial_free(SilcDList partials)676 void silc_mime_partial_free(SilcDList partials)
677 {
678   SilcBuffer buf;
679 
680   if (!partials)
681     return;
682 
683   silc_dlist_start(partials);
684   while ((buf = silc_dlist_get(partials)) != SILC_LIST_END)
685     silc_buffer_free(buf);
686   silc_dlist_uninit(partials);
687 }
688 
689 /* Add field */
690 
silc_mime_add_field(SilcMime mime,const char * field,const char * value)691 void silc_mime_add_field(SilcMime mime, const char *field, const char *value)
692 {
693   if (!mime || !field || !value)
694     return;
695 
696   silc_hash_table_add(mime->fields, strdup(field), strdup(value));
697 }
698 
699 /* Get field */
700 
silc_mime_get_field(SilcMime mime,const char * field)701 const char *silc_mime_get_field(SilcMime mime, const char *field)
702 {
703   char *value;
704 
705   if (!mime || !field)
706     return NULL;
707 
708   if (!silc_hash_table_find(mime->fields, (void *)field,
709 			    NULL, (void *)&value))
710     return NULL;
711 
712   return (const char *)value;
713 }
714 
715 /* Add data */
716 
silc_mime_add_data(SilcMime mime,const unsigned char * data,SilcUInt32 data_len)717 void silc_mime_add_data(SilcMime mime, const unsigned char *data,
718 			SilcUInt32 data_len)
719 {
720   if (!mime || !data)
721     return;
722 
723   if (mime->data)
724     silc_free(mime->data);
725 
726   mime->data = silc_memdup(data, data_len);
727   mime->data_len = data_len;
728 }
729 
730 /* Get data */
731 
silc_mime_get_data(SilcMime mime,SilcUInt32 * data_len)732 const unsigned char *silc_mime_get_data(SilcMime mime, SilcUInt32 *data_len)
733 {
734   if (!mime)
735     return NULL;
736 
737   if (data_len)
738     *data_len = mime->data_len;
739 
740   return mime->data;
741 }
742 
743 /* Steal data */
744 
silc_mime_steal_data(SilcMime mime,SilcUInt32 * data_len)745 unsigned char *silc_mime_steal_data(SilcMime mime, SilcUInt32 *data_len)
746 {
747   unsigned char *data;
748 
749   if (!mime)
750     return NULL;
751 
752   if (data_len)
753     *data_len = mime->data_len;
754 
755   data = mime->data;
756 
757   mime->data = NULL;
758   mime->data_len = 0;
759 
760   return data;
761 }
762 
763 /* Returns TRUE if partial message */
764 
silc_mime_is_partial(SilcMime mime)765 SilcBool silc_mime_is_partial(SilcMime mime)
766 {
767   const char *type = silc_mime_get_field(mime, "Content-Type");
768   if (!type)
769     return FALSE;
770 
771   if (!strstr(type, "message/partial"))
772     return FALSE;
773 
774   return TRUE;
775 }
776 
777 /* Set as multipart message */
778 
silc_mime_set_multipart(SilcMime mime,const char * type,const char * boundary)779 void silc_mime_set_multipart(SilcMime mime, const char *type,
780 			     const char *boundary)
781 {
782   char tmp[1024];
783 
784   if (!mime || !type || !boundary)
785     return;
786 
787   memset(tmp, 0, sizeof(tmp));
788   silc_snprintf(tmp, sizeof(tmp) - 1, "multipart/%s; boundary=%s", type, boundary);
789   silc_mime_add_field(mime, "Content-Type", tmp);
790   silc_free(mime->boundary);
791   mime->boundary = strdup(boundary);
792 
793   if (mime->multiparts)
794     return;
795   mime->multiparts = silc_dlist_init();
796 }
797 
798 /* Add multipart */
799 
silc_mime_add_multipart(SilcMime mime,SilcMime part)800 SilcBool silc_mime_add_multipart(SilcMime mime, SilcMime part)
801 {
802   if (!mime || !mime->multiparts || !part)
803     return FALSE;
804 
805   silc_dlist_add(mime->multiparts, part);
806   return TRUE;
807 }
808 
809 /* Return TRUE if has multiparts */
810 
silc_mime_is_multipart(SilcMime mime)811 SilcBool silc_mime_is_multipart(SilcMime mime)
812 {
813   if (!mime)
814     return FALSE;
815 
816   return mime->multiparts != NULL;
817 }
818 
819 /* Returns multiparts */
820 
silc_mime_get_multiparts(SilcMime mime,const char ** type)821 SilcDList silc_mime_get_multiparts(SilcMime mime, const char **type)
822 {
823   if (!mime)
824     return NULL;
825 
826   if (type)
827     *type = (const char *)mime->multitype;
828 
829   return mime->multiparts;
830 }
831