From 503d152bfb5023a77ce30f7a22c938e2f3b16e75 Mon Sep 17 00:00:00 2001 From: Hans Leidekker Date: Tue, 4 Oct 2016 13:28:34 +0200 Subject: [PATCH] webservices: Add support for writing WS_DATETIME values. Signed-off-by: Hans Leidekker Signed-off-by: Alexandre Julliard --- dlls/webservices/reader.c | 23 +----- dlls/webservices/tests/writer.c | 88 +++++++++++++++++++++++ dlls/webservices/webservices_private.h | 18 +++++ dlls/webservices/writer.c | 98 ++++++++++++++++++++++++++ include/webservices.h | 6 ++ 5 files changed, 212 insertions(+), 21 deletions(-) diff --git a/dlls/webservices/reader.c b/dlls/webservices/reader.c index eb4bb7be39e..d106b18a646 100644 --- a/dlls/webservices/reader.c +++ b/dlls/webservices/reader.c @@ -2468,32 +2468,15 @@ static HRESULT str_to_guid( const unsigned char *str, ULONG len, GUID *ret ) 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]; + return day > 0 && day <= month_days[leap_year( year )][month - 1]; } static inline int leap_days_before( int year ) @@ -2585,7 +2568,7 @@ static HRESULT str_to_datetime( const unsigned char *bytes, ULONG len, WS_DATETI 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 += month_offsets[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; @@ -2610,8 +2593,6 @@ static HRESULT str_to_datetime( const unsigned char *bytes, ULONG len, WS_DATETI return S_OK; } -#define TICKS_1601_01_01 504911232000000000 - /************************************************************************** * WsDateTimeToFileTime [webservices.@] */ diff --git a/dlls/webservices/tests/writer.c b/dlls/webservices/tests/writer.c index 2309ab5c8ab..c817615509f 100644 --- a/dlls/webservices/tests/writer.c +++ b/dlls/webservices/tests/writer.c @@ -2547,6 +2547,93 @@ static void test_write_option(void) WsFreeWriter( writer ); } +static BOOL check_result( WS_XML_WRITER *writer, const char *expected ) +{ + WS_BYTES bytes; + ULONG size = sizeof(bytes); + int len = strlen( expected ); + + memset( &bytes, 0, sizeof(bytes) ); + WsGetWriterProperty( writer, WS_XML_WRITER_PROPERTY_BYTES, &bytes, size, NULL ); + if (bytes.length != len) return FALSE; + return !memcmp( bytes.bytes, expected, len ); +} + +static void test_datetime(void) +{ + WS_XML_STRING localname = {1, (BYTE *)"t"}, ns = {0, NULL}; + static const struct + { + unsigned __int64 ticks; + WS_DATETIME_FORMAT format; + HRESULT hr; + const char *result; + const char *result2; + } + tests[] = + { + { 0, WS_DATETIME_FORMAT_UTC, S_OK, "0001-01-01T00:00:00Z" }, + { 0, WS_DATETIME_FORMAT_LOCAL, S_OK, "0001-01-01T00:00:00+00:00" }, + { 0, WS_DATETIME_FORMAT_NONE, S_OK, "0001-01-01T00:00:00" }, + { 1, WS_DATETIME_FORMAT_UTC, S_OK, "0001-01-01T00:00:00.0000001Z" }, + { 1, WS_DATETIME_FORMAT_LOCAL, S_OK, "0001-01-01T00:00:00.0000001+00:00" }, + { 1, WS_DATETIME_FORMAT_NONE, S_OK, "0001-01-01T00:00:00.0000001" }, + { 0x23c34600, WS_DATETIME_FORMAT_LOCAL, S_OK, "0001-01-01T00:01:00+00:00" }, + { 0x861c46800, WS_DATETIME_FORMAT_LOCAL, S_OK, "0001-01-01T01:00:00+00:00" }, + { 0x430e234000, WS_DATETIME_FORMAT_LOCAL, S_OK, "0001-01-01T08:00:00+00:00" }, + { 0x4b6fe7a800, WS_DATETIME_FORMAT_LOCAL, S_OK, "0001-01-01T09:00:00+00:00" }, + { 0x989680, WS_DATETIME_FORMAT_NONE, S_OK, "0001-01-01T00:00:01" }, + { 0x23c34600, WS_DATETIME_FORMAT_NONE, S_OK, "0001-01-01T00:01:00" }, + { 0x861c46800, WS_DATETIME_FORMAT_NONE, S_OK, "0001-01-01T01:00:00" }, + { 0xc92a69c000, WS_DATETIME_FORMAT_NONE, S_OK, "0001-01-02T00:00:00" }, + { 0x11ed178c6c000, WS_DATETIME_FORMAT_NONE, S_OK, "0002-01-01T00:00:00" }, + { 0x2bca2875f4373fff, WS_DATETIME_FORMAT_UTC, S_OK, "9999-12-31T23:59:59.9999999Z" }, + { 0x2bca2875f4373fff, WS_DATETIME_FORMAT_LOCAL, S_OK, "9999-12-31T15:59:59.9999999-08:00", + "9999-12-31T17:59:59.9999999-06:00" /* win7 */ }, + { 0x2bca2875f4373fff, WS_DATETIME_FORMAT_NONE, S_OK, "9999-12-31T23:59:59.9999999" }, + { 0x2bca2875f4374000, WS_DATETIME_FORMAT_UTC, WS_E_INVALID_FORMAT }, + { 0x2bca2875f4374000, WS_DATETIME_FORMAT_LOCAL, WS_E_INVALID_FORMAT }, + { 0x2bca2875f4374000, WS_DATETIME_FORMAT_NONE, WS_E_INVALID_FORMAT }, + { 0x8d3123e7df74000, WS_DATETIME_FORMAT_LOCAL, S_OK, "2015-12-31T16:00:00-08:00", + "2015-12-31T18:00:00-06:00" /* win7 */ }, + { 0x701ce1722770000, WS_DATETIME_FORMAT_LOCAL, S_OK, "1601-01-01T00:00:00+00:00" }, + { 0x701ce5a309a4000, WS_DATETIME_FORMAT_LOCAL, S_OK, "1601-01-01T00:00:00-08:00", + "1601-01-01T02:00:00-06:00" /* win7 */ }, + { 0x701ce5e617c7400, WS_DATETIME_FORMAT_LOCAL, S_OK, "1601-01-01T00:30:00-08:00", + "1601-01-01T02:30:00-06:00" /* win7 */ }, + { 0x701ce51ced5d800, WS_DATETIME_FORMAT_LOCAL, S_OK, "1601-01-01T07:00:00+00:00", + "1601-01-01T01:00:00-06:00" /* win7 */ }, + { 0, WS_DATETIME_FORMAT_NONE + 1, WS_E_INVALID_FORMAT }, + }; + HRESULT hr; + WS_XML_WRITER *writer; + WS_DATETIME date; + ULONG i; + + hr = WsCreateWriter( NULL, 0, &writer, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) + { + hr = set_output( writer ); + ok( hr == S_OK, "got %08x\n", hr ); + + date.ticks = tests[i].ticks; + date.format = tests[i].format; + WsWriteStartElement( writer, NULL, &localname, &ns, NULL ); + hr = WsWriteType( writer, WS_ELEMENT_TYPE_MAPPING, WS_DATETIME_TYPE, NULL, WS_WRITE_REQUIRED_VALUE, + &date, sizeof(date), NULL ); + WsWriteEndElement( writer, NULL ); + ok( hr == tests[i].hr, "%u: got %08x\n", i, hr ); + if (hr == S_OK) + { + ok( check_result( writer, tests[i].result ) || broken(check_result( writer, tests[i].result2 )), + "%u: wrong result\n", i ); + } + } + + WsFreeWriter( writer ); +} + START_TEST(writer) { test_WsCreateWriter(); @@ -2578,4 +2665,5 @@ START_TEST(writer) test_WsWriteArray(); test_escapes(); test_write_option(); + test_datetime(); } diff --git a/dlls/webservices/webservices_private.h b/dlls/webservices/webservices_private.h index f72d2b93fa2..cdaa4b5ca09 100644 --- a/dlls/webservices/webservices_private.h +++ b/dlls/webservices/webservices_private.h @@ -100,6 +100,24 @@ HRESULT message_insert_http_headers( WS_MESSAGE *, HINTERNET ) DECLSPEC_HIDDEN; HRESULT channel_send_message( WS_CHANNEL *, WS_MESSAGE * ) DECLSPEC_HIDDEN; HRESULT channel_receive_message( WS_CHANNEL *, char **, ULONG * ) DECLSPEC_HIDDEN; +#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 +#define TICKS_1601_01_01 504911232000000000 + +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 leap_year( int year ) +{ + return !(year % 4) && (year % 100 || !(year % 400)); +} + static inline BOOL is_nil_value( const char *value, ULONG size ) { ULONG i; diff --git a/dlls/webservices/writer.c b/dlls/webservices/writer.c index cc734d29628..7a818eb2398 100644 --- a/dlls/webservices/writer.c +++ b/dlls/webservices/writer.c @@ -1187,6 +1187,68 @@ static ULONG format_double( const double *ptr, unsigned char *buf ) #endif } +static inline int year_size( int year ) +{ + return leap_year( year ) ? 366 : 365; +} + +#define TZ_OFFSET 8 +static ULONG format_datetime( const WS_DATETIME *ptr, unsigned char *buf ) +{ + static const char fmt[] = "%04u-%02u-%02uT%02u:%02u:%02u"; + int day, hour, min, sec, sec_frac, month = 1, year = 1, tz_hour; + unsigned __int64 ticks, day_ticks; + ULONG len; + + if (ptr->format == WS_DATETIME_FORMAT_LOCAL && + ptr->ticks >= TICKS_1601_01_01 + TZ_OFFSET * TICKS_PER_HOUR) + { + ticks = ptr->ticks - TZ_OFFSET * TICKS_PER_HOUR; + tz_hour = TZ_OFFSET; + } + else + { + ticks = ptr->ticks; + tz_hour = 0; + } + day = ticks / TICKS_PER_DAY; + day_ticks = ticks % TICKS_PER_DAY; + hour = day_ticks / TICKS_PER_HOUR; + min = (day_ticks % TICKS_PER_HOUR) / TICKS_PER_MIN; + sec = (day_ticks % TICKS_PER_MIN) / TICKS_PER_SEC; + sec_frac = day_ticks % TICKS_PER_SEC; + + while (day >= year_size( year )) + { + day -= year_size( year ); + year++; + } + while (day >= month_days[leap_year( year )][month]) + { + day -= month_days[leap_year( year )][month]; + month++; + } + day++; + + len = sprintf( (char *)buf, fmt, year, month, day, hour, min, sec ); + if (sec_frac) + { + static const char fmt_frac[] = ".%07u"; + len += sprintf( (char *)buf + len, fmt_frac, sec_frac ); + } + if (ptr->format == WS_DATETIME_FORMAT_UTC) + { + buf[len++] = 'Z'; + } + else if (ptr->format == WS_DATETIME_FORMAT_LOCAL) + { + static const char fmt_tz[] = "%c%02u:00"; + len += sprintf( (char *)buf + len, fmt_tz, tz_hour ? '-' : '+', tz_hour ); + } + + return len; +} + static ULONG format_guid( const GUID *ptr, unsigned char *buf ) { static const char fmt[] = "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x"; @@ -1284,6 +1346,13 @@ static HRESULT text_to_utf8text( const WS_XML_TEXT *text, WS_XML_UTF8_TEXT **ret (*ret)->value.length = format_urn( &id->value, (*ret)->value.bytes ); return S_OK; } + case WS_XML_TEXT_TYPE_DATETIME: + { + const WS_XML_DATETIME_TEXT *dt = (const WS_XML_DATETIME_TEXT *)text; + if (!(*ret = alloc_utf8_text( NULL, 34 ))) return E_OUTOFMEMORY; + (*ret)->value.length = format_datetime( &dt->value, (*ret)->value.bytes ); + return S_OK; + } default: FIXME( "unhandled text type %u\n", text->textType ); return E_NOTIMPL; @@ -1666,6 +1735,32 @@ static HRESULT write_type_uint64( struct writer *writer, WS_TYPE_MAPPING mapping return write_type_text( writer, mapping, &utf8.text ); } +static HRESULT write_type_datetime( struct writer *writer, WS_TYPE_MAPPING mapping, + const WS_DATETIME_DESCRIPTION *desc, WS_WRITE_OPTION option, + const void *value, ULONG size ) +{ + WS_XML_UTF8_TEXT utf8; + unsigned char buf[34]; /* "0000-00-00T00:00:00.0000000-00:00" */ + const WS_DATETIME *ptr; + HRESULT hr; + + if (desc) + { + FIXME( "description not supported\n" ); + return E_NOTIMPL; + } + + if (!option || option == WS_WRITE_NILLABLE_VALUE) return E_INVALIDARG; + if ((hr = get_value_ptr( option, value, size, sizeof(WS_DATETIME), (const void **)&ptr )) != S_OK) return hr; + if (option == WS_WRITE_NILLABLE_POINTER && !ptr) return write_add_nil_attribute( writer ); + if (ptr->ticks > TICKS_MAX || ptr->format > WS_DATETIME_FORMAT_NONE) return WS_E_INVALID_FORMAT; + + utf8.text.textType = WS_XML_TEXT_TYPE_UTF8; + utf8.value.bytes = buf; + utf8.value.length = format_datetime( ptr, buf ); + return write_type_text( writer, mapping, &utf8.text ); +} + static HRESULT write_type_guid( struct writer *writer, WS_TYPE_MAPPING mapping, const WS_GUID_DESCRIPTION *desc, WS_WRITE_OPTION option, const void *value, ULONG size ) @@ -1931,6 +2026,9 @@ static HRESULT write_type( struct writer *writer, WS_TYPE_MAPPING mapping, WS_TY case WS_UINT64_TYPE: return write_type_uint64( writer, mapping, desc, option, value, size ); + case WS_DATETIME_TYPE: + return write_type_datetime( writer, mapping, desc, option, value, size ); + case WS_GUID_TYPE: return write_type_guid( writer, mapping, desc, option, value, size ); diff --git a/include/webservices.h b/include/webservices.h index f09d15b79f3..13fdaa7dfc9 100644 --- a/include/webservices.h +++ b/include/webservices.h @@ -58,6 +58,7 @@ 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_XML_DATETIME_TEXT WS_XML_DATETIME_TEXT; typedef struct _WS_DATETIME_DESCRIPTION WS_DATETIME_DESCRIPTION; typedef struct _WS_GUID_DESCRIPTION WS_GUID_DESCRIPTION; typedef struct _WS_UNIQUE_ID_DESCRIPTION WS_UNIQUE_ID_DESCRIPTION; @@ -1106,6 +1107,11 @@ struct _WS_DATETIME_DESCRIPTION { WS_DATETIME maxValue; }; +struct _WS_XML_DATETIME_TEXT { + WS_XML_TEXT text; + WS_DATETIME value; +}; + typedef enum { WS_URL_HTTP_SCHEME_TYPE, WS_URL_HTTPS_SCHEME_TYPE,