• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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