(root)/
glib-2.79.0/
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    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
     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    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 *
     151  _g_winhttp_vfs_new (void)
     152  {
     153    return g_object_new (G_TYPE_WINHTTP_VFS, NULL);
     154  }
     155  
     156  static GFile *
     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 *
     164  g_winhttp_vfs_get_file_for_uri (GVfs       *vfs,
     165                                  const char *uri)
     166  {
     167    GWinHttpVfs *winhttp_vfs = G_WINHTTP_VFS (vfs);
     168    gsize 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 *
     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    gsize 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 *
     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
     241  g_winhttp_vfs_is_active (GVfs *vfs)
     242  {
     243    return TRUE;
     244  }
     245  
     246  static void
     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 *
     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
     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
     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
     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  }