/* MMS Message structure definition, encoder and decoder and helper functions * */ #include #include #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 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 (iheaders, 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); imultiparts, 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, ¶ms); 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, ¶ms); 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); ibody.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; }