1 #include "AmMimeBody.h"
2 #include "sip/parse_common.h"
3 #include "sip/parse_header.h"
4 #include "sip/defs.h"
5
6 #include "log.h"
7 #include "AmUtils.h"
8
9 #include <memory>
10 using std::unique_ptr;
11
12 #define MULTIPART "multipart"
13 #define MULTIPART_MIXED "multipart/mixed"
14 #define MULTIPART_ALTERNATIVE "multipart/alternative"
15
16 #define BOUNDARY_str "boundary"
17 #define BOUNDARY_len (sizeof(BOUNDARY_str)-/*0-term*/1)
18
AmMimeBody()19 AmMimeBody::AmMimeBody()
20 : content_len(0),
21 payload(NULL)
22 {
23 }
24
AmMimeBody(const AmMimeBody & body)25 AmMimeBody::AmMimeBody(const AmMimeBody& body)
26 : ct(body.ct),
27 hdrs(body.hdrs),
28 content_len(0),
29 payload(NULL)
30 {
31 if(body.payload && body.content_len) {
32 setPayload(body.payload,body.content_len);
33 }
34
35 for(Parts::const_iterator it = body.parts.begin();
36 it != body.parts.end(); ++it) {
37 parts.push_back(new AmMimeBody(**it));
38 }
39 }
40
~AmMimeBody()41 AmMimeBody::~AmMimeBody()
42 {
43 clearParts();
44 clearPayload();
45 }
46
operator =(const AmMimeBody & r_body)47 const AmMimeBody& AmMimeBody::operator = (const AmMimeBody& r_body)
48 {
49 if (&r_body != this) {
50 ct = r_body.ct;
51 hdrs = r_body.hdrs;
52
53 if(r_body.payload && r_body.content_len) {
54 setPayload(r_body.payload,r_body.content_len);
55 }
56 else {
57 clearPayload();
58 }
59
60 clearParts();
61 for(Parts::const_iterator it = r_body.parts.begin();
62 it != r_body.parts.end(); ++it) {
63 parts.push_back(new AmMimeBody(**it));
64 }
65 }
66 return r_body;
67 }
68
AmContentType()69 AmContentType::AmContentType()
70 : mp_boundary(NULL)
71 {
72 }
73
AmContentType(const AmContentType & ct)74 AmContentType::AmContentType(const AmContentType& ct)
75 : type(ct.type),
76 subtype(ct.subtype),
77 mp_boundary(NULL)
78 {
79 for(Params::const_iterator it = ct.params.begin();
80 it != ct.params.end(); ++it) {
81 params.push_back(new Param(**it));
82 if((*it)->type == Param::BOUNDARY)
83 mp_boundary = params.back();
84 }
85 }
86
~AmContentType()87 AmContentType::~AmContentType()
88 {
89 clearParams();
90 }
91
operator =(const AmContentType & r_ct)92 const AmContentType& AmContentType::operator = (const AmContentType& r_ct)
93 {
94 type = r_ct.type;
95 subtype = r_ct.subtype;
96
97 clearParams();
98 for(Params::const_iterator it = r_ct.params.begin();
99 it != r_ct.params.end(); ++it) {
100 params.push_back(new Param(**it));
101 if((*it)->type == Param::BOUNDARY)
102 mp_boundary = params.back();
103 }
104
105 return r_ct;
106 }
107
clearParams()108 void AmContentType::clearParams()
109 {
110 mp_boundary = NULL;
111 while(!params.empty()){
112 delete params.front();
113 params.pop_front();
114 }
115 }
116
resetBoundary()117 void AmContentType::resetBoundary()
118 {
119 Params::iterator it = params.begin();
120 while (it != params.end()) {
121 Params::iterator l_it = it;
122 it++;
123 if ((*l_it)->type == Param::BOUNDARY) {
124 delete *l_it;
125 params.erase(l_it);
126 }
127 }
128
129 params.push_back(new Param(BOUNDARY_str, int2hex(get_random())));
130 params.back()->type = Param::BOUNDARY;
131 mp_boundary = params.back();
132 }
133
clearParts()134 void AmMimeBody::clearParts()
135 {
136 while(!parts.empty()){
137 delete parts.front();
138 parts.pop_front();
139 }
140 }
141
clearPart(Parts::iterator position)142 void AmMimeBody::clearPart(Parts::iterator position)
143 {
144 parts.erase(position);
145 }
146
clearPayload()147 void AmMimeBody::clearPayload()
148 {
149 delete [] payload;
150 payload = NULL;
151 }
152
parse(const string & ct)153 int AmContentType::parse(const string& ct)
154 {
155 enum {
156 CT_TYPE=0,
157 CT_SLASH_SWS,
158 CT_SUBTYPE_SWS,
159 CT_SUBTYPE
160 };
161
162 const char* c = ct.c_str();
163 const char* beg = c;
164 const char* end = c + ct.length();
165
166 int st = CT_TYPE;
167 int saved_st = 0;
168
169 for(;c < end; c++) {
170 switch(st){
171
172 case CT_TYPE:
173 switch(*c) {
174 case_CR_LF;
175 case SP:
176 case HTAB:
177 type = string(beg,c-beg);
178 st = CT_SLASH_SWS;
179 break;
180
181 case SLASH:
182 type = string(beg,c-beg);
183 st = CT_SUBTYPE_SWS;
184 break;
185 }
186 break;
187
188 case CT_SLASH_SWS:
189 switch(*c){
190 case_CR_LF;
191 case SP:
192 case HTAB:
193 break;
194
195 case SLASH:
196 st = CT_SUBTYPE_SWS;
197 break;
198
199 default:
200 DBG("Missing '/' after media type in 'Content-Type' hdr field\n");
201 return -1;
202 }
203 break;
204
205 case CT_SUBTYPE_SWS:
206 switch(*c){
207 case_CR_LF;
208 case SP:
209 case HTAB:
210 break;
211
212 default:
213 st = CT_SUBTYPE;
214 beg = c;
215 break;
216 }
217 break;
218
219 case CT_SUBTYPE:
220 switch(*c){
221
222 case_CR_LF;
223
224 case SP:
225 case HTAB:
226 case SEMICOLON:
227 subtype = string(beg,c-beg);
228 return parseParams(c,end);
229 }
230 break;
231
232 case_ST_CR(*c);
233 case ST_LF:
234 case ST_CRLF:
235 switch(saved_st){
236 case CT_TYPE:
237 if(!IS_WSP(*c)){
238 // should not happen: parse_headers() should already
239 // have triggered an error
240 DBG("Malformed Content-Type value: <%.*s>\n",(int)(end-beg),beg);
241 return -1;
242 }
243 else {
244 type = string(beg,(c-(st==ST_CRLF?2:1))-beg);
245 saved_st = CT_SLASH_SWS;
246 }
247 break;
248
249 case CT_SLASH_SWS:
250 case CT_SUBTYPE_SWS:
251 if(!IS_WSP(*c)){
252 // should not happen: parse_headers() should already
253 // have triggered an error
254 DBG("Malformed Content-Type value: <%.*s>\n",(int)(end-beg),beg);
255 return -1;
256 }
257 break;
258
259 case CT_SUBTYPE:
260 subtype = string(beg,(c-(st==ST_CRLF?2:1))-beg);
261 if(!IS_WSP(*c)){
262 // should not happen: parse_headers() should already
263 // have triggered an error
264 return 0;
265 }
266 return parseParams(c,end);
267 }
268
269 st = saved_st;
270 break;
271 }
272 }
273
274 // end-of-string
275 switch(st){
276 case CT_TYPE:
277 case CT_SLASH_SWS:
278 case CT_SUBTYPE_SWS:
279 DBG("Malformed Content-Type value: <%.*s>\n",(int)(end-beg),beg);
280 return -1;
281
282 case CT_SUBTYPE:
283 subtype = string(beg,c-beg);
284 break;
285 }
286
287 return 0;
288 }
289
parseParams(const char * c,const char * end)290 int AmContentType::parseParams(const char* c, const char* end)
291 {
292 list<sip_avp*> avp_params;
293 if(parse_gen_params_sc(&avp_params, &c, end-c, '\0') < 0) {
294 if(!avp_params.empty()) free_gen_params(&avp_params);
295 return -1;
296 }
297
298 for(list<sip_avp*>::iterator it_ct_param = avp_params.begin();
299 it_ct_param != avp_params.end();++it_ct_param) {
300
301 DBG("parsed new content-type parameter: <%.*s>=<%.*s>",
302 (*it_ct_param)->name.len,(*it_ct_param)->name.s,
303 (*it_ct_param)->value.len,(*it_ct_param)->value.s);
304
305 Param* p = new Param(c2stlstr((*it_ct_param)->name),
306 c2stlstr((*it_ct_param)->value));
307
308 if(p->parseType()) {
309 free_gen_params(&avp_params);
310 delete p;
311 return -1;
312 }
313
314 if(p->type == Param::BOUNDARY)
315 mp_boundary = p;
316
317 params.push_back(p);
318 }
319
320 free_gen_params(&avp_params);
321 return 0;
322 }
323
parseType()324 int AmContentType::Param::parseType()
325 {
326 const char* c = name.c_str();
327 unsigned len = name.length();
328
329 switch(len){
330 case BOUNDARY_len:
331 if(!lower_cmp(c,BOUNDARY_str,len)){
332 if(value.empty()) {
333 DBG("Content-Type boundary parameter is missing a value\n");
334 return -1;
335 }
336 type = Param::BOUNDARY;
337 }
338 else type = Param::OTHER;
339 break;
340 default:
341 type = Param::OTHER;
342 break;
343 }
344
345 return 0;
346 }
347
findNextBoundary(unsigned char ** beg,unsigned char ** end)348 int AmMimeBody::findNextBoundary(unsigned char** beg, unsigned char** end)
349 {
350 enum {
351 B_START=0,
352 B_CR,
353 B_LF,
354 B_HYPHEN,
355 B_BOUNDARY,
356 B_HYPHEN2,
357 B_HYPHEN3,
358 B_CR2,
359 B_LF2,
360 B_MATCHED
361 };
362
363 if(!ct.mp_boundary)
364 return -1;
365
366 unsigned char* c = *beg;
367 unsigned char* b_ini = (unsigned char*)ct.mp_boundary->value.c_str();
368 unsigned char* b = b_ini;
369 unsigned char* b_end = b + ct.mp_boundary->value.length();
370
371 int st=B_START;
372
373 // Allow the buffer to start directly
374 // with the boundary delimiter
375 if(*c == HYPHEN)
376 st=B_LF;
377
378 #define case_BOUNDARY(_st_,_ch_,_st1_) \
379 case _st_: \
380 switch(*c){ \
381 case _ch_: \
382 st = _st1_; \
383 break; \
384 default: \
385 st = B_START; \
386 break; \
387 } \
388 break
389
390 #define case_BOUNDARY2(_st_,_ch_,_st1_) \
391 case _st_: \
392 switch(*c){ \
393 case _ch_: \
394 st = _st1_; \
395 break; \
396 default: \
397 b = b_ini; \
398 is_final = false; \
399 st = B_START; \
400 break; \
401 } \
402 break
403
404
405 bool is_final = false;
406
407 for(;st != B_MATCHED && (c < *end-(b_end-b)); c++){
408
409 switch(st){
410 case B_START:
411 if(*c == CR) {
412 *beg = c;
413 st = B_CR;
414 }
415 break;
416
417 case_BOUNDARY(B_CR, LF, B_LF);
418
419 case B_LF:
420 switch(*c){
421 case HYPHEN:
422 st = B_HYPHEN;
423 break;
424 case CR:
425 st = B_CR;
426 *beg = c;
427 break;
428 default:
429 st = B_START;
430 break;
431 }
432 break;
433
434 case_BOUNDARY(B_HYPHEN, HYPHEN, B_BOUNDARY);
435
436 case B_BOUNDARY:
437 if(*c == *b) {
438 if(++b == b_end) {
439 // reached end boundary buffer
440 st = B_HYPHEN2;
441 }
442 }
443 else {
444 b = b_ini;
445 st = B_START;
446 }
447 break;
448
449 case B_HYPHEN2:
450 switch(*c) {
451 case HYPHEN:
452 is_final = true;
453 st = B_HYPHEN3;
454 break;
455
456 case CR:
457 st = B_LF2;
458 break;
459
460 default:
461 b = b_ini;
462 st = B_START;
463 break;
464 }
465 break;
466
467 case_BOUNDARY2(B_HYPHEN3, HYPHEN, B_CR2);
468 case_BOUNDARY2(B_CR2, CR, B_LF2);
469 case_BOUNDARY2(B_LF2, LF, B_MATCHED);
470 }
471 }
472
473 if(st == B_MATCHED || (st == B_HYPHEN3) || (st == B_CR2) || (st == B_LF2)) {
474 *end = c;
475 return is_final ? 1 : 0;
476 }
477
478 return -1;
479 }
480
parseSinglePart(unsigned char * buf,unsigned int len)481 int AmMimeBody::parseSinglePart(unsigned char* buf, unsigned int len)
482 {
483 list<sip_header*> hdrs;
484 char* c = (char*)buf;
485 char* end = c + len;
486
487 // parse headers first
488 if(parse_headers(hdrs,&c,c+len) < 0) {
489 DBG("could not parse part headers\n");
490 free_headers(hdrs);
491 return -1;
492 }
493
494 unique_ptr<AmMimeBody> sub_part(new AmMimeBody());
495
496 string sub_part_hdrs;
497 string sub_part_ct;
498 for(list<sip_header*>::iterator it = hdrs.begin();
499 it != hdrs.end(); ++it) {
500
501 DBG("Part header: <%.*s>: <%.*s>\n",
502 (*it)->name.len,(*it)->name.s,
503 (*it)->value.len,(*it)->value.s);
504
505 if((*it)->type == sip_header::H_CONTENT_TYPE) {
506 sub_part_ct = c2stlstr((*it)->value);
507 }
508 else {
509 sub_part_hdrs += c2stlstr((*it)->name) + COLSP
510 + c2stlstr((*it)->value) + CRLF;
511 }
512 }
513
514 if(!sub_part_ct.empty() &&
515 (sub_part->parse(sub_part_ct,(unsigned char*)c,end-c) == 0)) {
516 // TODO: check errors
517 DBG("Successfully parsed subpart.\n");
518 }
519 else {
520 DBG("Either no Content-Type, or subpart parsing failed.\n");
521 sub_part->ct.setType("");
522 sub_part->ct.setSubType("");
523 sub_part->setPayload((unsigned char*)c,end-c);
524 }
525
526 sub_part->setHeaders(sub_part_hdrs);
527 parts.push_back(sub_part.release());
528
529 free_headers(hdrs);
530 return 0;
531 }
532
parseMultipart(const unsigned char * buf,unsigned int len)533 int AmMimeBody::parseMultipart(const unsigned char* buf, unsigned int len)
534 {
535 if(!ct.mp_boundary) {
536 DBG("boundary parameter missing in a multipart MIME body\n");
537 return -1;
538 }
539
540 unsigned char* buf_end = (unsigned char*)buf + len;
541 unsigned char* part_end = (unsigned char*)buf;
542 unsigned char* next_part = (unsigned char*)buf_end;
543
544 int err = findNextBoundary(&part_end,&next_part);
545 if(err < 0) {
546 DBG("unexpected end-of-buffer\n");
547 return -1;
548 }
549
550 unsigned char* part_beg = NULL;
551 do {
552 part_beg = next_part;
553 part_end = part_beg;
554 next_part = buf_end;
555
556 err = findNextBoundary(&part_end,&next_part);
557 if(err < 0) {
558 DBG("unexpected end-of-buffer while searching for MIME body boundary\n");
559 return -1;
560 }
561
562 if(parseSinglePart(part_beg,part_end-part_beg) < 0) {
563 DBG("Failed parsing part\n");
564 }
565 else {
566 AmMimeBody* part = parts.back();
567 DBG("Added new part:\n%.*s\n",
568 part->content_len,part->payload);
569 }
570
571 } while(!err);
572
573 DBG("End-of-multipart body found\n");
574
575 return 0;
576 }
577
parse(const string & content_type,const unsigned char * buf,unsigned int len)578 int AmMimeBody::parse(const string& content_type,
579 const unsigned char* buf,
580 unsigned int len)
581 {
582 if(ct.parse(content_type) < 0)
583 return -1;
584
585 if(ct.isType(MULTIPART)) {
586
587 DBG("parsing multi-part body\n");
588 return parseMultipart(buf,len);
589 }
590 else {
591 DBG("saving single-part body\n");
592 setPayload(buf,len);
593 }
594
595 return 0;
596 }
597
convertToMultipart()598 void AmMimeBody::convertToMultipart()
599 {
600 AmContentType n_ct;
601 n_ct.parse(MULTIPART_MIXED); // never fails
602 n_ct.resetBoundary();
603
604 AmMimeBody* n_part = new AmMimeBody(*this);
605 n_part->ct = ct;
606
607 parts.push_back(n_part);
608 ct = n_ct;
609
610 content_len = 0;
611 }
612
convertToSinglepart()613 void AmMimeBody::convertToSinglepart()
614 {
615 if (parts.size() == 1) {
616 this->ct = parts.front()->ct;
617 setPayload(parts.front()->payload, parts.front()->content_len);
618 clearParts();
619 } else {
620 DBG("body does not have exactly one part\n");
621 }
622 }
623
setType(const string & t)624 void AmContentType::setType(const string& t)
625 {
626 type = t;
627 }
628
setSubType(const string & st)629 void AmContentType::setSubType(const string& st)
630 {
631 subtype = st;
632 }
633
isType(const string & t) const634 bool AmContentType::isType(const string& t) const
635 {
636 return !lower_cmp_n(t.c_str(),t.length(),
637 type.c_str(),type.length());
638 }
639
isSubType(const string & st) const640 bool AmContentType::isSubType(const string& st) const
641 {
642 return !lower_cmp_n(st.c_str(),st.length(),
643 subtype.c_str(),subtype.length());
644 }
645
646
setHeaders(const string & hdrs)647 void AmMimeBody::setHeaders(const string& hdrs)
648 {
649 this->hdrs = hdrs;
650 }
651
addPart(const string & content_type)652 AmMimeBody* AmMimeBody::addPart(const string& content_type)
653 {
654 AmMimeBody* body = NULL;
655 if(ct.type.empty() && ct.subtype.empty()) {
656 // fill *this* body
657 if(ct.parse(content_type)) {
658 DBG("could not parse content-type\n");
659 return NULL;
660 }
661
662 body = this;
663 }
664 else if(!ct.isType(MULTIPART)) {
665 // convert to multipart
666 convertToMultipart();
667 body = new AmMimeBody();
668 if(body->ct.parse(content_type)) {
669 DBG("parsing new content-type failed\n");
670 delete body;
671 return NULL;
672 }
673
674 // add new part
675 parts.push_back(body);
676 }
677
678 return body;
679 }
680
deletePart(const string & content_type)681 int AmMimeBody::deletePart(const string& content_type)
682 {
683 if (!ct.isType(MULTIPART)) {
684 DBG("body is not multipart\n");
685 return -1;
686 }
687
688 for(Parts::iterator it = parts.begin();
689 it != parts.end(); ++it) {
690 if((*it)->hasContentType(content_type)) {
691 clearPart(it);
692 if (parts.size() == 1) convertToSinglepart();
693 return 0;
694 }
695 }
696
697 DBG("no match");
698 return -1;
699 }
700
setPayload(const unsigned char * buf,unsigned int len)701 void AmMimeBody::setPayload(const unsigned char* buf, unsigned int len)
702 {
703 if(payload)
704 clearPayload();
705
706 payload = new unsigned char [len+1];
707 memcpy(payload,buf,len);
708 content_len = len;
709
710 // zero-term for the SDP parser
711 payload[len] = '\0';
712 }
713
empty() const714 bool AmMimeBody::empty() const
715 {
716 return (!payload || !content_len)
717 && parts.empty();
718 }
719
hasContentType(const string & content_type) const720 bool AmContentType::hasContentType(const string& content_type) const
721 {
722 if(content_type.empty() && type.empty() && subtype.empty())
723 return true;
724
725 if(content_type.empty() != (type.empty() && subtype.empty()))
726 return false;
727
728 // Quick & dirty comparison, might not always be correct
729 string cmp_ct = type + "/" + subtype;
730 return !lower_cmp_n(cmp_ct.c_str(),cmp_ct.length(),
731 content_type.c_str(),content_type.length());
732 }
733
getStr() const734 string AmContentType::getStr() const
735 {
736 if(type.empty() && subtype.empty())
737 return "";
738
739 return type + "/" + subtype;
740 }
741
getHdr() const742 string AmContentType::getHdr() const
743 {
744 string ct = getStr();
745 if(ct.empty())
746 return ct;
747
748 for(Params::const_iterator it = params.begin();
749 it != params.end(); ++it) {
750
751 ct += ";" + (*it)->name + "=" + (*it)->value;
752 }
753
754 return ct;
755 }
756
isContentType(const string & content_type) const757 bool AmMimeBody::isContentType(const string& content_type) const
758 {
759 return ct.hasContentType(content_type);
760 }
761
hasContentType(const string & content_type)762 AmMimeBody* AmMimeBody::hasContentType(const string& content_type)
763 {
764 if(isContentType(content_type)) {
765 return this;
766 }
767 else if(ct.isType(MULTIPART)) {
768 for(Parts::iterator it = parts.begin();
769 it != parts.end(); ++it) {
770
771 if((*it)->hasContentType(content_type)) {
772 return *it;
773 }
774 }
775 }
776
777 return NULL;
778 }
779
hasContentType(const string & content_type) const780 const AmMimeBody* AmMimeBody::hasContentType(const string& content_type) const
781 {
782 if(isContentType(content_type)) {
783 return this;
784 }
785 else if(ct.isType(MULTIPART)) {
786 for(Parts::const_iterator it = parts.begin();
787 it != parts.end(); ++it) {
788
789 if((*it)->hasContentType(content_type)) {
790 return *it;
791 }
792 }
793 }
794
795 return NULL;
796 }
797
print(string & buf) const798 void AmMimeBody::print(string& buf) const
799 {
800 if(empty())
801 return;
802
803 if(content_len) {
804 buf += string((const char*)payload,content_len);
805 }
806 else {
807
808 // if (ct.mp_boundary == NULL)
809 // ct.resetBoundary();
810
811 for(Parts::const_iterator it = parts.begin();
812 it != parts.end(); ++it) {
813
814 buf += "--" + (ct.mp_boundary != NULL ? ct.mp_boundary->value : string("") ) + CRLF;
815 buf += SIP_HDR_CONTENT_TYPE COLSP + (*it)->getCTHdr() + CRLF;
816 buf += (*it)->hdrs + CRLF;
817 (*it)->print(buf);
818 buf += CRLF;
819 }
820
821 if(!parts.empty()) {
822 buf += "--" + (ct.mp_boundary != NULL ? ct.mp_boundary->value : string("") ) + "--" CRLF;
823 }
824 }
825 }
826