mirror of https://github.com/sipwise/sems.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
823 lines
16 KiB
823 lines
16 KiB
#include "AmMimeBody.h"
|
|
#include "sip/parse_common.h"
|
|
#include "sip/parse_header.h"
|
|
#include "sip/defs.h"
|
|
|
|
#include "log.h"
|
|
#include "AmUtils.h"
|
|
|
|
#include <memory>
|
|
using std::auto_ptr;
|
|
|
|
#define MULTIPART "multipart"
|
|
#define MULTIPART_MIXED "multipart/mixed"
|
|
#define MULTIPART_ALTERNATIVE "multipart/alternative"
|
|
|
|
#define BOUNDARY_str "boundary"
|
|
#define BOUNDARY_len (sizeof(BOUNDARY_str)-/*0-term*/1)
|
|
|
|
AmMimeBody::AmMimeBody()
|
|
: content_len(0),
|
|
payload(NULL)
|
|
{
|
|
}
|
|
|
|
AmMimeBody::AmMimeBody(const AmMimeBody& body)
|
|
: ct(body.ct),
|
|
hdrs(body.hdrs),
|
|
content_len(0),
|
|
payload(NULL)
|
|
{
|
|
if(body.payload && body.content_len) {
|
|
setPayload(body.payload,body.content_len);
|
|
}
|
|
|
|
for(Parts::const_iterator it = body.parts.begin();
|
|
it != body.parts.end(); ++it) {
|
|
parts.push_back(new AmMimeBody(**it));
|
|
}
|
|
}
|
|
|
|
AmMimeBody::~AmMimeBody()
|
|
{
|
|
clearParts();
|
|
clearPayload();
|
|
}
|
|
|
|
const AmMimeBody& AmMimeBody::operator = (const AmMimeBody& r_body)
|
|
{
|
|
ct = r_body.ct;
|
|
hdrs = r_body.hdrs;
|
|
|
|
if(r_body.payload && r_body.content_len) {
|
|
setPayload(r_body.payload,r_body.content_len);
|
|
}
|
|
else {
|
|
clearPayload();
|
|
}
|
|
|
|
clearParts();
|
|
for(Parts::const_iterator it = r_body.parts.begin();
|
|
it != r_body.parts.end(); ++it) {
|
|
parts.push_back(new AmMimeBody(**it));
|
|
}
|
|
|
|
return r_body;
|
|
}
|
|
|
|
AmContentType::AmContentType()
|
|
: mp_boundary(NULL)
|
|
{
|
|
}
|
|
|
|
AmContentType::AmContentType(const AmContentType& ct)
|
|
: type(ct.type),
|
|
subtype(ct.subtype),
|
|
mp_boundary(NULL)
|
|
{
|
|
for(Params::const_iterator it = ct.params.begin();
|
|
it != ct.params.end(); ++it) {
|
|
params.push_back(new Param(**it));
|
|
if((*it)->type == Param::BOUNDARY)
|
|
mp_boundary = params.back();
|
|
}
|
|
}
|
|
|
|
AmContentType::~AmContentType()
|
|
{
|
|
clearParams();
|
|
}
|
|
|
|
const AmContentType& AmContentType::operator = (const AmContentType& r_ct)
|
|
{
|
|
type = r_ct.type;
|
|
subtype = r_ct.subtype;
|
|
|
|
clearParams();
|
|
for(Params::const_iterator it = r_ct.params.begin();
|
|
it != r_ct.params.end(); ++it) {
|
|
params.push_back(new Param(**it));
|
|
if((*it)->type == Param::BOUNDARY)
|
|
mp_boundary = params.back();
|
|
}
|
|
|
|
return r_ct;
|
|
}
|
|
|
|
void AmContentType::clearParams()
|
|
{
|
|
mp_boundary = NULL;
|
|
while(!params.empty()){
|
|
delete params.front();
|
|
params.pop_front();
|
|
}
|
|
}
|
|
|
|
void AmContentType::resetBoundary()
|
|
{
|
|
Params::iterator it = params.begin();
|
|
while (it != params.end()) {
|
|
Params::iterator l_it = it;
|
|
it++;
|
|
if ((*l_it)->type == Param::BOUNDARY)
|
|
delete *l_it;
|
|
params.erase(l_it);
|
|
}
|
|
|
|
params.push_back(new Param(BOUNDARY_str, int2hex(get_random())));
|
|
params.back()->type = Param::BOUNDARY;
|
|
mp_boundary = params.back();
|
|
}
|
|
|
|
void AmMimeBody::clearParts()
|
|
{
|
|
while(!parts.empty()){
|
|
delete parts.front();
|
|
parts.pop_front();
|
|
}
|
|
}
|
|
|
|
void AmMimeBody::clearPart(Parts::iterator position)
|
|
{
|
|
parts.erase(position);
|
|
}
|
|
|
|
void AmMimeBody::clearPayload()
|
|
{
|
|
delete [] payload;
|
|
payload = NULL;
|
|
}
|
|
|
|
int AmContentType::parse(const string& ct)
|
|
{
|
|
enum {
|
|
CT_TYPE=0,
|
|
CT_SLASH_SWS,
|
|
CT_SUBTYPE_SWS,
|
|
CT_SUBTYPE
|
|
};
|
|
|
|
const char* c = ct.c_str();
|
|
const char* beg = c;
|
|
const char* end = c + ct.length();
|
|
|
|
int st = CT_TYPE;
|
|
int saved_st = 0;
|
|
|
|
for(;c < end; c++) {
|
|
switch(st){
|
|
|
|
case CT_TYPE:
|
|
switch(*c) {
|
|
case_CR_LF;
|
|
case SP:
|
|
case HTAB:
|
|
type = string(beg,c-beg);
|
|
st = CT_SLASH_SWS;
|
|
break;
|
|
|
|
case SLASH:
|
|
type = string(beg,c-beg);
|
|
st = CT_SUBTYPE_SWS;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case CT_SLASH_SWS:
|
|
switch(*c){
|
|
case_CR_LF;
|
|
case SP:
|
|
case HTAB:
|
|
break;
|
|
|
|
case SLASH:
|
|
st = CT_SUBTYPE_SWS;
|
|
break;
|
|
|
|
default:
|
|
DBG("Missing '/' after media type in 'Content-Type' hdr field\n");
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case CT_SUBTYPE_SWS:
|
|
switch(*c){
|
|
case_CR_LF;
|
|
case SP:
|
|
case HTAB:
|
|
break;
|
|
|
|
default:
|
|
st = CT_SUBTYPE;
|
|
beg = c;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case CT_SUBTYPE:
|
|
switch(*c){
|
|
|
|
case_CR_LF;
|
|
|
|
case SP:
|
|
case HTAB:
|
|
case SEMICOLON:
|
|
subtype = string(beg,c-beg);
|
|
return parseParams(c,end);
|
|
}
|
|
break;
|
|
|
|
case_ST_CR(*c);
|
|
case ST_LF:
|
|
case ST_CRLF:
|
|
switch(saved_st){
|
|
case CT_TYPE:
|
|
if(!IS_WSP(*c)){
|
|
// should not happen: parse_headers() should already
|
|
// have triggered an error
|
|
DBG("Malformed Content-Type value: <%.*s>\n",(int)(end-beg),beg);
|
|
return -1;
|
|
}
|
|
else {
|
|
type = string(beg,(c-(st==ST_CRLF?2:1))-beg);
|
|
saved_st = CT_SLASH_SWS;
|
|
}
|
|
break;
|
|
|
|
case CT_SLASH_SWS:
|
|
case CT_SUBTYPE_SWS:
|
|
if(!IS_WSP(*c)){
|
|
// should not happen: parse_headers() should already
|
|
// have triggered an error
|
|
DBG("Malformed Content-Type value: <%.*s>\n",(int)(end-beg),beg);
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case CT_SUBTYPE:
|
|
subtype = string(beg,(c-(st==ST_CRLF?2:1))-beg);
|
|
if(!IS_WSP(*c)){
|
|
// should not happen: parse_headers() should already
|
|
// have triggered an error
|
|
return 0;
|
|
}
|
|
return parseParams(c,end);
|
|
}
|
|
|
|
st = saved_st;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// end-of-string
|
|
switch(st){
|
|
case CT_TYPE:
|
|
case CT_SLASH_SWS:
|
|
case CT_SUBTYPE_SWS:
|
|
DBG("Malformed Content-Type value: <%.*s>\n",(int)(end-beg),beg);
|
|
return -1;
|
|
|
|
case CT_SUBTYPE:
|
|
subtype = string(beg,c-beg);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int AmContentType::parseParams(const char* c, const char* end)
|
|
{
|
|
list<sip_avp*> avp_params;
|
|
if(parse_gen_params_sc(&avp_params, &c, end-c, '\0') < 0) {
|
|
if(!avp_params.empty()) free_gen_params(&avp_params);
|
|
return -1;
|
|
}
|
|
|
|
for(list<sip_avp*>::iterator it_ct_param = avp_params.begin();
|
|
it_ct_param != avp_params.end();++it_ct_param) {
|
|
|
|
DBG("parsed new content-type parameter: <%.*s>=<%.*s>",
|
|
(*it_ct_param)->name.len,(*it_ct_param)->name.s,
|
|
(*it_ct_param)->value.len,(*it_ct_param)->value.s);
|
|
|
|
Param* p = new Param(c2stlstr((*it_ct_param)->name),
|
|
c2stlstr((*it_ct_param)->value));
|
|
|
|
if(p->parseType()) {
|
|
free_gen_params(&avp_params);
|
|
delete p;
|
|
return -1;
|
|
}
|
|
|
|
if(p->type == Param::BOUNDARY)
|
|
mp_boundary = p;
|
|
|
|
params.push_back(p);
|
|
}
|
|
|
|
free_gen_params(&avp_params);
|
|
return 0;
|
|
}
|
|
|
|
int AmContentType::Param::parseType()
|
|
{
|
|
const char* c = name.c_str();
|
|
unsigned len = name.length();
|
|
|
|
switch(len){
|
|
case BOUNDARY_len:
|
|
if(!lower_cmp(c,BOUNDARY_str,len)){
|
|
if(value.empty()) {
|
|
DBG("Content-Type boundary parameter is missing a value\n");
|
|
return -1;
|
|
}
|
|
type = Param::BOUNDARY;
|
|
}
|
|
else type = Param::OTHER;
|
|
break;
|
|
default:
|
|
type = Param::OTHER;
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int AmMimeBody::findNextBoundary(unsigned char** beg, unsigned char** end)
|
|
{
|
|
enum {
|
|
B_START=0,
|
|
B_CR,
|
|
B_LF,
|
|
B_HYPHEN,
|
|
B_BOUNDARY,
|
|
B_HYPHEN2,
|
|
B_HYPHEN3,
|
|
B_CR2,
|
|
B_LF2,
|
|
B_MATCHED
|
|
};
|
|
|
|
if(!ct.mp_boundary)
|
|
return -1;
|
|
|
|
unsigned char* c = *beg;
|
|
unsigned char* b_ini = (unsigned char*)ct.mp_boundary->value.c_str();
|
|
unsigned char* b = b_ini;
|
|
unsigned char* b_end = b + ct.mp_boundary->value.length();
|
|
|
|
int st=B_START;
|
|
|
|
// Allow the buffer to start directly
|
|
// with the boundary delimiter
|
|
if(*c == HYPHEN)
|
|
st=B_LF;
|
|
|
|
#define case_BOUNDARY(_st_,_ch_,_st1_) \
|
|
case _st_: \
|
|
switch(*c){ \
|
|
case _ch_: \
|
|
st = _st1_; \
|
|
break; \
|
|
default: \
|
|
st = B_START; \
|
|
break; \
|
|
} \
|
|
break
|
|
|
|
#define case_BOUNDARY2(_st_,_ch_,_st1_) \
|
|
case _st_: \
|
|
switch(*c){ \
|
|
case _ch_: \
|
|
st = _st1_; \
|
|
break; \
|
|
default: \
|
|
b = b_ini; \
|
|
is_final = false; \
|
|
st = B_START; \
|
|
break; \
|
|
} \
|
|
break
|
|
|
|
|
|
bool is_final = false;
|
|
|
|
for(;st != B_MATCHED && (c < *end-(b_end-b)); c++){
|
|
|
|
switch(st){
|
|
case B_START:
|
|
if(*c == CR) {
|
|
*beg = c;
|
|
st = B_CR;
|
|
}
|
|
break;
|
|
|
|
case_BOUNDARY(B_CR, LF, B_LF);
|
|
|
|
case B_LF:
|
|
switch(*c){
|
|
case HYPHEN:
|
|
st = B_HYPHEN;
|
|
break;
|
|
case CR:
|
|
st = B_CR;
|
|
break;
|
|
default:
|
|
st = B_START;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case_BOUNDARY(B_HYPHEN, HYPHEN, B_BOUNDARY);
|
|
|
|
case B_BOUNDARY:
|
|
if(*c == *b) {
|
|
if(++b == b_end) {
|
|
// reached end boundary buffer
|
|
st = B_HYPHEN2;
|
|
}
|
|
}
|
|
else {
|
|
b = b_ini;
|
|
st = B_START;
|
|
}
|
|
break;
|
|
|
|
case B_HYPHEN2:
|
|
switch(*c) {
|
|
case HYPHEN:
|
|
is_final = true;
|
|
st = B_HYPHEN3;
|
|
break;
|
|
|
|
case CR:
|
|
st = B_LF2;
|
|
break;
|
|
|
|
default:
|
|
b = b_ini;
|
|
st = B_START;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case_BOUNDARY2(B_HYPHEN3, HYPHEN, B_CR2);
|
|
case_BOUNDARY2(B_CR2, CR, B_LF2);
|
|
case_BOUNDARY2(B_LF2, LF, B_MATCHED);
|
|
}
|
|
}
|
|
|
|
if(st == B_MATCHED || (st == B_HYPHEN3) || (st == B_CR2) || (st == B_LF2)) {
|
|
*end = c;
|
|
return is_final ? 1 : 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int AmMimeBody::parseSinglePart(unsigned char* buf, unsigned int len)
|
|
{
|
|
list<sip_header*> hdrs;
|
|
char* c = (char*)buf;
|
|
char* end = c + len;
|
|
|
|
// parse headers first
|
|
if(parse_headers(hdrs,&c,c+len) < 0) {
|
|
DBG("could not parse part headers\n");
|
|
free_headers(hdrs);
|
|
return -1;
|
|
}
|
|
|
|
auto_ptr<AmMimeBody> sub_part(new AmMimeBody());
|
|
|
|
string sub_part_hdrs;
|
|
string sub_part_ct;
|
|
for(list<sip_header*>::iterator it = hdrs.begin();
|
|
it != hdrs.end(); ++it) {
|
|
|
|
DBG("Part header: <%.*s>: <%.*s>\n",
|
|
(*it)->name.len,(*it)->name.s,
|
|
(*it)->value.len,(*it)->value.s);
|
|
|
|
if((*it)->type == sip_header::H_CONTENT_TYPE) {
|
|
sub_part_ct = c2stlstr((*it)->value);
|
|
}
|
|
else {
|
|
sub_part_hdrs += c2stlstr((*it)->name) + COLSP
|
|
+ c2stlstr((*it)->value) + CRLF;
|
|
}
|
|
}
|
|
|
|
if(!sub_part_ct.empty() &&
|
|
(sub_part->parse(sub_part_ct,(unsigned char*)c,end-c) == 0)) {
|
|
// TODO: check errors
|
|
DBG("Successfully parsed subpart.\n");
|
|
}
|
|
else {
|
|
DBG("Either no Content-Type, or subpart parsing failed.\n");
|
|
sub_part->ct.setType("");
|
|
sub_part->ct.setSubType("");
|
|
sub_part->setPayload((unsigned char*)c,end-c);
|
|
}
|
|
|
|
sub_part->setHeaders(sub_part_hdrs);
|
|
parts.push_back(sub_part.release());
|
|
|
|
free_headers(hdrs);
|
|
return 0;
|
|
}
|
|
|
|
int AmMimeBody::parseMultipart(const unsigned char* buf, unsigned int len)
|
|
{
|
|
if(!ct.mp_boundary) {
|
|
DBG("boundary parameter missing in a multipart MIME body\n");
|
|
return -1;
|
|
}
|
|
|
|
unsigned char* buf_end = (unsigned char*)buf + len;
|
|
unsigned char* part_end = (unsigned char*)buf;
|
|
unsigned char* next_part = (unsigned char*)buf_end;
|
|
|
|
int err = findNextBoundary(&part_end,&next_part);
|
|
if(err < 0) {
|
|
DBG("unexpected end-of-buffer\n");
|
|
return -1;
|
|
}
|
|
|
|
unsigned char* part_beg = NULL;
|
|
do {
|
|
part_beg = next_part;
|
|
part_end = part_beg;
|
|
next_part = buf_end;
|
|
|
|
err = findNextBoundary(&part_end,&next_part);
|
|
if(err < 0) {
|
|
DBG("unexpected end-of-buffer while searching for MIME body boundary\n");
|
|
return -1;
|
|
}
|
|
|
|
if(parseSinglePart(part_beg,part_end-part_beg) < 0) {
|
|
DBG("Failed parsing part\n");
|
|
}
|
|
else {
|
|
AmMimeBody* part = parts.back();
|
|
DBG("Added new part:\n%.*s\n",
|
|
part->content_len,part->payload);
|
|
}
|
|
|
|
} while(!err);
|
|
|
|
DBG("End-of-multipart body found\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int AmMimeBody::parse(const string& content_type,
|
|
const unsigned char* buf,
|
|
unsigned int len)
|
|
{
|
|
if(ct.parse(content_type) < 0)
|
|
return -1;
|
|
|
|
if(ct.isType(MULTIPART)) {
|
|
|
|
DBG("parsing multi-part body\n");
|
|
return parseMultipart(buf,len);
|
|
}
|
|
else {
|
|
DBG("saving single-part body\n");
|
|
setPayload(buf,len);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void AmMimeBody::convertToMultipart()
|
|
{
|
|
AmContentType n_ct;
|
|
n_ct.parse(MULTIPART_MIXED); // never fails
|
|
n_ct.resetBoundary();
|
|
|
|
AmMimeBody* n_part = new AmMimeBody(*this);
|
|
n_part->ct = ct;
|
|
|
|
parts.push_back(n_part);
|
|
ct = n_ct;
|
|
|
|
content_len = 0;
|
|
}
|
|
|
|
void AmMimeBody::convertToSinglepart()
|
|
{
|
|
if (parts.size() == 1) {
|
|
this->ct = parts.front()->ct;
|
|
setPayload(parts.front()->payload, parts.front()->content_len);
|
|
clearParts();
|
|
} else {
|
|
DBG("body does not have exactly one part\n");
|
|
}
|
|
}
|
|
|
|
void AmContentType::setType(const string& t)
|
|
{
|
|
type = t;
|
|
}
|
|
|
|
void AmContentType::setSubType(const string& st)
|
|
{
|
|
subtype = st;
|
|
}
|
|
|
|
bool AmContentType::isType(const string& t) const
|
|
{
|
|
return !lower_cmp_n(t.c_str(),t.length(),
|
|
type.c_str(),type.length());
|
|
}
|
|
|
|
bool AmContentType::isSubType(const string& st) const
|
|
{
|
|
return !lower_cmp_n(st.c_str(),st.length(),
|
|
subtype.c_str(),subtype.length());
|
|
}
|
|
|
|
|
|
void AmMimeBody::setHeaders(const string& hdrs)
|
|
{
|
|
this->hdrs = hdrs;
|
|
}
|
|
|
|
AmMimeBody* AmMimeBody::addPart(const string& content_type)
|
|
{
|
|
AmMimeBody* body = NULL;
|
|
if(ct.type.empty() && ct.subtype.empty()) {
|
|
// fill *this* body
|
|
if(ct.parse(content_type)) {
|
|
DBG("could not parse content-type\n");
|
|
return NULL;
|
|
}
|
|
|
|
body = this;
|
|
}
|
|
else if(!ct.isType(MULTIPART)) {
|
|
// convert to multipart
|
|
convertToMultipart();
|
|
body = new AmMimeBody();
|
|
if(body->ct.parse(content_type)) {
|
|
DBG("parsing new content-type failed\n");
|
|
delete body;
|
|
return NULL;
|
|
}
|
|
|
|
// add new part
|
|
parts.push_back(body);
|
|
}
|
|
|
|
return body;
|
|
}
|
|
|
|
int AmMimeBody::deletePart(const string& content_type)
|
|
{
|
|
if (!ct.isType(MULTIPART)) {
|
|
DBG("body is not multipart\n");
|
|
return -1;
|
|
}
|
|
|
|
for(Parts::iterator it = parts.begin();
|
|
it != parts.end(); ++it) {
|
|
if((*it)->hasContentType(content_type)) {
|
|
clearPart(it);
|
|
if (parts.size() == 1) convertToSinglepart();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
DBG("no match");
|
|
return -1;
|
|
}
|
|
|
|
void AmMimeBody::setPayload(const unsigned char* buf, unsigned int len)
|
|
{
|
|
if(payload)
|
|
clearPayload();
|
|
|
|
payload = new unsigned char [len+1];
|
|
memcpy(payload,buf,len);
|
|
content_len = len;
|
|
|
|
// zero-term for the SDP parser
|
|
payload[len] = '\0';
|
|
}
|
|
|
|
bool AmMimeBody::empty() const
|
|
{
|
|
return (!payload || !content_len)
|
|
&& parts.empty();
|
|
}
|
|
|
|
bool AmContentType::hasContentType(const string& content_type) const
|
|
{
|
|
if(content_type.empty() && type.empty() && subtype.empty())
|
|
return true;
|
|
|
|
if(content_type.empty() != (type.empty() && subtype.empty()))
|
|
return false;
|
|
|
|
// Quick & dirty comparison, might not always be correct
|
|
string cmp_ct = type + "/" + subtype;
|
|
return !lower_cmp_n(cmp_ct.c_str(),cmp_ct.length(),
|
|
content_type.c_str(),content_type.length());
|
|
}
|
|
|
|
string AmContentType::getStr() const
|
|
{
|
|
if(type.empty() && subtype.empty())
|
|
return "";
|
|
|
|
return type + "/" + subtype;
|
|
}
|
|
|
|
string AmContentType::getHdr() const
|
|
{
|
|
string ct = getStr();
|
|
if(ct.empty())
|
|
return ct;
|
|
|
|
for(Params::const_iterator it = params.begin();
|
|
it != params.end(); ++it) {
|
|
|
|
ct += ";" + (*it)->name + "=" + (*it)->value;
|
|
}
|
|
|
|
return ct;
|
|
}
|
|
|
|
bool AmMimeBody::isContentType(const string& content_type) const
|
|
{
|
|
return ct.hasContentType(content_type);
|
|
}
|
|
|
|
AmMimeBody* AmMimeBody::hasContentType(const string& content_type)
|
|
{
|
|
if(isContentType(content_type)) {
|
|
return this;
|
|
}
|
|
else if(ct.isType(MULTIPART)) {
|
|
for(Parts::iterator it = parts.begin();
|
|
it != parts.end(); ++it) {
|
|
|
|
if((*it)->hasContentType(content_type)) {
|
|
return *it;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
const AmMimeBody* AmMimeBody::hasContentType(const string& content_type) const
|
|
{
|
|
if(isContentType(content_type)) {
|
|
return this;
|
|
}
|
|
else if(ct.isType(MULTIPART)) {
|
|
for(Parts::const_iterator it = parts.begin();
|
|
it != parts.end(); ++it) {
|
|
|
|
if((*it)->hasContentType(content_type)) {
|
|
return *it;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void AmMimeBody::print(string& buf) const
|
|
{
|
|
if(empty())
|
|
return;
|
|
|
|
if(content_len) {
|
|
buf += string((const char*)payload,content_len);
|
|
}
|
|
else {
|
|
|
|
// if (ct.mp_boundary == NULL)
|
|
// ct.resetBoundary();
|
|
|
|
for(Parts::const_iterator it = parts.begin();
|
|
it != parts.end(); ++it) {
|
|
|
|
buf += "--" + (ct.mp_boundary != NULL ? ct.mp_boundary->value : string("") ) + CRLF;
|
|
buf += SIP_HDR_CONTENT_TYPE COLSP + (*it)->getCTHdr() + CRLF;
|
|
buf += (*it)->hdrs + CRLF;
|
|
(*it)->print(buf);
|
|
buf += CRLF;
|
|
}
|
|
|
|
if(!parts.empty()) {
|
|
buf += "--" + (ct.mp_boundary != NULL ? ct.mp_boundary->value : string("") ) + "--" CRLF;
|
|
}
|
|
}
|
|
}
|