serialui: Convert Japanese resource to UTF-8.
[wine] / dlls / winhttp / url.c
1 /*
2  * Copyright 2008 Hans Leidekker for CodeWeavers
3  *
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.
8  *
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.
13  *
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
17  */
18
19 #include "config.h"
20 #include <stdarg.h>
21
22 #include "wine/debug.h"
23
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winhttp.h"
27
28 #include "winhttp_private.h"
29
30 WINE_DEFAULT_DEBUG_CHANNEL(winhttp);
31
32 #define SCHEME_HTTP  3
33 #define SCHEME_HTTPS 4
34
35 BOOL WINAPI InternetCrackUrlW( LPCWSTR, DWORD, DWORD, LPURL_COMPONENTSW );
36
37 /***********************************************************************
38  *          WinHttpCrackUrl (winhttp.@)
39  */
40 BOOL WINAPI WinHttpCrackUrl( LPCWSTR url, DWORD len, DWORD flags, LPURL_COMPONENTSW components )
41 {
42     BOOL ret;
43
44     TRACE("%s, %d, %x, %p\n", debugstr_w(url), len, flags, components);
45
46     if ((ret = InternetCrackUrlW( url, len, flags, components )))
47     {
48         /* fix up an incompatibility between wininet and winhttp */
49         if (components->nScheme == SCHEME_HTTP) components->nScheme = INTERNET_SCHEME_HTTP;
50         else if (components->nScheme == SCHEME_HTTPS) components->nScheme = INTERNET_SCHEME_HTTPS;
51         else
52         {
53             set_last_error( ERROR_WINHTTP_UNRECOGNIZED_SCHEME );
54             return FALSE;
55         }
56     }
57     return ret;
58 }
59
60 static const WCHAR scheme_http[] = {'h','t','t','p',0};
61 static const WCHAR scheme_https[] = {'h','t','t','p','s',0};
62
63 static INTERNET_SCHEME get_scheme( const WCHAR *scheme, DWORD len )
64 {
65     if (!strncmpW( scheme, scheme_http, len )) return INTERNET_SCHEME_HTTP;
66     if (!strncmpW( scheme, scheme_https, len )) return INTERNET_SCHEME_HTTPS;
67     return 0;
68 }
69
70 static const WCHAR *get_scheme_string( INTERNET_SCHEME scheme )
71 {
72     if (scheme == INTERNET_SCHEME_HTTP) return scheme_http;
73     if (scheme == INTERNET_SCHEME_HTTPS) return scheme_https;
74     return NULL;
75 }
76
77 static BOOL uses_default_port( INTERNET_SCHEME scheme, INTERNET_PORT port )
78 {
79     if ((scheme == INTERNET_SCHEME_HTTP) && (port == INTERNET_DEFAULT_HTTP_PORT)) return TRUE;
80     if ((scheme == INTERNET_SCHEME_HTTPS) && (port == INTERNET_DEFAULT_HTTPS_PORT)) return TRUE;
81     return FALSE;
82 }
83
84 static BOOL need_escape( WCHAR c )
85 {
86     if (isalnumW( c )) return FALSE;
87
88     if (c <= 31 || c >= 127) return TRUE;
89     else
90     {
91         switch (c)
92         {
93         case ' ':
94         case '"':
95         case '#':
96         case '%':
97         case '<':
98         case '>':
99         case ']':
100         case '\\':
101         case '[':
102         case '^':
103         case '`':
104         case '{':
105         case '|':
106         case '}':
107         case '~':
108             return TRUE;
109         default:
110             return FALSE;
111         }
112     }
113 }
114
115 static DWORD copy_escape( WCHAR *dst, const WCHAR *src, DWORD len )
116 {
117     static const WCHAR hex[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
118     DWORD ret = len;
119     unsigned int i;
120     WCHAR *p = dst;
121
122     for (i = 0; i < len; i++, p++)
123     {
124         if (need_escape( src[i] ))
125         {
126             p[0] = '%';
127             p[1] = hex[(src[i] >> 4) & 0xf];
128             p[2] = hex[src[i] & 0xf];
129             ret += 2;
130             p += 2;
131         }
132         else *p = src[i];
133     }
134     dst[ret] = 0;
135     return ret;
136 }
137
138 static DWORD comp_length( DWORD len, DWORD flags, WCHAR *comp )
139 {
140     DWORD ret;
141     unsigned int i;
142
143     ret = len ? len : strlenW( comp );
144     if (!(flags & ICU_ESCAPE)) return ret;
145     for (i = 0; i < len; i++) if (need_escape( comp[i] )) ret += 2;
146     return ret;
147 }
148
149 static BOOL calc_length( URL_COMPONENTS *uc, DWORD flags, LPDWORD len )
150 {
151     static const WCHAR formatW[] = {'%','d',0};
152     INTERNET_SCHEME scheme;
153
154     *len = 0;
155     if (uc->lpszScheme)
156     {
157         DWORD scheme_len = comp_length( uc->dwSchemeLength, 0, uc->lpszScheme );
158         *len += scheme_len;
159         scheme = get_scheme( uc->lpszScheme, scheme_len );
160     }
161     else
162     {
163         scheme = uc->nScheme;
164         if (!scheme) scheme = INTERNET_SCHEME_HTTP;
165         *len += strlenW( get_scheme_string( scheme ) );
166     }
167     *len += 1; /* ':' */
168     if (uc->lpszHostName) *len += 2; /* "//" */
169
170     if (uc->lpszUserName)
171     {
172         *len += comp_length( uc->dwUserNameLength, 0, uc->lpszUserName );
173         *len += 1; /* "@" */
174     }
175     else
176     {
177         if (uc->lpszPassword)
178         {
179             set_last_error( ERROR_INVALID_PARAMETER );
180             return FALSE;
181         }
182     }
183     if (uc->lpszPassword)
184     {
185         *len += 1; /* ":" */
186         *len += comp_length( uc->dwPasswordLength, 0, uc->lpszPassword );
187     }
188     if (uc->lpszHostName)
189     {
190         *len += comp_length( uc->dwHostNameLength, 0, uc->lpszHostName );
191
192         if (!uses_default_port( scheme, uc->nPort ))
193         {
194             WCHAR port[sizeof("65535")];
195
196             sprintfW( port, formatW, uc->nPort );
197             *len += strlenW( port );
198             *len += 1; /* ":" */
199         }
200         if (uc->lpszUrlPath && *uc->lpszUrlPath != '/') *len += 1; /* '/' */
201     }
202     if (uc->lpszUrlPath) *len += comp_length( uc->dwUrlPathLength, flags, uc->lpszUrlPath );
203     if (uc->lpszExtraInfo) *len += comp_length( uc->dwExtraInfoLength, flags, uc->lpszExtraInfo );
204     return TRUE;
205 }
206
207 /***********************************************************************
208  *          WinHttpCreateUrl (winhttp.@)
209  */
210 BOOL WINAPI WinHttpCreateUrl( LPURL_COMPONENTS uc, DWORD flags, LPWSTR url, LPDWORD required )
211 {
212     static const WCHAR formatW[] = {'%','d',0};
213     static const WCHAR twoslashW[] = {'/','/'};
214
215     DWORD len;
216     INTERNET_SCHEME scheme;
217
218     TRACE("%p, 0x%08x, %p, %p\n", uc, flags, url, required);
219
220     if (!uc || uc->dwStructSize != sizeof(URL_COMPONENTS) || !required)
221     {
222         set_last_error( ERROR_INVALID_PARAMETER );
223         return FALSE;
224     }
225
226     if (!calc_length( uc, flags, &len )) return FALSE;
227
228     if (!url || *required < len)
229     {
230         *required = len + 1;
231         set_last_error( ERROR_INSUFFICIENT_BUFFER );
232         return FALSE;
233     }
234
235     url[0] = 0;
236     *required = len;
237     if (uc->lpszScheme)
238     {
239         len = comp_length( uc->dwSchemeLength, 0, uc->lpszScheme );
240         memcpy( url, uc->lpszScheme, len * sizeof(WCHAR) );
241         url += len;
242
243         scheme = get_scheme( uc->lpszScheme, len );
244     }
245     else
246     {
247         const WCHAR *schemeW;
248         scheme = uc->nScheme;
249
250         if (!scheme) scheme = INTERNET_SCHEME_HTTP;
251
252         schemeW = get_scheme_string( scheme );
253         len = strlenW( schemeW );
254         memcpy( url, schemeW, len * sizeof(WCHAR) );
255         url += len;
256     }
257
258     /* all schemes are followed by at least a colon */
259     *url = ':';
260     url++;
261
262     if (uc->lpszHostName)
263     {
264         memcpy( url, twoslashW, sizeof(twoslashW) );
265         url += sizeof(twoslashW) / sizeof(twoslashW[0]);
266     }
267     if (uc->lpszUserName)
268     {
269         len = comp_length( uc->dwUserNameLength, 0, uc->lpszUserName );
270         memcpy( url, uc->lpszUserName, len * sizeof(WCHAR) );
271         url += len;
272
273         if (uc->lpszPassword)
274         {
275             *url = ':';
276             url++;
277
278             len = comp_length( uc->dwPasswordLength, 0, uc->lpszPassword );
279             memcpy( url, uc->lpszPassword, len * sizeof(WCHAR) );
280             url += len;
281         }
282         *url = '@';
283         url++;
284     }
285     if (uc->lpszHostName)
286     {
287         len = comp_length( uc->dwHostNameLength, 0, uc->lpszHostName );
288         memcpy( url, uc->lpszHostName, len * sizeof(WCHAR) );
289         url += len;
290
291         if (!uses_default_port( scheme, uc->nPort ))
292         {
293             WCHAR port[sizeof("65535")];
294
295             sprintfW( port, formatW, uc->nPort );
296             *url = ':';
297             url++;
298
299             len = strlenW( port );
300             memcpy( url, port, len * sizeof(WCHAR) );
301             url += len;
302         }
303
304         /* add slash between hostname and path if necessary */
305         if (uc->lpszUrlPath && *uc->lpszUrlPath != '/')
306         {
307             *url = '/';
308             url++;
309         }
310     }
311     if (uc->lpszUrlPath)
312     {
313         len = comp_length( uc->dwUrlPathLength, 0, uc->lpszUrlPath );
314         if (flags & ICU_ESCAPE) url += copy_escape( url, uc->lpszUrlPath, len );
315         else
316         {
317             memcpy( url, uc->lpszUrlPath, len * sizeof(WCHAR) );
318             url += len;
319         }
320     }
321     if (uc->lpszExtraInfo)
322     {
323         len = comp_length( uc->dwExtraInfoLength, 0, uc->lpszExtraInfo );
324         if (flags & ICU_ESCAPE) url += copy_escape( url, uc->lpszExtraInfo, len );
325         else
326         {
327             memcpy( url, uc->lpszExtraInfo, len * sizeof(WCHAR) );
328             url += len;
329         }
330     }
331     *url = 0;
332     return TRUE;
333 }