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