1 /* GIO - GLib Input, Output and Streaming Library
3 * Copyright (C) 2006-2007 Red Hat, Inc.
4 * Copyright (C) 2008 Novell, Inc.
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.
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.
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/>.
19 * Author: Alexander Larsson <alexl@redhat.com>
20 * Author: Tor Lillqvist <tml@novell.com>
27 #include "gio/gioerror.h"
28 #include "gio/giomodule.h"
31 #include "gwinhttpfile.h"
32 #include "gwinhttpvfs.h"
34 static gboolean lookup_done
= FALSE
;
35 static gboolean funcs_found
= FALSE
;
36 static GWinHttpDllFuncs funcs
;
41 HMODULE winhttp
= NULL
;
42 char winhttp_dll
[MAX_PATH
+ 100];
48 n
= GetSystemDirectory (winhttp_dll
, MAX_PATH
);
49 if (n
> 0 && n
< MAX_PATH
)
51 if (winhttp_dll
[n
-1] != '\\' &&
52 winhttp_dll
[n
-1] != '/')
53 strcat (winhttp_dll
, "\\");
54 strcat (winhttp_dll
, "winhttp.dll");
55 winhttp
= LoadLibrary (winhttp_dll
);
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");
73 if (funcs
.pWinHttpCloseHandle
&&
74 funcs
.pWinHttpCrackUrl
&&
75 funcs
.pWinHttpConnect
&&
76 funcs
.pWinHttpCreateUrl
&&
78 funcs
.pWinHttpOpenRequest
&&
79 funcs
.pWinHttpQueryDataAvailable
&&
80 funcs
.pWinHttpQueryHeaders
&&
81 funcs
.pWinHttpReadData
&&
82 funcs
.pWinHttpReceiveResponse
&&
83 funcs
.pWinHttpSendRequest
&&
84 funcs
.pWinHttpWriteData
)
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
,
95 g_io_extension_point_implement (G_VFS_EXTENSION_POINT_NAME
,
101 static const gchar
*winhttp_uri_schemes
[] = { "http", "https" };
104 g_winhttp_vfs_finalize (GObject
*object
)
108 vfs
= G_WINHTTP_VFS (object
);
110 (G_WINHTTP_VFS_GET_CLASS (vfs
)->funcs
->pWinHttpCloseHandle
) (vfs
->session
);
113 if (vfs
->wrapped_vfs
)
114 g_object_unref (vfs
->wrapped_vfs
);
115 vfs
->wrapped_vfs
= NULL
;
117 G_OBJECT_CLASS (g_winhttp_vfs_parent_class
)->finalize (object
);
121 g_winhttp_vfs_init (GWinHttpVfs
*vfs
)
125 vfs
->wrapped_vfs
= g_vfs_get_local ();
127 wagent
= g_utf8_to_utf16 (g_get_prgname (), -1, NULL
, NULL
, NULL
);
130 wagent
= g_utf8_to_utf16 ("GWinHttpVfs", -1, NULL
, NULL
, NULL
);
132 vfs
->session
= (G_WINHTTP_VFS_GET_CLASS (vfs
)->funcs
->pWinHttpOpen
)
134 WINHTTP_ACCESS_TYPE_DEFAULT_PROXY
,
135 WINHTTP_NO_PROXY_NAME
,
136 WINHTTP_NO_PROXY_BYPASS
,
145 * Returns a new #GVfs handle for a WinHttp vfs.
147 * Returns: a new #GVfs handle.
150 _g_winhttp_vfs_new (void)
152 return g_object_new (G_TYPE_WINHTTP_VFS
, NULL
);
156 g_winhttp_vfs_get_file_for_path (GVfs
*vfs
,
159 return g_vfs_get_file_for_path (G_WINHTTP_VFS (vfs
)->wrapped_vfs
, path
);
163 g_winhttp_vfs_get_file_for_uri (GVfs
*vfs
,
166 GWinHttpVfs
*winhttp_vfs
= G_WINHTTP_VFS (vfs
);
169 /* If it matches one of "our" schemes, handle it */
170 for (i
= 0; i
< G_N_ELEMENTS (winhttp_uri_schemes
); i
++)
171 if (g_ascii_strncasecmp (uri
, winhttp_uri_schemes
[i
], strlen (winhttp_uri_schemes
[i
])) == 0 &&
172 uri
[strlen (winhttp_uri_schemes
[i
])] == ':')
173 return _g_winhttp_file_new (winhttp_vfs
, uri
);
175 /* For other URIs fallback to the wrapped GVfs */
176 return g_vfs_parse_name (winhttp_vfs
->wrapped_vfs
, uri
);
179 static const gchar
* const *
180 g_winhttp_vfs_get_supported_uri_schemes (GVfs
*vfs
)
182 GWinHttpVfs
*winhttp_vfs
= G_WINHTTP_VFS (vfs
);
183 const gchar
* const *wrapped_vfs_uri_schemes
= g_vfs_get_supported_uri_schemes (winhttp_vfs
->wrapped_vfs
);
185 const gchar
**retval
;
188 while (wrapped_vfs_uri_schemes
[n
] != NULL
)
191 retval
= g_new (const gchar
*, n
+ G_N_ELEMENTS (winhttp_uri_schemes
) + 1);
193 while (wrapped_vfs_uri_schemes
[n
] != NULL
)
195 retval
[n
] = wrapped_vfs_uri_schemes
[n
];
199 for (i
= 0; i
< G_N_ELEMENTS (winhttp_uri_schemes
); i
++)
201 retval
[n
] = winhttp_uri_schemes
[i
];
211 g_winhttp_vfs_parse_name (GVfs
*vfs
,
212 const char *parse_name
)
214 GWinHttpVfs
*winhttp_vfs
= G_WINHTTP_VFS (vfs
);
216 g_return_val_if_fail (G_IS_VFS (vfs
), NULL
);
217 g_return_val_if_fail (parse_name
!= NULL
, NULL
);
219 /* For plain file paths fallback to the wrapped GVfs */
220 if (g_path_is_absolute (parse_name
))
221 return g_vfs_parse_name (winhttp_vfs
->wrapped_vfs
, parse_name
);
223 /* Otherwise assume it is an URI, so pass on to
224 * g_winhttp_vfs_get_file_for_uri().
226 return g_winhttp_vfs_get_file_for_uri (vfs
, parse_name
);
230 g_winhttp_vfs_is_active (GVfs
*vfs
)
236 g_winhttp_vfs_class_init (GWinHttpVfsClass
*class)
238 GObjectClass
*object_class
;
239 GVfsClass
*vfs_class
;
241 object_class
= (GObjectClass
*) class;
243 object_class
->finalize
= g_winhttp_vfs_finalize
;
245 vfs_class
= G_VFS_CLASS (class);
247 vfs_class
->is_active
= g_winhttp_vfs_is_active
;
248 vfs_class
->get_file_for_path
= g_winhttp_vfs_get_file_for_path
;
249 vfs_class
->get_file_for_uri
= g_winhttp_vfs_get_file_for_uri
;
250 vfs_class
->get_supported_uri_schemes
= g_winhttp_vfs_get_supported_uri_schemes
;
251 vfs_class
->parse_name
= g_winhttp_vfs_parse_name
;
255 class->funcs
= &funcs
;
261 _g_winhttp_error_message (DWORD error_code
)
263 /* The FormatMessage() API that g_win32_error_message() uses doesn't
264 * seem to know about WinHttp errors, unfortunately.
266 if (error_code
>= WINHTTP_ERROR_BASE
&& error_code
< WINHTTP_ERROR_BASE
+ 200)
270 /* FIXME: Use meaningful error messages */
271 #define CASE(x) case ERROR_WINHTTP_##x: return g_strdup ("WinHttp error: " #x);
272 CASE (AUTO_PROXY_SERVICE_ERROR
);
273 CASE (AUTODETECTION_FAILED
);
274 CASE (BAD_AUTO_PROXY_SCRIPT
);
275 CASE (CANNOT_CALL_AFTER_OPEN
);
276 CASE (CANNOT_CALL_AFTER_SEND
);
277 CASE (CANNOT_CALL_BEFORE_OPEN
);
278 CASE (CANNOT_CALL_BEFORE_SEND
);
279 CASE (CANNOT_CONNECT
);
280 CASE (CHUNKED_ENCODING_HEADER_SIZE_OVERFLOW
);
281 CASE (CLIENT_AUTH_CERT_NEEDED
);
282 CASE (CONNECTION_ERROR
);
283 CASE (HEADER_ALREADY_EXISTS
);
284 CASE (HEADER_COUNT_EXCEEDED
);
285 CASE (HEADER_NOT_FOUND
);
286 CASE (HEADER_SIZE_OVERFLOW
);
287 CASE (INCORRECT_HANDLE_STATE
);
288 CASE (INCORRECT_HANDLE_TYPE
);
289 CASE (INTERNAL_ERROR
);
290 CASE (INVALID_OPTION
);
291 CASE (INVALID_QUERY_REQUEST
);
292 CASE (INVALID_SERVER_RESPONSE
);
294 CASE (LOGIN_FAILURE
);
295 CASE (NAME_NOT_RESOLVED
);
296 CASE (NOT_INITIALIZED
);
297 CASE (OPERATION_CANCELLED
);
298 CASE (OPTION_NOT_SETTABLE
);
299 CASE (OUT_OF_HANDLES
);
300 CASE (REDIRECT_FAILED
);
301 CASE (RESEND_REQUEST
);
302 CASE (RESPONSE_DRAIN_OVERFLOW
);
303 CASE (SECURE_CERT_CN_INVALID
);
304 CASE (SECURE_CERT_DATE_INVALID
);
305 CASE (SECURE_CERT_REV_FAILED
);
306 CASE (SECURE_CERT_REVOKED
);
307 CASE (SECURE_CERT_WRONG_USAGE
);
308 CASE (SECURE_CHANNEL_ERROR
);
309 CASE (SECURE_FAILURE
);
310 CASE (SECURE_INVALID_CA
);
311 CASE (SECURE_INVALID_CERT
);
314 CASE (UNABLE_TO_DOWNLOAD_SCRIPT
);
315 CASE (UNRECOGNIZED_SCHEME
);
318 return g_strdup_printf ("WinHttp error %ld", error_code
);
322 return g_win32_error_message (error_code
);
326 _g_winhttp_set_error (GError
**error
,
330 char *emsg
= _g_winhttp_error_message (error_code
);
332 g_set_error (error
, G_IO_ERROR
, G_IO_ERROR_FAILED
,
333 "%s failed: %s", what
, emsg
);
338 _g_winhttp_response (GWinHttpVfs
*vfs
,
343 wchar_t *status_code
;
344 DWORD status_code_len
;
346 if (!G_WINHTTP_VFS_GET_CLASS (vfs
)->funcs
->pWinHttpReceiveResponse (request
, NULL
))
348 _g_winhttp_set_error (error
, GetLastError (), what
);
354 if (!G_WINHTTP_VFS_GET_CLASS (vfs
)->funcs
->pWinHttpQueryHeaders
356 WINHTTP_QUERY_STATUS_CODE
,
361 GetLastError () != ERROR_INSUFFICIENT_BUFFER
)
363 _g_winhttp_set_error (error
, GetLastError (), what
);
368 status_code
= g_malloc (status_code_len
);
370 if (!G_WINHTTP_VFS_GET_CLASS (vfs
)->funcs
->pWinHttpQueryHeaders
372 WINHTTP_QUERY_STATUS_CODE
,
378 _g_winhttp_set_error (error
, GetLastError (), what
);
379 g_free (status_code
);
384 if (status_code
[0] != L
'2')
386 wchar_t *status_text
= NULL
;
387 DWORD status_text_len
;
389 if (!G_WINHTTP_VFS_GET_CLASS (vfs
)->funcs
->pWinHttpQueryHeaders
391 WINHTTP_QUERY_STATUS_TEXT
,
396 GetLastError () == ERROR_INSUFFICIENT_BUFFER
)
398 status_text
= g_malloc (status_text_len
);
400 if (!G_WINHTTP_VFS_GET_CLASS (vfs
)->funcs
->pWinHttpQueryHeaders
402 WINHTTP_QUERY_STATUS_TEXT
,
408 g_free (status_text
);
413 g_set_error (error
, G_IO_ERROR
, G_IO_ERROR_FAILED
,
415 what
, status_code
, status_text
? status_text
: L
"");
416 g_free (status_code
);
417 g_free (status_text
);
422 g_free (status_code
);
428 _g_winhttp_query_header (GWinHttpVfs
*vfs
,
430 const char *request_description
,
435 DWORD header_len
= 0;
437 if (!G_WINHTTP_VFS_GET_CLASS (vfs
)->funcs
->pWinHttpQueryHeaders
444 GetLastError () != ERROR_INSUFFICIENT_BUFFER
)
446 _g_winhttp_set_error (error
, GetLastError (), request_description
);
451 *header
= g_malloc (header_len
);
452 if (!G_WINHTTP_VFS_GET_CLASS (vfs
)->funcs
->pWinHttpQueryHeaders
460 _g_winhttp_set_error (error
, GetLastError (), request_description
);