win32/gwinhttpvfs.c win32/gwinhttpvfs.h win32/gwinhttpfile.c

2008-08-13  Tor Lillqvist  <tml@novell.com>

	* 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
This commit is contained in:
Tor Lillqvist 2008-08-13 19:39:49 +00:00 committed by Tor Lillqvist
parent d303e31605
commit d5863830ad
7 changed files with 439 additions and 106 deletions

View File

@ -1,3 +1,18 @@
2008-08-13 Tor Lillqvist <tml@novell.com>
* 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 <tml@novell.com>
Bug 546582 - Callbacks from GFileMonitor present a GFile in the

View File

@ -28,6 +28,7 @@
#include "gfile.h"
#include "gfileattribute.h"
#include "gfileinfo.h"
#include "gwinhttpfile.h"
#include "gwinhttpfileinputstream.h"
#include "gwinhttpfileoutputstream.h"
@ -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;
}
@ -394,8 +406,7 @@ 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;
}
@ -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

View File

@ -132,36 +132,24 @@ 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);
_g_winhttp_set_error (error, GetLastError (), "GET request");
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);
if (!_g_winhttp_response (winhttp_stream->file->vfs,
winhttp_stream->request,
error,
"GET request"))
return -1;
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;
}

View File

@ -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,12 +149,7 @@ 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);
@ -172,11 +162,7 @@ g_winhttp_file_output_stream_write (GOutputStream *stream,
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);
@ -185,16 +171,13 @@ g_winhttp_file_output_stream_write (GOutputStream *stream,
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;
}

View File

@ -24,6 +24,9 @@
#include "config.h"
#include <wchar.h>
#include "gioerror.h"
#include "giomodule.h"
#include "gvfs.h"
@ -272,3 +275,148 @@ _g_winhttp_error_message (DWORD 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;
}

View File

@ -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__ */

View File

@ -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 <winhttp.h>. 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