webservices: Add support for reading date values.

Signed-off-by: Hans Leidekker <hans@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Hans Leidekker 2016-04-29 20:21:51 +02:00 committed by Alexandre Julliard
parent 7ff383e5e2
commit 3268ceb573
3 changed files with 304 additions and 0 deletions

View File

@ -1881,6 +1881,148 @@ static HRESULT str_to_uint64( const unsigned char *str, ULONG len, UINT64 max, U
return S_OK;
}
#define TICKS_PER_SEC 10000000
#define TICKS_PER_MIN (60 * (ULONGLONG)TICKS_PER_SEC)
#define TICKS_PER_HOUR (3600 * (ULONGLONG)TICKS_PER_SEC)
#define TICKS_PER_DAY (86400 * (ULONGLONG)TICKS_PER_SEC)
#define TICKS_MAX 3155378975999999999
static const int month_offsets[2][12] =
{
{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}
};
static const int month_days[2][12] =
{
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
static inline int is_leap_year( int year )
{
return !(year % 4) && (year % 100 || !(year % 400));
}
static inline int valid_day( int year, int month, int day )
{
return day > 0 && day <= month_days[is_leap_year( year )][month - 1];
}
static inline int leap_days_before( int year )
{
return (year - 1) / 4 - (year - 1) / 100 + (year - 1) / 400;
}
static HRESULT str_to_datetime( const unsigned char *bytes, ULONG len, WS_DATETIME *ret )
{
const unsigned char *p = bytes, *q;
int year, month, day, hour, min, sec, sec_frac = 0, tz_hour, tz_min, tz_neg;
while (len && read_isspace( *p )) { p++; len--; }
while (len && read_isspace( p[len - 1] )) { len--; }
q = p;
while (len && isdigit( *q )) { q++; len--; };
if (q - p != 4 || !len || *q != '-') return WS_E_INVALID_FORMAT;
year = (p[0] - '0') * 1000 + (p[1] - '0') * 100 + (p[2] - '0') * 10 + p[3] - '0';
if (year < 1) return WS_E_INVALID_FORMAT;
p = ++q; len--;
while (len && isdigit( *q )) { q++; len--; };
if (q - p != 2 || !len || *q != '-') return WS_E_INVALID_FORMAT;
month = (p[0] - '0') * 10 + p[1] - '0';
if (month < 1 || month > 12) return WS_E_INVALID_FORMAT;
p = ++q; len--;
while (len && isdigit( *q )) { q++; len--; };
if (q - p != 2 || !len || *q != 'T') return WS_E_INVALID_FORMAT;
day = (p[0] - '0') * 10 + p[1] - '0';
if (!valid_day( year, month, day )) return WS_E_INVALID_FORMAT;
p = ++q; len--;
while (len && isdigit( *q )) { q++; len--; };
if (q - p != 2 || !len || *q != ':') return WS_E_INVALID_FORMAT;
hour = (p[0] - '0') * 10 + p[1] - '0';
if (hour > 24) return WS_E_INVALID_FORMAT;
p = ++q; len--;
while (len && isdigit( *q )) { q++; len--; };
if (q - p != 2 || !len || *q != ':') return WS_E_INVALID_FORMAT;
min = (p[0] - '0') * 10 + p[1] - '0';
if (min > 59 || (min > 0 && hour == 24)) return WS_E_INVALID_FORMAT;
p = ++q; len--;
while (len && isdigit( *q )) { q++; len--; };
if (q - p != 2 || !len) return WS_E_INVALID_FORMAT;
sec = (p[0] - '0') * 10 + p[1] - '0';
if (sec > 59 || (sec > 0 && hour == 24)) return WS_E_INVALID_FORMAT;
if (*q == '.')
{
unsigned int i, nb_digits, mul = TICKS_PER_SEC / 10;
p = ++q; len--;
while (len && isdigit( *q )) { q++; len--; };
nb_digits = q - p;
if (nb_digits < 1 || nb_digits > 7) return WS_E_INVALID_FORMAT;
for (i = 0; i < nb_digits; i++)
{
sec_frac += (p[i] - '0') * mul;
mul /= 10;
}
}
if (*q == 'Z')
{
if (--len) return WS_E_INVALID_FORMAT;
tz_hour = tz_min = tz_neg = 0;
ret->format = WS_DATETIME_FORMAT_UTC;
}
else if (*q == '+' || *q == '-')
{
tz_neg = (*q == '-') ? 1 : 0;
p = ++q; len--;
while (len && isdigit( *q )) { q++; len--; };
if (q - p != 2 || !len || *q != ':') return WS_E_INVALID_FORMAT;
tz_hour = (p[0] - '0') * 10 + p[1] - '0';
if (tz_hour > 14) return WS_E_INVALID_FORMAT;
p = ++q; len--;
while (len && isdigit( *q )) { q++; len--; };
if (q - p != 2 || len) return WS_E_INVALID_FORMAT;
tz_min = (p[0] - '0') * 10 + p[1] - '0';
if (tz_min > 59 || (tz_min > 0 && tz_hour == 14)) return WS_E_INVALID_FORMAT;
ret->format = WS_DATETIME_FORMAT_LOCAL;
}
else return WS_E_INVALID_FORMAT;
ret->ticks = ((year - 1) * 365 + leap_days_before( year )) * TICKS_PER_DAY;
ret->ticks += month_offsets[is_leap_year( year )][month - 1] * TICKS_PER_DAY;
ret->ticks += (day - 1) * TICKS_PER_DAY;
ret->ticks += hour * TICKS_PER_HOUR;
ret->ticks += min * TICKS_PER_MIN;
ret->ticks += sec * TICKS_PER_SEC;
ret->ticks += sec_frac;
if (tz_neg)
{
if (tz_hour * TICKS_PER_HOUR + tz_min * TICKS_PER_MIN + ret->ticks > TICKS_MAX)
return WS_E_INVALID_FORMAT;
ret->ticks += tz_hour * TICKS_PER_HOUR;
ret->ticks += tz_min * TICKS_PER_MIN;
}
else
{
if (tz_hour * TICKS_PER_HOUR + tz_min * TICKS_PER_MIN > ret->ticks)
return WS_E_INVALID_FORMAT;
ret->ticks -= tz_hour * TICKS_PER_HOUR;
ret->ticks -= tz_min * TICKS_PER_MIN;
}
return S_OK;
}
static HRESULT read_get_node_text( struct reader *reader, WS_XML_UTF8_TEXT **ret )
{
WS_XML_TEXT_NODE *text;
@ -2570,6 +2712,53 @@ static HRESULT read_type_enum( struct reader *reader, WS_TYPE_MAPPING mapping,
return S_OK;
}
static HRESULT read_type_datetime( struct reader *reader, WS_TYPE_MAPPING mapping,
const WS_XML_STRING *localname, const WS_XML_STRING *ns,
const WS_DATETIME_DESCRIPTION *desc, WS_READ_OPTION option,
WS_HEAP *heap, void *ret, ULONG size )
{
WS_XML_UTF8_TEXT *utf8;
HRESULT hr;
WS_DATETIME val = {0, WS_DATETIME_FORMAT_UTC};
BOOL found;
if (desc) FIXME( "ignoring description\n" );
if ((hr = read_get_text( reader, mapping, localname, ns, &utf8, &found )) != S_OK) return hr;
if (found && (hr = str_to_datetime( utf8->value.bytes, utf8->value.length, &val )) != S_OK) return hr;
switch (option)
{
case WS_READ_REQUIRED_VALUE:
if (!found) return WS_E_INVALID_FORMAT;
if (size != sizeof(WS_DATETIME)) return E_INVALIDARG;
*(WS_DATETIME *)ret = val;
break;
case WS_READ_REQUIRED_POINTER:
if (!found) return WS_E_INVALID_FORMAT;
/* fall through */
case WS_READ_OPTIONAL_POINTER:
{
WS_DATETIME *heap_val = NULL;
if (size != sizeof(heap_val)) return E_INVALIDARG;
if (found)
{
if (!(heap_val = ws_alloc( heap, sizeof(*heap_val) ))) return WS_E_QUOTA_EXCEEDED;
*heap_val = val;
}
*(WS_DATETIME **)ret = heap_val;
break;
}
default:
FIXME( "read option %u not supported\n", option );
return E_NOTIMPL;
}
return S_OK;
}
static BOOL is_empty_text_node( const struct node *node )
{
const WS_XML_TEXT_NODE *text = (const WS_XML_TEXT_NODE *)node;
@ -2643,6 +2832,9 @@ static ULONG get_type_size( WS_TYPE type, const WS_STRUCT_DESCRIPTION *desc )
case WS_UINT64_TYPE:
return sizeof(INT64);
case WS_DATETIME_TYPE:
return sizeof(WS_DATETIME);
case WS_WSZ_TYPE:
return sizeof(WCHAR *);
@ -2746,6 +2938,7 @@ static WS_READ_OPTION map_field_options( WS_TYPE type, ULONG options )
case WS_UINT32_TYPE:
case WS_UINT64_TYPE:
case WS_ENUM_TYPE:
case WS_DATETIME_TYPE:
return WS_READ_REQUIRED_VALUE;
case WS_WSZ_TYPE:
@ -2979,6 +3172,11 @@ static HRESULT read_type( struct reader *reader, WS_TYPE_MAPPING mapping, WS_TYP
return hr;
break;
case WS_DATETIME_TYPE:
if ((hr = read_type_datetime( reader, mapping, localname, ns, desc, option, heap, value, size )) != S_OK)
return hr;
break;
default:
FIXME( "type %u not supported\n", type );
return E_NOTIMPL;

View File

@ -2924,6 +2924,91 @@ static void test_WsResetHeap(void)
WsFreeHeap( heap );
}
static void test_datetime(void)
{
static const struct
{
const char *str;
HRESULT hr;
__int64 ticks;
WS_DATETIME_FORMAT format;
}
tests[] =
{
{"<t>0000-01-01T00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
{"<t>0001-01-01T00:00:00Z</t>", S_OK, 0, WS_DATETIME_FORMAT_UTC},
{"<t>0001-01-01T00:00:00.Z</t>", WS_E_INVALID_FORMAT, 0, 0},
{"<t>0001-01-01T00:00:00.0Z</t>", S_OK, 0, WS_DATETIME_FORMAT_UTC},
{"<t>0001-01-01T00:00:00.1Z</t>", S_OK, 0x0000f4240, WS_DATETIME_FORMAT_UTC},
{"<t>0001-01-01T00:00:00.01Z</t>", S_OK, 0x0000186a0, WS_DATETIME_FORMAT_UTC},
{"<t>0001-01-01T00:00:00.0000001Z</t>", S_OK, 1, WS_DATETIME_FORMAT_UTC},
{"<t>0001-01-01T00:00:00.9999999Z</t>", S_OK, 0x00098967f, WS_DATETIME_FORMAT_UTC},
{"<t>0001-01-01T00:00:00.0000000Z</t>", S_OK, 0, WS_DATETIME_FORMAT_UTC},
{"<t>0001-01-01T00:00:00.00000001Z</t>", WS_E_INVALID_FORMAT, 0, 0},
{"<t>0001-01-01T00:00:00Z-</t>", WS_E_INVALID_FORMAT, 0},
{"<t>-0001-01-01T00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
{"<t>0001-00-01T00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
{"<t>0001-13-01T00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
{"<t>0001-12-01T00:00:00Z</t>", S_OK, 0x1067555f88000, WS_DATETIME_FORMAT_UTC},
{"<t>0001-01-00T00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
{"<t>2001-01-32T00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
{"<t>2001-01-31T00:00:00Z</t>", S_OK, 0x8c2592fe3794000, WS_DATETIME_FORMAT_UTC},
{"<t>1900-02-29T00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
{"<t>2000-02-29T00:00:00Z</t>", S_OK, 0x8c1505f0e438000, 0},
{"<t>2001-02-29T00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
{"<t>2001-02-28T00:00:00Z</t>", S_OK, 0x8c26f30870a4000, WS_DATETIME_FORMAT_UTC},
{"<t>0001-00-01U00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
{"<t>0001-01-01T24:00:00Z</t>", S_OK, 0xc92a69c000, WS_DATETIME_FORMAT_UTC},
{"<t>0001-01-01T24:00:01Z</t>", WS_E_INVALID_FORMAT, 0, 0},
{"<t>0001-01-01T00:60:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
{"<t>0001-01-01T00:00:60Z</t>", WS_E_INVALID_FORMAT, 0, 0},
{"<t>0001-01-01T00:00:00Y</t>", WS_E_INVALID_FORMAT, 0, 0},
{"<t>0001-01-01T00:00:00+00:01</t>", WS_E_INVALID_FORMAT, 0, 0},
{"<t>0001-01-01T00:00:00-00:01</t>", S_OK, 0x023c34600, WS_DATETIME_FORMAT_LOCAL},
{"<t>9999-12-31T24:00:00+00:01</t>", S_OK, 0x2bca2875d073fa00, WS_DATETIME_FORMAT_LOCAL},
{"<t>9999-12-31T24:00:00-00:01</t>", WS_E_INVALID_FORMAT, 0, 0},
{"<t>0002-01-01T00:00:00+14:01</t>", WS_E_INVALID_FORMAT, 0, 0},
{"<t>0002-01-01T00:00:00+15:00</t>", WS_E_INVALID_FORMAT, 0, 0},
{"<t>0002-01-01T00:00:00+13:60</t>", WS_E_INVALID_FORMAT, 0, 0},
{"<t>0002-01-01T00:00:00+13:59</t>", S_OK, 0x11e5c43cc5600, WS_DATETIME_FORMAT_LOCAL},
{"<t>0002-01-01T00:00:00+01:00</t>", S_OK, 0x11ec917025800, WS_DATETIME_FORMAT_LOCAL},
{"<t>2016-01-01T00:00:00-01:00</t>", S_OK, 0x8d31246dfbba800, WS_DATETIME_FORMAT_LOCAL},
{"<t>2016-01-01T00:00:00Z</t>", S_OK, 0x8d3123e7df74000, WS_DATETIME_FORMAT_UTC},
{"<t> 2016-01-02T03:04:05Z </t>", S_OK, 0x8d313215fb64080, WS_DATETIME_FORMAT_UTC},
{"<t>+2016-01-01T00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
{"<t></t>", WS_E_INVALID_FORMAT, 0, 0},
{"<t>01-01-01T00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
{"<t>1601-01-01T00:00:00Z</t>", S_OK, 0x701ce1722770000, WS_DATETIME_FORMAT_UTC},
};
HRESULT hr;
WS_XML_READER *reader;
WS_HEAP *heap;
WS_DATETIME date;
ULONG i;
hr = WsCreateHeap( 1 << 16, 0, NULL, 0, &heap, NULL );
ok( hr == S_OK, "got %08x\n", hr );
hr = WsCreateReader( NULL, 0, &reader, NULL ) ;
ok( hr == S_OK, "got %08x\n", hr );
for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++)
{
memset( &date, 0, sizeof(date) );
prepare_type_test( reader, tests[i].str, strlen(tests[i].str) );
hr = WsReadType( reader, WS_ELEMENT_CONTENT_TYPE_MAPPING, WS_DATETIME_TYPE, NULL,
WS_READ_REQUIRED_VALUE, heap, &date, sizeof(date), NULL );
ok( hr == tests[i].hr, "%u: got %08x\n", i, hr );
if (hr == S_OK)
{
ok( date.ticks == tests[i].ticks, "%u: got %x%08x\n", i, (ULONG)(date.ticks >> 32), (ULONG)date.ticks );
ok( date.format == tests[i].format, "%u: got %u\n", i, date.format );
}
}
WsFreeReader( reader );
WsFreeHeap( heap );
}
START_TEST(reader)
{
test_WsCreateError();
@ -2949,4 +3034,5 @@ START_TEST(reader)
test_complex_struct_type();
test_repeating_element();
test_WsResetHeap();
test_datetime();
}

View File

@ -57,6 +57,8 @@ typedef struct _WS_PARAMETER_DESCRIPTION WS_PARAMETER_DESCRIPTION;
typedef struct _WS_OPERATION_CONTEXT WS_OPERATION_CONTEXT;
typedef struct _WS_CALL_PROPERTY WS_CALL_PROPERTY;
typedef struct _WS_DOUBLE_DESCRIPTION WS_DOUBLE_DESCRIPTION;
typedef struct _WS_DATETIME WS_DATETIME;
typedef struct _WS_DATETIME_DESCRIPTION WS_DATETIME_DESCRIPTION;
struct _WS_STRUCT_DESCRIPTION;
struct _WS_XML_STRING;
@ -989,6 +991,22 @@ struct _WS_CALL_PROPERTY {
ULONG valueSize;
};
typedef enum {
WS_DATETIME_FORMAT_UTC,
WS_DATETIME_FORMAT_LOCAL,
WS_DATETIME_FORMAT_NONE
} WS_DATETIME_FORMAT;
struct _WS_DATETIME {
unsigned __int64 ticks;
WS_DATETIME_FORMAT format;
};
struct _WS_DATETIME_DESCRIPTION {
WS_DATETIME minValue;
WS_DATETIME maxValue;
};
HRESULT WINAPI WsAlloc(WS_HEAP*, SIZE_T, void**, WS_ERROR*);
HRESULT WINAPI WsCall(WS_SERVICE_PROXY*, const WS_OPERATION_DESCRIPTION*, const void**,
WS_HEAP*, const WS_CALL_PROPERTY*, const ULONG, const WS_ASYNC_CONTEXT*,
@ -1015,6 +1033,8 @@ HRESULT WINAPI WsCreateServiceProxyFromTemplate(WS_CHANNEL_TYPE, const WS_PROXY_
HRESULT WINAPI WsCreateWriter(const WS_XML_WRITER_PROPERTY*, ULONG, WS_XML_WRITER**, WS_ERROR*);
HRESULT WINAPI WsCreateXmlBuffer(WS_HEAP*, const WS_XML_BUFFER_PROPERTY*, ULONG, WS_XML_BUFFER**,
WS_ERROR*);
HRESULT WINAPI WsDateTimeToFileTime(const WS_DATETIME*, FILETIME*, WS_ERROR*);
HRESULT WINAPI WsFileTimeToDateTime(const FILETIME*, WS_DATETIME*, WS_ERROR*);
HRESULT WINAPI WsFillReader(WS_XML_READER*, ULONG, const WS_ASYNC_CONTEXT*, WS_ERROR*);
HRESULT WINAPI WsFindAttribute(WS_XML_READER*, const WS_XML_STRING*, const WS_XML_STRING*, BOOL,
ULONG*, WS_ERROR*);