From Jakub Zawadzki (bug 3331):
[metze/wireshark/wip.git] / epan / dissectors / packet-multipart.c
1 /* packet-multipart.c
2  * Routines for multipart media encapsulation dissection
3  * Copyright 2004, Anders Broman.
4  * Copyright 2004, Olivier Biot.
5  *
6  * $Id$
7  *
8  * Refer to the AUTHORS file or the AUTHORS section in the man page
9  * for contacting the author(s) of this file.
10  *
11  * Wireshark - Network traffic analyzer
12  * By Gerald Combs <gerald@wireshark.org>
13  * Copyright 1998 Gerald Combs
14  *
15  *
16  * This program is free software; you can redistribute it and/or
17  * modify it under the terms of the GNU General Public License
18  * as published by the Free Software Foundation; either version 2
19  * of the License, or (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, write to the Free Software
28  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29  *
30  * References for "media-type multipart/mixed :
31  * http://www.iana.org/assignments/media-types/index.html
32  * http://www.ietf.org/rfc/rfc2045.txt?number=2045
33  * http://www.rfc-editor.org/rfc/rfc2046.txt
34  * http://www.rfc-editor.org/rfc/rfc2047.txt
35  * http://www.rfc-editor.org/rfc/rfc2048.txt
36  * http://www.rfc-editor.org/rfc/rfc2049.txt
37  *
38  * Part of the code is modeled from the SIP and HTTP dissectors
39  *
40  * General format of a MIME multipart document:
41  *              [ preamble line-end ]
42  *              dash-boundary transport-padding line-end
43  *              body-part
44  *              *encapsulation
45  *              close-delimiter transport-padding
46  *              [ line-end epilogue ]
47  *
48  * Where:
49  *              dash-boundary     := "--" boundary
50  *              encapsulation     := delimiter transport-padding line-end body-part
51  *              delimiter         := line-end body-part
52  *              close-delimiter   := delimiter "--"
53  *              body-part         := MIME-part-headers [ line-end *OCTET ]
54  *              transport-padding := *LWSP-char
55  * 
56  * Note that line-end is often a LF instead of a CRLF.
57 */
58
59 #ifdef HAVE_CONFIG_H
60 #include "config.h"
61 #endif
62
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <epan/prefs.h>
67 #include <glib.h>
68 #include <ctype.h>
69 #include <epan/base64.h>
70 #include <epan/emem.h>
71
72 #include <epan/packet.h>
73
74 #include "packet-imf.h"
75
76 /* Dissector table for media requiring special attention in multipart
77  * encapsulation. */
78 static dissector_table_t multipart_media_subdissector_table;
79
80 /* Initialize the protocol and registered fields */
81 static int proto_multipart = -1;
82
83 /* Initialize the subtree pointers */
84 static gint ett_multipart = -1;
85 static gint ett_multipart_main = -1;
86 static gint ett_multipart_body = -1;
87
88 /* Not sure that compact_name exists for multipart, but choose to keep
89  * the structure from SIP dissector, all the content- is also from SIP */
90
91
92 typedef struct {
93         const char *name;
94         const char *compact_name;
95 } multipart_header_t;
96
97 static const multipart_header_t multipart_headers[] = {
98         { "Unknown-header", NULL },             /* Pad so that the real headers start at index 1 */
99         { "Content-Disposition", NULL },
100         { "Content-Encoding", "e" },
101         { "Content-Id", NULL },
102         { "Content-Language", NULL },
103         { "Content-Length", "l" },
104         { "Content-Transfer-Encoding", NULL },
105         { "Content-Type", "c" },
106 };
107
108 #define POS_CONTENT_DISPOSITION                 1
109 #define POS_CONTENT_ENCODING                    2
110 #define POS_CONTENT_ID                                  3
111 #define POS_CONTENT_LANGUAGE                    4
112 #define POS_CONTENT_LENGTH                              5
113 #define POS_CONTENT_TRANSFER_ENCODING   6
114 #define POS_CONTENT_TYPE                                7
115
116 /* Initialize the header fields */
117 static gint hf_multipart_type = -1;
118 static gint hf_multipart_part = -1;
119
120 static gint hf_header_array[] = {
121         -1, /* "Unknown-header" - Pad so that the real headers start at index 1 */
122         -1, /* "Content-Disposition" */
123         -1, /* "Content-Encoding" */
124         -1, /* "Content-Id" */
125         -1, /* "Content-Language" */
126         -1, /* "Content-Length" */
127         -1, /* "Content-Transfer-Encoding" */
128         -1, /* "Content-Type" */
129 };
130
131 /* Define media_type/Content type table */
132 static dissector_table_t media_type_dissector_table;
133
134 /* Data and media dissector handles */
135 static dissector_handle_t data_handle;
136 static dissector_handle_t media_handle;
137
138 /* Determins if bodies with no media type dissector shoud be displayed
139  * as raw text, may cause problems with images sound etc
140  * TODO improve to check for different content types ?
141  */
142 static gboolean display_unknown_body_as_text = FALSE;
143 static gboolean remove_base64_encoding = FALSE;
144
145
146 typedef struct {
147         const char *type; /* Type of multipart */
148         char *boundary; /* Boundary string (enclosing quotes removed if any) */
149         guint boundary_length; /* Length of the boundary string */
150 } multipart_info_t;
151
152
153
154 static gint
155 find_first_boundary(tvbuff_t *tvb, gint start, const guint8 *boundary,
156                 gint boundary_len, gint *boundary_line_len, gboolean *last_boundary);
157 static gint
158 find_next_boundary(tvbuff_t *tvb, gint start, const guint8 *boundary,
159                 gint boundary_len, gint *boundary_line_len, gboolean *last_boundary);
160 static gint
161 process_preamble(proto_tree *tree, tvbuff_t *tvb, const guint8 *boundary,
162                 gint boundary_len, gboolean *last_boundary);
163 static gint
164 process_body_part(proto_tree *tree, tvbuff_t *tvb, const guint8 *boundary,
165                 gint boundary_len, packet_info *pinfo, gint start,
166                 gboolean *last_boundary);
167 static gint
168 is_known_multipart_header(const char *header_str, guint len);
169 static gint
170 index_of_char(const char *str, const char c);
171 char *
172 unfold_and_compact_mime_header(const char *lines, gint *first_colon_offset);
173
174
175 /* Return a tvb that contains the binary representation of a base64
176    string */
177
178 static tvbuff_t *
179 base64_decode(packet_info *pinfo, tvbuff_t *b64_tvb, char *name)
180 {
181         char *data;
182         tvbuff_t *tvb;
183         data = g_strdup(tvb_get_ephemeral_string(b64_tvb, 0, tvb_length(b64_tvb)));
184
185         tvb = base64_to_tvb(data);
186         add_new_data_source(pinfo, tvb, name);
187
188         return tvb;
189 }
190
191 /*
192  * Unfold and clean up a MIME-like header, and process LWS as follows:
193  *              o Preserves LWS in quoted text
194  *              o Remove LWS before and after a separator
195  *              o Remove trailing LWS
196  *              o Replace other LWS with a single space
197  * Set value to the start of the value 
198  * Return the cleaned-up RFC2822 header (buffer must be freed).
199  */
200 char *
201 unfold_and_compact_mime_header(const char *lines, gint *first_colon_offset)
202 {
203         const char *p = lines;
204         char c;
205         char *ret, *q;
206         char sep_seen = 0; /* Did we see a separator ":;," */
207         char lws = FALSE; /* Did we see LWS (incl. folding) */
208         gint colon = -1;
209
210         if (! lines) return NULL;
211
212         c = *p;
213         ret = g_malloc(strlen(lines) + 1);
214         q = ret;
215
216         while (c) {
217                 if (c == ':') {
218                         lws = FALSE; /* Prevent leading LWS from showing up */
219                         if (colon == -1) {/* First colon */
220                                 colon = q - ret;
221                         }
222                         *(q++) = sep_seen = c;
223                         p++;
224                 } else if (c == ';' || c == ',' || c == '=') {
225                         lws = FALSE; /* Prevent leading LWS from showing up */
226                         *(q++) = sep_seen = c;
227                         p++;
228                 } else if (c == ' ' || c == '\t') {
229                         lws = TRUE;
230                         p++;
231                 } else if (c == '\n') {
232                         lws = FALSE; /* Skip trailing LWS */
233                         if ((c = *(p+1))) {
234                                 if (c == ' ' || c == '\t') { /* Header unfolding */
235                                         lws = TRUE;
236                                         p += 2;
237                                 } else {
238                                         *q = c = 0; /* Stop */
239                                 }
240                         }
241                 } else if (c == '\r') {
242                         lws = FALSE;
243                         if ((c = *(p+1))) {
244                                 if (c == '\n') {
245                                         if ((c = *(p+2))) {
246                                                 if (c == ' ' || c == '\t') { /* Header unfolding */
247                                                         lws = TRUE;
248                                                         p += 3;
249                                                 } else {
250                                                         *q = c = 0; /* Stop */
251                                                 }
252                                         }
253                                 } else if (c == ' ' || c == '\t') { /* Header unfolding */
254                                         lws = TRUE;
255                                         p += 2;
256                                 } else {
257                                         *q = c = 0; /* Stop */
258                                 }
259                         }
260                 } else if (c == '"') { /* Start of quoted-string */
261                         lws = FALSE;
262                         *(q++) = c;
263                         while (c) {
264                                 c = *(q++) = *(++p);
265                                 if (c == '"') {
266                                         p++; /* Skip closing quote */
267                                         break;
268                                 }
269                         }
270                         /* if already zero terminated now, rewind one char to avoid an "off by one" */
271                         if(c == 0) {
272                                 q--;
273                         }
274                 } else { /* Regular character */
275                         if (sep_seen) {
276                                 sep_seen = 0;
277                                 lws = FALSE;
278                         } else {
279                                 if (lws) {
280                                         *(q++) = ' ';
281                                         lws = FALSE;
282                                 }
283                         }
284                         lws = FALSE;
285                         *(q++) = c;
286                         p++; /* OK */
287                 }
288
289                 if (c) {
290                         c = *p;
291                 }
292         }
293         *q = 0;
294
295         *first_colon_offset = colon;
296         return (ret);
297 }
298
299 /* Return the index of a given char in the given string,
300  * or -1 if not found.
301  */
302 static gint
303 index_of_char(const char *str, const char c)
304 {
305         gint len = 0;
306         const char *p = str;
307
308         while (*p && *p != c) {
309                 p++;
310                 len++;
311         }
312
313         if (*p)
314                 return len;
315         return -1;
316 }
317
318 static char *find_parameter(char *parameters, const char *key, int *retlen)
319 {
320         char *start, *p;
321         int   keylen = 0;
322         int   len = 0;
323
324         if(!parameters || !*parameters || !key || !(keylen = strlen(key)))
325                 /* we won't be able to find anything */
326                 return NULL;
327
328         p = parameters;
329
330         while (*p) {
331
332                 while ((*p) && isspace((guchar)*p))
333                         p++; /* Skip white space */
334                 
335                 if (g_ascii_strncasecmp(p, key, keylen) == 0)
336                         break;
337                 /* Skip to next parameter */
338                 p = strchr(p, ';');
339                 if (p == NULL)
340                 {
341                         return NULL;
342                 }
343                 p++; /* Skip semicolon */
344
345         }
346         start = p + keylen;
347         if (start[0] == 0) {
348                 return NULL;
349         }
350
351         /*
352          * Process the parameter value
353          */
354         if (start[0] == '"') {
355                 /*
356                  * Parameter value is a quoted-string
357                  */
358                 start++; /* Skip the quote */
359                 len = index_of_char(start, '"');
360                 if (len < 0) {
361                         /*
362                          * No closing quote
363                          */
364                         return NULL;
365                 }
366         } else {
367                 /*
368                  * Look for end of boundary
369                  */
370                 p = start;
371                 while (*p) {
372                         if (*p == ';' || isspace((guchar)*p))
373                                 break;
374                         p++;
375                         len++;
376                 }
377         }
378
379         if(retlen)
380                 (*retlen) = len;
381
382         return start;
383 }
384
385 /* Retrieve the media information from pinfo->private_data,
386  * and compute the boundary string and its length.
387  * Return a pointer to a filled-in multipart_info_t, or NULL on failure.
388  * 
389  * Boundary delimiters must not appear within the encapsulated material,
390  * and must be no longer than 70 characters, not counting the two
391  * leading hyphens. (quote from rfc2046)
392  */
393 static multipart_info_t *
394 get_multipart_info(packet_info *pinfo)
395 {
396         const char *start;
397         int len = 0;
398         multipart_info_t *m_info = NULL;
399         const char *type = pinfo->match_string;
400         char *parameters;
401         gint dummy;
402
403         if ((type == NULL) || (pinfo->private_data == NULL)) {
404                 /*
405                  * We need both a content type AND parameters
406                  * for multipart dissection.
407                  */
408                 return NULL;
409         }
410
411         /* Clean up the parameters */
412         parameters = unfold_and_compact_mime_header(pinfo->private_data, &dummy);
413
414         start = find_parameter(parameters, "boundary=", &len);
415
416         if(!start) {
417                 g_free(parameters);
418                 return NULL;
419         }
420         
421         /*
422          * There is a value for the boundary string
423          */
424         m_info = g_malloc(sizeof(multipart_info_t));
425         m_info->type = type;
426         m_info->boundary = g_strndup(start, len);
427         m_info->boundary_length = len;
428         g_free(parameters);
429
430         return m_info;
431 }
432
433 static void
434 cleanup_multipart_info(void *data)
435 {
436         multipart_info_t *m_info = data;
437         if (m_info) {
438                 g_free(m_info->boundary);
439                 g_free(m_info);
440         }
441 }
442
443 /*
444  * The first boundary does not implicitly contain the leading
445  * line-end sequence.
446  *
447  * Return the offset to the 1st byte of the boundary delimiter line.
448  * Set boundary_line_len to the length of the entire boundary delimiter.
449  * Set last_boundary to TRUE if we've seen the last-boundary delimiter.
450  */
451 static gint
452 find_first_boundary(tvbuff_t *tvb, gint start, const guint8 *boundary,
453                 gint boundary_len, gint *boundary_line_len, gboolean *last_boundary)
454 {
455         gint offset = start, next_offset, line_len, boundary_start;
456
457         while (tvb_length_remaining(tvb, offset + 2 + boundary_len) > 0) {
458                 boundary_start = offset;
459                 if (((tvb_strneql(tvb, offset, (const guint8 *)"--", 2) == 0)
460                                         && (tvb_strneql(tvb, offset + 2, boundary,      boundary_len) == 0)))
461                 {
462                         /* Boundary string; now check if last */
463                         if ((tvb_length_remaining(tvb, offset + 2 + boundary_len + 2) >= 0)
464                                         && (tvb_strneql(tvb, offset + 2 + boundary_len,
465                                                         (const guint8 *)"--", 2) == 0)) {
466                                 *last_boundary = TRUE;
467                         } else {
468                                 *last_boundary = FALSE;
469                         }
470                         /* Look for line end of the boundary line */
471                         line_len =  tvb_find_line_end(tvb, offset, -1, &offset, FALSE);
472                         if (line_len == -1) {
473                                 *boundary_line_len = -1;
474                         } else {
475                                 *boundary_line_len = offset - boundary_start;
476                         }
477                         return boundary_start;
478                 }
479                 line_len =  tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE);
480                 if (line_len == -1) {
481                         return -1;
482                 }
483                 offset = next_offset;
484         }
485
486         return -1;
487 }
488
489 /*
490  * Unless the first boundary, subsequent boundaries include a line-end sequence
491  * before the dashed boundary string.
492  *
493  * Return the offset to the 1st byte of the boundary delimiter line.
494  * Set boundary_line_len to the length of the entire boundary delimiter.
495  * Set last_boundary to TRUE if we've seen the last-boundary delimiter.
496  */
497 static gint
498 find_next_boundary(tvbuff_t *tvb, gint start, const guint8 *boundary,
499                 gint boundary_len, gint *boundary_line_len, gboolean *last_boundary)
500 {
501         gint offset = start, next_offset, line_len, boundary_start;
502
503         while (tvb_length_remaining(tvb, offset + 2 + boundary_len) > 0) {
504                 line_len =  tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE);
505                 if (line_len == -1) {
506                         return -1;
507                 }
508                 boundary_start = offset + line_len;
509                 if (((tvb_strneql(tvb, next_offset, (const guint8 *)"--", 2) == 0)
510                                         && (tvb_strneql(tvb, next_offset + 2, boundary, boundary_len) == 0)))
511                 {
512                         /* Boundary string; now check if last */
513                         if ((tvb_length_remaining(tvb, next_offset + 2 + boundary_len + 2) >= 0)
514                                         && (tvb_strneql(tvb, next_offset + 2 + boundary_len,
515                                                         (const guint8 *)"--", 2) == 0)) {
516                                 *last_boundary = TRUE;
517                         } else {
518                                 *last_boundary = FALSE;
519                         }
520                         /* Look for line end of the boundary line */
521                         line_len =  tvb_find_line_end(tvb, next_offset, -1, &offset, FALSE);
522                         if (line_len == -1) {
523                                 *boundary_line_len = -1;
524                         } else {
525                                 *boundary_line_len = offset - boundary_start;
526                         }
527                         return boundary_start;
528                 }
529                 offset = next_offset;
530         }
531
532         return -1;
533 }
534
535 /*
536  * Process the multipart preamble:
537  *              [ preamble line-end ] dashed-boundary transport-padding line-end
538  *
539  * Return the offset to the start of the first body-part.
540  */
541 static gint
542 process_preamble(proto_tree *tree, tvbuff_t *tvb, const guint8 *boundary,
543                 gint boundary_len, gboolean *last_boundary)
544 {
545         gint boundary_start, boundary_line_len, body_part_start;
546
547         body_part_start = 0;
548         boundary_start = find_first_boundary(tvb, 0, boundary, boundary_len,
549                         &boundary_line_len, last_boundary);
550         if (boundary_start == 0) {
551                 if (tree) {
552                         proto_tree_add_text(tree, tvb, boundary_start, boundary_line_len,
553                                         "First boundary: %s",
554                                         tvb_format_text(tvb, boundary_start, boundary_line_len));
555                 }
556                 return boundary_start + boundary_line_len;
557         } else if (boundary_start > 0) {
558                 if (boundary_line_len > 0) {
559                         gint body_part_start = boundary_start + boundary_line_len;
560
561                         if (tree) {
562                                 if (body_part_start > 0) {
563                                         proto_tree_add_text(tree, tvb, 0, body_part_start,
564                                                         "Preamble");
565                                 }
566                                 proto_tree_add_text(tree, tvb, boundary_start,
567                                                 boundary_line_len, "First boundary: %s",
568                                                 tvb_format_text(tvb, boundary_start,
569                                                         boundary_line_len));
570                         }
571                         return body_part_start;
572                 }
573         }
574         return -1;
575 }
576
577 /*
578  * Process a multipart body-part:
579  *              MIME-part-headers [ line-end *OCTET ]
580  *              line-end dashed-boundary transport-padding line-end
581  *
582  * If applicable, call a media subdissector.
583  *
584  * Return the offset to the start of the next body-part.
585  */
586 static gint
587 process_body_part(proto_tree *tree, tvbuff_t *tvb, const guint8 *boundary,
588                 gint boundary_len, packet_info *pinfo, gint start,
589                 gboolean *last_boundary)
590 {
591         proto_tree *subtree = NULL;
592         proto_item *ti = NULL;
593         gint offset = start, next_offset = 0;
594         char *parameters = NULL;
595         gint body_start, boundary_start, boundary_line_len;
596
597         char *content_type_str = NULL;
598         char *content_encoding_str = NULL;
599         char *filename = NULL;
600         char *typename = NULL;
601         int  len = 0;
602         gboolean last_field = FALSE;
603
604         if (tree) {
605                 ti = proto_tree_add_item(tree, hf_multipart_part, tvb, start, 0, FALSE);
606                 subtree = proto_item_add_subtree(ti, ett_multipart_body);
607         }
608         /*
609          * Process the MIME-part-headers
610          */
611
612         while (!last_field)
613         {
614                 gint colon_offset;
615                 char *hdr_str;
616                 char *header_str;
617
618                 /* Look for the end of the header (denoted by cr) 
619                  * 3:d argument to imf_find_field_end() maxlen; must be last offset in the tvb.
620                  */
621                 next_offset = imf_find_field_end(tvb, offset, tvb_length_remaining(tvb, offset)+offset, &last_field);
622                 /* If cr not found, won't have advanced - get out to avoid infinite loop! */
623                 if (next_offset == offset) {
624                         break;
625                 }
626
627                 hdr_str = tvb_get_ephemeral_string(tvb, offset, next_offset - offset);
628
629                 header_str = unfold_and_compact_mime_header(hdr_str, &colon_offset);
630                 if (colon_offset <= 0) {
631                         if (tree) {
632                                 proto_tree_add_text(subtree, tvb, offset, next_offset - offset,
633                                                 "%s",
634                                                 tvb_format_text(tvb, offset, next_offset - offset));
635                         }
636                 } else {
637                         gint hf_index;
638
639                         /* Split header name from header value */
640                         header_str[colon_offset] = '\0';
641                         hf_index = is_known_multipart_header(header_str, colon_offset);
642
643                         if (hf_index == -1) {
644                                 if (tree) {
645                                         proto_tree_add_text(subtree, tvb, offset,
646                                                         next_offset - offset,
647                                                         "%s",
648                                                         tvb_format_text(tvb, offset, next_offset - offset));
649                                 }
650                         } else {
651                                 char *value_str = header_str + colon_offset + 1;
652
653                                 if (tree) {
654                                         proto_tree_add_string_format(subtree,
655                                                         hf_header_array[hf_index], tvb,
656                                                         offset, next_offset - offset,
657                                                         (const char *)value_str, "%s",
658                                                         tvb_format_text(tvb, offset, next_offset - offset));
659                                 }
660
661                                 switch (hf_index) {
662                                         case POS_CONTENT_TYPE:
663                                                 {
664                                                         /* The Content-Type starts at colon_offset + 1 */
665                                                         gint semicolon_offset = index_of_char(
666                                                                         value_str, ';');
667
668                                                         if (semicolon_offset > 0) {
669                                                                 value_str[semicolon_offset] = '\0';
670                                                                 parameters = ep_strdup(value_str + semicolon_offset + 1);
671                                                         } else {
672                                                                 parameters = NULL;
673                                                         }
674
675                                                         content_type_str = g_ascii_strdown(value_str, -1);
676
677                                                         /* Show content-type in root 'part' label */
678                                                         proto_item_append_text(ti, " (%s)", content_type_str);
679                                                         
680                                                         /* find the "name" parameter in case we don't find a content disposition "filename" */
681                                                         if((typename = find_parameter(parameters, "name=", &len)) != NULL) {
682                                                           typename = g_strndup(typename, len);
683                                                         }
684                                                 }
685
686
687                                                 break;
688                                         case POS_CONTENT_TRANSFER_ENCODING:
689                                                 {
690                                                         /* The Content-Transfeing starts at colon_offset + 1 */
691                                                         gint cr_offset = index_of_char(value_str, '\r');
692
693                                                         if (cr_offset > 0) {
694                                                                 value_str[cr_offset] = '\0';
695                                                         }
696
697                                                         content_encoding_str = g_ascii_strdown(value_str, -1);
698                                                 }
699                                                 break;
700                                         case POS_CONTENT_DISPOSITION:
701                                                 {
702                                                         /* find the "filename" parameter */
703                                                         if((filename = find_parameter(value_str, "filename=", &len)) != NULL) {
704                                                                 filename = g_strndup(filename, len);
705                                                         }
706                                                 }
707                                                 break;
708                                         default:
709                                                 break;
710                                 }
711                         }
712                 }
713                 g_free(header_str);
714                 offset = next_offset;
715         }
716
717         body_start = next_offset;
718
719         /*
720          * Process the body
721          */
722
723         boundary_start = find_next_boundary(tvb, body_start, boundary, boundary_len,
724                         &boundary_line_len, last_boundary);
725         if (boundary_start > 0) {
726                 gint body_len = boundary_start - body_start;
727                 tvbuff_t *tmp_tvb = tvb_new_subset(tvb, body_start,
728                                 body_len, body_len);
729
730                 if (content_type_str) {
731
732                         /*
733                          * subdissection
734                          */
735                         void *save_private_data = pinfo->private_data;
736                         gboolean dissected;
737
738                         /* 
739                          * Try and remove any content transfer encoding so that each sub-dissector
740                          * doesn't have to do it itself 
741                          *
742                          */
743
744                         if(content_encoding_str && remove_base64_encoding) {
745
746                                 if(!g_ascii_strncasecmp(content_encoding_str, "base64", 6))
747                                         tmp_tvb = base64_decode(pinfo, tmp_tvb, filename ? filename : (typename ? typename : content_type_str));
748
749                         }
750
751                         pinfo->private_data = parameters;
752                         /*
753                          * First try the dedicated multipart dissector table
754                          */
755                         dissected = dissector_try_string(multipart_media_subdissector_table,
756                                                 content_type_str, tmp_tvb, pinfo, subtree);
757                         if (! dissected) {
758                                 /*
759                                  * Fall back to the default media dissector table
760                                  */
761                                 dissected = dissector_try_string(media_type_dissector_table,
762                                                 content_type_str, tmp_tvb, pinfo, subtree);
763                         }
764                         if (! dissected) {
765                                 const char *save_match_string = pinfo->match_string;
766                                 pinfo->match_string = content_type_str;
767                                 call_dissector(media_handle, tmp_tvb, pinfo, subtree);
768                                 pinfo->match_string = save_match_string;
769                         }
770                         pinfo->private_data = save_private_data;
771                         g_free(content_type_str);
772                         content_type_str = NULL;
773                         parameters = NULL; /* Shares same memory as content_type_str */
774                 } else {
775                         call_dissector(data_handle, tmp_tvb, pinfo, subtree);
776                 }
777                 if (tree) {
778                         proto_item_set_len(ti, boundary_start - start);
779                         if (*last_boundary == TRUE) {
780                                 proto_tree_add_text(tree, tvb,
781                                                 boundary_start, boundary_line_len,
782                                                 "Last boundary: %s",
783                                                 tvb_format_text(tvb, boundary_start,
784                                                         boundary_line_len));
785                         } else {
786                                 proto_tree_add_text(tree, tvb,
787                                                 boundary_start, boundary_line_len,
788                                                 "Boundary: %s",
789                                                 tvb_format_text(tvb, boundary_start,
790                                                         boundary_line_len));
791                         }
792                 }
793
794                 g_free(filename);
795                 g_free(typename);
796
797                 return boundary_start + boundary_line_len;
798         }
799
800         return -1;
801 }
802
803 /*
804  * Call this method to actually dissect the multipart body.
805  * NOTE - Only do so if a boundary string has been found!
806  */
807 static void dissect_multipart(tvbuff_t *tvb, packet_info *pinfo,
808                 proto_tree *tree)
809 {
810         proto_tree *subtree = NULL;
811         proto_item *ti = NULL;
812         multipart_info_t *m_info = get_multipart_info(pinfo);
813         gint header_start = 0;
814         guint8 *boundary;
815         gint boundary_len;
816         gint offset = 0;
817         gboolean last_boundary = FALSE;
818
819         if (m_info == NULL) {
820                 /*
821                  * We can't get the required multipart information
822                  */
823                 proto_tree_add_text(tree, tvb, 0, -1,
824                                 "The multipart dissector could not find "
825                                 "the required boundary parameter.");
826                 call_dissector(data_handle, tvb, pinfo, tree);
827                 return;
828         }
829         boundary = (guint8 *)m_info->boundary;
830         boundary_len = m_info->boundary_length;
831         /* Clean up the memory if an exception is thrown */
832         /* CLEANUP_PUSH(cleanup_multipart_info, m_info); */
833
834         /* Add stuff to the protocol tree */
835         if (tree) {
836                 proto_item *type_ti;
837                 ti = proto_tree_add_item(tree, proto_multipart,
838                                 tvb, 0, -1, FALSE);
839                 subtree = proto_item_add_subtree(ti, ett_multipart);
840                 proto_item_append_text(ti, ", Type: %s, Boundary: \"%s\"",
841                                 m_info->type, m_info->boundary);
842
843                 /* Show multi-part type as a generated field */
844                 type_ti = proto_tree_add_string(subtree, hf_multipart_type,
845                                                 tvb, 0, 0, pinfo->match_string);
846                 PROTO_ITEM_SET_GENERATED(type_ti);
847         }
848
849         /*
850          * Make no entries in Protocol column and Info column on summary display,
851          * but stop sub-dissectors from clearing entered text in summary display.
852          */
853         if (check_col(pinfo->cinfo, COL_INFO))
854                 col_set_fence(pinfo->cinfo, COL_INFO);
855
856         offset = 0;
857
858         /*
859          * Process the multipart preamble
860          */
861         header_start = process_preamble(subtree, tvb, boundary,
862                         boundary_len, &last_boundary);
863         if (header_start == -1) {
864                 call_dissector(data_handle, tvb, pinfo, subtree);
865                 /* Clean up the dynamically allocated memory */
866                 cleanup_multipart_info(m_info);
867                 return;
868         }
869         /*
870          * Process the encapsulated bodies
871          */
872         while (last_boundary == FALSE) {
873                 header_start = process_body_part(subtree, tvb, boundary, boundary_len,
874                                 pinfo, header_start, &last_boundary);
875                 if (header_start == -1) {
876                         /* Clean up the dynamically allocated memory */
877                         cleanup_multipart_info(m_info);
878                         return;
879                 }
880         }
881         /*
882          * Process the multipart trailer
883          */
884         if (tree) {
885                 if (tvb_length_remaining(tvb, header_start) > 0) {
886                         proto_tree_add_text(subtree, tvb, header_start, -1, "Trailer");
887                 }
888         }
889         /* Clean up the dynamically allocated memory */
890         cleanup_multipart_info(m_info);
891         return;
892 }
893
894 /* Returns index of method in multipart_headers */
895 static gint
896 is_known_multipart_header(const char *header_str, guint len)
897 {
898         guint i;
899
900         for (i = 1; i < array_length(multipart_headers); i++) {
901                 if (len == strlen(multipart_headers[i].name) &&
902                     g_ascii_strncasecmp(header_str, multipart_headers[i].name, len) == 0)
903                         return i;
904                 if (multipart_headers[i].compact_name != NULL &&
905                     len == strlen(multipart_headers[i].compact_name) &&
906                     g_ascii_strncasecmp(header_str, multipart_headers[i].compact_name, len) == 0)
907                         return i;
908         }
909
910         return -1;
911 }
912
913 /*
914  * Register the protocol with Wireshark.
915  *
916  * This format is required because a script is used to build the C function
917  * that calls all the protocol registration.
918  */
919
920 void
921 proto_register_multipart(void)
922 {
923
924 /* Setup list of header fields  See Section 1.6.1 for details */
925         static hf_register_info hf[] = {
926                 { &hf_multipart_type,
927                         {       "Type",
928                                 "mime_multipart.type",
929                                 FT_STRING, BASE_NONE, NULL, 0x00,
930                                 "MIME multipart encapsulation type", HFILL
931                         }
932                 },
933                 { &hf_multipart_part,
934                         {       "Encapsulated multipart part",
935                                 "mime_multipart.part",
936                                 FT_STRING, BASE_NONE, NULL, 0x00,
937                                 "Encapsulated multipart part", HFILL
938                         }
939                 },
940                 { &hf_header_array[POS_CONTENT_DISPOSITION],
941                         {       "Content-Disposition",
942                                 "mime_multipart.header.content-disposition",
943                                 FT_STRING, BASE_NONE, NULL, 0x00,
944                                 "RFC 2183: Content-Disposition Header", HFILL
945                         }
946                 },
947                 { &hf_header_array[POS_CONTENT_ENCODING],
948                         {       "Content-Encoding",
949                                 "mime_multipart.header.content-encoding",
950                                 FT_STRING, BASE_NONE, NULL, 0x00,
951                                 "Content-Encoding Header", HFILL
952                         }
953                 },
954                 { &hf_header_array[POS_CONTENT_ID],
955                         {       "Content-Id",
956                                 "mime_multipart.header.content-id",
957                                 FT_STRING, BASE_NONE, NULL, 0x00,
958                                 "RFC 2045: Content-Id Header", HFILL
959                         }
960                 },
961                 { &hf_header_array[POS_CONTENT_LANGUAGE],
962                         {       "Content-Language",
963                                 "mime_multipart.header.content-language",
964                                 FT_STRING, BASE_NONE, NULL, 0x00,
965                                 "Content-Language Header", HFILL
966                         }
967                 },
968                 { &hf_header_array[POS_CONTENT_LENGTH],
969                         {       "Content-Length",
970                                 "mime_multipart.header.content-length",
971                                 FT_STRING, BASE_NONE, NULL, 0x0,
972                                 "Content-Length Header", HFILL
973                         }
974                 },
975                 { &hf_header_array[POS_CONTENT_TRANSFER_ENCODING],
976                         {       "Content-Transfer-Encoding",
977                                 "mime_multipart.header.content-transfer-encoding",
978                                 FT_STRING, BASE_NONE, NULL, 0x00,
979                                 "RFC 2045: Content-Transfer-Encoding Header", HFILL
980                         }
981                 },
982                 { &hf_header_array[POS_CONTENT_TYPE],
983                         {       "Content-Type",
984                                 "mime_multipart.header.content-type",
985                                 FT_STRING, BASE_NONE,NULL,0x0,
986                                 "Content-Type Header", HFILL
987                         }
988                 },
989         };
990
991         /*
992          * Preferences
993          */
994         module_t *multipart_module;
995
996         /*
997          * Setup protocol subtree array
998          */
999         static gint *ett[] = {
1000                 &ett_multipart,
1001                 &ett_multipart_main,
1002                 &ett_multipart_body,
1003         };
1004
1005         /*
1006          * Register the protocol name and description
1007          */
1008         proto_multipart = proto_register_protocol(
1009                         "MIME Multipart Media Encapsulation",
1010                         "MIME multipart",
1011                         "mime_multipart");
1012
1013         /*
1014          * Required function calls to register
1015          * the header fields and subtrees used.
1016          */
1017         proto_register_field_array(proto_multipart, hf, array_length(hf));
1018         proto_register_subtree_array(ett, array_length(ett));
1019
1020         multipart_module = prefs_register_protocol(proto_multipart, NULL);
1021
1022         prefs_register_bool_preference(multipart_module,
1023                         "display_unknown_body_as_text",
1024                         "Display bodies without media type as text",
1025                         "Display multipart bodies with no media type dissector"
1026                         " as raw text (may cause problems with binary data).",
1027                         &display_unknown_body_as_text);
1028
1029         prefs_register_bool_preference(multipart_module,
1030                                        "remove_base64_encoding",
1031                                        "Remove base64 encoding from bodies",
1032                                        "Remove any base64 content-transfer encoding from bodies. "
1033                                        "This supports export of the body and its further dissection.",
1034                                        &remove_base64_encoding);
1035
1036         /*
1037          * Dissectors requiring different behavior in cases where the media
1038          * is contained in a multipart entity should register their multipart
1039          * dissector in the dissector table below, which is similar to the
1040          * "media_type" dissector table defined in the HTTP dissector code.
1041          */
1042         multipart_media_subdissector_table = register_dissector_table(
1043                         "multipart_media_type",
1044                         "Internet media type (for multipart processing)",
1045                         FT_STRING, BASE_NONE);
1046 }
1047
1048
1049 /* If this dissector uses sub-dissector registration add a registration routine.
1050    This format is required because a script is used to find these routines and
1051    create the code that calls these routines.
1052 */
1053 void
1054 proto_reg_handoff_multipart(void)
1055 {
1056         dissector_handle_t multipart_handle;
1057
1058         /*
1059          * When we cannot display the data, call the data dissector.
1060          * When there is no dissector for the given media, call the media dissector.
1061          */
1062         data_handle = find_dissector("data");
1063         media_handle = find_dissector("media");
1064
1065         /*
1066          * Get the content type and Internet media type table
1067          */
1068         media_type_dissector_table = find_dissector_table("media_type");
1069
1070         /*
1071          * Handle for multipart dissection
1072          */
1073         multipart_handle = create_dissector_handle(
1074                         dissect_multipart, proto_multipart);
1075
1076         dissector_add_string("media_type",
1077                         "multipart/mixed", multipart_handle);
1078         dissector_add_string("media_type",
1079                         "multipart/related", multipart_handle);
1080         dissector_add_string("media_type",
1081                         "multipart/alternative", multipart_handle);
1082         dissector_add_string("media_type",
1083                         "multipart/form-data", multipart_handle);
1084
1085 }