Home | History | Annotate | Download | only in win32
      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