| /* GIO - GLib Input, Output and Streaming Library | |
| * | |
| * Copyright (C) 2006-2007 Red Hat, Inc. | |
| * Copyright (C) 2008 Novell, Inc. | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General | |
| * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. | |
| * | |
| * Author: Alexander Larsson <alexl@redhat.com> | |
| * Author: Tor Lillqvist <tml@novell.com> | |
| */ | |
| #include "config.h" | |
| #include <wchar.h> | |
| #include "gio/gioerror.h" | |
| #include "gio/giomodule.h" | |
| #include "gio/gvfs.h" | |
| #include "gwinhttpfile.h" | |
| #include "gwinhttpvfs.h" | |
| static gboolean lookup_done = FALSE; | |
| static gboolean funcs_found = FALSE; | |
| static GWinHttpDllFuncs funcs; | |
| static void | |
| lookup_funcs (void) | |
| { | |
| HMODULE winhttp = NULL; | |
| WCHAR winhttp_dll[MAX_PATH + 100]; | |
| int n; | |
| if (lookup_done) | |
| return; | |
| n = GetSystemDirectoryW (winhttp_dll, MAX_PATH); | |
| if (n > 0 && n < MAX_PATH) | |
| { | |
| if (winhttp_dll[n-1] != L'\\' && | |
| winhttp_dll[n-1] != L'/') | |
| wcscat (winhttp_dll, L"\\"); | |
| wcscat (winhttp_dll, L"winhttp.dll"); | |
| winhttp = LoadLibraryW (winhttp_dll); | |
| } | |
| if (winhttp != NULL) | |
| { | |
| funcs.pWinHttpCloseHandle = (BOOL (WINAPI *) (HINTERNET)) GetProcAddress (winhttp, "WinHttpCloseHandle"); | |
| funcs.pWinHttpCrackUrl = (BOOL (WINAPI *) (LPCWSTR,DWORD,DWORD,LPURL_COMPONENTS)) GetProcAddress (winhttp, "WinHttpCrackUrl"); | |
| funcs.pWinHttpConnect = (HINTERNET (WINAPI *) (HINTERNET,LPCWSTR,INTERNET_PORT,DWORD)) GetProcAddress (winhttp, "WinHttpConnect"); | |
| funcs.pWinHttpCreateUrl = (BOOL (WINAPI *) (LPURL_COMPONENTS,DWORD,LPWSTR,LPDWORD)) GetProcAddress (winhttp, "WinHttpCreateUrl"); | |
| funcs.pWinHttpOpen = (HINTERNET (WINAPI *) (LPCWSTR,DWORD,LPCWSTR,LPCWSTR,DWORD)) GetProcAddress (winhttp, "WinHttpOpen"); | |
| funcs.pWinHttpOpenRequest = (HINTERNET (WINAPI *) (HINTERNET,LPCWSTR,LPCWSTR,LPCWSTR,LPCWSTR,LPCWSTR*,DWORD)) GetProcAddress (winhttp, "WinHttpOpenRequest"); | |
| funcs.pWinHttpQueryDataAvailable = (BOOL (WINAPI *) (HINTERNET,LPDWORD)) GetProcAddress (winhttp, "WinHttpQueryDataAvailable"); | |
| funcs.pWinHttpQueryHeaders = (BOOL (WINAPI *) (HINTERNET,DWORD,LPCWSTR,LPVOID,LPDWORD,LPDWORD)) GetProcAddress (winhttp, "WinHttpQueryHeaders"); | |
| funcs.pWinHttpReadData = (BOOL (WINAPI *) (HINTERNET,LPVOID,DWORD,LPDWORD)) GetProcAddress (winhttp, "WinHttpReadData"); | |
| funcs.pWinHttpReceiveResponse = (BOOL (WINAPI *) (HINTERNET,LPVOID)) GetProcAddress (winhttp, "WinHttpReceiveResponse"); | |
| funcs.pWinHttpSendRequest = (BOOL (WINAPI *) (HINTERNET,LPCWSTR,DWORD,LPVOID,DWORD,DWORD,DWORD_PTR)) GetProcAddress (winhttp, "WinHttpSendRequest"); | |
| funcs.pWinHttpWriteData = (BOOL (WINAPI *) (HINTERNET,LPCVOID,DWORD,LPDWORD)) GetProcAddress (winhttp, "WinHttpWriteData"); | |
| if (funcs.pWinHttpCloseHandle && | |
| funcs.pWinHttpCrackUrl && | |
| funcs.pWinHttpConnect && | |
| funcs.pWinHttpCreateUrl && | |
| funcs.pWinHttpOpen && | |
| funcs.pWinHttpOpenRequest && | |
| funcs.pWinHttpQueryDataAvailable && | |
| funcs.pWinHttpQueryHeaders && | |
| funcs.pWinHttpReadData && | |
| funcs.pWinHttpReceiveResponse && | |
| funcs.pWinHttpSendRequest && | |
| funcs.pWinHttpWriteData) | |
| funcs_found = TRUE; | |
| } | |
| lookup_done = TRUE; | |
| } | |
| #define g_winhttp_vfs_get_type _g_winhttp_vfs_get_type | |
| G_DEFINE_TYPE_WITH_CODE (GWinHttpVfs, g_winhttp_vfs, G_TYPE_VFS, | |
| { | |
| lookup_funcs (); | |
| if (funcs_found) | |
| g_io_extension_point_implement (G_VFS_EXTENSION_POINT_NAME, | |
| g_define_type_id, | |
| "winhttp", | |
| 10); | |
| }) | |
| static const gchar *winhttp_uri_schemes[] = { "http", "https" }; | |
| static void | |
| g_winhttp_vfs_finalize (GObject *object) | |
| { | |
| GWinHttpVfs *vfs; | |
| vfs = G_WINHTTP_VFS (object); | |
| (G_WINHTTP_VFS_GET_CLASS (vfs)->funcs->pWinHttpCloseHandle) (vfs->session); | |
| vfs->session = NULL; | |
| if (vfs->wrapped_vfs) | |
| g_object_unref (vfs->wrapped_vfs); | |
| vfs->wrapped_vfs = NULL; | |
| G_OBJECT_CLASS (g_winhttp_vfs_parent_class)->finalize (object); | |
| } | |
| static void | |
| g_winhttp_vfs_init (GWinHttpVfs *vfs) | |
| { | |
| wchar_t *wagent; | |
| const gchar *prgname = g_get_prgname (); | |
| vfs->wrapped_vfs = g_vfs_get_local (); | |
| if (prgname) | |
| wagent = g_utf8_to_utf16 (prgname, -1, NULL, NULL, NULL); | |
| else | |
| wagent = g_utf8_to_utf16 ("GWinHttpVfs", -1, NULL, NULL, NULL); | |
| vfs->session = (G_WINHTTP_VFS_GET_CLASS (vfs)->funcs->pWinHttpOpen) | |
| (wagent, | |
| WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, | |
| WINHTTP_NO_PROXY_NAME, | |
| WINHTTP_NO_PROXY_BYPASS, | |
| 0); | |
| g_free (wagent); | |
| } | |
| /** | |
| * g_winhttp_vfs_new: | |
| * | |
| * Returns a new #GVfs handle for a WinHttp vfs. | |
| * | |
| * Returns: a new #GVfs handle. | |
| **/ | |
| GVfs * | |
| _g_winhttp_vfs_new (void) | |
| { | |
| return g_object_new (G_TYPE_WINHTTP_VFS, NULL); | |
| } | |
| static GFile * | |
| g_winhttp_vfs_get_file_for_path (GVfs *vfs, | |
| const char *path) | |
| { | |
| return g_vfs_get_file_for_path (G_WINHTTP_VFS (vfs)->wrapped_vfs, path); | |
| } | |
| static GFile * | |
| g_winhttp_vfs_get_file_for_uri (GVfs *vfs, | |
| const char *uri) | |
| { | |
| GWinHttpVfs *winhttp_vfs = G_WINHTTP_VFS (vfs); | |
| gsize i; | |
| GFile *ret = NULL; | |
| /* If it matches one of "our" schemes, handle it */ | |
| for (i = 0; i < G_N_ELEMENTS (winhttp_uri_schemes); i++) | |
| { | |
| if (g_ascii_strncasecmp (uri, winhttp_uri_schemes[i], strlen (winhttp_uri_schemes[i])) == 0 && | |
| uri[strlen (winhttp_uri_schemes[i])] == ':') | |
| { | |
| ret = _g_winhttp_file_new (winhttp_vfs, uri); | |
| } | |
| } | |
| /* For other URIs fallback to the wrapped GVfs */ | |
| if (ret == NULL) | |
| ret = g_vfs_get_file_for_uri (winhttp_vfs->wrapped_vfs, uri); | |
| g_assert (ret != NULL); | |
| return g_steal_pointer (&ret); | |
| } | |
| static const gchar * const * | |
| g_winhttp_vfs_get_supported_uri_schemes (GVfs *vfs) | |
| { | |
| GWinHttpVfs *winhttp_vfs = G_WINHTTP_VFS (vfs); | |
| const gchar * const *wrapped_vfs_uri_schemes = g_vfs_get_supported_uri_schemes (winhttp_vfs->wrapped_vfs); | |
| gsize i, n; | |
| const gchar **retval; | |
| n = 0; | |
| while (wrapped_vfs_uri_schemes[n] != NULL) | |
| n++; | |
| retval = g_new (const gchar *, n + G_N_ELEMENTS (winhttp_uri_schemes) + 1); | |
| n = 0; | |
| while (wrapped_vfs_uri_schemes[n] != NULL) | |
| { | |
| retval[n] = wrapped_vfs_uri_schemes[n]; | |
| n++; | |
| } | |
| for (i = 0; i < G_N_ELEMENTS (winhttp_uri_schemes); i++) | |
| { | |
| retval[n] = winhttp_uri_schemes[i]; | |
| n++; | |
| } | |
| retval[n] = NULL; | |
| return retval; | |
| } | |
| static GFile * | |
| g_winhttp_vfs_parse_name (GVfs *vfs, | |
| const char *parse_name) | |
| { | |
| GWinHttpVfs *winhttp_vfs = G_WINHTTP_VFS (vfs); | |
| g_return_val_if_fail (G_IS_VFS (vfs), NULL); | |
| g_return_val_if_fail (parse_name != NULL, NULL); | |
| /* For plain file paths fallback to the wrapped GVfs */ | |
| if (g_path_is_absolute (parse_name)) | |
| return g_vfs_parse_name (winhttp_vfs->wrapped_vfs, parse_name); | |
| /* Otherwise assume it is an URI, so pass on to | |
| * g_winhttp_vfs_get_file_for_uri(). | |
| */ | |
| return g_winhttp_vfs_get_file_for_uri (vfs, parse_name); | |
| } | |
| static gboolean | |
| g_winhttp_vfs_is_active (GVfs *vfs) | |
| { | |
| return TRUE; | |
| } | |
| static void | |
| g_winhttp_vfs_class_init (GWinHttpVfsClass *class) | |
| { | |
| GObjectClass *object_class; | |
| GVfsClass *vfs_class; | |
| object_class = (GObjectClass *) class; | |
| object_class->finalize = g_winhttp_vfs_finalize; | |
| vfs_class = G_VFS_CLASS (class); | |
| vfs_class->is_active = g_winhttp_vfs_is_active; | |
| vfs_class->get_file_for_path = g_winhttp_vfs_get_file_for_path; | |
| vfs_class->get_file_for_uri = g_winhttp_vfs_get_file_for_uri; | |
| vfs_class->get_supported_uri_schemes = g_winhttp_vfs_get_supported_uri_schemes; | |
| vfs_class->parse_name = g_winhttp_vfs_parse_name; | |
| lookup_funcs (); | |
| if (funcs_found) | |
| class->funcs = &funcs; | |
| else | |
| class->funcs = NULL; | |
| } | |
| char * | |
| _g_winhttp_error_message (DWORD error_code) | |
| { | |
| /* The FormatMessage() API that g_win32_error_message() uses doesn't | |
| * seem to know about WinHttp errors, unfortunately. | |
| */ | |
| if (error_code >= WINHTTP_ERROR_BASE && error_code < WINHTTP_ERROR_BASE + 200) | |
| { | |
| switch (error_code) | |
| { | |
| /* FIXME: Use meaningful error messages */ | |
| #define CASE(x) case ERROR_WINHTTP_##x: return g_strdup ("WinHttp error: " #x); | |
| CASE (AUTO_PROXY_SERVICE_ERROR); | |
| CASE (AUTODETECTION_FAILED); | |
| CASE (BAD_AUTO_PROXY_SCRIPT); | |
| CASE (CANNOT_CALL_AFTER_OPEN); | |
| CASE (CANNOT_CALL_AFTER_SEND); | |
| CASE (CANNOT_CALL_BEFORE_OPEN); | |
| CASE (CANNOT_CALL_BEFORE_SEND); | |
| CASE (CANNOT_CONNECT); | |
| CASE (CHUNKED_ENCODING_HEADER_SIZE_OVERFLOW); | |
| CASE (CLIENT_AUTH_CERT_NEEDED); | |
| CASE (CONNECTION_ERROR); | |
| CASE (HEADER_ALREADY_EXISTS); | |
| CASE (HEADER_COUNT_EXCEEDED); | |
| CASE (HEADER_NOT_FOUND); | |
| CASE (HEADER_SIZE_OVERFLOW); | |
| CASE (INCORRECT_HANDLE_STATE); | |
| CASE (INCORRECT_HANDLE_TYPE); | |
| CASE (INTERNAL_ERROR); | |
| CASE (INVALID_OPTION); | |
| CASE (INVALID_QUERY_REQUEST); | |
| CASE (INVALID_SERVER_RESPONSE); | |
| CASE (INVALID_URL); | |
| CASE (LOGIN_FAILURE); | |
| CASE (NAME_NOT_RESOLVED); | |
| CASE (NOT_INITIALIZED); | |
| CASE (OPERATION_CANCELLED); | |
| CASE (OPTION_NOT_SETTABLE); | |
| CASE (OUT_OF_HANDLES); | |
| CASE (REDIRECT_FAILED); | |
| CASE (RESEND_REQUEST); | |
| CASE (RESPONSE_DRAIN_OVERFLOW); | |
| CASE (SECURE_CERT_CN_INVALID); | |
| CASE (SECURE_CERT_DATE_INVALID); | |
| CASE (SECURE_CERT_REV_FAILED); | |
| CASE (SECURE_CERT_REVOKED); | |
| CASE (SECURE_CERT_WRONG_USAGE); | |
| CASE (SECURE_CHANNEL_ERROR); | |
| CASE (SECURE_FAILURE); | |
| CASE (SECURE_INVALID_CA); | |
| CASE (SECURE_INVALID_CERT); | |
| CASE (SHUTDOWN); | |
| CASE (TIMEOUT); | |
| CASE (UNABLE_TO_DOWNLOAD_SCRIPT); | |
| CASE (UNRECOGNIZED_SCHEME); | |
| #undef CASE | |
| default: | |
| return g_strdup_printf ("WinHttp error %ld", error_code); | |
| } | |
| } | |
| else | |
| return g_win32_error_message (error_code); | |
| } | |
| void | |
| _g_winhttp_set_error (GError **error, | |
| DWORD error_code, | |
| const char *what) | |
| { | |
| char *emsg = _g_winhttp_error_message (error_code); | |
| g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, | |
| "%s failed: %s", what, emsg); | |
| g_free (emsg); | |
| } | |
| gboolean | |
| _g_winhttp_response (GWinHttpVfs *vfs, | |
| HINTERNET request, | |
| GError **error, | |
| const char *what) | |
| { | |
| wchar_t *status_code; | |
| DWORD status_code_len; | |
| if (!G_WINHTTP_VFS_GET_CLASS (vfs)->funcs->pWinHttpReceiveResponse (request, NULL)) | |
| { | |
| _g_winhttp_set_error (error, GetLastError (), what); | |
| return FALSE; | |
| } | |
| status_code_len = 0; | |
| if (!G_WINHTTP_VFS_GET_CLASS (vfs)->funcs->pWinHttpQueryHeaders | |
| (request, | |
| WINHTTP_QUERY_STATUS_CODE, | |
| NULL, | |
| NULL, | |
| &status_code_len, | |
| NULL) && | |
| GetLastError () != ERROR_INSUFFICIENT_BUFFER) | |
| { | |
| _g_winhttp_set_error (error, GetLastError (), what); | |
| return FALSE; | |
| } | |
| status_code = g_malloc (status_code_len); | |
| if (!G_WINHTTP_VFS_GET_CLASS (vfs)->funcs->pWinHttpQueryHeaders | |
| (request, | |
| WINHTTP_QUERY_STATUS_CODE, | |
| NULL, | |
| status_code, | |
| &status_code_len, | |
| NULL)) | |
| { | |
| _g_winhttp_set_error (error, GetLastError (), what); | |
| g_free (status_code); | |
| return FALSE; | |
| } | |
| if (status_code[0] != L'2') | |
| { | |
| wchar_t *status_text = NULL; | |
| DWORD status_text_len; | |
| if (!G_WINHTTP_VFS_GET_CLASS (vfs)->funcs->pWinHttpQueryHeaders | |
| (request, | |
| WINHTTP_QUERY_STATUS_TEXT, | |
| NULL, | |
| NULL, | |
| &status_text_len, | |
| NULL) && | |
| GetLastError () == ERROR_INSUFFICIENT_BUFFER) | |
| { | |
| status_text = g_malloc (status_text_len); | |
| if (!G_WINHTTP_VFS_GET_CLASS (vfs)->funcs->pWinHttpQueryHeaders | |
| (request, | |
| WINHTTP_QUERY_STATUS_TEXT, | |
| NULL, | |
| status_text, | |
| &status_text_len, | |
| NULL)) | |
| { | |
| g_free (status_text); | |
| status_text = NULL; | |
| } | |
| } | |
| g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, | |
| "%s failed: %S %S", | |
| what, status_code, status_text ? status_text : L""); | |
| g_free (status_code); | |
| g_free (status_text); | |
| return FALSE; | |
| } | |
| g_free (status_code); | |
| return TRUE; | |
| } | |
| gboolean | |
| _g_winhttp_query_header (GWinHttpVfs *vfs, | |
| HINTERNET request, | |
| const char *request_description, | |
| DWORD which_header, | |
| wchar_t **header, | |
| GError **error) | |
| { | |
| DWORD header_len = 0; | |
| if (!G_WINHTTP_VFS_GET_CLASS (vfs)->funcs->pWinHttpQueryHeaders | |
| (request, | |
| which_header, | |
| NULL, | |
| NULL, | |
| &header_len, | |
| NULL) && | |
| GetLastError () != ERROR_INSUFFICIENT_BUFFER) | |
| { | |
| _g_winhttp_set_error (error, GetLastError (), request_description); | |
| return FALSE; | |
| } | |
| *header = g_malloc (header_len); | |
| if (!G_WINHTTP_VFS_GET_CLASS (vfs)->funcs->pWinHttpQueryHeaders | |
| (request, | |
| which_header, | |
| NULL, | |
| *header, | |
| &header_len, | |
| NULL)) | |
| { | |
| _g_winhttp_set_error (error, GetLastError (), request_description); | |
| g_free (*header); | |
| *header = NULL; | |
| return FALSE; | |
| } | |
| return TRUE; | |
| } |