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 (at) redhat.com> 22 * Author: Tor Lillqvist <tml (at) 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 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 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 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 * 141 _g_winhttp_vfs_new (void) 142 { 143 return g_object_new (G_TYPE_WINHTTP_VFS, NULL); 144 } 145 146 static GFile * 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 * 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 * 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 * 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 221 g_winhttp_vfs_is_active (GVfs *vfs) 222 { 223 return TRUE; 224 } 225 226 static void 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 * 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 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 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 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