1
0
Fork 0
mbuni/mbuni/mmlib/mms_msg.c

1286 lines
35 KiB
C

/* MMS Message structure definition, encoder and decoder and helper functions
*
*/
#include <time.h>
#include <ctype.h>
#include "mms_msg.h"
#include "mms_util.h"
struct MmsMsg {
int message_type;
Octstr *msgId;
List *headers; /* Of type HTTPHeader. */
unsigned char ismultipart;
union {
List *l;
Octstr *s;
} body; /* list of MIMEEntity (for multipart), text otherwise.*/
};
#define SIZHINT 47
static void mm_destroy(MIMEEntity *mx);
static void pack_short_integer(Octstr *s, int ch)
{
unsigned char c = ch|0x80;
octstr_append_data(s, &c, 1);
}
#if 0
static void encode_uint(Octstr *os, unsigned int l)
{
char xbuf[5];
int i = 0;
do {
xbuf[i++] = l&0x7f;
l>>=7;
} while (i < 5 && l);
while (--i > 0) {
xbuf[i] |= 0x80;
octstr_append_data(os, &xbuf[i], 1);
}
octstr_append_data(os, &xbuf[0], 1);
}
#endif
int mms_validate_address(Octstr *s)
{
int i = octstr_search_char(s, '/', 0);
int l = octstr_len(s);
if (octstr_search_char(s, '@', 0) > 0)
return 0;
else if (i >= 0)
if (octstr_case_search(s, octstr_imm("PLMN"), 0) + 4 == l ||
octstr_case_search(s, octstr_imm("IPv4"), 0) + 4 == l ||
octstr_case_search(s, octstr_imm("IPv6"), 0) + 4 == l)
return 0;
else
return -1;
else
return -1;
}
static int decode_multipart(ParseContext *context, List *body)
{
int i=0, n;
n = parse_get_uintvar(context);
for (i = 0; i<n && parse_octets_left(context) > 0 ; i++) {
int dlen, hlen;
MIMEEntity *x = gw_malloc(sizeof *x);
Octstr *hs;
Octstr *content;
Octstr *content_type;
memset(x, 0,sizeof *x);
hlen = parse_get_uintvar(context);
dlen = parse_get_uintvar(context);
parse_limit(context, hlen);
hs = parse_get_octets(context, parse_octets_left(context));
x->headers = wsp_headers_unpack(hs, 1);
octstr_destroy(hs);
parse_skip_to_limit(context);
parse_pop_limit(context);
content_type = http_header_value(x->headers, octstr_imm("Content-Type"));
content = parse_get_octets(context, dlen);
if (!content || !content_type) {
int pleft = parse_octets_left(context);
warning(0, "Parse error reading mime body [hlen=%d, dlen=%d, left=%d]!",hlen,dlen, pleft);
mime_entity_destroy(x);
if (content_type) octstr_destroy(content_type);
return -1;
}
if (octstr_case_compare(content_type,
octstr_imm("application/vnd.wap.multipart.related")) == 0 ||
octstr_case_compare(content_type,
octstr_imm("application/vnd.wap.multipart.alternative")) == 0 ||
octstr_case_compare(content_type,
octstr_imm("application/vnd.wap.multipart.mixed")) == 0) { /* Body is multipart. */
ParseContext *p = parse_context_create(content);
int res;
List *ml = x->multiparts = list_create();
res = decode_multipart(p, x->multiparts);
octstr_destroy(content);
parse_context_destroy(p);
if (res < 0) {
list_destroy(ml, (list_item_destructor_t *)mime_entity_destroy);
return -1;
}
} else
x->body = content;
octstr_destroy(content_type);
list_append(body, x);
}
return 0;
}
static int encode_multipart(Octstr *os, List *body)
{
int i=0, n;
n = list_len(body);
octstr_append_uintvar(os, n);
while (i<n) {
Octstr *mhdr, *mbody = octstr_create("");
MIMEEntity *x = list_get(body, i);
mhdr = wsp_headers_pack(x->headers, 1, WSP_1_3);
if (x->multiparts &&
list_len(x->multiparts) > 0) /* This is a multi-part, go down further. */
encode_multipart(mbody, x->multiparts);
else if (x->body)
octstr_append(mbody, x->body);
octstr_append_uintvar(os, octstr_len(mhdr));
octstr_append_uintvar(os, octstr_len(mbody));
octstr_append(os, mhdr);
octstr_append(os, mbody);
octstr_destroy(mhdr);
octstr_destroy(mbody);
i++;
}
return 0;
}
static int decode_msgbody(ParseContext *context, MmsMsg *msg)
{
int res = 0;
if (msg->ismultipart) {
msg->body.l = list_create();
res = decode_multipart(context, msg->body.l);
} else
msg->body.s = parse_get_rest(context);
return res;
}
static void encode_msgbody(Octstr *os, MmsMsg *msg)
{
if (msg->ismultipart)
encode_multipart(os, msg->body.l);
else
octstr_append(os, msg->body.s);
}
/* Decodes it and returns the value, which is a pointer into the string so be careful! */
static void mms_unpack_well_known_field(List *unpacked, int field_type,
ParseContext *context, Octstr *xfrom)
{
int val, ret;
char *hname = NULL;
Octstr *decoded = NULL;
char *ch = NULL;
ret = wsp_field_value(context, &val);
if (parse_error(context)) {
warning(0, "Faulty header [code = %d], skipping remaining headers.", field_type);
parse_skip_to_limit(context);
return;
}
hname = mms_header_to_cstr(field_type);
if (ret == WSP_FIELD_VALUE_NUL_STRING)
decoded = parse_get_nul_string(context);
switch (field_type) {
case MMS_HEADER_TO:
case MMS_HEADER_CC:
case MMS_HEADER_BCC:
if (ret == WSP_FIELD_VALUE_DATA) { /* expect charset text. */
long charset; /* Get it and ignore it. */
wsp_secondary_field_value(context, &charset);
decoded = parse_get_nul_string(context);
} else if (ret != WSP_FIELD_VALUE_NUL_STRING) {
warning(0, "Faulty header value for %s!\n", hname);
decoded = octstr_imm("");
}
if (mms_validate_address(decoded))
warning(0, "Faulty address [%s] format in field %s!",
octstr_get_cstr(decoded), hname);
break;
case MMS_HEADER_SUBJECT:
case MMS_HEADER_RESPONSE_TEXT:
case MMS_HEADER_RETRIEVE_TEXT:
if (ret == WSP_FIELD_VALUE_DATA) { /* encoded string, expect charset then text. */
long charset; /* Get it and ignore it. */
wsp_secondary_field_value(context, &charset);
decoded = parse_get_nul_string(context);
} else if (ret != WSP_FIELD_VALUE_NUL_STRING) {
warning(0, "Faulty header value for %s!\n", hname);
decoded = octstr_imm("");
}
break;
case MMS_HEADER_TRANSACTION_ID:
case MMS_HEADER_CONTENT_LOCATION:
case MMS_HEADER_MESSAGE_ID:
case MMS_HEADER_REPLY_CHARGING_ID:
if (ret != WSP_FIELD_VALUE_NUL_STRING)
warning(0, "Unexpected field value type %d for header %s\n",
ret, hname);
break;
case MMS_HEADER_MMS_VERSION:
if (ret == WSP_FIELD_VALUE_ENCODED)
decoded = wsp_unpack_version_value(val);
break;
/* integer/octet values */
case MMS_HEADER_DELIVERY_REPORT:
case MMS_HEADER_REPORT_ALLOWED:
case MMS_HEADER_READ_REPORT:
ch = mms_reports_to_cstr(val);
break;
case MMS_HEADER_MESSAGE_TYPE:
ch = mms_message_type_to_cstr(val);
break;
case MMS_HEADER_PRIORITY:
ch = mms_priority_to_cstr(val);
break;
case MMS_HEADER_READ_STATUS:
ch = mms_read_status_to_cstr(val);
break;
case MMS_HEADER_REPLY_CHARGING:
ch = mms_reply_charging_to_cstr(val);
break;
case MMS_HEADER_RESPONSE_STATUS:
ch = mms_response_status_to_cstr(val);
break;
case MMS_HEADER_RETRIEVE_STATUS:
ch = mms_retrieve_status_to_cstr(val);
break;
case MMS_HEADER_STATUS:
ch = mms_status_to_cstr(val);
break;
case MMS_HEADER_SENDER_VISIBILITY:
ch = mms_visibility_to_cstr(val);
break;
case MMS_HEADER_MESSAGE_CLASS:
if (ret != WSP_FIELD_VALUE_NUL_STRING)
ch = mms_message_class_to_cstr(val);
break;
case MMS_HEADER_DATE: /* Date values. */
parse_skip(context, -1);
decoded = wsp_unpack_date_value(context);
break;
case MMS_HEADER_MESSAGE_SIZE:
parse_skip(context, -1);
decoded = wsp_unpack_integer_value(context);
break;
case MMS_HEADER_CONTENT_TYPE:
if (ret == WSP_FIELD_VALUE_ENCODED)
ch = wsp_content_type_to_cstr(val);
else if (ret == WSP_FIELD_VALUE_DATA)
decoded = wsp_unpack_accept_general_form(context);
break;
case MMS_HEADER_DELIVERY_TIME:
case MMS_HEADER_EXPIRY:
case MMS_HEADER_REPLY_CHARGING_DEADLINE:
if (ret != WSP_FIELD_VALUE_DATA)
warning(0, "Error in value format, field %s!",
hname);
else {
int n = parse_get_char(context);
if (n == 0x80) /* Absolute time */
decoded = wsp_unpack_date_value(context);
else if (n == 0x81) { /* Relative time. */
long l = 0;
time_t t = time(NULL);
Octstr *s = wsp_unpack_integer_value(context);
octstr_parse_long(&l, s, 0, 10);
octstr_destroy(s);
t += l;
decoded = date_format_http(t);
} else
decoded = date_format_http(time(NULL)); /* A default. */
}
break;
case MMS_HEADER_FROM:
if (ret != WSP_FIELD_VALUE_DATA)
warning(0, "Error in value format, field %s!",hname);
else {
int n = parse_get_char(context);
if (n == 0x80) { /* Address present. */
int val = 0;
int ret2 = wsp_field_value(context, &val);
if (ret2 == WSP_FIELD_VALUE_DATA) { /* expect charset text. */
long charset; /* Get it and ignore it. */
wsp_secondary_field_value(context, &charset);
decoded = parse_get_nul_string(context);
parse_skip_to_limit(context);
parse_pop_limit(context);
} else if (ret2 != WSP_FIELD_VALUE_NUL_STRING) {
warning(0, "Faulty header value for %s!\n", hname);
decoded = octstr_imm("");
} else
decoded = parse_get_nul_string(context);
if (mms_validate_address(decoded))
warning(0, "Faulty address [%s] format in field %s!",
octstr_get_cstr(decoded), hname);
} else /* Insert address. */
decoded = octstr_duplicate(xfrom);
}
break;
case MMS_HEADER_PREVIOUSLY_SENT_BY:
case MMS_HEADER_PREVIOUSLY_SENT_DATE:
if (ret != WSP_FIELD_VALUE_DATA)
warning(0, "Error in value format, field %s!",hname);
else {
Octstr *t;
decoded = wsp_unpack_integer_value(context);
if (field_type == MMS_HEADER_PREVIOUSLY_SENT_BY) {
int val = 0;
int ret2 = wsp_field_value(context, &val);
if (ret2 == WSP_FIELD_VALUE_DATA) { /* expect charset text. */
long charset; /* Get it and ignore it. */
wsp_secondary_field_value(context, &charset);
t = parse_get_nul_string(context);
parse_skip_to_limit(context);
parse_pop_limit(context);
} else if (ret2 != WSP_FIELD_VALUE_NUL_STRING) {
warning(0, "Faulty header value for %s!\n", hname);
t = octstr_imm("");
} else
t = parse_get_nul_string(context);
} else
t = wsp_unpack_date_value(context);
octstr_append(decoded, t);
octstr_destroy(t);
}
break;
default:
warning(0, "MMS: Unknown header with code 0x%02x!", field_type);
}
if (ret == WSP_FIELD_VALUE_DATA) {
parse_skip_to_limit(context);
parse_pop_limit(context);
}
if (ch == NULL && decoded != NULL)
ch = octstr_get_cstr(decoded);
if (ch == NULL)
goto value_error;
if (!hname) {
warning(0, "Unknown header number 0x%02x.", field_type);
goto value_error;
}
http_header_add(unpacked, hname, ch);
if (decoded) octstr_destroy(decoded);
return;
value_error:
warning(0, "Skipping faulty header [code = %d, val=%d]!", field_type, val);
octstr_destroy(decoded);
}
static int decode_msgheaders(ParseContext *context, MmsMsg *msg, Octstr *from)
{
int fcont = 1;
gw_assert(msg->headers == NULL);
msg->headers = list_create();
mms_strings_init(); /* Just in case. */
while (fcont && parse_octets_left(context) &&
!parse_error(context)) {
int byte = parse_get_char(context);
if (byte >= 128)
mms_unpack_well_known_field(msg->headers, byte&0x7f, context, from);
else {
parse_skip(context, -1); /* Go back a bit. */
wsp_unpack_app_header(msg->headers, context);
}
if ((byte&0x7f) == MMS_HEADER_CONTENT_TYPE)
fcont = 0;
}
return 0;
}
static void mms_pack_well_known_field(Octstr *os, int field_type, Octstr *value)
{
Octstr *encoded = octstr_create("");
int ch = 0;
unsigned char c;
switch (field_type) {
case MMS_HEADER_TO:
case MMS_HEADER_CC:
case MMS_HEADER_BCC:
case MMS_HEADER_SUBJECT:
case MMS_HEADER_TRANSACTION_ID:
case MMS_HEADER_CONTENT_LOCATION:
case MMS_HEADER_MESSAGE_ID:
case MMS_HEADER_REPLY_CHARGING_ID:
case MMS_HEADER_RESPONSE_TEXT:
case MMS_HEADER_RETRIEVE_TEXT:
wsp_pack_text(os, value);
break;
case MMS_HEADER_MMS_VERSION:
wsp_pack_version_value(os, value);
break;
/* integer/octet values: Need to stream line this with better error-checking */
case MMS_HEADER_DELIVERY_REPORT:
case MMS_HEADER_REPORT_ALLOWED:
case MMS_HEADER_READ_REPORT:
pack_short_integer(os, mms_string_to_reports(value));
break;
case MMS_HEADER_MESSAGE_TYPE:
pack_short_integer(os, mms_string_to_message_type(value));
break;
case MMS_HEADER_PRIORITY:
pack_short_integer(os, mms_string_to_priority(value));
break;
case MMS_HEADER_READ_STATUS:
pack_short_integer(os, mms_string_to_read_status(value));
break;
case MMS_HEADER_REPLY_CHARGING:
pack_short_integer(os, mms_string_to_reply_charging(value));
break;
case MMS_HEADER_RESPONSE_STATUS:
pack_short_integer(os, mms_string_to_response_status(value));
break;
case MMS_HEADER_RETRIEVE_STATUS:
pack_short_integer(os, mms_string_to_retrieve_status(value));
break;
case MMS_HEADER_STATUS:
pack_short_integer(os, mms_string_to_status(value));
break;
case MMS_HEADER_SENDER_VISIBILITY:
pack_short_integer(os, mms_string_to_visibility(value));
break;
case MMS_HEADER_MESSAGE_CLASS:
ch = mms_string_to_message_class(value);
if (ch < 0)
wsp_pack_text(os, value);
else
pack_short_integer(os, ch);
break;
case MMS_HEADER_DATE: /* Date values. */
wsp_pack_date(os, value);
break;
case MMS_HEADER_MESSAGE_SIZE:
wsp_pack_integer_string(os, value);
break;
case MMS_HEADER_CONTENT_TYPE:
wsp_pack_content_type(os, value);
break;
case MMS_HEADER_DELIVERY_TIME:
case MMS_HEADER_EXPIRY:
case MMS_HEADER_REPLY_CHARGING_DEADLINE:
if (octstr_isnum(value) == 1) { /* A delta value. */
long l;
sscanf(octstr_get_cstr(value), "%ld", &l);
c = 129;
octstr_append_char(encoded, c);
wsp_pack_long_integer(encoded, l);
} else { /* Must be a date value .*/
c = 128;
octstr_append_char(encoded, c);
wsp_pack_date(encoded, value);
}
wsp_pack_value(os, encoded);
break;
case MMS_HEADER_FROM:
if (octstr_compare(octstr_imm("#insert"), value) == 0) {
c = 129;
octstr_append_data(encoded, &c, 1);
} else {
c = 128;
octstr_append_data(encoded, &c, 1);
wsp_pack_text(encoded, value);
}
wsp_pack_value(os, encoded);
break;
case MMS_HEADER_PREVIOUSLY_SENT_BY:
case MMS_HEADER_PREVIOUSLY_SENT_DATE:
{
long i, l;
Octstr *s;
i = octstr_parse_long(&l, value, 0, 10);
if (i <0) {
warning(0, "Bad counter indicator for field!");
i = 0;
}
wsp_pack_integer_value(encoded, l);
s = octstr_copy(value, i, octstr_len(value));
if (field_type == MMS_HEADER_PREVIOUSLY_SENT_BY)
wsp_pack_text(encoded, s);
else
wsp_pack_date(encoded, s);
octstr_destroy(s);
wsp_pack_value(os, encoded);
}
break;
default:
warning(0, "MMS: Unknown header with code 0x%02x!", field_type);
}
if (encoded) octstr_destroy(encoded);
return;
}
static int encode_msgheaders(Octstr *os, MmsMsg *msg)
{
int fcont = 1;
List *hdrs = msg->headers;
int i, l = list_len(hdrs), mtype;
Octstr *msgtype = NULL, *transid = NULL, *version = NULL, *ctype;
/* First ensure that top headers are in place. */
version = http_header_value(hdrs,
octstr_imm("X-Mms-MMS-Version"));
transid = http_header_value(hdrs,
octstr_imm("X-Mms-Transaction-Id"));
msgtype = http_header_value(hdrs,
octstr_imm("X-Mms-Message-Type"));
ctype = http_header_value(hdrs,
octstr_imm("Content-Type"));
mtype = mms_string_to_message_type(msgtype);
pack_short_integer(os, MMS_HEADER_MESSAGE_TYPE);
pack_short_integer(os, mtype);
octstr_destroy(msgtype);
if (transid) {
pack_short_integer(os, MMS_HEADER_TRANSACTION_ID);
wsp_pack_text(os, transid);
octstr_destroy(transid);
}
pack_short_integer(os, MMS_HEADER_MMS_VERSION);
wsp_pack_version_value(os, version);
octstr_destroy(version);
/* Now pack the rest. */
for (i = 0; fcont && i < l; i++) {
Octstr *field = NULL, *value = NULL;
int htype;
http_header_get(hdrs, i, &field, &value);
htype = mms_string_to_header(field);
if (htype == MMS_HEADER_MMS_VERSION ||
htype == MMS_HEADER_MESSAGE_TYPE ||
htype == MMS_HEADER_TRANSACTION_ID ||
htype == MMS_HEADER_CONTENT_TYPE)
goto loop1;
if (htype < 0)
wsp_pack_application_header(os, field, value);
else {
pack_short_integer(os, htype);
mms_pack_well_known_field(os, htype, value);
}
loop1:
if (field) octstr_destroy(field);
if (value) octstr_destroy(value);
}
if (ctype) {
pack_short_integer(os, MMS_HEADER_CONTENT_TYPE);
wsp_pack_content_type(os, ctype);
octstr_destroy(ctype);
} else if (mtype == MMS_MSGTYPE_SEND_REQ ||
mtype == MMS_MSGTYPE_RETRIEVE_CONF)
warning(0, "MMS: Content type missing in encode_headers!");
return 0;
}
/* Does basic fixups on a message. */
static int fixup_msg(MmsMsg *m, Octstr *from)
{
if (!m)
return -1;
if (m->message_type == MMS_MSGTYPE_SEND_REQ) {
Octstr *s = NULL;
/* Check for from. */
if (from && (s = http_header_value(m->headers, octstr_imm("From"))) == NULL)
http_header_add(m->headers, "From", octstr_get_cstr(from));
else if (s)
octstr_destroy(s);
/* Check for date. */
if ((s = http_header_value(m->headers, octstr_imm("Date"))) == NULL) {
Octstr *t = date_format_http(time(NULL));
http_header_add(m->headers, "Date", octstr_get_cstr(t));
octstr_destroy(t);
} else
octstr_destroy(s);
}
return 0;
}
MmsMsg *mms_frombinary(Octstr *msg, Octstr *from)
{
int res = 0;
MmsMsg _m = {0}, *m = NULL;
ParseContext *p = parse_context_create(msg);
Octstr *s;
decode_msgheaders(p, &_m, from);
if (_m.headers == NULL ||
list_len(_m.headers) == 0)
goto done;
/* Get the message type and also set flag for whether multipart.*/
s = http_header_value(_m.headers, octstr_imm("Content-Type"));
if (s &&
octstr_search(s, octstr_imm("application/vnd.wap.multipart"), 0) == 0)
_m.ismultipart = 1;
if (s)
octstr_destroy(s);
s = http_header_value(_m.headers, octstr_imm("X-Mms-Message-Type"));
if (s) {
_m.message_type = mms_string_to_message_type(s);
octstr_destroy(s);
} else {
if (_m.headers) http_destroy_headers(_m.headers);
goto done;
}
s = http_header_value(_m.headers, octstr_imm("Message-ID"));
_m.msgId = s;
if ((res = decode_msgbody(p, &_m)) < 0) {
MmsMsg *msg = &_m;
if (msg->ismultipart && msg->body.l)
list_destroy(msg->body.l, (list_item_destructor_t *)mm_destroy);
else if (msg->body.s)
octstr_destroy(msg->body.s);
}
m = gw_malloc(sizeof (*m));
*m = _m;
fixup_msg(m, from);
/* XXXX better error checking needed. */
done:
parse_context_destroy(p);
return m;
}
static void _x_mime_entity_dump(MIMEEntity *x, int level, int headers_only)
{
int i, n, ism;
ism = (x->multiparts && list_len(x->multiparts) > 0) ? 1 : 0;
debug("part.dump", 0, "%sMultipart -> ", ism ? "" : "Not ");
http_header_dump(x->headers);
if (ism)
for (i = 0, n = list_len(x->multiparts); i<n; i++)
_x_mime_entity_dump(list_get(x->multiparts, i), level+1, headers_only);
else if (x->body && !headers_only)
octstr_dump(x->body, level);
}
void mms_msgdump(MmsMsg *m, int headers_only)
{
int i, n;
http_header_dump(m->headers);
debug("mms.dump", 0, "Dumping MMS message body (%s) [%ld parts] --> ",
m->ismultipart ? "mulitpart" : "not multipart",
m->ismultipart ? list_len(m->body.l) : 0);
if (m->ismultipart)
for (i = 0, n = list_len(m->body.l); i< n; i++) {
MIMEEntity *x = list_get(m->body.l, i);
debug("mms.dump", 0, "--->Message part: %d --->", i);
_x_mime_entity_dump(x,0,headers_only);
}
else if (!headers_only)
octstr_dump(m->body.s, 0);
}
Octstr *mms_tobinary(MmsMsg *msg)
{
Octstr *s = octstr_create("");
encode_msgheaders(s, msg);
if (msg->body.s)
encode_msgbody(s, msg);
return s;
}
int mms_messagetype(MmsMsg *msg)
{
return msg->message_type;
}
static void convert_mime_msg(MIMEEntity *m)
{
int i, n;
Octstr *content_type, *params;
char *s = NULL;
get_content_type(m->headers, &content_type, &params);
if (content_type) {
if (octstr_str_compare(content_type,
"application/vnd.wap.multipart.related") == 0)
s = "multipart/related";
else if (octstr_str_compare(content_type,
"application/vnd.wap.multipart.alternative") == 0)
s = "multipart/alternative";
else if (octstr_str_compare(content_type,
"application/vnd.wap.multipart.mixed") == 0)
s = "multipart/mixed";
octstr_destroy(content_type);
}
if (s) {
Octstr *value;
value = (params && octstr_len(params) > 0) ?
octstr_format("%s; %S", s, params) : octstr_create(s);
http_header_remove_all(m->headers, "Content-Type");
http_header_add(m->headers, "Content-Type", octstr_get_cstr(value));
octstr_destroy(value);
}
if (params)
octstr_destroy(params);
if (m->multiparts)
for (i = 0, n = list_len(m->multiparts); i < n; i++)
convert_mime_msg(list_get(m->multiparts, i));
}
static void unconvert_mime_msg(MIMEEntity *m)
{
int i, n;
Octstr *content_type, *params;
char *s = NULL;
get_content_type(m->headers, &content_type, &params);
if (content_type) {
if (octstr_case_compare(content_type,
octstr_imm("multipart/related")) == 0)
s = "application/vnd.wap.multipart.related";
else if (octstr_case_compare(content_type,
octstr_imm("multipart/alternative")) == 0)
s = "application/vnd.wap.multipart.alternative";
else if (octstr_case_compare(content_type,
octstr_imm("multipart/mixed")) == 0)
s = "application/vnd.wap.multipart.mixed";
octstr_destroy(content_type);
}
if (s) {
Octstr *value;
if (params) {
List *h = get_value_parameters(params);
Octstr *ps;
http_header_remove_all(h,"boundary"); /* We don't need the boundary param if it is there. */
ps = make_value_parameters(h);
value = octstr_format("%s%s%S", s,
(ps && octstr_len(ps) > 0) ? "; " : "",
ps);
octstr_destroy(ps);
http_destroy_headers(h);
} else
value = octstr_create(s);
http_header_remove_all(m->headers, "Content-Type");
http_header_add(m->headers, "Content-Type", octstr_get_cstr(value));
octstr_destroy(value);
}
if (params)
octstr_destroy(params);
if (m->multiparts && list_len(m->multiparts) > 0)
for (i = 0, n = list_len(m->multiparts); i < n; i++)
unconvert_mime_msg(list_get(m->multiparts, i));
}
static MIMEEntity *mime_entity_duplicate(MIMEEntity *m)
{
MIMEEntity *mx = gw_malloc(sizeof *mx);
mx->headers = http_header_duplicate(m->headers);
if (m->multiparts && list_len(m->multiparts) > 0) {
int i, n;
mx->multiparts = list_create();
for (i = 0, n = list_len(m->multiparts); i < n; i++) {
MIMEEntity *x = mime_entity_duplicate(list_get(m->multiparts, i));
list_append(mx->multiparts, x);
}
mx->body = NULL;
} else {
mx->body = m->body ? octstr_duplicate(m->body) : NULL;
mx->multiparts = NULL;
}
mx->start = NULL;
return mx;
}
MIMEEntity *mms_tomime(MmsMsg *msg)
{
MIMEEntity *m = gw_malloc(sizeof *m);
int i, n;
memset(m, 0, sizeof *m);
m->body = NULL;
m->multiparts = NULL;
m->start = NULL;
m->headers = http_header_duplicate(msg->headers);
if (!msg->ismultipart)
m->body = msg->body.s ? octstr_duplicate(msg->body.s) : NULL;
else {
m->multiparts = list_create();
for (i = 0, n = list_len(msg->body.l); i < n; i++) {
MIMEEntity *mx = mime_entity_duplicate(list_get(msg->body.l, i));
list_append(m->multiparts, mx);
}
}
convert_mime_msg(m);
return m;
}
MmsMsg *mms_frommime(MIMEEntity *mime)
{
MmsMsg *m = gw_malloc(sizeof *m);
Octstr *s;
MIMEEntity *mx;
memset(m, 0, sizeof *m);
mx = mime_entity_duplicate(mime);
unconvert_mime_msg(mx); /* Fix-up content type issues. */
unpack_mimeheaders(mx);
unbase64_mimeparts(mx);
m->headers = mx->headers;
if (mx->multiparts && list_len(mx->multiparts) > 0) {
m->ismultipart = 1;
m->body.l = mx->multiparts;
} else {
m->ismultipart = 0;
m->body.s = mx->body ? mx->body : octstr_imm("");
}
gw_free(mx); /* Because all its bits are used above. XXX not very clean! */
/* Now check for important headers. If missing, put them in */
m->msgId = http_header_value(m->headers, octstr_imm("Message-ID"));
/* Default type is send */
if ((s = http_header_value(m->headers, octstr_imm("X-Mms-Message-Type"))) == NULL) {
http_header_add(m->headers, "X-Mms-Message-Type",
mms_message_type_to_cstr(MMS_MSGTYPE_SEND_REQ));
m->message_type = MMS_MSGTYPE_SEND_REQ;
} else {
m->message_type = mms_string_to_message_type(s);
if (m->message_type < 0) {
error(0, "Unknown message type: %s while parsing mime entity.", octstr_get_cstr(s));
octstr_destroy(s);
goto failed;
}
octstr_destroy(s);
}
if ((s = http_header_value(m->headers, octstr_imm("X-Mms-MMS-Version"))) == NULL)
http_header_add(m->headers, "X-Mms-MMS-Version", MMS_VERSION);
else
octstr_destroy(s);
/* Fix-up date string: Put it in GMT format, since it might not be. */
if ((s = http_header_value(m->headers, octstr_imm("Date"))) != NULL) {
struct tm xtm, ytm, *tm;
time_t t = time(NULL), t2;
char buf[64], *p, *q;
http_header_remove_all(m->headers, "Date");
localtime_r(&t, &xtm); /* Initialise it. */
strptime(octstr_get_cstr(s), "%a, %d %b %Y %T %z", &xtm); /* Parse date value with time zone. */
t2 = gw_mktime(&xtm); /* Convert to unix time... */
tm = gmtime_r(&t2, &ytm); /* Then convert to GM time. */
if (!tm || asctime_r(tm, buf) == NULL) /* Then convert to ascii. If that fails...*/
ctime_r(&t, buf); /* .. just use current time. */
/* Strip leading and trailing blanks. */
for (p = buf; *p && p < buf + sizeof buf; p++)
if (!isspace(*p))
break;
q = p + (strlen(p) - 1);
while (isspace(*q) && q > p)
*q-- = 0;
http_header_add(m->headers, "Date", p);
octstr_destroy(s);
}
/* XXXX Probably ought to handle some more headers here:
* Return-Receipt-To becomes Read request is yes
* Disposition-Notification-To: becomes X-Mms-Delivery-Report = Yes
*/
/* XXXX Also need to validate this message a bit better. */
fixup_msg(m, octstr_imm("anon@unknown"));
return m;
failed:
mms_destroy(m);
return NULL;
}
static void mm_destroy(MIMEEntity *mx)
{
http_destroy_headers(mx->headers);
if (mx->body)
octstr_destroy(mx->body);
else if (mx->multiparts)
list_destroy(mx->multiparts, (list_item_destructor_t *)mm_destroy);
gw_free(mx);
}
void mms_destroy(MmsMsg *msg)
{
if (msg->ismultipart)
list_destroy(msg->body.l, (list_item_destructor_t *)mm_destroy);
else if (msg->body.s)
octstr_destroy(msg->body.s);
http_destroy_headers(msg->headers);
octstr_destroy(msg->msgId);
gw_free(msg);
}
List *mms_message_headers(MmsMsg *msg)
{
return http_header_duplicate(msg->headers);
}
MmsMsg *mms_deliveryreport(Octstr *msgid, Octstr *to, time_t date, Octstr *status)
{
MmsMsg *m = gw_malloc(sizeof *m);
Octstr *s;
m->ismultipart = 0;
m->headers = http_create_empty_headers();
m->message_type = MMS_MSGTYPE_DELIVERY_IND;
m->body.s = NULL;
m->msgId = octstr_duplicate(msgid);
/* Now append headers. */
http_header_add(m->headers, "X-Mms-Message-Type", "m-delivery-ind");
http_header_add(m->headers, "X-Mms-MMS-Version", MMS_VERSION);
http_header_add(m->headers, "Message-ID", octstr_get_cstr(msgid));
http_header_add(m->headers, "To", octstr_get_cstr(to));
s = date_format_http(date);
http_header_add(m->headers, "Date", octstr_get_cstr(s));
http_header_add(m->headers, "X-Mms-Status", octstr_get_cstr(status));
octstr_destroy(s);
return m;
}
MmsMsg *mms_notification(MmsMsg *msg, unsigned int msize, Octstr *url,
Octstr *transactionid, time_t expiryt)
{
MmsMsg *m = gw_malloc(sizeof *m);
char buf[10];
time_t tnow = time(NULL);
m->ismultipart = 0;
m->msgId = NULL;
m->body.s = NULL;
m->headers = http_create_empty_headers();
m->message_type = MMS_MSGTYPE_NOTIFICATION_IND;
http_header_add(m->headers, "X-Mms-Message-Type", "m-notification-ind");
http_header_add(m->headers, "X-Mms-Transaction-ID",
octstr_get_cstr(transactionid));
http_header_add(m->headers, "X-Mms-MMS-Version", MMS_VERSION);
#define HX(h,d) do {\
Octstr *s = http_header_value(msg->headers, octstr_imm(#h)); \
if (s) { \
http_header_add(m->headers, #h, octstr_get_cstr(s)); \
octstr_destroy(s); \
} else if (d) \
http_header_add(m->headers, #h, d); \
} while(0)
HX(From,NULL);
HX(Subject,NULL);
HX(X-Mms-Message-Class, "Personal");
#undef HX
sprintf(buf, "%d", msize);
http_header_add(m->headers, "X-Mms-Message-Size", buf);
#define LARGET 365*24*3600
sprintf(buf, "%ld", expiryt ? expiryt - tnow : LARGET);
http_header_add(m->headers, "X-Mms-Expiry", buf);
/* No reply charge stuff for now. */
http_header_add(m->headers, "X-Mms-Content-Location", octstr_get_cstr(url));
return m;
}
MmsMsg *mms_retrieveconf(MmsMsg *msg, Octstr *transactionid,
char *err, char *errtxt, Octstr *opt_from)
{
MmsMsg *m;
if (msg) {
if (msg->message_type == MMS_MSGTYPE_RETRIEVE_CONF)
return msg; /* Unchanged. XXX should we copy it instead?!! */
if (msg->message_type != MMS_MSGTYPE_SEND_REQ)
return NULL;
}
m = gw_malloc(sizeof *m);
m->msgId = msg ? octstr_duplicate(msg->msgId) : NULL;
m->headers = http_create_empty_headers();
m->message_type = MMS_MSGTYPE_RETRIEVE_CONF;
http_header_add(m->headers, "X-Mms-Message-Type", "m-retrieve-conf");
if (transactionid)
http_header_add(m->headers, "X-Mms-Transaction-ID",
octstr_get_cstr(transactionid));
http_header_add(m->headers, "X-Mms-MMS-Version", MMS_VERSION);
if (!msg) {
Octstr *x = date_format_http(time(NULL));
m->ismultipart = 0;
http_header_add(m->headers, "Date", octstr_get_cstr(x));
http_header_add(m->headers, "X-Mms-Retrieve-Status",err);
if (err)
http_header_add(m->headers, "X-Mms-Retrieve-Text",err);
if (opt_from)
http_header_add(m->headers, "From", octstr_get_cstr(opt_from));
http_header_add(m->headers, "Content-Type", "text/plain");
if (errtxt)
m->body.s = octstr_create(errtxt);
else
m->body.s = octstr_create(" ");
octstr_destroy(x);
} else { /* Otherwise copy from message. */
List *h = mms_message_headers(msg);
int i, n;
#if 0 /* Some phones do not like this header! */
http_header_add(m->headers, "X-Mms-Retrieve-Status", "Ok");
#endif
http_header_combine(h, m->headers);
http_destroy_headers(m->headers);
m->headers = h;
m->ismultipart = msg->ismultipart;
if (!m->ismultipart)
m->body.s = octstr_duplicate(msg->body.s);
else
/* Body is a list of MIMEEntities, so recreate it. */
for (m->body.l = list_create(), i = 0,
n = list_len(msg->body.l);
i<n; i++)
list_append(m->body.l,
mime_entity_duplicate(list_get(msg->body.l, i)));
}
return m;
}
int mms_remove_headers(MmsMsg *m, char *name)
{
http_header_remove_all(m->headers, name);
return 0;
}
MmsMsg *mms_sendconf(char *errstr, char *msgid, char *transid, int isforward)
{
MmsMsg *m = gw_malloc(sizeof *m);
m->ismultipart = 0;
m->msgId = msgid ? octstr_create(msgid) : NULL;
m->body.s = NULL;
m->headers = http_create_empty_headers();
if (!isforward) {
m->message_type = MMS_MSGTYPE_SEND_CONF;
http_header_add(m->headers, "X-Mms-Message-Type", "m-send-conf");
} else {
m->message_type = MMS_MSGTYPE_FORWARD_CONF;
http_header_add(m->headers, "X-Mms-Message-Type", "m-forward-conf");
}
http_header_add(m->headers, "X-Mms-Transaction-ID", transid);
http_header_add(m->headers, "X-Mms-MMS-Version", MMS_VERSION);
http_header_add(m->headers, "X-Mms-Response-Status", errstr);
if (msgid)
http_header_add(m->headers, "Message-ID", msgid);
return m;
}
int mms_replace_header_value(MmsMsg *msg, char *hname, char *value)
{
http_header_remove_all(msg->headers, hname);
http_header_add(msg->headers, hname, value);
return 0;
}
Octstr *mms_get_header_value(MmsMsg *msg, Octstr *header)
{
return http_header_value(msg->headers, header);
}
int mms_convert_readrec2readorig(MmsMsg *msg)
{
if (msg->message_type != MMS_MSGTYPE_READ_REC_IND)
return -1;
mms_replace_header_value(msg, "X-Mms-Message-Type", "m-read-orig-ind");
msg->message_type = MMS_MSGTYPE_READ_ORIG_IND;
return 0;
}