| /* |
| * |
| * Multimedia Messaging Service |
| * |
| * Copyright (C) 2010-2011 Intel Corporation. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <string.h> |
| |
| #include <glib.h> |
| |
| #include "wsputil.h" |
| |
| struct wsp_hex_str_entry { |
| unsigned int type; |
| const char *type_str; |
| }; |
| |
| /* |
| * http://www.wapforum.org/wina/wsp-content-type.htm |
| */ |
| static const char *content_types[] = { |
| "*/*", |
| "text/*", |
| "text/html", |
| "text/plain", |
| "text/x-hdml", |
| "text/x-ttml", |
| "text/x-vCalendar", |
| "text/x-vCard", |
| "text/vnd.wap.wml", |
| "text/vnd.wap.wmlscript", |
| "text/vnd.wap.wta-event", |
| "multipart/*", |
| "multipart/mixed", |
| "multipart/form-data", |
| "multipart/byterantes", |
| "multipart/alternative", |
| "application/*", |
| "application/java-vm", |
| "application/x-www-form-urlencoded", |
| "application/x-hdmlc", |
| "application/vnd.wap.wmlc", |
| "application/vnd.wap.wmlscriptc", |
| "application/vnd.wap.wta-eventc", |
| "application/vnd.wap.uaprof", |
| "application/vnd.wap.wtls-ca-certificate", |
| "application/vnd.wap.wtls-user-certificate", |
| "application/x-x509-ca-cert", |
| "application/x-x509-user-cert", |
| "image/*", |
| "image/gif", |
| "image/jpeg", |
| "image/tiff", |
| "image/png", |
| "image/vnd.wap.wbmp", |
| "application/vnd.wap.multipart.*", |
| "application/vnd.wap.multipart.mixed", |
| "application/vnd.wap.multipart.form-data", |
| "application/vnd.wap.multipart.byteranges", |
| "application/vnd.wap.multipart.alternative", |
| "application/xml", |
| "text/xml", |
| "application/vnd.wap.wbxml", |
| "application/x-x968-cross-cert", |
| "application/x-x968-ca-cert", |
| "application/x-x968-user-cert", |
| "text/vnd.wap.si", |
| "application/vnd.wap.sic", |
| "text/vnd.wap.sl", |
| "application/vnd.wap.slc", |
| "text/vnd.wap.co", |
| "application/vnd.wap.coc", |
| "application/vnd.wap.multipart.related", |
| "application/vnd.wap.sia", |
| "text/vnd.wap.connectivity-xml", |
| "application/vnd.wap.connectivity-wbxml", |
| "application/pkcs7-mime", |
| "application/vnd.wap.hashed-certificate", |
| "application/vnd.wap.signed-certificate", |
| "application/vnd.wap.cert-response", |
| "application/xhtml+xml", |
| "application/wml+xml", |
| "text/css", |
| "application/vnd.wap.mms-message", |
| "application/vnd.wap.rollover-certificate", |
| "application/vnd.wap.locc+wbxml", |
| "application/vnd.wap.loc+xml", |
| "application/vnd.syncml.dm+wbxml", |
| "application/vnd.syncml.dm+xml", |
| "application/vnd.syncml.notification", |
| "application/vnd.wap.xhtml+xml", |
| "application/vnd.wv.csp.cir", |
| "application/vnd.oma.dd+xml", |
| "application/vnd.oma.drm.message", |
| "application/vnd.oma.drm.content", |
| "application/vnd.oma.drm.rights+xml", |
| "application/vnd.oma.drm.rights+wbxml", |
| }; |
| |
| #define LAST_CONTENT_TYPE (sizeof(content_types) / sizeof(const char *)) |
| |
| /* |
| * http://www.wapforum.org/wina/push-app-id.htm |
| */ |
| static const struct wsp_hex_str_entry app_id[] = { |
| { 0x04, "x-wap-application:mms.ua" }, |
| { 0x07, "x-wap-application:syncml.dm" }, |
| { 0x08, "x-wap-application:drm.ua" }, |
| { 0xFF, NULL } |
| }; |
| |
| /* http://www.wapforum.org/wina/wsp-content-type.htm */ |
| static const struct wsp_hex_str_entry extension_mimetypes[] = { |
| { 0x0201, "application/vnd.uplanet.cacheop-wbxml" }, |
| { 0x0202, "application/vnd.uplanet.signal" }, |
| { 0x0203, "application/vnd.uplanet.alert-wbxml" }, |
| { 0x0204, "application/vnd.uplanet.list-wbxml" }, |
| { 0x0205, "application/vnd.uplanet.listcmd-wbxml" }, |
| { 0x0206, "application/vnd.uplanet.channel-wbxml" }, |
| { 0x0207, "application/vnd.uplanet.provisioning-status-uri" }, |
| { 0x0208, "x-wap.multipart/vnd.uplanet.header-set" }, |
| { 0x0209, "application/vnd.uplanet.bearer-choice-wbxml" }, |
| { 0x020A, "application/vnd.phonecom.mmc-wbxml" }, |
| { 0x020B, "application/vnd.nokia.syncset+wbxml" }, |
| { 0x020C, "image/x-up-wpng" }, |
| { 0xFFFF, NULL }, |
| }; |
| |
| static const struct wsp_hex_str_entry charset_assignments[] = { |
| { 0x0000, "*" }, |
| { 0x07EA, "big5" }, |
| { 0x03E8, "iso-10646-ucs-2" }, |
| { 0x0004, "iso-8859-1" }, |
| { 0x0005, "iso-8859-2" }, |
| { 0x0006, "iso-8859-3" }, |
| { 0x0007, "iso-8859-4" }, |
| { 0x0008, "iso-8859-5" }, |
| { 0x0009, "iso-8859-6" }, |
| { 0x000A, "iso-8859-7" }, |
| { 0x000B, "iso-8859-8" }, |
| { 0x000C, "iso-8859-9" }, |
| { 0x0011, "shift_JIS" }, |
| { 0x0003, "us-ascii" }, |
| { 0x006A, "utf-8" }, |
| { 0x03F7, "utf-16" }, |
| { 0xFFFF, NULL }, |
| }; |
| |
| /* |
| * Control Characters 0-8, 10-31 and 127. The tab character is omitted |
| * since it is included in the sep chars array and the most generic TEXT |
| * type of RFC 2616 explicitly allows tabs |
| */ |
| static const char *ctl_chars = "\x01\x02\x03\x04\x05\x06\x07\x08\x0A" |
| "\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14" |
| "\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E" |
| "\x1F\x7F"; |
| |
| static const char *sep_chars = "()<>@,;:\\\"/[]?={} \t"; |
| |
| static const char *decode_text_common(const unsigned char *pdu, |
| unsigned int len, |
| gboolean filter_ctl, |
| gboolean filter_sep, |
| unsigned int *consumed) |
| { |
| unsigned char *c; |
| |
| c = memchr(pdu, '\0', len); |
| |
| if (c == NULL) |
| return NULL; |
| |
| c += 1; |
| |
| /* RFC 2616 Section 2.2 */ |
| if (filter_ctl && strpbrk((const char *) pdu, ctl_chars) != NULL) |
| return NULL; |
| |
| if (filter_sep && strpbrk((const char *) pdu, sep_chars) != NULL) |
| return NULL; |
| |
| if (consumed) |
| *consumed = c - pdu; |
| |
| return (const char *) pdu; |
| } |
| |
| const char *wsp_decode_token_text(const unsigned char *pdu, unsigned int len, |
| unsigned int *consumed) |
| { |
| return decode_text_common(pdu, len, TRUE, TRUE, consumed); |
| } |
| |
| const char *wsp_decode_text(const unsigned char *pdu, unsigned int len, |
| unsigned int *consumed) |
| { |
| const char *r; |
| unsigned int fudge = 0; |
| |
| if (*pdu == 127) { |
| pdu++; |
| |
| if (*pdu < 128) |
| return NULL; |
| |
| len -= 1; |
| fudge += 1; |
| } |
| |
| r = decode_text_common(pdu, len, TRUE, FALSE, consumed); |
| |
| if (consumed) |
| *consumed += fudge; |
| |
| return r; |
| } |
| |
| const char *wsp_decode_quoted_string(const unsigned char *pdu, unsigned int len, |
| unsigned int *consumed) |
| { |
| const char *text; |
| |
| text = wsp_decode_text(pdu, len, consumed); |
| if (text == NULL) |
| return NULL; |
| |
| if (*text != '"') |
| return NULL; |
| |
| /* Skip initial quote */ |
| text++; |
| |
| return text; |
| } |
| |
| gboolean wsp_decode_uintvar(const unsigned char *pdu, unsigned int len, |
| unsigned int *out_len, unsigned int *consumed) |
| { |
| unsigned int var; |
| unsigned int i; |
| unsigned int cont; |
| |
| for (i = 0, var = 0, cont = TRUE; i < 5 && i < len && cont; i++) { |
| cont = pdu[i] & 0x80; |
| var = (var << 7) | (pdu[i] & 0x7f); |
| } |
| |
| if (cont) |
| return FALSE; |
| |
| if (out_len) |
| *out_len = var; |
| |
| if (consumed) |
| *consumed = i; |
| |
| return TRUE; |
| } |
| |
| gboolean wsp_decode_integer(const unsigned char *pdu, unsigned int len, |
| unsigned int *out_val, unsigned int *consumed) |
| { |
| unsigned int var; |
| unsigned int i; |
| unsigned int count; |
| |
| if (pdu[0] & 0x80) { |
| var = pdu[0] & 0x7f; |
| count = 1; |
| } else if (pdu[0] <= 30) { |
| unsigned int value_len = *pdu; |
| if (value_len > (len - 1)) |
| return FALSE; |
| |
| if (value_len > sizeof(unsigned int)) |
| return FALSE; |
| |
| var = 0; |
| for (i = 0; i < value_len; i++) |
| var = (var << 8) | pdu[i + 1]; |
| count = value_len + 1; |
| } else |
| return FALSE; |
| |
| if (out_val) |
| *out_val = var; |
| |
| if (consumed) |
| *consumed = count; |
| |
| return TRUE; |
| } |
| |
| gboolean wsp_decode_field(const unsigned char *pdu, unsigned int max, |
| enum wsp_value_type *out_type, |
| const void **out_value, |
| unsigned int *out_len, |
| unsigned int *out_read) |
| { |
| const unsigned char *end = pdu + max; |
| const unsigned char *begin = pdu; |
| unsigned int len; |
| enum wsp_value_type value; |
| unsigned int consumed; |
| |
| if (*pdu <= 30) { |
| len = *pdu; |
| pdu++; |
| |
| if (pdu + len > end) |
| return FALSE; |
| |
| value = WSP_VALUE_TYPE_LONG; |
| } else if (*pdu >= 128) { |
| len = 1; |
| value = WSP_VALUE_TYPE_SHORT; |
| } else if (*pdu == 31) { |
| pdu++; |
| |
| if (pdu == end) |
| return FALSE; |
| |
| if (wsp_decode_uintvar(pdu, end - pdu, |
| &len, &consumed) == FALSE) |
| return FALSE; |
| |
| pdu += consumed; |
| |
| if (pdu + len > end) |
| return FALSE; |
| |
| value = WSP_VALUE_TYPE_LONG; |
| } else { |
| if (decode_text_common(pdu, end - pdu, |
| TRUE, FALSE, &len) == NULL) |
| return FALSE; |
| |
| value = WSP_VALUE_TYPE_TEXT; |
| } |
| |
| if (out_type) |
| *out_type = value; |
| |
| if (out_value) |
| *out_value = pdu; |
| |
| if (out_len) |
| *out_len = len; |
| |
| if (out_read) |
| *out_read = pdu - begin + len; |
| |
| return TRUE; |
| } |
| |
| gboolean wsp_get_well_known_content_type(const char *text, |
| unsigned int *out_val) |
| { |
| unsigned int i; |
| |
| for (i = 0; i < LAST_CONTENT_TYPE; i++) { |
| if (g_str_equal(text, content_types[i]) == TRUE) { |
| *out_val = i; |
| return TRUE; |
| } |
| } |
| |
| return FALSE; |
| } |
| |
| gboolean wsp_get_well_known_charset(const char *text, unsigned int *out_val) |
| { |
| unsigned int i; |
| |
| for (i = 0; charset_assignments[i].type_str != NULL; i++) { |
| if (g_str_equal(charset_assignments[i].type_str, |
| text) == TRUE) { |
| *out_val = charset_assignments[i].type; |
| return TRUE; |
| } |
| } |
| |
| return FALSE; |
| } |
| |
| static const char *get_text_entry(unsigned int value, |
| const struct wsp_hex_str_entry *table) |
| { |
| unsigned int i; |
| |
| for (i = 0; table[i].type_str != NULL; i++) { |
| if (table[i].type == value) |
| return table[i].type_str; |
| } |
| |
| return NULL; |
| } |
| |
| gboolean wsp_decode_content_type(const unsigned char *pdu, unsigned int max, |
| const void **out_value, |
| unsigned int *out_read, |
| unsigned int *out_param_len) |
| { |
| unsigned int param_len = 0; |
| unsigned int len; |
| const void *data; |
| enum wsp_value_type value_type; |
| unsigned int consumed; |
| |
| if (wsp_decode_field(pdu, max, &value_type, &data, |
| &len, &consumed) != TRUE) |
| return FALSE; |
| |
| if (value_type == WSP_VALUE_TYPE_LONG) { |
| unsigned int media_len; |
| unsigned int value_len; |
| |
| if (wsp_decode_field(data, max, &value_type, &data, |
| &value_len, &media_len) != TRUE) |
| return FALSE; |
| |
| param_len = len - media_len; |
| consumed -= param_len; |
| |
| /* Handle Well-Known-Media Long-Integer case */ |
| if (value_type == WSP_VALUE_TYPE_LONG) { |
| const unsigned char *pdu_val = data; |
| unsigned int var = 0; |
| unsigned int i; |
| |
| if (value_len > sizeof(unsigned int)) |
| return FALSE; |
| |
| for (i = 0; i < value_len; i++) |
| var = (var << 8) | pdu_val[i + 1]; |
| |
| data = get_text_entry(var, extension_mimetypes); |
| } |
| } |
| |
| if (value_type == WSP_VALUE_TYPE_SHORT) { |
| const unsigned char *pdu_val = data; |
| unsigned int val; |
| |
| val = *pdu_val & 0x7f; |
| |
| if (val >= LAST_CONTENT_TYPE) |
| return FALSE; |
| |
| data = content_types[val]; |
| } |
| |
| if (out_value) |
| *out_value = data; |
| |
| if (out_read) |
| *out_read = consumed; |
| |
| if (out_param_len) |
| *out_param_len = param_len; |
| |
| return TRUE; |
| } |
| |
| gboolean wsp_decode_application_id(struct wsp_header_iter *iter, |
| const void **out_value) |
| { |
| const unsigned char *pdu_val = wsp_header_iter_get_val(iter); |
| unsigned int val; |
| unsigned int val_len; |
| unsigned int i; |
| |
| switch (wsp_header_iter_get_val_type(iter)) { |
| case WSP_VALUE_TYPE_TEXT: |
| if (out_value) |
| *out_value = pdu_val; |
| |
| break; |
| |
| /* |
| * Well-known field values MUST be encoded using the |
| * compact binary formats |
| */ |
| case WSP_VALUE_TYPE_SHORT: |
| val = *pdu_val & 0x7f; |
| |
| if (out_value) |
| *out_value = get_text_entry(val, app_id); |
| |
| break; |
| |
| case WSP_VALUE_TYPE_LONG: |
| val_len = wsp_header_iter_get_val_len(iter); |
| |
| if (val_len > 2) |
| return FALSE; |
| |
| for (i = 0, val = 0; i < val_len && i < sizeof(val); i++) |
| val = (val << 8) | pdu_val[i]; |
| |
| if (out_value) |
| *out_value = get_text_entry(val, app_id); |
| |
| break; |
| } |
| |
| return TRUE; |
| } |
| |
| static inline gboolean is_content_type_header(const unsigned char *pdu, |
| unsigned char code_page, |
| unsigned int flags) |
| { |
| /* Check for MMS Content-Type header */ |
| if (flags & WSP_HEADER_ITER_FLAG_DETECT_MMS_MULTIPART) |
| if (code_page == 1 && *pdu == 0x84) |
| return TRUE; |
| |
| /* Check for WSP default Content-Type header */ |
| if (code_page == 1 && *pdu == 0x91) |
| return TRUE; |
| |
| return FALSE; |
| } |
| |
| gboolean wsp_encode_uintvar(unsigned int value, unsigned char *dest, |
| unsigned int dest_size, unsigned int *written) |
| { |
| unsigned char d[5]; |
| unsigned int count = 0; |
| |
| /* Separate into 7-bit chunks, LS first */ |
| while (value || !count) { |
| d[count++] = value & 0x7F; |
| value = value >> 7; |
| } |
| |
| if (count > dest_size) |
| return FALSE; |
| |
| *written = count; |
| |
| /* |
| * Output to stream, MS first! |
| * 0x80 flag = "continue". LS byte does not have this flag. |
| */ |
| while (--count) |
| *dest++ = d[count] | 0x80; |
| |
| *dest = d[count]; |
| |
| return TRUE; |
| } |
| |
| gboolean wsp_encode_value_length(unsigned int len, unsigned char *dest, |
| unsigned int dest_size, unsigned int *written) |
| { |
| if (dest_size < 1) |
| return FALSE; |
| |
| if (len <= 30) { |
| *dest = len; |
| *written = 1; |
| |
| return TRUE; |
| } |
| |
| /* 31 is escape for variable length int */ |
| *dest++ = 31; |
| dest_size--; |
| |
| if (wsp_encode_uintvar(len, dest, dest_size, written) == FALSE) |
| return FALSE; |
| |
| *written += 1; |
| |
| return TRUE; |
| } |
| |
| gboolean wsp_encode_integer(unsigned int value, unsigned char *dest, |
| unsigned int dest_size, unsigned int *written) |
| { |
| unsigned char moi[sizeof(unsigned int)]; |
| unsigned int count; |
| |
| if (dest_size < 1) |
| return FALSE; |
| |
| if (value < 0x80) { |
| *dest = value | 0x80; |
| *written = 1; |
| |
| return TRUE; |
| } |
| |
| for (count = 0; count < sizeof(unsigned int) && value; count++) { |
| moi[count] = value & 0xFF; |
| value = value >> 8; |
| } |
| |
| if (count + 1 > dest_size) |
| return FALSE; |
| |
| *written = count + 1; |
| *dest++ = count; |
| |
| for (; count > 0; count--) |
| *dest++ = moi[count - 1]; |
| |
| return TRUE; |
| } |
| |
| void wsp_header_iter_init(struct wsp_header_iter *iter, |
| const unsigned char *pdu, unsigned int len, |
| unsigned int flags) |
| { |
| iter->pdu = pdu; |
| iter->pos = 0; |
| iter->max = len; |
| iter->code_page = 1; |
| iter->flags = flags; |
| } |
| |
| gboolean wsp_header_iter_next(struct wsp_header_iter *iter) |
| { |
| const unsigned char *pdu = iter->pdu + iter->pos; |
| const unsigned char *end = iter->pdu + iter->max; |
| enum wsp_header_type header; |
| const void *hdr; |
| unsigned int consumed; |
| |
| if (pdu == end) |
| return FALSE; |
| |
| /* |
| * 8.4.2.6 Header |
| * The following rules are used to encode headers. |
| * Header = Message-header | Shift-sequence |
| * Shift-sequence = (Shift-delimiter Page-identity) | |
| * Short-cut-shift-delimiter |
| * Shift-delimiter = <Octet 127> |
| * Page-identity = <Any octet 1-255> |
| * Short-cut-shift-delimiter = <Any octet 1-31> |
| * Message-header = Well-known-header | Application-header |
| * Well-known-header = Well-known-field-name Wap-value |
| * Application-header = Token-text Application-specific-value |
| * Well-known-field-name = Short-integer |
| * Application-specific-value = Text-string |
| */ |
| while (*pdu == 127 || (*pdu >= 1 && *pdu <= 31)) { |
| if (iter->flags & WSP_HEADER_ITER_FLAG_REJECT_CP) |
| return FALSE; |
| |
| if (*pdu == 127) { |
| pdu++; |
| |
| if (pdu == end) |
| return FALSE; |
| |
| iter->code_page = *pdu; |
| pdu++; |
| } else |
| iter->code_page = *pdu++; |
| } |
| |
| if (pdu == end) |
| return FALSE; |
| |
| if (*pdu >= 0x80) { |
| if (is_content_type_header(pdu, iter->code_page, iter->flags)) |
| return FALSE; |
| |
| header = WSP_HEADER_TYPE_WELL_KNOWN; |
| hdr = pdu; |
| pdu++; |
| } else { |
| if (wsp_decode_token_text(pdu, end - pdu, &consumed) == NULL) |
| return FALSE; |
| |
| header = WSP_HEADER_TYPE_APPLICATION; |
| hdr = pdu; |
| pdu += consumed; |
| } |
| |
| if (pdu == end) |
| return FALSE; |
| |
| /* |
| * Section 8.4.1.2 of WAP-230: |
| * If the field name is encoded in text format, textual values MUST |
| * be used. |
| */ |
| if ((*pdu < 32 || *pdu > 127) && header == WSP_HEADER_TYPE_APPLICATION) |
| return FALSE; |
| |
| if (wsp_decode_field(pdu, end - pdu, &iter->value_type, |
| &iter->value, &iter->len, &consumed) == FALSE) |
| return FALSE; |
| |
| iter->header_type = header; |
| iter->header = hdr; |
| |
| iter->pos = pdu + consumed - iter->pdu; |
| |
| return TRUE; |
| } |
| |
| unsigned char wsp_header_iter_get_code_page(struct wsp_header_iter *iter) |
| { |
| return iter->code_page; |
| } |
| |
| gboolean wsp_header_iter_at_end(struct wsp_header_iter *iter) |
| { |
| if (iter->pos == iter->max) |
| return TRUE; |
| |
| return FALSE; |
| } |
| |
| gboolean wsp_header_iter_is_multipart(struct wsp_header_iter *iter) |
| { |
| const unsigned char *pdu = iter->pdu + iter->pos; |
| |
| return is_content_type_header(pdu, iter->code_page, iter->flags); |
| } |
| |
| enum wsp_header_type wsp_header_iter_get_hdr_type(struct wsp_header_iter *iter) |
| { |
| return iter->header_type; |
| } |
| |
| const unsigned char *wsp_header_iter_get_pdu(struct wsp_header_iter *iter) |
| { |
| return iter->pdu; |
| } |
| |
| const void *wsp_header_iter_get_hdr(struct wsp_header_iter *iter) |
| { |
| return iter->header; |
| } |
| |
| enum wsp_value_type wsp_header_iter_get_val_type(struct wsp_header_iter *iter) |
| { |
| return iter->value_type; |
| } |
| |
| const void *wsp_header_iter_get_val(struct wsp_header_iter *iter) |
| { |
| return iter->value; |
| } |
| |
| unsigned int wsp_header_iter_get_val_len(struct wsp_header_iter *iter) |
| { |
| return iter->len; |
| } |
| |
| gboolean wsp_multipart_iter_init(struct wsp_multipart_iter *mi, |
| struct wsp_header_iter *hi, |
| const void **out_content_type, |
| unsigned int *out_content_type_len) |
| { |
| const unsigned char *pdu = hi->pdu + hi->pos; |
| const unsigned char *end = hi->pdu + hi->max; |
| unsigned int consumed; |
| unsigned int ct_len; |
| |
| /* Assume content-type header is well known */ |
| if (pdu + 1 > end) |
| return FALSE; |
| |
| pdu++; |
| |
| /* Consume the Content-Type value of Content-Type header */ |
| if (wsp_decode_field(pdu, end - pdu, |
| NULL, NULL, NULL, &consumed) == FALSE) |
| return FALSE; |
| |
| pdu += consumed; |
| ct_len = consumed; |
| |
| /* |
| * Consume the uinvar specifying the number of parts. This is set to |
| * 0 in later specifications and can be safely ignored |
| */ |
| if (wsp_decode_uintvar(pdu, end - pdu, NULL, &consumed) == FALSE) |
| return FALSE; |
| |
| memset(mi, 0, sizeof(*mi)); |
| mi->pdu = hi->pdu + hi->pos; |
| mi->max = hi->max - hi->pos; |
| mi->pos = pdu + consumed - mi->pdu; |
| |
| if (out_content_type) |
| *out_content_type = mi->pdu + 1; |
| |
| if (out_content_type_len) |
| *out_content_type_len = ct_len; |
| |
| return TRUE; |
| } |
| |
| gboolean wsp_multipart_iter_next(struct wsp_multipart_iter *mi) |
| { |
| const unsigned char *pdu = mi->pdu + mi->pos; |
| const unsigned char *end = mi->pdu + mi->max; |
| unsigned int headers_len; |
| unsigned int body_len; |
| unsigned int consumed; |
| |
| if (wsp_decode_uintvar(pdu, end - pdu, |
| &headers_len, &consumed) == FALSE) |
| return FALSE; |
| |
| pdu += consumed; |
| |
| if (wsp_decode_uintvar(pdu, end - pdu, &body_len, &consumed) == FALSE) |
| return FALSE; |
| |
| pdu += consumed; |
| |
| if (pdu + headers_len + body_len > end) |
| return FALSE; |
| |
| /* Consume the Content-Type value */ |
| if (wsp_decode_field(pdu, end - pdu, |
| NULL, NULL, NULL, &consumed) == FALSE) |
| return FALSE; |
| |
| mi->content_type = pdu; |
| mi->content_type_len = consumed; |
| mi->headers = pdu + consumed; |
| mi->headers_len = headers_len - consumed; |
| mi->body = pdu + headers_len; |
| mi->body_len = body_len; |
| |
| mi->pos = pdu - mi->pdu + headers_len + body_len; |
| |
| return TRUE; |
| } |
| |
| const void *wsp_multipart_iter_get_content_type(struct wsp_multipart_iter *mi) |
| { |
| return mi->content_type; |
| } |
| |
| unsigned int wsp_multipart_iter_get_content_type_len( |
| struct wsp_multipart_iter *mi) |
| { |
| return mi->content_type_len; |
| } |
| |
| const void *wsp_multipart_iter_get_hdr(struct wsp_multipart_iter *mi) |
| { |
| return mi->headers; |
| } |
| |
| unsigned int wsp_multipart_iter_get_hdr_len(struct wsp_multipart_iter *mi) |
| { |
| return mi->headers_len; |
| } |
| |
| const void *wsp_multipart_iter_get_body(struct wsp_multipart_iter *mi) |
| { |
| return mi->body; |
| } |
| |
| unsigned int wsp_multipart_iter_get_body_len(struct wsp_multipart_iter *mi) |
| { |
| return mi->body_len; |
| } |
| |
| gboolean wsp_multipart_iter_close(struct wsp_multipart_iter *mi, |
| struct wsp_header_iter *hi) |
| { |
| if (mi->pos != mi->max) |
| return FALSE; |
| |
| hi->pos += mi->pos; |
| |
| return TRUE; |
| } |
| |
| void wsp_parameter_iter_init(struct wsp_parameter_iter *pi, |
| const unsigned char *pdu, unsigned int len) |
| { |
| pi->pdu = pdu; |
| pi->max = len; |
| pi->pos = 0; |
| } |
| |
| gboolean wsp_parameter_iter_next(struct wsp_parameter_iter *pi, |
| struct wsp_parameter *out) |
| { |
| const unsigned char *pdu = pi->pdu + pi->pos; |
| const unsigned char *end = pi->pdu + pi->max; |
| unsigned int token; |
| unsigned int consumed; |
| const char *untyped; |
| const char *value; |
| |
| /* Well known parameter token */ |
| if (wsp_decode_integer(pdu, end - pdu, &token, &consumed) == TRUE) { |
| pdu += consumed; |
| |
| switch (token) { |
| case WSP_PARAMETER_TYPE_LEVEL: |
| case WSP_PARAMETER_TYPE_DIFFERENCES: |
| if (*pdu & 0x80) { |
| unsigned int i = *pdu & 0x7f; |
| |
| pdu += 1; |
| pi->pos = pdu - pi->pdu; |
| |
| if (out) { |
| out->type = token; |
| out->value = WSP_PARAMETER_VALUE_INT; |
| out->integer = i; |
| } |
| |
| return TRUE; |
| } |
| |
| /* Continue to the string case */ |
| |
| /* fall through */ |
| case WSP_PARAMETER_TYPE_NAME_DEFUNCT: |
| case WSP_PARAMETER_TYPE_FILENAME_DEFUNCT: |
| case WSP_PARAMETER_TYPE_START_DEFUNCT: |
| case WSP_PARAMETER_TYPE_START_INFO_DEFUNCT: |
| case WSP_PARAMETER_TYPE_COMMENT_DEFUNCT: |
| case WSP_PARAMETER_TYPE_DOMAIN_DEFUNCT: |
| case WSP_PARAMETER_TYPE_PATH_DEFUNCT: |
| case WSP_PARAMETER_TYPE_NAME: |
| case WSP_PARAMETER_TYPE_FILENAME: |
| case WSP_PARAMETER_TYPE_START: |
| case WSP_PARAMETER_TYPE_START_INFO: |
| case WSP_PARAMETER_TYPE_COMMENT: |
| case WSP_PARAMETER_TYPE_DOMAIN: |
| case WSP_PARAMETER_TYPE_PATH: |
| case WSP_PARAMETER_TYPE_MAC: |
| { |
| const char *text = wsp_decode_text(pdu, end - pdu, |
| &consumed); |
| |
| if (text == NULL) |
| return FALSE; |
| |
| pdu += consumed; |
| pi->pos = pdu - pi->pdu; |
| |
| if (out) { |
| out->type = token; |
| out->value = WSP_PARAMETER_VALUE_TEXT; |
| out->text = text; |
| } |
| |
| return TRUE; |
| } |
| |
| case WSP_PARAMETER_TYPE_TYPE: |
| case WSP_PARAMETER_TYPE_SIZE: |
| case WSP_PARAMETER_TYPE_MAX_AGE: |
| { |
| unsigned int i; |
| |
| if (wsp_decode_integer(pdu, end - pdu, |
| &i, &consumed) == FALSE) |
| return FALSE; |
| |
| pdu += consumed; |
| pi->pos = pdu - pi->pdu; |
| |
| if (out) { |
| out->type = token; |
| out->value = WSP_PARAMETER_VALUE_INT; |
| out->integer = i; |
| } |
| |
| return TRUE; |
| } |
| |
| case WSP_PARAMETER_TYPE_PADDING: |
| case WSP_PARAMETER_TYPE_SEC: |
| { |
| if ((*pdu & 0x80) == 0) |
| return FALSE; |
| |
| pdu += 1; |
| pi->pos = pdu - pi->pdu; |
| |
| if (out) { |
| out->type = token; |
| out->value = WSP_PARAMETER_VALUE_INT; |
| out->integer = *pdu & 0x7f; |
| } |
| |
| return TRUE; |
| } |
| |
| case WSP_PARAMETER_TYPE_CREATION_DATE: |
| case WSP_PARAMETER_TYPE_MODIFICATION_DATE: |
| case WSP_PARAMETER_TYPE_READ_DATE: |
| { |
| unsigned int i; |
| |
| if (wsp_decode_integer(pdu, end - pdu, |
| &i, &consumed) == FALSE) |
| return FALSE; |
| |
| pdu += consumed; |
| pi->pos = pdu - pi->pdu; |
| |
| if (out) { |
| out->type = token; |
| out->value = WSP_PARAMETER_VALUE_DATE; |
| out->integer = i; |
| } |
| |
| return TRUE; |
| } |
| |
| case WSP_PARAMETER_TYPE_SECURE: |
| if (*pdu != 0) |
| return FALSE; |
| |
| pdu += 1; |
| pi->pos = pdu - pi->pdu; |
| |
| if (out) { |
| out->type = token; |
| out->value = WSP_PARAMETER_VALUE_TEXT; |
| out->text = (const char *) pdu - 1; |
| } |
| |
| return TRUE; |
| |
| case WSP_PARAMETER_TYPE_CHARSET: |
| { |
| unsigned int i; |
| const char *charset; |
| |
| if (wsp_decode_integer(pdu, end - pdu, |
| &i, &consumed) == FALSE) |
| return FALSE; |
| |
| charset = get_text_entry(i, charset_assignments); |
| if (charset == NULL) |
| return FALSE; |
| |
| pdu += consumed; |
| pi->pos = pdu - pi->pdu; |
| |
| if (out) { |
| out->type = token; |
| out->value = WSP_PARAMETER_VALUE_TEXT; |
| out->text = charset; |
| } |
| |
| return TRUE; |
| } |
| |
| case WSP_PARAMETER_TYPE_CONTENT_TYPE: |
| { |
| const char *ct; |
| |
| if (*pdu & 0x80) { |
| unsigned int i = *pdu & 0x7f; |
| |
| if (i >= LAST_CONTENT_TYPE) |
| return FALSE; |
| |
| ct = content_types[i]; |
| pdu += 1; |
| } else if ((ct = wsp_decode_text(pdu, end - pdu, |
| &consumed))) |
| pdu += consumed; |
| else |
| return FALSE; |
| |
| pi->pos = pdu - pi->pdu; |
| |
| if (out) { |
| out->type = token; |
| out->value = WSP_PARAMETER_VALUE_TEXT; |
| out->text = ct; |
| } |
| |
| return TRUE; |
| } |
| |
| /* TODO */ |
| case WSP_PARAMETER_TYPE_Q: |
| default: |
| return FALSE; |
| } |
| } |
| |
| untyped = wsp_decode_text(pdu, end - pdu, &consumed); |
| if (untyped == NULL) |
| return FALSE; |
| |
| pdu += consumed; |
| |
| if (wsp_decode_integer(pdu, end - pdu, &token, &consumed) == TRUE) { |
| pdu += consumed; |
| pi->pos = pdu - pi->pdu; |
| |
| out->type = WSP_PARAMETER_TYPE_UNTYPED; |
| out->value = WSP_PARAMETER_VALUE_INT; |
| out->integer = token; |
| out->untyped = untyped; |
| |
| return TRUE; |
| } |
| |
| value = wsp_decode_text(pdu, end - pdu, &consumed); |
| if (value == NULL) |
| return FALSE; |
| |
| pdu += consumed; |
| pi->pos = pdu - pi->pdu; |
| |
| out->type = WSP_PARAMETER_TYPE_UNTYPED; |
| out->value = WSP_PARAMETER_VALUE_TEXT; |
| out->text = value; |
| out->untyped = untyped; |
| |
| return TRUE; |
| } |
| |
| static const char *decode_token(char *buf, gboolean accept_quotes, |
| unsigned int *out_consumed, |
| char *out_terminator) |
| { |
| unsigned int pos = 0; |
| unsigned int start; |
| unsigned int end; |
| char *endp; |
| char terminator = '\0'; |
| |
| /* Skip leading space */ |
| while (buf[pos] == ' ' || buf[pos] == '\t') |
| pos += 1; |
| |
| if (buf[pos] == '\0') |
| return NULL; |
| |
| start = pos; |
| |
| if (buf[pos] == '"') { |
| if (accept_quotes == FALSE) |
| return NULL; |
| |
| pos += 1; |
| start = pos; |
| |
| endp = strchr(buf + pos, '"'); |
| if (endp == NULL) |
| return NULL; |
| |
| pos = endp - buf; |
| end = pos; |
| pos += 1; |
| } else { |
| endp = strpbrk(buf + pos, sep_chars); |
| |
| if (endp == NULL) |
| pos = strlen(buf); |
| else |
| pos = endp - buf; |
| |
| end = pos; |
| } |
| |
| while (buf[pos] == ' ' || buf[pos] == '\t') |
| pos += 1; |
| |
| if (buf[pos] != '\0') { |
| terminator = buf[pos]; |
| pos += 1; |
| } |
| |
| buf[end] = '\0'; |
| |
| if (strpbrk(buf + start, ctl_chars) != NULL) |
| return NULL; |
| |
| *out_consumed = pos; |
| *out_terminator = terminator; |
| |
| return buf + start; |
| } |
| |
| gboolean wsp_text_header_iter_init(struct wsp_text_header_iter *iter, |
| const char *hdr) |
| { |
| unsigned int len = strlen(hdr); |
| char terminator; |
| unsigned int consumed; |
| const char *key; |
| const char *value; |
| |
| if (len > MAX_TEXT_HEADER_SIZE) |
| return FALSE; |
| |
| memcpy(iter->hdr, hdr, len); |
| iter->hdr[len] = '\0'; |
| iter->pos = 0; |
| iter->key = NULL; |
| iter->value = NULL; |
| |
| key = decode_token(iter->hdr, FALSE, &consumed, &terminator); |
| if (key == NULL) |
| return FALSE; |
| |
| if (terminator != ':') |
| return FALSE; |
| |
| len = consumed; |
| |
| value = decode_token(iter->hdr + len, TRUE, &consumed, &terminator); |
| if (value == NULL) |
| return FALSE; |
| |
| if (terminator != '\0' && terminator != ';') |
| return FALSE; |
| |
| len += consumed; |
| |
| iter->key = key; |
| iter->value = value; |
| iter->pos = len; |
| |
| return TRUE; |
| } |
| |
| gboolean wsp_text_header_iter_param_next(struct wsp_text_header_iter *iter) |
| { |
| unsigned int pos = iter->pos; |
| char terminator; |
| unsigned int consumed; |
| const char *key; |
| const char *value; |
| |
| key = decode_token(iter->hdr + pos, FALSE, &consumed, &terminator); |
| if (key == NULL) |
| return FALSE; |
| |
| /* Empty value */ |
| if (terminator == ';' || terminator == '\0') { |
| iter->key = key; |
| iter->value = NULL; |
| iter->pos += consumed; |
| |
| return TRUE; |
| } |
| |
| if (terminator != '=') |
| return FALSE; |
| |
| pos += consumed; |
| |
| value = decode_token(iter->hdr + pos, TRUE, &consumed, &terminator); |
| if (value == NULL) |
| return FALSE; |
| |
| if (terminator != '\0' && terminator != ';') |
| return FALSE; |
| |
| pos += consumed; |
| |
| iter->key = key; |
| iter->value = value; |
| iter->pos = pos; |
| |
| return TRUE; |
| } |
| |
| const char *wsp_text_header_iter_get_key(struct wsp_text_header_iter *iter) |
| { |
| return iter->key; |
| } |
| |
| const char *wsp_text_header_iter_get_value(struct wsp_text_header_iter *iter) |
| { |
| return iter->value; |
| } |