Imported Upstream version 2.61.3
[platform/upstream/glib.git] / gio / win32 / gwinhttpvfs.c
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
39 lookup_funcs (void)
40 {
41   HMODULE winhttp = NULL;
42   char winhttp_dll[MAX_PATH + 100];
43   int n;
44
45   if (lookup_done)
46     return;
47
48   n = GetSystemDirectory (winhttp_dll, MAX_PATH);
49   if (n > 0 && n < MAX_PATH)
50     {
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);
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
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
121 g_winhttp_vfs_init (GWinHttpVfs *vfs)
122 {
123   wchar_t *wagent;
124
125   vfs->wrapped_vfs = g_vfs_get_local ();
126
127   wagent = g_utf8_to_utf16 (g_get_prgname (), -1, NULL, NULL, NULL);
128
129   if (!wagent)
130     wagent = g_utf8_to_utf16 ("GWinHttpVfs", -1, NULL, NULL, NULL);
131
132   vfs->session = (G_WINHTTP_VFS_GET_CLASS (vfs)->funcs->pWinHttpOpen)
133     (wagent,
134      WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
135      WINHTTP_NO_PROXY_NAME,
136      WINHTTP_NO_PROXY_BYPASS,
137      0);
138
139   g_free (wagent);
140 }
141
142 /**
143  * g_winhttp_vfs_new:
144  *
145  * Returns a new #GVfs handle for a WinHttp vfs.
146  *
147  * Returns: a new #GVfs handle.
148  **/
149 GVfs *
150 _g_winhttp_vfs_new (void)
151 {
152   return g_object_new (G_TYPE_WINHTTP_VFS, NULL);
153 }
154
155 static GFile *
156 g_winhttp_vfs_get_file_for_path (GVfs       *vfs,
157                                  const char *path)
158 {
159   return g_vfs_get_file_for_path (G_WINHTTP_VFS (vfs)->wrapped_vfs, path);
160 }
161
162 static GFile *
163 g_winhttp_vfs_get_file_for_uri (GVfs       *vfs,
164                                 const char *uri)
165 {
166   GWinHttpVfs *winhttp_vfs = G_WINHTTP_VFS (vfs);
167   int i;
168   GFile *ret = NULL;
169
170   /* If it matches one of "our" schemes, handle it */
171   for (i = 0; i < G_N_ELEMENTS (winhttp_uri_schemes); i++)
172     {
173       if (g_ascii_strncasecmp (uri, winhttp_uri_schemes[i], strlen (winhttp_uri_schemes[i])) == 0 &&
174           uri[strlen (winhttp_uri_schemes[i])] == ':')
175         {
176           ret = _g_winhttp_file_new (winhttp_vfs, uri);
177         }
178     }
179
180   /* For other URIs fallback to the wrapped GVfs */
181   if (ret == NULL)
182     ret = g_vfs_get_file_for_uri (winhttp_vfs->wrapped_vfs, uri);
183
184   g_assert (ret != NULL);
185
186   return g_steal_pointer (&ret);
187 }
188
189 static const gchar * const *
190 g_winhttp_vfs_get_supported_uri_schemes (GVfs *vfs)
191 {
192   GWinHttpVfs *winhttp_vfs = G_WINHTTP_VFS (vfs);
193   const gchar * const *wrapped_vfs_uri_schemes = g_vfs_get_supported_uri_schemes (winhttp_vfs->wrapped_vfs);
194   int i, n;
195   const gchar **retval;
196
197   n = 0;
198   while (wrapped_vfs_uri_schemes[n] != NULL)
199     n++;
200
201   retval = g_new (const gchar *, n + G_N_ELEMENTS (winhttp_uri_schemes) + 1);
202   n = 0;
203   while (wrapped_vfs_uri_schemes[n] != NULL)
204     {
205       retval[n] = wrapped_vfs_uri_schemes[n];
206       n++;
207     }
208
209   for (i = 0; i < G_N_ELEMENTS (winhttp_uri_schemes); i++)
210     {
211       retval[n] = winhttp_uri_schemes[i];
212       n++;
213     }
214
215   retval[n] = NULL;
216
217   return retval;
218 }
219
220 static GFile *
221 g_winhttp_vfs_parse_name (GVfs       *vfs,
222                           const char *parse_name)
223 {
224   GWinHttpVfs *winhttp_vfs = G_WINHTTP_VFS (vfs);
225
226   g_return_val_if_fail (G_IS_VFS (vfs), NULL);
227   g_return_val_if_fail (parse_name != NULL, NULL);
228
229   /* For plain file paths fallback to the wrapped GVfs */
230   if (g_path_is_absolute (parse_name))
231     return g_vfs_parse_name (winhttp_vfs->wrapped_vfs, parse_name);
232
233   /* Otherwise assume it is an URI, so pass on to
234    * g_winhttp_vfs_get_file_for_uri().
235    */
236   return g_winhttp_vfs_get_file_for_uri (vfs, parse_name);
237 }
238
239 static gboolean
240 g_winhttp_vfs_is_active (GVfs *vfs)
241 {
242   return TRUE;
243 }
244
245 static void
246 g_winhttp_vfs_class_init (GWinHttpVfsClass *class)
247 {
248   GObjectClass *object_class;
249   GVfsClass *vfs_class;
250
251   object_class = (GObjectClass *) class;
252
253   object_class->finalize = g_winhttp_vfs_finalize;
254
255   vfs_class = G_VFS_CLASS (class);
256
257   vfs_class->is_active = g_winhttp_vfs_is_active;
258   vfs_class->get_file_for_path = g_winhttp_vfs_get_file_for_path;
259   vfs_class->get_file_for_uri = g_winhttp_vfs_get_file_for_uri;
260   vfs_class->get_supported_uri_schemes = g_winhttp_vfs_get_supported_uri_schemes;
261   vfs_class->parse_name = g_winhttp_vfs_parse_name;
262
263   lookup_funcs ();
264   if (funcs_found)
265     class->funcs = &funcs;
266   else
267     class->funcs = NULL;
268 }
269
270 char *
271 _g_winhttp_error_message (DWORD error_code)
272 {
273   /* The FormatMessage() API that g_win32_error_message() uses doesn't
274    * seem to know about WinHttp errors, unfortunately.
275    */
276   if (error_code >= WINHTTP_ERROR_BASE && error_code < WINHTTP_ERROR_BASE + 200)
277     {
278       switch (error_code)
279         {
280           /* FIXME: Use meaningful error messages */
281 #define CASE(x) case ERROR_WINHTTP_##x: return g_strdup ("WinHttp error: " #x);
282           CASE (AUTO_PROXY_SERVICE_ERROR);
283           CASE (AUTODETECTION_FAILED);
284           CASE (BAD_AUTO_PROXY_SCRIPT);
285           CASE (CANNOT_CALL_AFTER_OPEN);
286           CASE (CANNOT_CALL_AFTER_SEND);
287           CASE (CANNOT_CALL_BEFORE_OPEN);
288           CASE (CANNOT_CALL_BEFORE_SEND);
289           CASE (CANNOT_CONNECT);
290           CASE (CHUNKED_ENCODING_HEADER_SIZE_OVERFLOW);
291           CASE (CLIENT_AUTH_CERT_NEEDED);
292           CASE (CONNECTION_ERROR);
293           CASE (HEADER_ALREADY_EXISTS);
294           CASE (HEADER_COUNT_EXCEEDED);
295           CASE (HEADER_NOT_FOUND);
296           CASE (HEADER_SIZE_OVERFLOW);
297           CASE (INCORRECT_HANDLE_STATE);
298           CASE (INCORRECT_HANDLE_TYPE);
299           CASE (INTERNAL_ERROR);
300           CASE (INVALID_OPTION);
301           CASE (INVALID_QUERY_REQUEST);
302           CASE (INVALID_SERVER_RESPONSE);
303           CASE (INVALID_URL);
304           CASE (LOGIN_FAILURE);
305           CASE (NAME_NOT_RESOLVED);
306           CASE (NOT_INITIALIZED);
307           CASE (OPERATION_CANCELLED);
308           CASE (OPTION_NOT_SETTABLE);
309           CASE (OUT_OF_HANDLES);
310           CASE (REDIRECT_FAILED);
311           CASE (RESEND_REQUEST);
312           CASE (RESPONSE_DRAIN_OVERFLOW);
313           CASE (SECURE_CERT_CN_INVALID);
314           CASE (SECURE_CERT_DATE_INVALID);
315           CASE (SECURE_CERT_REV_FAILED);
316           CASE (SECURE_CERT_REVOKED);
317           CASE (SECURE_CERT_WRONG_USAGE);
318           CASE (SECURE_CHANNEL_ERROR);
319           CASE (SECURE_FAILURE);
320           CASE (SECURE_INVALID_CA);
321           CASE (SECURE_INVALID_CERT);
322           CASE (SHUTDOWN);
323           CASE (TIMEOUT);
324           CASE (UNABLE_TO_DOWNLOAD_SCRIPT);
325           CASE (UNRECOGNIZED_SCHEME);
326           #undef CASE
327         default:
328           return g_strdup_printf ("WinHttp error %ld", error_code);
329         }
330     }
331   else
332     return g_win32_error_message (error_code);
333 }
334
335 void
336 _g_winhttp_set_error (GError     **error,
337                       DWORD        error_code,
338                       const char  *what)
339 {
340   char *emsg = _g_winhttp_error_message (error_code);
341
342   g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
343                "%s failed: %s", what, emsg);
344   g_free (emsg);
345 }
346
347 gboolean
348 _g_winhttp_response (GWinHttpVfs *vfs,
349                      HINTERNET    request,
350                      GError     **error,
351                      const char  *what)
352 {
353   wchar_t *status_code;
354   DWORD status_code_len;
355
356   if (!G_WINHTTP_VFS_GET_CLASS (vfs)->funcs->pWinHttpReceiveResponse (request, NULL))
357     {
358       _g_winhttp_set_error (error, GetLastError (), what);
359
360       return FALSE;
361     }
362
363   status_code_len = 0;
364   if (!G_WINHTTP_VFS_GET_CLASS (vfs)->funcs->pWinHttpQueryHeaders
365       (request,
366        WINHTTP_QUERY_STATUS_CODE,
367        NULL,
368        NULL,
369        &status_code_len,
370        NULL) &&
371       GetLastError () != ERROR_INSUFFICIENT_BUFFER)
372     {
373       _g_winhttp_set_error (error, GetLastError (), what);
374
375       return FALSE;
376     }
377
378   status_code = g_malloc (status_code_len);
379
380   if (!G_WINHTTP_VFS_GET_CLASS (vfs)->funcs->pWinHttpQueryHeaders
381       (request,
382        WINHTTP_QUERY_STATUS_CODE,
383        NULL,
384        status_code,
385        &status_code_len,
386        NULL))
387     {
388       _g_winhttp_set_error (error, GetLastError (), what);
389       g_free (status_code);
390
391       return FALSE;
392     }
393
394   if (status_code[0] != L'2')
395     {
396       wchar_t *status_text = NULL;
397       DWORD status_text_len;
398
399       if (!G_WINHTTP_VFS_GET_CLASS (vfs)->funcs->pWinHttpQueryHeaders
400           (request,
401            WINHTTP_QUERY_STATUS_TEXT,
402            NULL,
403            NULL,
404            &status_text_len,
405            NULL) &&
406           GetLastError () == ERROR_INSUFFICIENT_BUFFER)
407         {
408           status_text = g_malloc (status_text_len);
409
410           if (!G_WINHTTP_VFS_GET_CLASS (vfs)->funcs->pWinHttpQueryHeaders
411               (request,
412                WINHTTP_QUERY_STATUS_TEXT,
413                NULL,
414                status_text,
415                &status_text_len,
416                NULL))
417             {
418               g_free (status_text);
419               status_text = NULL;
420             }
421         }
422
423       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
424                    "%s failed: %S %S",
425                    what, status_code, status_text ? status_text : L"");
426       g_free (status_code);
427       g_free (status_text);
428
429       return FALSE;
430     }
431
432   g_free (status_code);
433
434   return TRUE;
435 }
436
437 gboolean
438 _g_winhttp_query_header (GWinHttpVfs *vfs,
439                          HINTERNET    request,
440                          const char  *request_description,
441                          DWORD        which_header,
442                          wchar_t    **header,
443                          GError     **error)
444 {
445   DWORD header_len = 0;
446
447   if (!G_WINHTTP_VFS_GET_CLASS (vfs)->funcs->pWinHttpQueryHeaders
448       (request,
449        which_header,
450        NULL,
451        NULL,
452        &header_len,
453        NULL) &&
454       GetLastError () != ERROR_INSUFFICIENT_BUFFER)
455     {
456       _g_winhttp_set_error (error, GetLastError (), request_description);
457
458       return FALSE;
459     }
460
461   *header = g_malloc (header_len);
462   if (!G_WINHTTP_VFS_GET_CLASS (vfs)->funcs->pWinHttpQueryHeaders
463       (request,
464        which_header,
465        NULL,
466        *header,
467        &header_len,
468        NULL))
469     {
470       _g_winhttp_set_error (error, GetLastError (), request_description);
471       g_free (*header);
472       *header = NULL;
473
474       return FALSE;
475     }
476
477   return TRUE;
478 }