2 * Copyright 2016 Hans Leidekker for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #include "webservices.h"
26 #include "wine/debug.h"
27 #include "wine/heap.h"
28 #include "wine/list.h"
29 #include "wine/unicode.h"
30 #include "webservices_private.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(webservices
);
34 static const WCHAR http
[] = {'h','t','t','p'};
35 static const WCHAR https
[] = {'h','t','t','p','s'};
36 static const WCHAR nettcp
[] = {'n','e','t','.','t','c','p'};
37 static const WCHAR soapudp
[] = {'s','o','a','p','.','u','d','p'};
38 static const WCHAR netpipe
[] = {'n','e','t','.','p','i','p','e'};
40 static WS_URL_SCHEME_TYPE
scheme_type( const WCHAR
*str
, ULONG len
)
42 if (len
== sizeof(http
)/sizeof(http
[0]) && !memicmpW( str
, http
, sizeof(http
)/sizeof(http
[0]) ))
43 return WS_URL_HTTP_SCHEME_TYPE
;
45 if (len
== sizeof(https
)/sizeof(https
[0]) && !memicmpW( str
, https
, sizeof(https
)/sizeof(https
[0]) ))
46 return WS_URL_HTTPS_SCHEME_TYPE
;
48 if (len
== sizeof(nettcp
)/sizeof(nettcp
[0]) && !memicmpW( str
, nettcp
, sizeof(nettcp
)/sizeof(nettcp
[0]) ))
49 return WS_URL_NETTCP_SCHEME_TYPE
;
51 if (len
== sizeof(soapudp
)/sizeof(soapudp
[0]) && !memicmpW( str
, soapudp
, sizeof(soapudp
)/sizeof(soapudp
[0]) ))
52 return WS_URL_SOAPUDP_SCHEME_TYPE
;
54 if (len
== sizeof(netpipe
)/sizeof(netpipe
[0]) && !memicmpW( str
, netpipe
, sizeof(netpipe
)/sizeof(netpipe
[0]) ))
55 return WS_URL_NETPIPE_SCHEME_TYPE
;
60 static USHORT
default_port( WS_URL_SCHEME_TYPE scheme
)
64 case WS_URL_HTTP_SCHEME_TYPE
: return 80;
65 case WS_URL_HTTPS_SCHEME_TYPE
: return 443;
66 case WS_URL_NETTCP_SCHEME_TYPE
: return 808;
67 case WS_URL_SOAPUDP_SCHEME_TYPE
:
68 case WS_URL_NETPIPE_SCHEME_TYPE
: return 65535;
70 ERR( "unhandled scheme %u\n", scheme
);
75 static unsigned char *strdup_utf8( const WCHAR
*str
, ULONG len
, ULONG
*ret_len
)
78 *ret_len
= WideCharToMultiByte( CP_UTF8
, 0, str
, len
, NULL
, 0, NULL
, NULL
);
79 if ((ret
= heap_alloc( *ret_len
)))
80 WideCharToMultiByte( CP_UTF8
, 0, str
, len
, (char *)ret
, *ret_len
, NULL
, NULL
);
84 static inline int url_decode_byte( char c1
, char c2
)
88 if (c1
>= '0' && c1
<= '9') ret
= (c1
- '0') * 16;
89 else if (c1
>= 'a' && c1
<= 'f') ret
= (c1
- 'a' + 10) * 16;
90 else ret
= (c1
- 'A' + 10) * 16;
92 if (c2
>= '0' && c2
<= '9') ret
+= c2
- '0';
93 else if (c2
>= 'a' && c2
<= 'f') ret
+= c2
- 'a' + 10;
94 else ret
+= c2
- 'A' + 10;
99 static WCHAR
*url_decode( WCHAR
*str
, ULONG len
, WS_HEAP
*heap
, ULONG
*ret_len
)
101 WCHAR
*p
= str
, *q
, *ret
;
102 BOOL decode
= FALSE
, convert
= FALSE
;
103 ULONG i
, len_utf8
, len_left
;
104 unsigned char *utf8
, *r
;
107 for (i
= 0; i
< len
; i
++, p
++)
109 if ((len
- i
) < 3) break;
110 if (p
[0] == '%' && isxdigitW( p
[1] ) && isxdigitW( p
[2] ))
113 if (url_decode_byte( p
[1], p
[2] ) > 159)
121 if (!decode
) return str
;
124 if (!(q
= ret
= ws_alloc( heap
, *ret_len
* sizeof(WCHAR
) ))) return NULL
;
128 if (len
>= 3 && p
[0] == '%' && isxdigitW( p
[1] ) && isxdigitW( p
[2] ))
130 *q
++ = url_decode_byte( p
[1], p
[2] );
143 if (!(r
= utf8
= strdup_utf8( str
, len
, &len_utf8
))) return NULL
;
147 if (len_left
>= 3 && r
[0] == '%' && isxdigit( r
[1] ) && isxdigit( r
[2] ))
149 r
[0] = url_decode_byte( r
[1], r
[2] );
151 memmove( r
+ 1, r
+ 3, len_left
);
158 if (!(*ret_len
= MultiByteToWideChar( CP_UTF8
, MB_ERR_INVALID_CHARS
, (char *)utf8
,
159 len_utf8
, NULL
, 0 )))
161 WARN( "invalid UTF-8 sequence\n" );
165 if ((ret
= ws_alloc( heap
, *ret_len
* sizeof(WCHAR
) )))
166 MultiByteToWideChar( CP_UTF8
, 0, (char *)utf8
, len_utf8
, ret
, *ret_len
);
172 /**************************************************************************
173 * WsDecodeUrl [webservices.@]
175 HRESULT WINAPI
WsDecodeUrl( const WS_STRING
*str
, ULONG flags
, WS_HEAP
*heap
, WS_URL
**ret
,
178 HRESULT hr
= WS_E_QUOTA_EXCEEDED
;
179 WCHAR
*p
, *q
, *decoded
= NULL
;
180 WS_HTTP_URL
*url
= NULL
;
181 ULONG len
, len_decoded
, port
= 0;
183 TRACE( "%s %08x %p %p %p\n", str
? debugstr_wn(str
->chars
, str
->length
) : "null", flags
,
185 if (error
) FIXME( "ignoring error parameter\n" );
187 if (!str
|| !heap
) return E_INVALIDARG
;
188 if (!str
->length
) return WS_E_INVALID_FORMAT
;
191 FIXME( "unimplemented flags %08x\n", flags
);
194 if (!(decoded
= url_decode( str
->chars
, str
->length
, heap
, &len_decoded
)) ||
195 !(url
= ws_alloc( heap
, sizeof(*url
) ))) goto error
;
197 hr
= WS_E_INVALID_FORMAT
;
201 while (len
&& *q
!= ':') { q
++; len
--; };
202 if (*q
!= ':') goto error
;
203 if ((url
->url
.scheme
= scheme_type( p
, q
- p
)) == ~0u) goto error
;
205 if (!--len
|| *++q
!= '/') goto error
;
206 if (!--len
|| *++q
!= '/') goto error
;
209 while (len
&& *q
!= '/' && *q
!= ':' && *q
!= '?' && *q
!= '#') { q
++; len
--; };
210 if (q
== p
) goto error
;
211 url
->host
.length
= q
- p
;
214 if (len
&& *q
== ':')
217 while (len
&& isdigitW( *q
))
219 if ((port
= port
* 10 + *q
- '0') > 65535) goto error
;
223 url
->portAsString
.length
= q
- p
;
224 url
->portAsString
.chars
= p
;
228 url
->port
= default_port( url
->url
.scheme
);
229 url
->portAsString
.length
= 0;
230 url
->portAsString
.chars
= NULL
;
233 if (len
&& *q
== '/')
236 while (len
&& *q
!= '?') { q
++; len
--; };
237 url
->path
.length
= q
- p
;
240 else url
->path
.length
= 0;
242 if (len
&& *q
== '?')
245 while (len
&& *q
!= '#') { q
++; len
--; };
246 url
->query
.length
= q
- p
;
247 url
->query
.chars
= p
;
249 else url
->query
.length
= 0;
251 if (len
&& *q
== '#')
254 while (len
&& *q
!= '#') { q
++; len
--; };
255 url
->fragment
.length
= q
- p
;
256 url
->fragment
.chars
= p
;
258 else url
->fragment
.length
= 0;
260 *ret
= (WS_URL
*)url
;
264 if (decoded
!= str
->chars
) ws_free( heap
, decoded
, len_decoded
);
265 ws_free( heap
, url
, sizeof(*url
) );
269 static const WCHAR
*scheme_str( WS_URL_SCHEME_TYPE scheme
, ULONG
*len
)
273 case WS_URL_HTTP_SCHEME_TYPE
:
274 *len
= sizeof(http
)/sizeof(http
[0]);
277 case WS_URL_HTTPS_SCHEME_TYPE
:
278 *len
= sizeof(https
)/sizeof(https
[0]);
281 case WS_URL_NETTCP_SCHEME_TYPE
:
282 *len
= sizeof(nettcp
)/sizeof(nettcp
[0]);
285 case WS_URL_SOAPUDP_SCHEME_TYPE
:
286 *len
= sizeof(soapudp
)/sizeof(soapudp
[0]);
289 case WS_URL_NETPIPE_SCHEME_TYPE
:
290 *len
= sizeof(netpipe
)/sizeof(netpipe
[0]);
294 ERR( "unhandled scheme %u\n", scheme
);
299 static inline ULONG
escape_size( unsigned char ch
, const char *except
)
301 const char *p
= except
;
304 if (*p
== ch
) return 1;
307 if ((ch
>= 'a' && ch
<= 'z' ) || (ch
>= 'A' && ch
<= 'Z') || (ch
>= '0' && ch
<= '9')) return 1;
308 if (ch
< 33 || ch
> 126) return 3;
332 static HRESULT
url_encode_size( const WCHAR
*str
, ULONG len
, const char *except
, ULONG
*ret_len
)
335 BOOL convert
= FALSE
;
339 for (i
= 0; i
< len
; i
++)
346 *ret_len
+= escape_size( str
[i
], except
);
348 if (!convert
) return S_OK
;
351 if (!(utf8
= strdup_utf8( str
, len
, &len_utf8
))) return E_OUTOFMEMORY
;
352 for (i
= 0; i
< len_utf8
; i
++) *ret_len
+= escape_size( utf8
[i
], except
);
358 static ULONG
url_encode_byte( unsigned char byte
, const char *except
, WCHAR
*buf
)
360 static const WCHAR hex
[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
361 switch (escape_size( byte
, except
))
365 buf
[1] = hex
[(byte
>> 4) & 0xf];
366 buf
[2] = hex
[byte
& 0xf];
374 ERR( "unhandled escape size\n" );
380 static HRESULT
url_encode( const WCHAR
*str
, ULONG len
, WCHAR
*buf
, const char *except
, ULONG
*ret_len
)
383 ULONG i
, len_utf8
, len_enc
;
384 BOOL convert
= FALSE
;
389 for (i
= 0; i
< len
; i
++)
396 len_enc
= url_encode_byte( str
[i
], except
, p
);
400 if (!convert
) return S_OK
;
404 if (!(utf8
= strdup_utf8( str
, len
, &len_utf8
))) return E_OUTOFMEMORY
;
405 for (i
= 0; i
< len_utf8
; i
++)
407 len_enc
= url_encode_byte( utf8
[i
], except
, p
);
416 /**************************************************************************
417 * WsEncodeUrl [webservices.@]
419 HRESULT WINAPI
WsEncodeUrl( const WS_URL
*base
, ULONG flags
, WS_HEAP
*heap
, WS_STRING
*ret
,
422 static const WCHAR fmtW
[] = {':','%','u',0};
423 ULONG len
= 0, len_scheme
, len_enc
, ret_size
;
424 const WS_HTTP_URL
*url
= (const WS_HTTP_URL
*)base
;
430 TRACE( "%p %08x %p %p %p\n", base
, flags
, heap
, ret
, error
);
431 if (error
) FIXME( "ignoring error parameter\n" );
433 if (!url
|| !heap
|| !ret
) return E_INVALIDARG
;
436 FIXME( "unimplemented flags %08x\n", flags
);
439 if (!(scheme
= scheme_str( url
->url
.scheme
, &len_scheme
))) return WS_E_INVALID_FORMAT
;
440 len
= len_scheme
+ 3; /* '://' */
441 len
+= 6; /* ':65535' */
443 if ((hr
= url_encode_size( url
->host
.chars
, url
->host
.length
, "", &len_enc
)) != S_OK
)
447 if ((hr
= url_encode_size( url
->path
.chars
, url
->path
.length
, "/", &len_enc
)) != S_OK
)
451 if ((hr
= url_encode_size( url
->query
.chars
, url
->query
.length
, "/?", &len_enc
)) != S_OK
)
453 len
+= len_enc
+ 1; /* '?' */
455 if ((hr
= url_encode_size( url
->fragment
.chars
, url
->fragment
.length
, "/?", &len_enc
)) != S_OK
)
457 len
+= len_enc
+ 1; /* '#' */
459 ret_size
= len
* sizeof(WCHAR
);
460 if (!(str
= ws_alloc( heap
, ret_size
))) return WS_E_QUOTA_EXCEEDED
;
462 memcpy( str
, scheme
, len_scheme
* sizeof(WCHAR
) );
463 p
= str
+ len_scheme
;
468 if ((hr
= url_encode( url
->host
.chars
, url
->host
.length
, p
, "", &len_enc
)) != S_OK
)
472 if (url
->portAsString
.length
)
474 q
= url
->portAsString
.chars
;
475 len
= url
->portAsString
.length
;
476 while (len
&& isdigitW( *q
))
478 if ((port
= port
* 10 + *q
- '0') > 65535)
480 hr
= WS_E_INVALID_FORMAT
;
485 if (url
->port
&& port
!= url
->port
)
490 } else port
= url
->port
;
492 if (port
== default_port( url
->url
.scheme
)) port
= 0;
496 len
= sprintfW( buf
, fmtW
, port
);
497 memcpy( p
, buf
, len
* sizeof(WCHAR
) );
501 if ((hr
= url_encode( url
->path
.chars
, url
->path
.length
, p
, "/", &len_enc
)) != S_OK
)
505 if (url
->query
.length
)
508 if ((hr
= url_encode( url
->query
.chars
, url
->query
.length
, p
, "/?", &len_enc
)) != S_OK
)
513 if (url
->fragment
.length
)
516 if ((hr
= url_encode( url
->fragment
.chars
, url
->fragment
.length
, p
, "/?", &len_enc
)) != S_OK
)
521 ret
->length
= p
- str
;
526 ws_free( heap
, str
, ret_size
);