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