1 /* GIO - GLib Input, Output and Streaming Library
2 *
3 * Copyright (C) 2006-2007 Red Hat, Inc.
4 * Copyright (C) 2008 Novell, Inc.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General
17 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 *
19 * Author: Alexander Larsson <alexl@redhat.com>
20 * Author: Tor Lillqvist <tml@novell.com>
21 */
22
23 #include "config.h"
24
25 #include <wchar.h>
26
27 #include "gio/gioerror.h"
28 #include "gio/giomodule.h"
29 #include "gio/gvfs.h"
30
31 #include "gwinhttpfile.h"
32 #include "gwinhttpvfs.h"
33
34 static gboolean lookup_done = FALSE;
35 static gboolean funcs_found = FALSE;
36 static GWinHttpDllFuncs funcs;
37
38 static void
lookup_funcs(void)39 lookup_funcs (void)
40 {
41 HMODULE winhttp = NULL;
42 WCHAR winhttp_dll[MAX_PATH + 100];
43 int n;
44
45 if (lookup_done)
46 return;
47
48 n = GetSystemDirectoryW (winhttp_dll, MAX_PATH);
49 if (n > 0 && n < MAX_PATH)
50 {
51 if (winhttp_dll[n-1] != L'\\' &&
52 winhttp_dll[n-1] != L'/')
53 wcscat (winhttp_dll, L"\\");
54 wcscat (winhttp_dll, L"winhttp.dll");
55 winhttp = LoadLibraryW (winhttp_dll);
56 }
57
58 if (winhttp != NULL)
59 {
60 funcs.pWinHttpCloseHandle = (BOOL (WINAPI *) (HINTERNET)) GetProcAddress (winhttp, "WinHttpCloseHandle");
61 funcs.pWinHttpCrackUrl = (BOOL (WINAPI *) (LPCWSTR,DWORD,DWORD,LPURL_COMPONENTS)) GetProcAddress (winhttp, "WinHttpCrackUrl");
62 funcs.pWinHttpConnect = (HINTERNET (WINAPI *) (HINTERNET,LPCWSTR,INTERNET_PORT,DWORD)) GetProcAddress (winhttp, "WinHttpConnect");
63 funcs.pWinHttpCreateUrl = (BOOL (WINAPI *) (LPURL_COMPONENTS,DWORD,LPWSTR,LPDWORD)) GetProcAddress (winhttp, "WinHttpCreateUrl");
64 funcs.pWinHttpOpen = (HINTERNET (WINAPI *) (LPCWSTR,DWORD,LPCWSTR,LPCWSTR,DWORD)) GetProcAddress (winhttp, "WinHttpOpen");
65 funcs.pWinHttpOpenRequest = (HINTERNET (WINAPI *) (HINTERNET,LPCWSTR,LPCWSTR,LPCWSTR,LPCWSTR,LPCWSTR*,DWORD)) GetProcAddress (winhttp, "WinHttpOpenRequest");
66 funcs.pWinHttpQueryDataAvailable = (BOOL (WINAPI *) (HINTERNET,LPDWORD)) GetProcAddress (winhttp, "WinHttpQueryDataAvailable");
67 funcs.pWinHttpQueryHeaders = (BOOL (WINAPI *) (HINTERNET,DWORD,LPCWSTR,LPVOID,LPDWORD,LPDWORD)) GetProcAddress (winhttp, "WinHttpQueryHeaders");
68 funcs.pWinHttpReadData = (BOOL (WINAPI *) (HINTERNET,LPVOID,DWORD,LPDWORD)) GetProcAddress (winhttp, "WinHttpReadData");
69 funcs.pWinHttpReceiveResponse = (BOOL (WINAPI *) (HINTERNET,LPVOID)) GetProcAddress (winhttp, "WinHttpReceiveResponse");
70 funcs.pWinHttpSendRequest = (BOOL (WINAPI *) (HINTERNET,LPCWSTR,DWORD,LPVOID,DWORD,DWORD,DWORD_PTR)) GetProcAddress (winhttp, "WinHttpSendRequest");
71 funcs.pWinHttpWriteData = (BOOL (WINAPI *) (HINTERNET,LPCVOID,DWORD,LPDWORD)) GetProcAddress (winhttp, "WinHttpWriteData");
72
73 if (funcs.pWinHttpCloseHandle &&
74 funcs.pWinHttpCrackUrl &&
75 funcs.pWinHttpConnect &&
76 funcs.pWinHttpCreateUrl &&
77 funcs.pWinHttpOpen &&
78 funcs.pWinHttpOpenRequest &&
79 funcs.pWinHttpQueryDataAvailable &&
80 funcs.pWinHttpQueryHeaders &&
81 funcs.pWinHttpReadData &&
82 funcs.pWinHttpReceiveResponse &&
83 funcs.pWinHttpSendRequest &&
84 funcs.pWinHttpWriteData)
85 funcs_found = TRUE;
86 }
87 lookup_done = TRUE;
88 }
89
90 #define g_winhttp_vfs_get_type _g_winhttp_vfs_get_type
91 G_DEFINE_TYPE_WITH_CODE (GWinHttpVfs, g_winhttp_vfs, G_TYPE_VFS,
92 {
93 lookup_funcs ();
94 if (funcs_found)
95 g_io_extension_point_implement (G_VFS_EXTENSION_POINT_NAME,
96 g_define_type_id,
97 "winhttp",
98 10);
99 })
100
101 static const gchar *winhttp_uri_schemes[] = { "http", "https" };
102
103 static void
g_winhttp_vfs_finalize(GObject * object)104 g_winhttp_vfs_finalize (GObject *object)
105 {
106 GWinHttpVfs *vfs;
107
108 vfs = G_WINHTTP_VFS (object);
109
110 (G_WINHTTP_VFS_GET_CLASS (vfs)->funcs->pWinHttpCloseHandle) (vfs->session);
111 vfs->session = NULL;
112
113 if (vfs->wrapped_vfs)
114 g_object_unref (vfs->wrapped_vfs);
115 vfs->wrapped_vfs = NULL;
116
117 G_OBJECT_CLASS (g_winhttp_vfs_parent_class)->finalize (object);
118 }
119
120 static void
g_winhttp_vfs_init(GWinHttpVfs * vfs)121 g_winhttp_vfs_init (GWinHttpVfs *vfs)
122 {
123 wchar_t *wagent;
124 const gchar *prgname = g_get_prgname ();
125
126 vfs->wrapped_vfs = g_vfs_get_local ();
127
128 if (prgname)
129 wagent = g_utf8_to_utf16 (prgname, -1, NULL, NULL, NULL);
130 else
131 wagent = g_utf8_to_utf16 ("GWinHttpVfs", -1, NULL, NULL, NULL);
132
133 vfs->session = (G_WINHTTP_VFS_GET_CLASS (vfs)->funcs->pWinHttpOpen)
134 (wagent,
135 WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
136 WINHTTP_NO_PROXY_NAME,
137 WINHTTP_NO_PROXY_BYPASS,
138 0);
139
140 g_free (wagent);
141 }
142
143 /**
144 * g_winhttp_vfs_new:
145 *
146 * Returns a new #GVfs handle for a WinHttp vfs.
147 *
148 * Returns: a new #GVfs handle.
149 **/
150 GVfs *
_g_winhttp_vfs_new(void)151 _g_winhttp_vfs_new (void)
152 {
153 return g_object_new (G_TYPE_WINHTTP_VFS, NULL);
154 }
155
156 static GFile *
g_winhttp_vfs_get_file_for_path(GVfs * vfs,const char * path)157 g_winhttp_vfs_get_file_for_path (GVfs *vfs,
158 const char *path)
159 {
160 return g_vfs_get_file_for_path (G_WINHTTP_VFS (vfs)->wrapped_vfs, path);
161 }
162
163 static GFile *
g_winhttp_vfs_get_file_for_uri(GVfs * vfs,const char * uri)164 g_winhttp_vfs_get_file_for_uri (GVfs *vfs,
165 const char *uri)
166 {
167 GWinHttpVfs *winhttp_vfs = G_WINHTTP_VFS (vfs);
168 int i;
169 GFile *ret = NULL;
170
171 /* If it matches one of "our" schemes, handle it */
172 for (i = 0; i < G_N_ELEMENTS (winhttp_uri_schemes); i++)
173 {
174 if (g_ascii_strncasecmp (uri, winhttp_uri_schemes[i], strlen (winhttp_uri_schemes[i])) == 0 &&
175 uri[strlen (winhttp_uri_schemes[i])] == ':')
176 {
177 ret = _g_winhttp_file_new (winhttp_vfs, uri);
178 }
179 }
180
181 /* For other URIs fallback to the wrapped GVfs */
182 if (ret == NULL)
183 ret = g_vfs_get_file_for_uri (winhttp_vfs->wrapped_vfs, uri);
184
185 g_assert (ret != NULL);
186
187 return g_steal_pointer (&ret);
188 }
189
190 static const gchar * const *
g_winhttp_vfs_get_supported_uri_schemes(GVfs * vfs)191 g_winhttp_vfs_get_supported_uri_schemes (GVfs *vfs)
192 {
193 GWinHttpVfs *winhttp_vfs = G_WINHTTP_VFS (vfs);
194 const gchar * const *wrapped_vfs_uri_schemes = g_vfs_get_supported_uri_schemes (winhttp_vfs->wrapped_vfs);
195 int i, n;
196 const gchar **retval;
197
198 n = 0;
199 while (wrapped_vfs_uri_schemes[n] != NULL)
200 n++;
201
202 retval = g_new (const gchar *, n + G_N_ELEMENTS (winhttp_uri_schemes) + 1);
203 n = 0;
204 while (wrapped_vfs_uri_schemes[n] != NULL)
205 {
206 retval[n] = wrapped_vfs_uri_schemes[n];
207 n++;
208 }
209
210 for (i = 0; i < G_N_ELEMENTS (winhttp_uri_schemes); i++)
211 {
212 retval[n] = winhttp_uri_schemes[i];
213 n++;
214 }
215
216 retval[n] = NULL;
217
218 return retval;
219 }
220
221 static GFile *
g_winhttp_vfs_parse_name(GVfs * vfs,const char * parse_name)222 g_winhttp_vfs_parse_name (GVfs *vfs,
223 const char *parse_name)
224 {
225 GWinHttpVfs *winhttp_vfs = G_WINHTTP_VFS (vfs);
226
227 g_return_val_if_fail (G_IS_VFS (vfs), NULL);
228 g_return_val_if_fail (parse_name != NULL, NULL);
229
230 /* For plain file paths fallback to the wrapped GVfs */
231 if (g_path_is_absolute (parse_name))
232 return g_vfs_parse_name (winhttp_vfs->wrapped_vfs, parse_name);
233
234 /* Otherwise assume it is an URI, so pass on to
235 * g_winhttp_vfs_get_file_for_uri().
236 */
237 return g_winhttp_vfs_get_file_for_uri (vfs, parse_name);
238 }
239
240 static gboolean
g_winhttp_vfs_is_active(GVfs * vfs)241 g_winhttp_vfs_is_active (GVfs *vfs)
242 {
243 return TRUE;
244 }
245
246 static void
g_winhttp_vfs_class_init(GWinHttpVfsClass * class)247 g_winhttp_vfs_class_init (GWinHttpVfsClass *class)
248 {
249 GObjectClass *object_class;
250 GVfsClass *vfs_class;
251
252 object_class = (GObjectClass *) class;
253
254 object_class->finalize = g_winhttp_vfs_finalize;
255
256 vfs_class = G_VFS_CLASS (class);
257
258 vfs_class->is_active = g_winhttp_vfs_is_active;
259 vfs_class->get_file_for_path = g_winhttp_vfs_get_file_for_path;
260 vfs_class->get_file_for_uri = g_winhttp_vfs_get_file_for_uri;
261 vfs_class->get_supported_uri_schemes = g_winhttp_vfs_get_supported_uri_schemes;
262 vfs_class->parse_name = g_winhttp_vfs_parse_name;
263
264 lookup_funcs ();
265 if (funcs_found)
266 class->funcs = &funcs;
267 else
268 class->funcs = NULL;
269 }
270
271 char *
_g_winhttp_error_message(DWORD error_code)272 _g_winhttp_error_message (DWORD error_code)
273 {
274 /* The FormatMessage() API that g_win32_error_message() uses doesn't
275 * seem to know about WinHttp errors, unfortunately.
276 */
277 if (error_code >= WINHTTP_ERROR_BASE && error_code < WINHTTP_ERROR_BASE + 200)
278 {
279 switch (error_code)
280 {
281 /* FIXME: Use meaningful error messages */
282 #define CASE(x) case ERROR_WINHTTP_##x: return g_strdup ("WinHttp error: " #x);
283 CASE (AUTO_PROXY_SERVICE_ERROR);
284 CASE (AUTODETECTION_FAILED);
285 CASE (BAD_AUTO_PROXY_SCRIPT);
286 CASE (CANNOT_CALL_AFTER_OPEN);
287 CASE (CANNOT_CALL_AFTER_SEND);
288 CASE (CANNOT_CALL_BEFORE_OPEN);
289 CASE (CANNOT_CALL_BEFORE_SEND);
290 CASE (CANNOT_CONNECT);
291 CASE (CHUNKED_ENCODING_HEADER_SIZE_OVERFLOW);
292 CASE (CLIENT_AUTH_CERT_NEEDED);
293 CASE (CONNECTION_ERROR);
294 CASE (HEADER_ALREADY_EXISTS);
295 CASE (HEADER_COUNT_EXCEEDED);
296 CASE (HEADER_NOT_FOUND);
297 CASE (HEADER_SIZE_OVERFLOW);
298 CASE (INCORRECT_HANDLE_STATE);
299 CASE (INCORRECT_HANDLE_TYPE);
300 CASE (INTERNAL_ERROR);
301 CASE (INVALID_OPTION);
302 CASE (INVALID_QUERY_REQUEST);
303 CASE (INVALID_SERVER_RESPONSE);
304 CASE (INVALID_URL);
305 CASE (LOGIN_FAILURE);
306 CASE (NAME_NOT_RESOLVED);
307 CASE (NOT_INITIALIZED);
308 CASE (OPERATION_CANCELLED);
309 CASE (OPTION_NOT_SETTABLE);
310 CASE (OUT_OF_HANDLES);
311 CASE (REDIRECT_FAILED);
312 CASE (RESEND_REQUEST);
313 CASE (RESPONSE_DRAIN_OVERFLOW);
314 CASE (SECURE_CERT_CN_INVALID);
315 CASE (SECURE_CERT_DATE_INVALID);
316 CASE (SECURE_CERT_REV_FAILED);
317 CASE (SECURE_CERT_REVOKED);
318 CASE (SECURE_CERT_WRONG_USAGE);
319 CASE (SECURE_CHANNEL_ERROR);
320 CASE (SECURE_FAILURE);
321 CASE (SECURE_INVALID_CA);
322 CASE (SECURE_INVALID_CERT);
323 CASE (SHUTDOWN);
324 CASE (TIMEOUT);
325 CASE (UNABLE_TO_DOWNLOAD_SCRIPT);
326 CASE (UNRECOGNIZED_SCHEME);
327 #undef CASE
328 default:
329 return g_strdup_printf ("WinHttp error %ld", error_code);
330 }
331 }
332 else
333 return g_win32_error_message (error_code);
334 }
335
336 void
_g_winhttp_set_error(GError ** error,DWORD error_code,const char * what)337 _g_winhttp_set_error (GError **error,
338 DWORD error_code,
339 const char *what)
340 {
341 char *emsg = _g_winhttp_error_message (error_code);
342
343 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
344 "%s failed: %s", what, emsg);
345 g_free (emsg);
346 }
347
348 gboolean
_g_winhttp_response(GWinHttpVfs * vfs,HINTERNET request,GError ** error,const char * what)349 _g_winhttp_response (GWinHttpVfs *vfs,
350 HINTERNET request,
351 GError **error,
352 const char *what)
353 {
354 wchar_t *status_code;
355 DWORD status_code_len;
356
357 if (!G_WINHTTP_VFS_GET_CLASS (vfs)->funcs->pWinHttpReceiveResponse (request, NULL))
358 {
359 _g_winhttp_set_error (error, GetLastError (), what);
360
361 return FALSE;
362 }
363
364 status_code_len = 0;
365 if (!G_WINHTTP_VFS_GET_CLASS (vfs)->funcs->pWinHttpQueryHeaders
366 (request,
367 WINHTTP_QUERY_STATUS_CODE,
368 NULL,
369 NULL,
370 &status_code_len,
371 NULL) &&
372 GetLastError () != ERROR_INSUFFICIENT_BUFFER)
373 {
374 _g_winhttp_set_error (error, GetLastError (), what);
375
376 return FALSE;
377 }
378
379 status_code = g_malloc (status_code_len);
380
381 if (!G_WINHTTP_VFS_GET_CLASS (vfs)->funcs->pWinHttpQueryHeaders
382 (request,
383 WINHTTP_QUERY_STATUS_CODE,
384 NULL,
385 status_code,
386 &status_code_len,
387 NULL))
388 {
389 _g_winhttp_set_error (error, GetLastError (), what);
390 g_free (status_code);
391
392 return FALSE;
393 }
394
395 if (status_code[0] != L'2')
396 {
397 wchar_t *status_text = NULL;
398 DWORD status_text_len;
399
400 if (!G_WINHTTP_VFS_GET_CLASS (vfs)->funcs->pWinHttpQueryHeaders
401 (request,
402 WINHTTP_QUERY_STATUS_TEXT,
403 NULL,
404 NULL,
405 &status_text_len,
406 NULL) &&
407 GetLastError () == ERROR_INSUFFICIENT_BUFFER)
408 {
409 status_text = g_malloc (status_text_len);
410
411 if (!G_WINHTTP_VFS_GET_CLASS (vfs)->funcs->pWinHttpQueryHeaders
412 (request,
413 WINHTTP_QUERY_STATUS_TEXT,
414 NULL,
415 status_text,
416 &status_text_len,
417 NULL))
418 {
419 g_free (status_text);
420 status_text = NULL;
421 }
422 }
423
424 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
425 "%s failed: %S %S",
426 what, status_code, status_text ? status_text : L"");
427 g_free (status_code);
428 g_free (status_text);
429
430 return FALSE;
431 }
432
433 g_free (status_code);
434
435 return TRUE;
436 }
437
438 gboolean
_g_winhttp_query_header(GWinHttpVfs * vfs,HINTERNET request,const char * request_description,DWORD which_header,wchar_t ** header,GError ** error)439 _g_winhttp_query_header (GWinHttpVfs *vfs,
440 HINTERNET request,
441 const char *request_description,
442 DWORD which_header,
443 wchar_t **header,
444 GError **error)
445 {
446 DWORD header_len = 0;
447
448 if (!G_WINHTTP_VFS_GET_CLASS (vfs)->funcs->pWinHttpQueryHeaders
449 (request,
450 which_header,
451 NULL,
452 NULL,
453 &header_len,
454 NULL) &&
455 GetLastError () != ERROR_INSUFFICIENT_BUFFER)
456 {
457 _g_winhttp_set_error (error, GetLastError (), request_description);
458
459 return FALSE;
460 }
461
462 *header = g_malloc (header_len);
463 if (!G_WINHTTP_VFS_GET_CLASS (vfs)->funcs->pWinHttpQueryHeaders
464 (request,
465 which_header,
466 NULL,
467 *header,
468 &header_len,
469 NULL))
470 {
471 _g_winhttp_set_error (error, GetLastError (), request_description);
472 g_free (*header);
473 *header = NULL;
474
475 return FALSE;
476 }
477
478 return TRUE;
479 }
480