blob: 9793b05a95773197dca148e73aa3c37a5d67c90c [file] [log] [blame]
/*
*
* 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;
}