From d5863830ad51529cb4f5f82016790ea8c03110e9 Mon Sep 17 00:00:00 2001 From: Tor Lillqvist Date: Wed, 13 Aug 2008 19:39:49 +0000 Subject: [PATCH] win32/gwinhttpvfs.c win32/gwinhttpvfs.h win32/gwinhttpfile.c 2008-08-13 Tor Lillqvist * win32/gwinhttpvfs.c * win32/gwinhttpvfs.h * win32/gwinhttpfile.c * win32/gwinhttpfileinputstream.c * win32/gwinhttpfileoutputstream.c: Refactor some common code snippets into helper functions. Check HTTP response status codes. Implement g_winhttp_file_query_info(), looking at Content-Length, Content-Type and Last-Modified. * win32/winhttp.h: Add some symbolic constants that are not publicly documented. Just a handful, so it should be OK to use information from the Windows SDK's headers. svn path=/trunk/; revision=7350 --- gio/ChangeLog | 15 +++ gio/win32/gwinhttpfile.c | 243 +++++++++++++++++++++++++++++------ gio/win32/gwinhttpfileinputstream.c | 46 +++---- gio/win32/gwinhttpfileoutputstream.c | 49 +++---- gio/win32/gwinhttpvfs.c | 160 ++++++++++++++++++++++- gio/win32/gwinhttpvfs.h | 20 ++- gio/win32/winhttp.h | 12 ++ 7 files changed, 439 insertions(+), 106 deletions(-) diff --git a/gio/ChangeLog b/gio/ChangeLog index edb3c8b..a18244d 100644 --- a/gio/ChangeLog +++ b/gio/ChangeLog @@ -1,5 +1,20 @@ 2008-08-13 Tor Lillqvist + * win32/gwinhttpvfs.c + * win32/gwinhttpvfs.h + * win32/gwinhttpfile.c + * win32/gwinhttpfileinputstream.c + * win32/gwinhttpfileoutputstream.c: Refactor some common code + snippets into helper functions. Check HTTP response status + codes. Implement g_winhttp_file_query_info(), looking at + Content-Length, Content-Type and Last-Modified. + + * win32/winhttp.h: Add some symbolic constants that are not + publicly documented. Just a handful, so it should be OK to use + information from the Windows SDK's headers. + +2008-08-13 Tor Lillqvist + Bug 546582 - Callbacks from GFileMonitor present a GFile in the wrong folder diff --git a/gio/win32/gwinhttpfile.c b/gio/win32/gwinhttpfile.c index 2e24116..b5ce3bd 100644 --- a/gio/win32/gwinhttpfile.c +++ b/gio/win32/gwinhttpfile.c @@ -1,5 +1,5 @@ /* GIO - GLib Input, Output and Streaming Library - * + * * Copyright (C) 2006-2007 Red Hat, Inc. * Copyright (C) 2008 Novell, Inc. * @@ -28,6 +28,7 @@ #include "gfile.h" #include "gfileattribute.h" +#include "gfileinfo.h" #include "gwinhttpfile.h" #include "gwinhttpfileinputstream.h" #include "gwinhttpfileoutputstream.h" @@ -78,7 +79,7 @@ g_winhttp_file_init (GWinHttpFile *winhttp) * _g_winhttp_file_new: * @vfs: GWinHttpVfs to use * @uri: URI of the GWinHttpFile to create. - * + * * Returns: new winhttp #GFile. **/ GFile * @@ -129,7 +130,7 @@ _g_winhttp_file_new (GWinHttpVfs *vfs, g_free (wuri); return NULL; } - + g_free (wuri); return G_FILE (file); } @@ -206,6 +207,17 @@ g_winhttp_file_get_uri (GFile *file) retval = g_utf16_to_utf8 (wuri, -1, NULL, NULL, NULL); g_free (wuri); + if (g_str_has_prefix (retval, "http://:@")) + { + memmove (retval + 7, retval + 9, strlen (retval) - 9); + retval[strlen (retval) - 2] = '\0'; + } + else if (g_str_has_prefix (retval, "https://:@")) + { + memmove (retval + 8, retval + 10, strlen (retval) - 10); + retval[strlen (retval) - 2] = '\0'; + } + return retval; } @@ -230,7 +242,7 @@ g_winhttp_file_get_parent (GFile *file) uri = g_winhttp_file_get_uri (file); if (uri == NULL) return NULL; - + last_slash = strrchr (uri, '/'); if (last_slash == NULL || *(last_slash+1) == 0) { @@ -245,7 +257,7 @@ g_winhttp_file_get_parent (GFile *file) parent = _g_winhttp_file_new (winhttp_file->vfs, uri); g_free (uri); - + return parent; } @@ -282,12 +294,12 @@ g_winhttp_file_equal (GFile *file1, g_free (uri1); g_free (uri2); - + return retval; } static const char * -match_prefix (const char *path, +match_prefix (const char *path, const char *prefix) { int prefix_len; @@ -295,10 +307,10 @@ match_prefix (const char *path, prefix_len = strlen (prefix); if (strncmp (path, prefix, prefix_len) != 0) return NULL; - + if (prefix_len > 0 && prefix[prefix_len-1] == '/') prefix_len--; - + return path + prefix_len; } @@ -334,7 +346,7 @@ g_winhttp_file_get_relative_path (GFile *parent, char *retval; remainder = match_prefix (descendant_uri, parent_uri); - + if (remainder != NULL && *remainder == '/') retval = g_strdup (remainder + 1); else @@ -379,7 +391,7 @@ g_winhttp_file_resolve_relative_path (GFile *file, child->url.dwUrlPathLength = 2*(wcslen (wnew_path)+1); child->url.lpszExtraInfo = NULL; child->url.dwExtraInfoLength = 0; - + return (GFile *) child; } @@ -394,15 +406,14 @@ g_winhttp_file_get_child_for_display_name (GFile *file, basename = g_locale_from_utf8 (display_name, -1, NULL, NULL, NULL); if (basename == NULL) { - g_set_error (error, G_IO_ERROR, - G_IO_ERROR_INVALID_FILENAME, + g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_FILENAME, _("Invalid filename %s"), display_name); return NULL; } new_file = g_file_get_child (file, basename); g_free (basename); - + return new_file; } @@ -412,13 +423,186 @@ g_winhttp_file_set_display_name (GFile *file, GCancellable *cancellable, GError **error) { - g_set_error_literal (error, G_IO_ERROR, - G_IO_ERROR_NOT_SUPPORTED, + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Operation not supported")); return NULL; } +static time_t +mktime_utc (SYSTEMTIME *t) +{ + time_t retval; + + static const gint days_before[] = + { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 + }; + + if (t->wMonth < 1 || t->wMonth > 12) + return (time_t) -1; + + retval = (t->wYear - 1970) * 365; + retval += (t->wYear - 1968) / 4; + retval += days_before[t->wMonth-1] + t->wDay - 1; + + if (t->wYear % 4 == 0 && t->wMonth < 3) + retval -= 1; + + retval = ((((retval * 24) + t->wHour) * 60) + t->wMinute) * 60 + t->wSecond; + + return retval; +} + +static GFileInfo * +g_winhttp_file_query_info (GFile *file, + const char *attributes, + GFileQueryInfoFlags flags, + GCancellable *cancellable, + GError **error) +{ + GWinHttpFile *winhttp_file = G_WINHTTP_FILE (file); + HINTERNET connection, request; + const wchar_t *accept_types[] = + { + L"*/*", + NULL, + }; + GFileInfo *info; + GFileAttributeMatcher *matcher; + char *basename; + wchar_t *content_length; + wchar_t *content_type; + SYSTEMTIME last_modified; + DWORD last_modified_len; + + connection = G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->pWinHttpConnect + (G_WINHTTP_VFS (winhttp_file->vfs)->session, + winhttp_file->url.lpszHostName, + winhttp_file->url.nPort, + 0); + + if (connection == NULL) + { + _g_winhttp_set_error (error, GetLastError (), "HTTP connection"); + + return NULL; + } + + request = G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->pWinHttpOpenRequest + (connection, + L"HEAD", + winhttp_file->url.lpszUrlPath, + NULL, + WINHTTP_NO_REFERER, + accept_types, + winhttp_file->url.nScheme == INTERNET_SCHEME_HTTPS ? WINHTTP_FLAG_SECURE : 0); + + if (request == NULL) + { + _g_winhttp_set_error (error, GetLastError (), "HEAD request"); + + return NULL; + } + + if (!G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->pWinHttpSendRequest + (request, + NULL, 0, + NULL, 0, + 0, + 0)) + { + _g_winhttp_set_error (error, GetLastError (), "HEAD request"); + + return NULL; + } + + if (!_g_winhttp_response (winhttp_file->vfs, request, error, "HEAD request")) + return NULL; + + matcher = g_file_attribute_matcher_new (attributes); + info = g_file_info_new (); + g_file_info_set_attribute_mask (info, matcher); + + basename = g_winhttp_file_get_basename (file); + g_file_info_set_name (info, basename); + g_free (basename); + + content_length = NULL; + if (_g_winhttp_query_header (winhttp_file->vfs, + request, + "HEAD request", + WINHTTP_QUERY_CONTENT_LENGTH, + &content_length, + NULL)) + { + gint64 cl; + int n; + + if (swscanf (content_length, L"%I64d%n", &cl, &n) == 1 && + n == wcslen (content_length)) + g_file_info_set_size (info, cl); + + g_free (content_length); + } + + if (matcher == NULL) + return info; + + content_type = NULL; + if (_g_winhttp_query_header (winhttp_file->vfs, + request, + "HEAD request", + WINHTTP_QUERY_CONTENT_TYPE, + &content_type, + NULL)) + { + char *ct = g_utf16_to_utf8 (content_type, -1, NULL, NULL, NULL); + + if (ct != NULL) + { + char *p = strchr (ct, ';'); + + if (p != NULL) + { + char *tmp = g_strndup (ct, p - ct); + + g_file_info_set_content_type (info, tmp); + g_free (tmp); + } + else + g_file_info_set_content_type (info, ct); + } + + g_free (ct); + } + + last_modified_len = sizeof (last_modified); + if (G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->pWinHttpQueryHeaders + (request, + WINHTTP_QUERY_LAST_MODIFIED | WINHTTP_QUERY_FLAG_SYSTEMTIME, + NULL, + &last_modified, + &last_modified_len, + NULL) && + last_modified_len == sizeof (last_modified) && + /* Don't bother comparing to the exact Y2038 moment */ + last_modified.wYear >= 1970 && + last_modified.wYear < 2038) + { + GTimeVal tv; + + tv.tv_sec = mktime_utc (&last_modified); + tv.tv_usec = last_modified.wMilliseconds * 1000; + + g_file_info_set_modification_time (info, &tv); + } + + g_file_attribute_matcher_unref (matcher); + + return info; +} + static GFileInputStream * g_winhttp_file_read (GFile *file, GCancellable *cancellable, @@ -440,12 +624,7 @@ g_winhttp_file_read (GFile *file, if (connection == NULL) { - char *emsg = _g_winhttp_error_message (GetLastError ()); - - g_set_error (error, G_IO_ERROR, - G_IO_ERROR_FAILED, - "%s", emsg); - g_free (emsg); + _g_winhttp_set_error (error, GetLastError (), "HTTP connection"); return NULL; } @@ -461,12 +640,7 @@ g_winhttp_file_read (GFile *file, if (request == NULL) { - char *emsg = _g_winhttp_error_message (GetLastError ()); - - g_set_error (error, G_IO_ERROR, - G_IO_ERROR_FAILED, - "%s", emsg); - g_free (emsg); + _g_winhttp_set_error (error, GetLastError (), "GET request"); return NULL; } @@ -481,7 +655,7 @@ g_winhttp_file_create (GFile *file, GError **error) { GWinHttpFile *winhttp_file = G_WINHTTP_FILE (file); - HINTERNET connection, request; + HINTERNET connection; connection = G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->pWinHttpConnect (G_WINHTTP_VFS (winhttp_file->vfs)->session, @@ -491,12 +665,7 @@ g_winhttp_file_create (GFile *file, if (connection == NULL) { - char *emsg = _g_winhttp_error_message (GetLastError ()); - - g_set_error (error, G_IO_ERROR, - G_IO_ERROR_FAILED, - "%s", emsg); - g_free (emsg); + _g_winhttp_set_error (error, GetLastError (), "HTTP connection"); return NULL; } @@ -550,7 +719,8 @@ g_winhttp_file_copy (GFile *source, GError **error) { /* Fall back to default copy?? */ - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Copy not supported"); + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + "Copy not supported"); return FALSE; } @@ -590,6 +760,7 @@ g_winhttp_file_file_iface_init (GFileIface *iface) iface->resolve_relative_path = g_winhttp_file_resolve_relative_path; iface->get_child_for_display_name = g_winhttp_file_get_child_for_display_name; iface->set_display_name = g_winhttp_file_set_display_name; + iface->query_info = g_winhttp_file_query_info; iface->read_fn = g_winhttp_file_read; iface->create = g_winhttp_file_create; #if 0 diff --git a/gio/win32/gwinhttpfileinputstream.c b/gio/win32/gwinhttpfileinputstream.c index f00bc78..c36fcab 100644 --- a/gio/win32/gwinhttpfileinputstream.c +++ b/gio/win32/gwinhttpfileinputstream.c @@ -1,5 +1,5 @@ /* GIO - GLib Input, Output and Streaming Library - * + * * Copyright (C) 2006-2007 Red Hat, Inc. * Copyright (C) 2008 Novell, Inc. * @@ -61,7 +61,7 @@ static void g_winhttp_file_input_stream_finalize (GObject *object) { GWinHttpFileInputStream *winhttp_stream; - + winhttp_stream = G_WINHTTP_FILE_INPUT_STREAM (object); if (winhttp_stream->request != NULL) @@ -77,7 +77,7 @@ g_winhttp_file_input_stream_class_init (GWinHttpFileInputStreamClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass); - + gobject_class->finalize = g_winhttp_file_input_stream_finalize; stream_class->read_fn = g_winhttp_file_input_stream_read; @@ -90,10 +90,10 @@ g_winhttp_file_input_stream_init (GWinHttpFileInputStream *info) /** * g_winhttp_file_input_stream_new: - * @file: the GWinHttpFile being read + * @file: the GWinHttpFile being read * @connection: handle to the HTTP connection, as from WinHttpConnect() * @request: handle to the HTTP request, as from WinHttpOpenRequest - * + * * Returns: #GFileInputStream for the given request **/ GFileInputStream * @@ -109,7 +109,7 @@ _g_winhttp_file_input_stream_new (GWinHttpFile *file, stream->request_sent = FALSE; stream->connection = connection; stream->request = request; - + return G_FILE_INPUT_STREAM (stream); } @@ -132,39 +132,27 @@ g_winhttp_file_input_stream_read (GInputStream *stream, 0, 0)) { - char *emsg = _g_winhttp_error_message (GetLastError ()); - - g_set_error (error, G_IO_ERROR, - G_IO_ERROR_FAILED, - "%s", emsg); - g_free (emsg); - - return -1; - } - - if (!G_WINHTTP_VFS_GET_CLASS (winhttp_stream->file->vfs)->pWinHttpReceiveResponse - (winhttp_stream->request, NULL)) - { - char *emsg = _g_winhttp_error_message (GetLastError ()); - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "%s", emsg); - g_free (emsg); + _g_winhttp_set_error (error, GetLastError (), "GET request"); return -1; } + + if (!_g_winhttp_response (winhttp_stream->file->vfs, + winhttp_stream->request, + error, + "GET request")) + return -1; + winhttp_stream->request_sent = TRUE; } - + if (!G_WINHTTP_VFS_GET_CLASS (winhttp_stream->file->vfs)->pWinHttpReadData (winhttp_stream->request, buffer, count, &bytes_read)) { - char *emsg = _g_winhttp_error_message (GetLastError ()); - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "%s", emsg); - g_free (emsg); + _g_winhttp_set_error (error, GetLastError (), "GET request"); return -1; } - + return bytes_read; } diff --git a/gio/win32/gwinhttpfileoutputstream.c b/gio/win32/gwinhttpfileoutputstream.c index 898a729..d283399 100644 --- a/gio/win32/gwinhttpfileoutputstream.c +++ b/gio/win32/gwinhttpfileoutputstream.c @@ -1,5 +1,5 @@ /* GIO - GLib Input, Output and Streaming Library - * + * * Copyright (C) 2006-2007 Red Hat, Inc. * Copyright (C) 2008 Novell, Inc. * @@ -60,7 +60,7 @@ static void g_winhttp_file_output_stream_finalize (GObject *object) { GWinHttpFileOutputStream *winhttp_stream; - + winhttp_stream = G_WINHTTP_FILE_OUTPUT_STREAM (object); if (winhttp_stream->connection != NULL) @@ -74,7 +74,7 @@ g_winhttp_file_output_stream_class_init (GWinHttpFileOutputStreamClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GOutputStreamClass *stream_class = G_OUTPUT_STREAM_CLASS (klass); - + gobject_class->finalize = g_winhttp_file_output_stream_finalize; stream_class->write_fn = g_winhttp_file_output_stream_write; @@ -87,10 +87,10 @@ g_winhttp_file_output_stream_init (GWinHttpFileOutputStream *info) /** * g_winhttp_file_output_stream_new: - * @file: the GWinHttpFile being read + * @file: the GWinHttpFile being read * @connection: handle to the HTTP connection, as from WinHttpConnect() * @request: handle to the HTTP request, as from WinHttpOpenRequest - * + * * Returns: #GFileOutputStream for the given request **/ GFileOutputStream * @@ -132,12 +132,7 @@ g_winhttp_file_output_stream_write (GOutputStream *stream, if (request == NULL) { - char *emsg = _g_winhttp_error_message (GetLastError ()); - - g_set_error (error, G_IO_ERROR, - G_IO_ERROR_FAILED, - "%s", emsg); - g_free (emsg); + _g_winhttp_set_error (error, GetLastError (), "PUT request"); return -1; } @@ -154,47 +149,35 @@ g_winhttp_file_output_stream_write (GOutputStream *stream, count, 0)) { - char *emsg = _g_winhttp_error_message (GetLastError ()); - - g_set_error (error, G_IO_ERROR, - G_IO_ERROR_FAILED, - "%s", emsg); - g_free (emsg); - + _g_winhttp_set_error (error, GetLastError (), "PUT request"); + G_WINHTTP_VFS_GET_CLASS (winhttp_stream->file->vfs)->pWinHttpCloseHandle (request); g_free (wheaders); return -1; } - + g_free (wheaders); if (!G_WINHTTP_VFS_GET_CLASS (winhttp_stream->file->vfs)->pWinHttpWriteData (request, buffer, count, &bytes_written)) { - char *emsg = _g_winhttp_error_message (GetLastError ()); - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "%s", - emsg); - g_free (emsg); + _g_winhttp_set_error (error, GetLastError (), "PUT request"); G_WINHTTP_VFS_GET_CLASS (winhttp_stream->file->vfs)->pWinHttpCloseHandle (request); return -1; } - + winhttp_stream->offset += bytes_written; - if (!G_WINHTTP_VFS_GET_CLASS (winhttp_stream->file->vfs)->pWinHttpReceiveResponse - (request, NULL)) + if (!_g_winhttp_response (winhttp_stream->file->vfs, + request, + error, + "PUT request")) { - char *emsg = _g_winhttp_error_message (GetLastError ()); - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "%s", - emsg); - g_free (emsg); - G_WINHTTP_VFS_GET_CLASS (winhttp_stream->file->vfs)->pWinHttpCloseHandle (request); + return -1; } diff --git a/gio/win32/gwinhttpvfs.c b/gio/win32/gwinhttpvfs.c index dddf12e..2f7a331 100644 --- a/gio/win32/gwinhttpvfs.c +++ b/gio/win32/gwinhttpvfs.c @@ -1,5 +1,5 @@ /* GIO - GLib Input, Output and Streaming Library - * + * * Copyright (C) 2006-2007 Red Hat, Inc. * Copyright (C) 2008 Novell, Inc. * @@ -24,6 +24,9 @@ #include "config.h" +#include + +#include "gioerror.h" #include "giomodule.h" #include "gvfs.h" @@ -69,7 +72,7 @@ g_winhttp_vfs_init (GWinHttpVfs *vfs) if (!wagent) wagent = g_utf8_to_utf16 ("GWinHttpVfs", -1, NULL, NULL, NULL); - + vfs->session = (G_WINHTTP_VFS_GET_CLASS (vfs)->pWinHttpOpen) (wagent, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, @@ -142,7 +145,7 @@ g_winhttp_vfs_get_supported_uri_schemes (GVfs *vfs) } retval[n] = NULL; - + return retval; } @@ -151,7 +154,7 @@ g_winhttp_vfs_parse_name (GVfs *vfs, const char *parse_name) { GWinHttpVfs *winhttp_vfs = G_WINHTTP_VFS (vfs); - + g_return_val_if_fail (G_IS_VFS (vfs), NULL); g_return_val_if_fail (parse_name != NULL, NULL); @@ -177,7 +180,7 @@ g_winhttp_vfs_class_init (GWinHttpVfsClass *class) GObjectClass *object_class; GVfsClass *vfs_class; HMODULE winhttp; - + object_class = (GObjectClass *) class; object_class->finalize = g_winhttp_vfs_finalize; @@ -266,9 +269,154 @@ _g_winhttp_error_message (DWORD error_code) CASE (UNRECOGNIZED_SCHEME); #undef CASE default: - return g_strdup_printf ("WinHttp error %ld", error_code); + return g_strdup_printf ("WinHttp error %ld", error_code); } } else return g_win32_error_message (error_code); } + +void +_g_winhttp_set_error (GError **error, + DWORD error_code, + const char *what) +{ + char *emsg = _g_winhttp_error_message (error_code); + + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "%s failed: %s", what, emsg); + g_free (emsg); +} + +gboolean +_g_winhttp_response (GWinHttpVfs *vfs, + HINTERNET request, + GError **error, + const char *what) +{ + wchar_t *status_code; + DWORD status_code_len; + + if (!G_WINHTTP_VFS_GET_CLASS (vfs)->pWinHttpReceiveResponse (request, NULL)) + { + _g_winhttp_set_error (error, GetLastError (), what); + + return FALSE; + } + + status_code_len = 0; + if (!G_WINHTTP_VFS_GET_CLASS (vfs)->pWinHttpQueryHeaders + (request, + WINHTTP_QUERY_STATUS_CODE, + NULL, + NULL, + &status_code_len, + NULL) && + GetLastError () != ERROR_INSUFFICIENT_BUFFER) + { + _g_winhttp_set_error (error, GetLastError (), what); + + return FALSE; + } + + status_code = g_malloc (status_code_len); + + if (!G_WINHTTP_VFS_GET_CLASS (vfs)->pWinHttpQueryHeaders + (request, + WINHTTP_QUERY_STATUS_CODE, + NULL, + status_code, + &status_code_len, + NULL)) + { + _g_winhttp_set_error (error, GetLastError (), what); + g_free (status_code); + + return FALSE; + } + + if (status_code[0] != L'2') + { + wchar_t *status_text = NULL; + DWORD status_text_len; + + if (!G_WINHTTP_VFS_GET_CLASS (vfs)->pWinHttpQueryHeaders + (request, + WINHTTP_QUERY_STATUS_TEXT, + NULL, + NULL, + &status_text_len, + NULL) && + GetLastError () == ERROR_INSUFFICIENT_BUFFER) + { + status_text = g_malloc (status_text_len); + + if (!G_WINHTTP_VFS_GET_CLASS (vfs)->pWinHttpQueryHeaders + (request, + WINHTTP_QUERY_STATUS_TEXT, + NULL, + status_text, + &status_text_len, + NULL)) + { + g_free (status_text); + status_text = NULL; + } + } + + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "%s failed: %S %S", + what, status_code, status_text ? status_text : L""); + g_free (status_code); + g_free (status_text); + + return FALSE; + } + + g_free (status_code); + + return TRUE; +} + +gboolean +_g_winhttp_query_header (GWinHttpVfs *vfs, + HINTERNET request, + const char *request_description, + DWORD which_header, + wchar_t **header, + GError **error) +{ + DWORD header_len = 0; + + if (!G_WINHTTP_VFS_GET_CLASS (vfs)->pWinHttpQueryHeaders + (request, + which_header, + NULL, + NULL, + &header_len, + NULL) && + GetLastError () != ERROR_INSUFFICIENT_BUFFER) + { + _g_winhttp_set_error (error, GetLastError (), request_description); + + return FALSE; + } + + *header = g_malloc (header_len); + if (!G_WINHTTP_VFS_GET_CLASS (vfs)->pWinHttpQueryHeaders + (request, + which_header, + NULL, + *header, + &header_len, + NULL)) + { + _g_winhttp_set_error (error, GetLastError (), request_description); + g_free (*header); + *header = NULL; + + return FALSE; + } + + return TRUE; +} diff --git a/gio/win32/gwinhttpvfs.h b/gio/win32/gwinhttpvfs.h index a7597a4..787aa0b 100644 --- a/gio/win32/gwinhttpvfs.h +++ b/gio/win32/gwinhttpvfs.h @@ -1,6 +1,6 @@ /* GIO - GLib Input, Output and Streaming Library - * - * Copyright (C) 2006-2007 Red Hat, Inc. + * + * Copyright (C) 2006-2007 Red Hat, Inc. * Copyright (C) 2008 Novell, Inc. * * This library is free software; you can redistribute it and/or @@ -83,6 +83,22 @@ GVfs *_g_winhttp_vfs_new (void); char *_g_winhttp_error_message (DWORD error_code); +void _g_winhttp_set_error (GError **error, + DWORD error_code, + const char *what); + +gboolean _g_winhttp_response (GWinHttpVfs *vfs, + HINTERNET request, + GError **error, + const char *what); + +gboolean _g_winhttp_query_header (GWinHttpVfs *vfs, + HINTERNET request, + const char *request_description, + DWORD which_header, + wchar_t **header, + GError **error); + G_END_DECLS #endif /* __G_WINHTTP_VFS_H__ */ diff --git a/gio/win32/winhttp.h b/gio/win32/winhttp.h index 1bc7f48..a8dd0c1 100644 --- a/gio/win32/winhttp.h +++ b/gio/win32/winhttp.h @@ -229,6 +229,18 @@ BOOL WINAPI WinHttpWriteData(HINTERNET,LPCVOID,DWORD,LPDWORD); #define ICU_ESCAPE 0x80000000 #define ICU_DECODE 0x10000000 +/* A few constants I couldn't find publicly documented, so I looked up + * their value from the Windows SDK . Presumably this falls + * under fair use. + */ +#define WINHTTP_QUERY_CONTENT_LENGTH 5 +#define WINHTTP_QUERY_CONTENT_TYPE 1 +#define WINHTTP_QUERY_LAST_MODIFIED 11 +#define WINHTTP_QUERY_STATUS_CODE 19 +#define WINHTTP_QUERY_STATUS_TEXT 20 + +#define WINHTTP_QUERY_FLAG_SYSTEMTIME 0x40000000 + #ifdef __cplusplus } #endif -- 2.7.4