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