Branch data Line data Source code
1 : : // $Id: MIME.cc 5906 2008-07-03 19:52:50Z vern $
2 : :
3 : : #include "config.h"
4 : :
5 : : #include "NetVar.h"
6 : : #include "MIME.h"
7 : : #include "Event.h"
8 : :
9 : : // Here are a few things to do:
10 : : //
11 : : // 1. Add a Bro internal function 'stop_deliver_data_of_entity' so
12 : : // that the engine does not decode and deliver further data for the
13 : : // entity (which may speed up the engine by avoiding copying).
14 : : //
15 : : // 2. Better support for structured header fields, in particular,
16 : : // headers of form: <name>=<value>; <param_1>=<param_val_1>;
17 : : // <param_2>=<param_val_2>; ... (so that
18 : :
19 : : static const data_chunk_t null_data_chunk = { 0, 0 };
20 : :
21 : 9670 : int is_null_data_chunk(data_chunk_t b)
22 : : {
23 : 9670 : return b.data == 0;
24 : : }
25 : :
26 : 0 : int fputs(data_chunk_t b, FILE* fp)
27 : : {
28 [ # # ]: 0 : for ( int i = 0; i < b.length; ++i )
29 [ # # ]: 0 : if ( fputc(b.data[i], fp) == EOF )
30 : 0 : return EOF;
31 : 0 : return 0;
32 : : }
33 : :
34 : 15968 : StringVal* new_string_val(int length, const char* data)
35 : : {
36 : 15968 : return new StringVal(length, data);
37 : : }
38 : :
39 : 1 : StringVal* new_string_val(const char* data, const char* end_of_data)
40 : : {
41 : 1 : return new StringVal(end_of_data - data, data);
42 : : }
43 : :
44 : 15968 : StringVal* new_string_val(const data_chunk_t buf)
45 : : {
46 : 15968 : return new_string_val(buf.length, buf.data);
47 : : }
48 : :
49 : 2 : data_chunk_t get_data_chunk(BroString* s)
50 : : {
51 : : data_chunk_t b;
52 : 2 : b.length = s->Len();
53 : 2 : b.data = (const char*) s->Bytes();
54 : 2 : return b;
55 : : }
56 : :
57 : : int mime_header_only = 0;
58 : : int mime_decode_data = 1;
59 : : int mime_submit_data = 1;
60 : :
61 : : enum MIME_HEADER_FIELDS {
62 : : MIME_CONTENT_TYPE,
63 : : MIME_CONTENT_TRANSFER_ENCODING,
64 : : MIME_FIELD_OTHER,
65 : : };
66 : :
67 : : enum MIME_CONTENT_SUBTYPE {
68 : : CONTENT_SUBTYPE_MIXED, // for multipart
69 : : CONTENT_SUBTYPE_ALTERNATIVE, // for multipart
70 : : CONTENT_SUBTYPE_DIGEST, // for multipart
71 : :
72 : : CONTENT_SUBTYPE_RFC822, // for message
73 : : CONTENT_SUBTYPE_PARTIAL, // for message
74 : : CONTENT_SUBTYPE_EXTERNAL_BODY, // for message
75 : :
76 : : CONTENT_SUBTYPE_PLAIN, // for text
77 : :
78 : : CONTENT_SUBTYPE_OTHER,
79 : : };
80 : :
81 : : enum MIME_CONTENT_ENCODING {
82 : : CONTENT_ENCODING_7BIT,
83 : : CONTENT_ENCODING_8BIT,
84 : : CONTENT_ENCODING_BINARY,
85 : : CONTENT_ENCODING_QUOTED_PRINTABLE,
86 : : CONTENT_ENCODING_BASE64,
87 : : CONTENT_ENCODING_OTHER,
88 : : };
89 : :
90 : : enum MIME_BOUNDARY_DELIMITER {
91 : : NOT_MULTIPART_BOUNDARY,
92 : : MULTIPART_BOUNDARY,
93 : : MULTIPART_CLOSING_BOUNDARY,
94 : : };
95 : :
96 : : static const char* MIMEHeaderName[] = {
97 : : "content-type",
98 : : "content-transfer-encoding",
99 : : 0,
100 : : };
101 : :
102 : : static const char* MIMEContentTypeName[] = {
103 : : "MULTIPART",
104 : : "MESSAGE",
105 : : "TEXT",
106 : : 0,
107 : : };
108 : :
109 : : static const char* MIMEContentSubtypeName[] = {
110 : : "MIXED", // for multipart
111 : : "ALTERNATIVE", // for multipart
112 : : "DIGEST", // for multipart
113 : :
114 : : "RFC822", // for message
115 : : "PARTIAL", // for message
116 : : "EXTERNAL-BODY", // for message
117 : :
118 : : "PLAIN", // for text
119 : :
120 : : 0, // other
121 : : };
122 : :
123 : : static const char* MIMEContentEncodingName[] = {
124 : : "7BIT",
125 : : "8BIT",
126 : : "BINARY",
127 : : "QUOTED-PRINTABLE",
128 : : "BASE64",
129 : : 0,
130 : : };
131 : :
132 : :
133 : 7984 : MIME_Multiline::MIME_Multiline()
134 : : {
135 : 7984 : line = 0;
136 : 7984 : }
137 : :
138 : 7984 : MIME_Multiline::~MIME_Multiline()
139 : : {
140 [ + - ][ # # ]: 7984 : delete line;
141 : 7984 : delete_strings(buffer);
142 : 7984 : }
143 : :
144 : 7984 : void MIME_Multiline::append(int len, const char* data)
145 : : {
146 : 7984 : buffer.push_back(new BroString((const u_char*) data, len, 1));
147 : 7984 : }
148 : :
149 : 7984 : BroString* MIME_Multiline::get_concatenated_line()
150 : : {
151 [ - + ]: 7984 : if ( buffer.size() == 0 )
152 : 0 : return 0;
153 : :
154 [ - + ]: 7984 : delete line;
155 : 7984 : line = concatenate(buffer);
156 : :
157 : 7984 : return line;
158 : : }
159 : :
160 : :
161 : 7984 : MIME_Header::MIME_Header(MIME_Multiline* hl)
162 : : {
163 : 7984 : lines = hl;
164 : 7984 : name = value = value_token = rest_value = null_data_chunk;
165 : :
166 : 7984 : BroString* s = hl->get_concatenated_line();
167 : 7984 : int len = s->Len();
168 : 7984 : const char* data = (const char*) s->Bytes();
169 : :
170 : 7984 : int offset = MIME_get_field_name(len, data, &name);
171 [ - + # # ]: 7984 : if ( offset < 0 )
172 : 0 : return;
173 : :
174 : 7984 : len -= offset; data += offset;
175 : 7984 : offset = MIME_skip_lws_comments(len, data);
176 : :
177 [ + - + - ]: 15968 : if ( offset < len && data[offset] == ':' )
[ # # ][ # # ]
178 : : {
179 : 7984 : value.length = len - offset - 1;
180 : 7984 : value.data = data + offset + 1;
181 [ + + ][ + + ]: 15961 : while ( value.length && isspace(*value.data) )
[ # # ][ # # ]
182 : : {
183 : 7977 : --value.length;
184 : 7977 : ++value.data;
185 : : }
186 : : }
187 : : else
188 : : // malformed header line
189 : 7984 : name = null_data_chunk;
190 : : }
191 : :
192 : 7984 : MIME_Header::~MIME_Header()
193 : : {
194 [ + - ][ # # ]: 7984 : delete lines;
195 : 7984 : }
196 : :
197 : 1321 : int MIME_Header::get_first_token()
198 : : {
199 [ + - ]: 1321 : if ( MIME_get_token(value.length, value.data, &value_token) >= 0 )
200 : : {
201 : 1321 : rest_value.data = value_token.data + value_token.length;
202 : 1321 : rest_value.length = value.data + value.length - rest_value.data;
203 : 1321 : return 1;
204 : : }
205 : : else
206 : : {
207 : 0 : value_token = rest_value = null_data_chunk;
208 : 1321 : return 0;
209 : : }
210 : : }
211 : :
212 : 1321 : data_chunk_t MIME_Header::get_value_token()
213 : : {
214 [ - + ]: 1321 : if ( ! is_null_data_chunk(value_token) )
215 : 0 : return value_token;
216 : 1321 : get_first_token();
217 : 1321 : return value_token;
218 : : }
219 : :
220 : 0 : data_chunk_t MIME_Header::get_value_after_token()
221 : : {
222 [ # # ]: 0 : if ( ! is_null_data_chunk(rest_value) )
223 : 0 : return rest_value;
224 : 0 : get_first_token();
225 : 0 : return rest_value;
226 : : }
227 : :
228 : 881 : MIME_Entity::MIME_Entity(MIME_Message* output_message, MIME_Entity* parent_entity)
229 : : {
230 : 881 : init();
231 : 881 : parent = parent_entity;
232 : 881 : message = output_message;
233 [ # # - + ]: 881 : if ( parent )
234 : 0 : content_encoding = parent->ContentTransferEncoding();
235 : 881 : }
236 : :
237 : 881 : void MIME_Entity::init()
238 : : {
239 : 881 : in_header = 1;
240 : 881 : end_of_data = 0;
241 : :
242 : 881 : current_header_line = 0;
243 : 881 : current_field_type = MIME_FIELD_OTHER;
244 : :
245 : 881 : need_to_parse_parameters = 0;
246 : :
247 : 881 : content_type_str = new StringVal("TEXT");
248 : 881 : content_subtype_str = new StringVal("PLAIN");
249 : :
250 : 881 : content_encoding_str = 0;
251 : 881 : multipart_boundary = 0;
252 : 881 : content_type = CONTENT_TYPE_TEXT;
253 : 881 : content_subtype = CONTENT_SUBTYPE_PLAIN;
254 : 881 : content_encoding = CONTENT_ENCODING_OTHER;
255 : :
256 : 881 : parent = 0;
257 : 881 : current_child_entity = 0;
258 : :
259 : 881 : base64_decoder = 0;
260 : :
261 : 881 : data_buf_length = 0;
262 : 881 : data_buf_data = 0;
263 : 881 : data_buf_offset = -1;
264 : :
265 : 881 : message = 0;
266 : 881 : }
267 : :
268 : 881 : MIME_Entity::~MIME_Entity()
269 : : {
270 [ # # ][ # # ]: 881 : if ( ! end_of_data )
[ - + ]
271 : 0 : internal_error("EndOfData must be called before delete a MIME_Entity");
272 : :
273 [ # # ][ # # ]: 881 : delete current_header_line;
[ - + ]
274 : 881 : Unref(content_type_str);
275 : 881 : Unref(content_subtype_str);
276 [ # # # # ]: 881 : delete content_encoding_str;
[ - + ]
277 [ # # ][ # # ]: 881 : delete multipart_boundary;
[ + + ]
278 : :
279 [ # # ][ # # ]: 8865 : for ( unsigned int i = 0; i < headers.size(); ++i )
[ + + ]
280 [ # # ][ # # ]: 7984 : delete headers[i];
[ + - ]
281 : 881 : headers.clear();
282 : :
283 [ # # # # ]: 881 : delete base64_decoder;
[ - + ]
284 [ # # ][ # # ]: 881 : }
[ - + ]
285 : :
286 : 8883 : void MIME_Entity::Deliver(int len, const char* data, int trailing_CRLF)
287 : : {
288 [ + - ]: 8883 : if ( in_header )
289 : : {
290 [ + + ][ - + ]: 9782 : if ( len == 0 || *data == '\0' )
291 : : { // an empty line at the end of header fields
292 : 899 : FinishHeader();
293 : 899 : in_header = 0;
294 : 899 : SubmitAllHeaders();
295 : :
296 : : // Note: it's possible that we are in the
297 : : // trailer of a chunked transfer (see HTTP.cc).
298 : : // In this case, end_of_data will be set in
299 : : // HTTP_Entity::SubmitAllHeaders(), and we
300 : : // should not begin a new body.
301 : :
302 [ + + ]: 899 : if ( ! end_of_data )
303 : 392 : BeginBody();
304 : : }
305 : :
306 [ - + ]: 7984 : else if ( is_lws(*data) )
307 : : // linear whitespace - a continuing header line
308 : 0 : ContHeader(len, data);
309 : : else
310 : 8883 : NewHeader(len, data);
311 : : }
312 : : else
313 : : {
314 [ # # ][ # # ]: 0 : if ( ! mime_header_only && data )
315 : 0 : NewDataLine(len, data, trailing_CRLF);
316 : : }
317 : 8883 : }
318 : :
319 : 392 : void MIME_Entity::BeginBody()
320 : : {
321 [ - + ]: 392 : if ( content_encoding == CONTENT_ENCODING_BASE64 )
322 : 0 : StartDecodeBase64();
323 : :
324 [ - + ]: 392 : if ( content_type == CONTENT_TYPE_MESSAGE )
325 : 0 : BeginChildEntity();
326 : 392 : }
327 : :
328 : 1745 : void MIME_Entity::EndOfData()
329 : : {
330 [ + + ]: 1745 : if ( end_of_data )
331 : 864 : return;
332 : :
333 : 881 : end_of_data = 1;
334 : :
335 [ + + ]: 881 : if ( in_header )
336 : : {
337 : 12 : FinishHeader();
338 : 12 : in_header = 0;
339 : 12 : SubmitAllHeaders();
340 : : message->SubmitEvent(MIME_EVENT_ILLEGAL_FORMAT,
341 : 12 : "entity body missing");
342 : : }
343 : :
344 : : else
345 : : {
346 [ - + ]: 869 : if ( current_child_entity != 0 )
347 : : {
348 [ # # ]: 0 : if ( content_type == CONTENT_TYPE_MULTIPART )
349 : 0 : IllegalFormat("multipart closing boundary delimiter missing");
350 : 0 : EndChildEntity();
351 : : }
352 : :
353 [ - + ]: 869 : if ( content_encoding == CONTENT_ENCODING_BASE64 )
354 : 0 : FinishDecodeBase64();
355 : :
356 [ - + ]: 869 : if ( data_buf_offset > 0 )
357 : : {
358 : 0 : SubmitData(data_buf_offset, data_buf_data);
359 : 0 : data_buf_offset = -1;
360 : : }
361 : : }
362 : :
363 : 1745 : message->EndEntity (this);
364 : : }
365 : :
366 : 0 : void MIME_Entity::NewDataLine(int len, const char* data, int trailing_CRLF)
367 : : {
368 [ # # ]: 0 : if ( content_type == CONTENT_TYPE_MULTIPART )
369 : : {
370 [ # # # ]: 0 : switch ( CheckBoundaryDelimiter(len, data) ) {
371 : : case MULTIPART_BOUNDARY:
372 [ # # ]: 0 : if ( current_child_entity != 0 )
373 : 0 : EndChildEntity();
374 : 0 : BeginChildEntity();
375 : 0 : return;
376 : :
377 : : case MULTIPART_CLOSING_BOUNDARY:
378 [ # # ]: 0 : if ( current_child_entity != 0 )
379 : 0 : EndChildEntity();
380 : 0 : EndOfData();
381 : 0 : return;
382 : : }
383 : : }
384 : :
385 [ # # ][ # # ]: 0 : if ( content_type == CONTENT_TYPE_MULTIPART ||
386 : : content_type == CONTENT_TYPE_MESSAGE )
387 : : {
388 : : // Here we ignore the difference among 7bit, 8bit and
389 : : // binary encoding, and thus do not need to decode
390 : : // before passing the data to child.
391 : :
392 [ # # ]: 0 : if ( current_child_entity != 0 )
393 : : // Data before the first or after the last
394 : : // boundary delimiter are ignored
395 : 0 : current_child_entity->Deliver(len, data, trailing_CRLF);
396 : : }
397 : : else
398 : : {
399 [ # # ]: 0 : if ( mime_decode_data )
400 : 0 : DecodeDataLine(len, data, trailing_CRLF);
401 : : }
402 : : }
403 : :
404 : 7984 : void MIME_Entity::NewHeader(int len, const char* data)
405 : : {
406 : 7984 : FinishHeader();
407 : :
408 [ + - ]: 7984 : if ( len == 0 )
409 : 7984 : return;
410 : :
411 [ - + ]: 7984 : ASSERT(! is_lws(*data));
412 : :
413 : 7984 : current_header_line = new MIME_Multiline();
414 : 7984 : current_header_line->append(len, data);
415 : : }
416 : :
417 : 0 : void MIME_Entity::ContHeader(int len, const char* data)
418 : : {
419 [ # # ]: 0 : if ( current_header_line == 0 )
420 : : {
421 : 0 : IllegalFormat("first header line starts with linear whitespace");
422 : :
423 : : // shall we try it as a new header or simply ignore this line?
424 : 0 : int ws = MIME_count_leading_lws(len, data);
425 : 0 : NewHeader(len - ws, data + ws);
426 : 0 : return;
427 : : }
428 : :
429 : 0 : current_header_line->append(len, data);
430 : : }
431 : :
432 : 8895 : void MIME_Entity::FinishHeader()
433 : : {
434 [ + + ]: 8895 : if ( current_header_line == 0 )
435 : 911 : return;
436 : :
437 : 7984 : MIME_Header* h = new MIME_Header(current_header_line);
438 : 7984 : current_header_line = 0;
439 : :
440 [ + - ]: 7984 : if ( ! is_null_data_chunk(h->get_name()) )
441 : : {
442 : 7984 : ParseMIMEHeader(h);
443 : 7984 : SubmitHeader(h);
444 : 7984 : headers.push_back(h);
445 : : }
446 : : else
447 [ # # ]: 8895 : delete h;
448 : : }
449 : :
450 : 7984 : int MIME_Entity::LookupMIMEHeaderName(data_chunk_t name)
451 : : {
452 : : // A linear lookup should be fine for now.
453 : : // header names are case-insensitive (RFC 822, 2822, 2045).
454 : :
455 [ + + ]: 23050 : for ( int i = 0; MIMEHeaderName[i] != 0; ++i )
456 [ + + ]: 15517 : if ( strcasecmp_n(name, MIMEHeaderName[i]) == 0 )
457 : 451 : return i;
458 : 7984 : return -1;
459 : : }
460 : :
461 : 7984 : void MIME_Entity::ParseMIMEHeader(MIME_Header* h)
462 : : {
463 [ - + ]: 7984 : if ( h == 0 )
464 : 0 : return;
465 : :
466 : 7984 : current_field_type = LookupMIMEHeaderName(h->get_name());
467 : :
468 [ + - + ]: 7984 : switch ( current_field_type ) {
469 : : case MIME_CONTENT_TYPE:
470 : 451 : ParseContentTypeField(h);
471 : 451 : break;
472 : :
473 : : case MIME_CONTENT_TRANSFER_ENCODING:
474 : 7984 : ParseContentEncodingField(h);
475 : : break;
476 : : }
477 : : }
478 : :
479 : 451 : int MIME_Entity::ParseContentTypeField(MIME_Header* h)
480 : : {
481 : 451 : data_chunk_t val = h->get_value();
482 : 451 : int len = val.length;
483 : 451 : const char* data = val.data;
484 : :
485 : : data_chunk_t ty, subty;
486 : : int offset;
487 : :
488 : 451 : offset = MIME_get_slash_token_pair(len, data, &ty, &subty);
489 [ - + ]: 451 : if ( offset < 0 )
490 : : {
491 : 0 : IllegalFormat("media type/subtype not found in content type");
492 : 0 : return 0;
493 : : }
494 : 451 : data += offset;
495 : 451 : len -= offset;
496 : :
497 : 451 : Unref(content_type_str);
498 : 451 : content_type_str = (new StringVal(ty.length, ty.data))->ToUpper();
499 : 451 : Unref(content_subtype_str);
500 : 451 : content_subtype_str = (new StringVal(subty.length, subty.data))->ToUpper();
501 : :
502 : 451 : ParseContentType(ty, subty);
503 : :
504 : : // Proceed to parameters.
505 [ + + ]: 451 : if ( need_to_parse_parameters )
506 : 2 : ParseFieldParameters(len, data);
507 : :
508 [ + + ][ - + ]: 451 : if ( content_type == CONTENT_TYPE_MULTIPART && ! multipart_boundary )
509 : : {
510 : 0 : IllegalFormat("boundary delimiter is not specified for a multipart entity -- content is treated as type application/octet-stream");
511 : 0 : content_type = CONTENT_TYPE_OTHER;
512 : 0 : content_subtype = CONTENT_SUBTYPE_OTHER;
513 : : }
514 : :
515 : 451 : return 1;
516 : : }
517 : :
518 : 0 : int MIME_Entity::ParseContentEncodingField(MIME_Header* h)
519 : : {
520 : : data_chunk_t enc;
521 : :
522 : 0 : enc = h->get_value_token();
523 [ # # ]: 0 : if ( is_null_data_chunk(enc) )
524 : : {
525 : 0 : IllegalFormat("encoding type not found in content encoding");
526 : 0 : return 0;
527 : : }
528 : :
529 : 0 : content_encoding_str = new BroString((const u_char*)enc.data, enc.length, 1);
530 : 0 : ParseContentEncoding(enc);
531 : :
532 [ # # ]: 0 : if ( need_to_parse_parameters )
533 : : {
534 : 0 : data_chunk_t val = h->get_value_after_token();
535 [ # # ]: 0 : if ( ! is_null_data_chunk(val) )
536 : 0 : ParseFieldParameters(val.length, val.data);
537 : : }
538 : :
539 : 0 : return 1;
540 : : }
541 : :
542 : 4 : int MIME_Entity::ParseFieldParameters(int len, const char* data)
543 : : {
544 : : data_chunk_t attr;
545 : : BroString* val;
546 : :
547 : 4 : while ( 1 )
548 : : {
549 : 4 : int offset = MIME_skip_lws_comments(len, data);
550 [ + - + + ]: 4 : if ( offset < 0 || offset >= len || data[offset] != ';' )
[ - + ]
551 : : break;
552 : :
553 : 2 : ++offset;
554 : 2 : data += offset;
555 : 2 : len -= offset;
556 : :
557 : 2 : offset = MIME_get_token(len, data, &attr);
558 [ - + ]: 2 : if ( offset < 0 )
559 : : {
560 : 0 : IllegalFormat("attribute name not found in parameter specification");
561 : 0 : return 0;
562 : : }
563 : :
564 : 2 : data += offset;
565 : 2 : len -= offset;
566 : :
567 : 2 : offset = MIME_skip_lws_comments(len, data);
568 [ + - + - ]: 2 : if ( offset < 0 || offset >= len || data[offset] != '=' )
[ - + ]
569 : : {
570 : 0 : IllegalFormat("= not found in parameter specification");
571 : 0 : continue;
572 : : }
573 : :
574 : 2 : ++offset;
575 : 2 : data += offset;
576 : 2 : len -= offset;
577 : :
578 : : // token or quoted-string
579 : 2 : offset = MIME_get_value(len, data, val);
580 [ - + ]: 2 : if ( offset < 0 )
581 : : {
582 : 0 : IllegalFormat("value not found in parameter specification");
583 : 0 : continue;
584 : : }
585 : :
586 : 2 : data += offset;
587 : 2 : len -= offset;
588 : :
589 : 2 : ParseParameter(attr, get_data_chunk(val));
590 [ + - ]: 2 : delete val;
591 : : }
592 : :
593 : 2 : return 1;
594 : : }
595 : :
596 : 451 : void MIME_Entity::ParseContentType(data_chunk_t type, data_chunk_t sub_type)
597 : : {
598 : : int i;
599 [ + + ]: 1682 : for ( i = 0; MIMEContentTypeName[i]; ++i )
600 [ + + ]: 1349 : if ( strcasecmp_n(type, MIMEContentTypeName[i]) == 0 )
601 : 118 : break;
602 : :
603 : 451 : content_type = i;
604 : :
605 [ + + ]: 3602 : for ( i = 0; MIMEContentSubtypeName[i]; ++i )
606 [ + + ]: 3157 : if ( strcasecmp_n(sub_type, MIMEContentSubtypeName[i]) == 0 )
607 : 6 : break;
608 : :
609 : 451 : content_subtype = i;
610 : :
611 [ + + ]: 451 : switch ( content_type ) {
612 : : case CONTENT_TYPE_MULTIPART:
613 : : case CONTENT_TYPE_MESSAGE:
614 : 2 : need_to_parse_parameters = 1;
615 : 2 : break;
616 : :
617 : : default:
618 : 449 : need_to_parse_parameters = 0;
619 : : break;
620 : : }
621 : 451 : }
622 : :
623 : 0 : void MIME_Entity::ParseContentEncoding(data_chunk_t encoding_mechanism)
624 : : {
625 : : int i;
626 [ # # ]: 0 : for ( i = 0; MIMEContentEncodingName[i]; ++i )
627 [ # # ]: 0 : if ( strcasecmp_n(encoding_mechanism,
628 : : MIMEContentEncodingName[i]) == 0 )
629 : 0 : break;
630 : :
631 : 0 : content_encoding = i;
632 : 0 : }
633 : :
634 : 2 : void MIME_Entity::ParseParameter(data_chunk_t attr, data_chunk_t val)
635 : : {
636 [ + - ]: 2 : switch ( current_field_type ) {
637 : : case MIME_CONTENT_TYPE:
638 [ + - ][ + - ]: 2 : if ( content_type == CONTENT_TYPE_MULTIPART &&
[ + - ]
639 : : strcasecmp_n(attr, "boundary") == 0 )
640 : 2 : multipart_boundary = new BroString((const u_char*)val.data, val.length, 1);
641 : : break;
642 : :
643 : : case MIME_CONTENT_TRANSFER_ENCODING:
644 : : break;
645 : :
646 : : default:
647 : : break;
648 : : }
649 : 2 : }
650 : :
651 : :
652 : 0 : int MIME_Entity::CheckBoundaryDelimiter(int len, const char* data)
653 : : {
654 [ # # ]: 0 : if ( ! multipart_boundary )
655 : : {
656 : 0 : warn("boundary delimiter was not specified for a multipart message\n");
657 : 0 : DEBUG_MSG("headers of the MIME entity for debug:\n");
658 : 0 : DebugPrintHeaders();
659 : 0 : return NOT_MULTIPART_BOUNDARY;
660 : : }
661 : :
662 [ # # ][ # # ]: 0 : if ( len >= 2 && data[0] == '-' && data[1] == '-' )
[ # # ]
663 : : {
664 : 0 : len -= 2; data += 2;
665 : :
666 : 0 : data_chunk_t delim = get_data_chunk(multipart_boundary);
667 : :
668 : : int i;
669 [ # # ][ # # ]: 0 : for ( i = 0; i < len && i < delim.length; ++i )
670 [ # # ]: 0 : if ( data[i] != delim.data[i] )
671 : 0 : return NOT_MULTIPART_BOUNDARY;
672 : :
673 [ # # ]: 0 : if ( i < delim.length )
674 : 0 : return NOT_MULTIPART_BOUNDARY;
675 : :
676 : 0 : len -= i;
677 : 0 : data += i;
678 : :
679 [ # # ][ # # ]: 0 : if ( len >= 2 && data[0] == '-' && data[1] == '-' )
[ # # ]
680 : 0 : return MULTIPART_CLOSING_BOUNDARY;
681 : : else
682 : 0 : return MULTIPART_BOUNDARY;
683 : : }
684 : :
685 : 0 : return NOT_MULTIPART_BOUNDARY;
686 : : }
687 : :
688 : :
689 : : // trailing_CRLF indicates whether an implicit CRLF sequence follows data
690 : : // (the CRLF sequence is not included in data).
691 : :
692 : 0 : void MIME_Entity::DecodeDataLine(int len, const char* data, int trailing_CRLF)
693 : : {
694 [ # # ]: 0 : if ( ! mime_submit_data )
695 : 0 : return;
696 : :
697 [ # # # # ]: 0 : switch ( content_encoding ) {
698 : : case CONTENT_ENCODING_QUOTED_PRINTABLE:
699 : 0 : DecodeQuotedPrintable(len, data);
700 : 0 : break;
701 : :
702 : : case CONTENT_ENCODING_BASE64:
703 : 0 : DecodeBase64(len, data);
704 : 0 : break;
705 : :
706 : : case CONTENT_ENCODING_7BIT:
707 : : case CONTENT_ENCODING_8BIT:
708 : : case CONTENT_ENCODING_BINARY:
709 : : case CONTENT_ENCODING_OTHER:
710 : 0 : DecodeBinary(len, data, trailing_CRLF);
711 : : break;
712 : : }
713 : : }
714 : :
715 : 0 : void MIME_Entity::DecodeBinary(int len, const char* data, int trailing_CRLF)
716 : : {
717 : 0 : DataOctets(len, data);
718 : :
719 [ # # ]: 0 : if ( trailing_CRLF )
720 : : {
721 : 0 : DataOctet(CR);
722 : 0 : DataOctet(LF);
723 : : }
724 : 0 : }
725 : :
726 : 0 : void MIME_Entity::DecodeQuotedPrintable(int len, const char* data)
727 : : {
728 : : // Ignore trailing HT and SP.
729 : : int i;
730 [ # # ]: 0 : for ( i = len - 1; i >= 0; --i )
731 [ # # ][ # # ]: 0 : if ( data[i] != HT && data[i] != SP )
732 : 0 : break;
733 : :
734 : 0 : int end_of_line = i;
735 : 0 : int soft_line_break = 0;
736 : :
737 [ # # ]: 0 : for ( i = 0; i <= end_of_line; ++i )
738 : : {
739 [ # # ]: 0 : if ( data[i] == '=' )
740 : : {
741 [ # # ]: 0 : if ( i == end_of_line )
742 : 0 : soft_line_break = 1;
743 : : else
744 : : {
745 : 0 : int legal = 0;
746 [ # # ]: 0 : if ( i + 2 < len )
747 : : {
748 : : int a, b;
749 : 0 : a = decode_hex(data[i+1]);
750 : 0 : b = decode_hex(data[i+2]);
751 : :
752 [ # # # # ]: 0 : if ( a >= 0 && b >= 0 )
753 : : {
754 : 0 : DataOctet((a << 4) + b);
755 : 0 : legal = 1;
756 : : }
757 : : }
758 : :
759 [ # # ]: 0 : if ( ! legal )
760 : : {
761 : : // Follows suggestions for a robust
762 : : // decoder. See RFC 2045 page 22.
763 : 0 : IllegalEncoding("= is not followed by two hexadecimal digits in quoted-printable encoding");
764 : 0 : DataOctet(data[i]);
765 : : }
766 : : }
767 : : }
768 : :
769 [ # # ][ # # ]: 0 : else if ( (data[i] >= 33 && data[i] <= 60) ||
[ # # ][ # # ]
770 : : // except controls, whitespace and '='
771 : : (data[i] >= 62 && data[i] <= 126) )
772 : 0 : DataOctet(data[i]);
773 : :
774 [ # # ][ # # ]: 0 : else if ( data[i] == HT || data[i] == SP )
775 : 0 : DataOctet(data[i]);
776 : :
777 : : else
778 : : {
779 : 0 : IllegalEncoding(fmt("control characters in quoted-printable encoding: %d", (int) (data[i])));
780 : 0 : DataOctet(data[i]);
781 : : }
782 : : }
783 : :
784 [ # # ]: 0 : if ( ! soft_line_break )
785 : : {
786 : 0 : DataOctet(CR);
787 : 0 : DataOctet(LF);
788 : : }
789 : 0 : }
790 : :
791 : 0 : void MIME_Entity::DecodeBase64(int len, const char* data)
792 : : {
793 : : int rlen;
794 : : char rbuf[128];
795 : :
796 [ # # ]: 0 : while ( len > 0 )
797 : : {
798 : 0 : rlen = 128;
799 : 0 : char* prbuf = rbuf;
800 : 0 : int decoded = base64_decoder->Decode(len, data, &rlen, &prbuf);
801 [ # # ]: 0 : if ( prbuf != rbuf )
802 : 0 : internal_error("buffer pointer modified in base64 decoding");
803 : 0 : DataOctets(rlen, rbuf);
804 : 0 : len -= decoded; data += decoded;
805 : : }
806 : 0 : }
807 : :
808 : 0 : void MIME_Entity::StartDecodeBase64()
809 : : {
810 [ # # ]: 0 : if ( base64_decoder )
811 : 0 : internal_error("previous Base64 decoder not released!");
812 : :
813 : 0 : base64_decoder = new Base64Decoder(message->GetAnalyzer());
814 : 0 : }
815 : :
816 : 0 : void MIME_Entity::FinishDecodeBase64()
817 : : {
818 [ # # ]: 0 : if ( ! base64_decoder )
819 : 0 : return;
820 : :
821 : 0 : int rlen = 128;
822 : : char rbuf[128];
823 : 0 : char* prbuf = rbuf;
824 : :
825 [ # # ]: 0 : if ( base64_decoder->Done(&rlen, &prbuf) )
826 : : { // some remaining data
827 [ # # ]: 0 : if ( prbuf != rbuf )
828 : 0 : internal_error("buffer pointer modified in base64 decoding");
829 [ # # ]: 0 : if ( rlen > 0 )
830 : 0 : DataOctets(rlen, rbuf);
831 : : }
832 : :
833 [ # # ]: 0 : delete base64_decoder;
834 : 0 : base64_decoder = 0;
835 : : }
836 : :
837 : 0 : int MIME_Entity::GetDataBuffer()
838 : : {
839 : 0 : int ret = message->RequestBuffer(&data_buf_length, &data_buf_data);
840 [ # # # # ]: 0 : if ( ! ret || data_buf_length == 0 || data_buf_data == 0 )
[ # # ]
841 : : {
842 : : // internal_error("cannot get data buffer from MIME_Message", "");
843 : 0 : return 0;
844 : : }
845 : :
846 : 0 : data_buf_offset = 0;
847 : 0 : return 1;
848 : : }
849 : :
850 : 0 : void MIME_Entity::DataOctet(char ch)
851 : : {
852 [ # # ][ # # ]: 0 : if ( data_buf_offset < 0 && ! GetDataBuffer() )
[ # # ]
853 : 0 : return;
854 : :
855 : 0 : data_buf_data[data_buf_offset] = ch;
856 : :
857 : 0 : ++data_buf_offset;
858 [ # # ]: 0 : if ( data_buf_offset == data_buf_length )
859 : : {
860 : 0 : SubmitData(data_buf_length, data_buf_data);
861 : 0 : data_buf_offset = -1;
862 : : }
863 : : }
864 : :
865 : 0 : void MIME_Entity::SubmitData(int len, const char* buf)
866 : : {
867 : 0 : message->SubmitData(len, buf);
868 : 0 : }
869 : :
870 : 0 : void MIME_Entity::DataOctets(int len, const char* data)
871 : : {
872 [ # # ]: 0 : while ( len > 0 )
873 : : {
874 [ # # ][ # # ]: 0 : if ( data_buf_offset < 0 && ! GetDataBuffer() )
[ # # ]
875 : 0 : return;
876 : :
877 [ # # ][ # # ]: 0 : while ( data_buf_offset < data_buf_length && len > 0 )
878 : : {
879 : 0 : data_buf_data[data_buf_offset++] = *data;
880 : 0 : ++data; --len;
881 : : }
882 : :
883 [ # # ]: 0 : if ( data_buf_offset == data_buf_length )
884 : : {
885 : 0 : SubmitData(data_buf_length, data_buf_data);
886 : 0 : data_buf_offset = -1;
887 : : }
888 : : }
889 : : }
890 : :
891 : 7984 : void MIME_Entity::SubmitHeader(MIME_Header* h)
892 : : {
893 : 7984 : message->SubmitHeader(h);
894 : 7984 : }
895 : :
896 : 881 : void MIME_Entity::SubmitAllHeaders()
897 : : {
898 : 881 : message->SubmitAllHeaders(headers);
899 : 881 : }
900 : :
901 : 0 : void MIME_Entity::BeginChildEntity()
902 : : {
903 [ # # ]: 0 : ASSERT(current_child_entity == 0);
904 : 0 : current_child_entity = NewChildEntity();
905 : 0 : message->BeginEntity(current_child_entity);
906 : 0 : }
907 : :
908 : 0 : void MIME_Entity::EndChildEntity()
909 : : {
910 [ # # ]: 0 : ASSERT(current_child_entity != 0);
911 : :
912 : 0 : current_child_entity->EndOfData();
913 [ # # ]: 0 : delete current_child_entity;
914 : 0 : current_child_entity = 0;
915 : 0 : }
916 : :
917 : 4 : void MIME_Entity::IllegalFormat(const char* explanation)
918 : : {
919 : 4 : message->SubmitEvent(MIME_EVENT_ILLEGAL_FORMAT, explanation);
920 : 4 : }
921 : :
922 : 0 : void MIME_Entity::IllegalEncoding(const char* explanation)
923 : : {
924 : 0 : message->SubmitEvent(MIME_EVENT_ILLEGAL_ENCODING, explanation);
925 : 0 : }
926 : :
927 : 0 : void MIME_Entity::DebugPrintHeaders()
928 : : {
929 : : #ifdef DEBUG_BRO
930 : : for ( unsigned int i = 0; i < headers.size(); ++i )
931 : : {
932 : : MIME_Header* h = headers[i];
933 : :
934 : : DEBUG_fputs(h->get_name(), stderr);
935 : : DEBUG_MSG(":\"");
936 : : DEBUG_fputs(h->get_value(), stderr);
937 : : DEBUG_MSG("\"\n");
938 : : }
939 : : #endif
940 : 0 : }
941 : :
942 : 0 : RecordVal* MIME_Message::BuildHeaderVal(MIME_Header* h)
943 : : {
944 : 0 : RecordVal* header_record = new RecordVal(mime_header_rec);
945 : 0 : header_record->Assign(0, new_string_val(h->get_name())->ToUpper());
946 : 0 : header_record->Assign(1, new_string_val(h->get_value()));
947 : 0 : return header_record;
948 : : }
949 : :
950 : 0 : TableVal* MIME_Message::BuildHeaderTable(MIME_HeaderList& hlist)
951 : : {
952 : 0 : TableVal* t = new TableVal(mime_header_list);
953 : :
954 [ # # ]: 0 : for ( unsigned int i = 0; i < hlist.size(); ++i )
955 : : {
956 : 0 : Val* index = new Val(i+1, TYPE_COUNT); // index starting from 1
957 : :
958 : 0 : MIME_Header* h = hlist[i];
959 : 0 : RecordVal* header_record = BuildHeaderVal(h);
960 : :
961 : 0 : t->Assign(index, header_record);
962 : :
963 : 0 : Unref(index);
964 : : }
965 : :
966 : 0 : return t;
967 : : }
968 : :
969 : 0 : MIME_Mail::MIME_Mail(Analyzer* mail_analyzer, int buf_size)
970 : 0 : : MIME_Message(mail_analyzer)
971 : : {
972 : 0 : analyzer = mail_analyzer;
973 : :
974 : 0 : min_overlap_length = mime_segment_overlap_length;
975 : 0 : max_chunk_length = mime_segment_length;
976 : 0 : int length = buf_size;
977 : :
978 [ # # # # ]: 0 : if ( min_overlap_length < 0 )
979 : 0 : min_overlap_length = 0;
980 : :
981 [ # # ][ # # ]: 0 : if ( max_chunk_length < min_overlap_length + 32 )
982 : 0 : max_chunk_length = min_overlap_length + 32;
983 : :
984 [ # # ][ # # ]: 0 : if ( length < max_chunk_length )
985 : 0 : length = max_chunk_length;
986 : :
987 : 0 : buffer_start = data_start = 0;
988 : 0 : data_buffer = new BroString(1, new u_char[length+1], length);
989 : :
990 [ # # # # ]: 0 : if ( mime_content_hash )
991 : : {
992 : 0 : compute_content_hash = 1;
993 : 0 : content_hash_length = 0;
994 : 0 : md5_init(&md5_hash);
995 : : }
996 : : else
997 : 0 : compute_content_hash = 0;
998 : :
999 : 0 : top_level = new MIME_Entity(this, 0); // to be changed to MIME_Mail
1000 : 0 : BeginEntity(top_level);
1001 : 0 : }
1002 : :
1003 : 0 : void MIME_Mail::Done()
1004 : : {
1005 : 0 : top_level->EndOfData();
1006 : :
1007 : 0 : SubmitAllData();
1008 : :
1009 [ # # # # ]: 0 : if ( compute_content_hash && mime_content_hash )
[ # # ]
1010 : : {
1011 : 0 : u_char* digest = new u_char[16];
1012 : 0 : md5_finish(&md5_hash, digest);
1013 : :
1014 : 0 : val_list* vl = new val_list;
1015 : 0 : vl->append(analyzer->BuildConnVal());
1016 : 0 : vl->append(new Val(content_hash_length, TYPE_COUNT));
1017 : 0 : vl->append(new StringVal(new BroString(1, digest, 16)));
1018 : 0 : analyzer->ConnectionEvent(mime_content_hash, vl);
1019 : : }
1020 : :
1021 : 0 : MIME_Message::Done();
1022 : 0 : }
1023 : :
1024 : 0 : MIME_Mail::~MIME_Mail()
1025 : : {
1026 : 0 : delete_strings(all_content);
1027 [ # # # # ]: 0 : delete data_buffer;
[ # # ]
1028 [ # # ][ # # ]: 0 : delete top_level;
[ # # ]
1029 [ # # ][ # # ]: 0 : }
[ # # ]
1030 : :
1031 : 0 : void MIME_Mail::BeginEntity(MIME_Entity* /* entity */)
1032 : : {
1033 [ # # ]: 0 : if ( mime_begin_entity )
1034 : : {
1035 : 0 : val_list* vl = new val_list;
1036 : 0 : vl->append(analyzer->BuildConnVal());
1037 : 0 : analyzer->ConnectionEvent(mime_begin_entity, vl);
1038 : : }
1039 : 0 : buffer_start = data_start = 0;
1040 [ # # ]: 0 : ASSERT(entity_content.size() == 0);
1041 : 0 : }
1042 : :
1043 : 0 : void MIME_Mail::EndEntity(MIME_Entity* /* entity */)
1044 : : {
1045 [ # # ]: 0 : if ( mime_entity_data )
1046 : : {
1047 : 0 : BroString* s = concatenate(entity_content);
1048 : :
1049 : 0 : val_list* vl = new val_list();
1050 : 0 : vl->append(analyzer->BuildConnVal());
1051 : 0 : vl->append(new Val(s->Len(), TYPE_COUNT));
1052 : 0 : vl->append(new StringVal(s));
1053 : :
1054 : 0 : analyzer->ConnectionEvent(mime_entity_data, vl);
1055 : :
1056 [ # # ]: 0 : if ( ! mime_all_data )
1057 : 0 : delete_strings(entity_content);
1058 : : else
1059 : 0 : entity_content.clear();
1060 : : }
1061 : :
1062 [ # # ]: 0 : if ( mime_end_entity )
1063 : : {
1064 : 0 : val_list* vl = new val_list;
1065 : 0 : vl->append(analyzer->BuildConnVal());
1066 : 0 : analyzer->ConnectionEvent(mime_end_entity, vl);
1067 : : }
1068 : 0 : }
1069 : :
1070 : 0 : void MIME_Mail::SubmitHeader(MIME_Header* h)
1071 : : {
1072 [ # # ]: 0 : if ( mime_one_header )
1073 : : {
1074 : 0 : val_list* vl = new val_list();
1075 : 0 : vl->append(analyzer->BuildConnVal());
1076 : 0 : vl->append(BuildHeaderVal(h));
1077 : 0 : analyzer->ConnectionEvent(mime_one_header, vl);
1078 : : }
1079 : 0 : }
1080 : :
1081 : 0 : void MIME_Mail::SubmitAllHeaders(MIME_HeaderList& hlist)
1082 : : {
1083 [ # # ]: 0 : if ( mime_all_headers )
1084 : : {
1085 : 0 : val_list* vl = new val_list();
1086 : 0 : vl->append(analyzer->BuildConnVal());
1087 : 0 : vl->append(BuildHeaderTable(hlist));
1088 : 0 : analyzer->ConnectionEvent(mime_all_headers, vl);
1089 : : }
1090 : 0 : }
1091 : :
1092 : 0 : void MIME_Mail::SubmitData(int len, const char* buf)
1093 : : {
1094 [ # # ]: 0 : if ( buf != (char*) data_buffer->Bytes() + buffer_start )
1095 : 0 : internal_error("buffer misalignment");
1096 : :
1097 [ # # ]: 0 : if ( compute_content_hash )
1098 : : {
1099 : 0 : content_hash_length += len;
1100 : 0 : md5_append(&md5_hash, (const u_char*) buf, len);
1101 : : }
1102 : :
1103 [ # # ][ # # ]: 0 : if ( mime_entity_data || mime_all_data )
[ # # ]
1104 : : {
1105 : 0 : BroString* s = new BroString((const u_char*) buf, len, 0);
1106 : :
1107 [ # # ]: 0 : if ( mime_entity_data )
1108 : 0 : entity_content.push_back(s);
1109 [ # # ]: 0 : if ( mime_all_data )
1110 : 0 : all_content.push_back(s);
1111 : : }
1112 : :
1113 [ # # ]: 0 : if ( mime_segment_data )
1114 : : {
1115 : 0 : const char* data = (char*) data_buffer->Bytes() + data_start;
1116 : 0 : int data_len = (buf + len) - data;
1117 : :
1118 : 0 : val_list* vl = new val_list();
1119 : 0 : vl->append(analyzer->BuildConnVal());
1120 : 0 : vl->append(new Val(data_len, TYPE_COUNT));
1121 : 0 : vl->append(new StringVal(data_len, data));
1122 : 0 : analyzer->ConnectionEvent(mime_segment_data, vl);
1123 : : }
1124 : :
1125 : 0 : buffer_start = (buf + len) - (char*)data_buffer->Bytes();
1126 : 0 : }
1127 : :
1128 : 0 : int MIME_Mail::RequestBuffer(int* plen, char** pbuf)
1129 : : {
1130 : 0 : data_start = buffer_start - min_overlap_length;
1131 [ # # ]: 0 : if ( data_start < 0 )
1132 : 0 : data_start = 0;
1133 : :
1134 : 0 : int overlap = buffer_start - data_start;
1135 : 0 : int buffer_end = data_start + max_chunk_length;
1136 [ # # ]: 0 : if ( buffer_end > data_buffer->Len() )
1137 : : {
1138 : : // Copy every thing in [data_start, buffer_start) to
1139 : : // [0, overlap).
1140 [ # # ]: 0 : if ( buffer_start > data_start )
1141 : : memcpy(data_buffer->Bytes(),
1142 : 0 : data_buffer->Bytes() + data_start, overlap);
1143 : 0 : data_start = 0;
1144 : 0 : buffer_start = overlap;
1145 : : }
1146 : :
1147 : 0 : *plen = max_chunk_length - overlap;
1148 : 0 : *pbuf = (char*) data_buffer->Bytes() + buffer_start;
1149 : :
1150 : 0 : return 1;
1151 : : }
1152 : :
1153 : 0 : void MIME_Mail::SubmitAllData()
1154 : : {
1155 [ # # ]: 0 : if ( mime_all_data )
1156 : : {
1157 : 0 : BroString* s = concatenate(all_content);
1158 : 0 : delete_strings(all_content);
1159 : :
1160 : 0 : val_list* vl = new val_list();
1161 : 0 : vl->append(analyzer->BuildConnVal());
1162 : 0 : vl->append(new Val(s->Len(), TYPE_COUNT));
1163 : 0 : vl->append(new StringVal(s));
1164 : :
1165 : 0 : analyzer->ConnectionEvent(mime_all_data, vl);
1166 : : }
1167 : 0 : }
1168 : :
1169 : 0 : void MIME_Mail::SubmitEvent(int event_type, const char* detail)
1170 : : {
1171 : 0 : const char* category = "";
1172 : :
1173 [ # # # ]: 0 : switch ( event_type ) {
1174 : : case MIME_EVENT_ILLEGAL_FORMAT:
1175 : 0 : category = "illegal format";
1176 : 0 : break;
1177 : :
1178 : : case MIME_EVENT_ILLEGAL_ENCODING:
1179 : 0 : category = "illegal encoding";
1180 : 0 : break;
1181 : :
1182 : : default:
1183 : 0 : internal_error("unrecognized MIME_Mail event");
1184 : : }
1185 : :
1186 [ # # ]: 0 : if ( mime_event )
1187 : : {
1188 : 0 : val_list* vl = new val_list();
1189 : 0 : vl->append(analyzer->BuildConnVal());
1190 : 0 : vl->append(new StringVal(category));
1191 : 0 : vl->append(new StringVal(detail));
1192 : 0 : analyzer->ConnectionEvent(mime_event, vl);
1193 : : }
1194 : 0 : }
1195 : :
1196 : :
1197 : 52267 : int strcasecmp_n(data_chunk_t s, const char* t)
1198 : : {
1199 : 52267 : return strcasecmp_n(s.length, s.data, t);
1200 : : }
1201 : :
1202 : 36841 : int is_lws(char ch)
1203 : : {
1204 [ + - ][ + + ]: 36841 : return ch == 9 || ch == 32;
1205 : : }
1206 : :
1207 : 0 : int MIME_count_leading_lws(int len, const char* data)
1208 : : {
1209 : : int i;
1210 [ # # ]: 0 : for ( i = 0; i < len; ++i )
1211 [ # # ]: 0 : if ( ! is_lws(data[i]) )
1212 : 0 : break;
1213 : 0 : return i;
1214 : : }
1215 : :
1216 : 0 : int MIME_count_trailing_lws(int len, const char* data)
1217 : : {
1218 : : int i;
1219 [ # # ]: 0 : for ( i = 0; i < len; ++i )
1220 [ # # ]: 0 : if ( ! is_lws(data[len - 1 - i]) )
1221 : 0 : break;
1222 : 0 : return i;
1223 : : }
1224 : :
1225 : : // See RFC 2822, page 11
1226 : 0 : int MIME_skip_comments(int len, const char* data)
1227 : : {
1228 [ # # ][ # # ]: 0 : if ( len == 0 || data[0] != '(' )
1229 : 0 : return 0;
1230 : :
1231 : 0 : int par = 0;
1232 [ # # ]: 0 : for ( int i = 0; i < len; ++i )
1233 : : {
1234 [ # # # # ]: 0 : switch ( data[i] ) {
1235 : : case '(':
1236 : 0 : ++par;
1237 : 0 : break;
1238 : :
1239 : : case ')':
1240 : 0 : --par;
1241 [ # # ]: 0 : if ( par == 0 )
1242 : 0 : return i + 1;
1243 : 0 : break;
1244 : :
1245 : : case '\\':
1246 : 0 : ++i;
1247 : : break;
1248 : : }
1249 : : }
1250 : :
1251 : 0 : return len;
1252 : : }
1253 : :
1254 : : // Skip over lws and comments, but not tspecials. Do not use this
1255 : : // function in quoted-string or comments.
1256 : 18654 : int MIME_skip_lws_comments(int len, const char* data)
1257 : : {
1258 : 18654 : int i = 0;
1259 [ + + ]: 18656 : while ( i < len )
1260 : : {
1261 [ + + ]: 18654 : if ( is_lws(data[i]) )
1262 : 2 : ++i;
1263 : : else
1264 : : {
1265 [ - + ]: 18652 : if ( data[i] == '(' )
1266 : 0 : i += MIME_skip_comments(len - i, data + i);
1267 : : else
1268 : 18652 : return i;
1269 : : }
1270 : : }
1271 : :
1272 : 18654 : return len;
1273 : : }
1274 : :
1275 : 7984 : int MIME_get_field_name(int len, const char* data, data_chunk_t* name)
1276 : : {
1277 : 7984 : int i = MIME_skip_lws_comments(len, data);
1278 [ + - ]: 7984 : while ( i < len )
1279 : : {
1280 : : int j;
1281 [ + - ]: 7984 : if ( MIME_is_field_name_char(data[i]) )
1282 : : {
1283 : 7984 : name->data = data + i;
1284 : :
1285 [ + - ]: 86641 : for ( j = i; j < len; ++j )
1286 [ + + ]: 86641 : if ( ! MIME_is_field_name_char(data[j]) )
1287 : 7984 : break;
1288 : :
1289 : 7984 : name->length = j - i;
1290 : 7984 : return j;
1291 : : }
1292 : :
1293 : 0 : j = MIME_skip_lws_comments(len - i, data + i);
1294 : 0 : i += (j > 0) ? j : 1;
1295 : : }
1296 : :
1297 : 7984 : return -1;
1298 : : }
1299 : :
1300 : : // See RFC 2045, page 12.
1301 : 17751 : int MIME_is_tspecial (char ch)
1302 : : {
1303 : : return ch == '(' || ch == ')' || ch == '<' || ch == '>' || ch == '@' ||
1304 : : ch == ',' || ch == ';' || ch == ':' || ch == '\\' || ch == '"' ||
1305 [ + - ][ + - ]: 17751 : ch == '/' || ch == '[' || ch == ']' || ch == '?' || ch == '=';
[ + - ][ + - ]
[ + - ][ + - ]
[ + + ][ + - ]
[ + - ][ + - ]
[ + + ][ + - ]
[ + - ][ + - ]
[ + + ]
1306 : : }
1307 : :
1308 : 94625 : int MIME_is_field_name_char (char ch)
1309 : : {
1310 [ + - ][ + - ]: 94625 : return ch >= 33 && ch <= 126 && ch != ':';
[ + + ]
1311 : : }
1312 : :
1313 : 17751 : int MIME_is_token_char (char ch)
1314 : : {
1315 [ + - ][ + - ]: 17751 : return ch >= 33 && ch <= 126 && ! MIME_is_tspecial(ch);
[ + + ]
1316 : : }
1317 : :
1318 : : // See RFC 2045, page 12.
1319 : : // A token is composed of characters that are not SPACE, CTLs or tspecials
1320 : 2227 : int MIME_get_token(int len, const char* data, data_chunk_t* token)
1321 : : {
1322 : 2227 : int i = MIME_skip_lws_comments(len, data);
1323 [ + - ]: 2227 : while ( i < len )
1324 : : {
1325 : : int j;
1326 : :
1327 [ + - ]: 2227 : if ( MIME_is_token_char(data[i]) )
1328 : : {
1329 : 2227 : token->data = (data + i);
1330 [ + + ]: 17198 : for ( j = i; j < len; ++j )
1331 : : {
1332 [ + + ]: 15524 : if ( ! MIME_is_token_char(data[j]) )
1333 : 553 : break;
1334 : : }
1335 : :
1336 : 2227 : token->length = j - i;
1337 : 2227 : return j;
1338 : : }
1339 : :
1340 : 0 : j = MIME_skip_lws_comments(len - i, data + i);
1341 : 0 : i += (j > 0) ? j : 1;
1342 : : }
1343 : :
1344 : 2227 : return -1;
1345 : : }
1346 : :
1347 : 451 : int MIME_get_slash_token_pair(int len, const char* data, data_chunk_t* first, data_chunk_t* second)
1348 : : {
1349 : : int offset;
1350 : 451 : const char* data_start = data;
1351 : :
1352 : 451 : offset = MIME_get_token(len, data, first);
1353 [ - + ]: 451 : if ( offset < 0 )
1354 : : {
1355 : : // DEBUG_MSG("first token missing in slash token pair");
1356 : 0 : return -1;
1357 : : }
1358 : :
1359 : 451 : data += offset;
1360 : 451 : len -= offset;
1361 : :
1362 : 451 : offset = MIME_skip_lws_comments(len, data);
1363 [ + - + - ]: 451 : if ( offset < 0 || offset >= len || data[offset] != '/' )
[ - + ]
1364 : : {
1365 : : // DEBUG_MSG("/ not found in slash token pair");
1366 : 0 : return -1;
1367 : : }
1368 : :
1369 : 451 : ++offset;
1370 : 451 : data += offset;
1371 : 451 : len -= offset;
1372 : :
1373 : 451 : offset = MIME_get_token(len, data, second);
1374 [ - + ]: 451 : if ( offset < 0 )
1375 : : {
1376 : : // DEBUG_MSG("second token missing in slash token pair");
1377 : 0 : return -1;
1378 : : }
1379 : :
1380 : 451 : data += offset;
1381 : 451 : len -= offset;
1382 : :
1383 : 451 : return data - data_start;
1384 : : }
1385 : :
1386 : : // See RFC 2822, page 13.
1387 : 0 : int MIME_get_quoted_string(int len, const char* data, data_chunk_t* str)
1388 : : {
1389 : 0 : int offset = MIME_skip_lws_comments(len, data);
1390 : :
1391 : 0 : len -= offset;
1392 : 0 : data += offset;
1393 : :
1394 [ # # # # ]: 0 : if ( len <= 0 || *data != '"' )
1395 : 0 : return -1;
1396 : :
1397 [ # # ]: 0 : for ( int i = 1; i < len; ++i )
1398 : : {
1399 [ # # # ]: 0 : switch ( data[i] ) {
1400 : : case '"':
1401 : 0 : str->data = data + 1;
1402 : 0 : str->length = i - 1;
1403 : 0 : return offset + i + 1;
1404 : :
1405 : : case '\\':
1406 : 0 : ++i;
1407 : : break;
1408 : : }
1409 : : }
1410 : :
1411 : 0 : return -1;
1412 : : }
1413 : :
1414 : 2 : int MIME_get_value(int len, const char* data, BroString*& buf)
1415 : : {
1416 : 2 : int offset = MIME_skip_lws_comments(len, data);
1417 : :
1418 : 2 : len -= offset;
1419 : 2 : data += offset;
1420 : :
1421 [ + - - + ]: 2 : if ( len > 0 && *data == '"' )
1422 : : {
1423 : : data_chunk_t str;
1424 : 0 : int end = MIME_get_quoted_string(len, data, &str);
1425 [ # # ]: 0 : if ( end < 0 )
1426 : 0 : return -1;
1427 : :
1428 : 0 : buf = MIME_decode_quoted_pairs(str);
1429 : 0 : return offset + end;
1430 : : }
1431 : :
1432 : : else
1433 : : {
1434 : : data_chunk_t str;
1435 : 2 : int end = MIME_get_token(len, data, &str);
1436 [ - + ]: 2 : if ( end < 0 )
1437 : 0 : return -1;
1438 : :
1439 : 4 : buf = new BroString((const u_char*)str.data, str.length, 1);
1440 : 2 : return offset + end;
1441 : : }
1442 : : }
1443 : :
1444 : : // Decode each quoted-pair: a '\' followed by a character by the
1445 : : // quoted character. The decoded string is returned.
1446 : :
1447 : 0 : BroString* MIME_decode_quoted_pairs(data_chunk_t buf)
1448 : : {
1449 : 0 : const char* data = buf.data;
1450 : 0 : char* dest = new char[buf.length+1];
1451 : 0 : int j = 0;
1452 [ # # ]: 0 : for ( int i = 0; i < buf.length; ++i )
1453 [ # # ]: 0 : if ( data[i] == '\\' )
1454 : : {
1455 [ # # ]: 0 : if ( ++i < buf.length )
1456 : 0 : dest[j++] = data[i];
1457 : : else
1458 : : {
1459 : : // a trailing '\' -- don't know what
1460 : : // to do with it -- ignore it.
1461 : : }
1462 : : }
1463 : : else
1464 : 0 : dest[j++] = data[i];
1465 : 0 : dest[j] = 0;
1466 : :
1467 : 0 : return new BroString(1, (byte_vec) dest, j);
1468 [ + - ][ + - ]: 6 : }
|