1 /* GIO - GLib Input, Output and Streaming Library
3 * Copyright (C) 2006-2007 Red Hat, Inc.
4 * Copyright (C) 2008 Novell, Inc.
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.
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.
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.
21 * Author: Alexander Larsson <alexl@redhat.com>
22 * Author: Tor Lillqvist <tml@novell.com>
32 #include "gfileattribute.h"
33 #include "gfileinfo.h"
34 #include "gwinhttpfile.h"
35 #include "gwinhttpfileinputstream.h"
36 #include "gwinhttpfileoutputstream.h"
43 static void g_winhttp_file_file_iface_init (GFileIface *iface);
45 #define g_winhttp_file_get_type _g_winhttp_file_get_type
46 G_DEFINE_TYPE_WITH_CODE (GWinHttpFile, g_winhttp_file, G_TYPE_OBJECT,
47 G_IMPLEMENT_INTERFACE (G_TYPE_FILE,
48 g_winhttp_file_file_iface_init))
51 g_winhttp_file_finalize (GObject *object)
55 file = G_WINHTTP_FILE (object);
57 g_free (file->url.lpszScheme);
58 g_free (file->url.lpszHostName);
59 g_free (file->url.lpszUserName);
60 g_free (file->url.lpszPassword);
61 g_free (file->url.lpszUrlPath);
62 g_free (file->url.lpszExtraInfo);
64 g_object_unref (file->vfs);
66 G_OBJECT_CLASS (g_winhttp_file_parent_class)->finalize (object);
70 g_winhttp_file_class_init (GWinHttpFileClass *klass)
72 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
74 gobject_class->finalize = g_winhttp_file_finalize;
78 g_winhttp_file_init (GWinHttpFile *winhttp)
83 * _g_winhttp_file_new:
84 * @vfs: GWinHttpVfs to use
85 * @uri: URI of the GWinHttpFile to create.
87 * Returns: new winhttp #GFile.
90 _g_winhttp_file_new (GWinHttpVfs *vfs,
96 wuri = g_utf8_to_utf16 (uri, -1, NULL, NULL, NULL);
101 file = g_object_new (G_TYPE_WINHTTP_FILE, NULL);
102 file->vfs = g_object_ref (vfs);
104 memset (&file->url, 0, sizeof (file->url));
105 file->url.dwStructSize = sizeof (file->url);
106 file->url.dwSchemeLength = 1;
107 file->url.dwHostNameLength = 1;
108 file->url.dwUserNameLength = 1;
109 file->url.dwPasswordLength = 1;
110 file->url.dwUrlPathLength = 1;
111 file->url.dwExtraInfoLength = 1;
113 if (!G_WINHTTP_VFS_GET_CLASS (vfs)->funcs->pWinHttpCrackUrl (wuri, 0, 0, &file->url))
119 file->url.lpszScheme = g_new (wchar_t, ++file->url.dwSchemeLength);
120 file->url.lpszHostName = g_new (wchar_t, ++file->url.dwHostNameLength);
121 file->url.lpszUserName = g_new (wchar_t, ++file->url.dwUserNameLength);
122 file->url.lpszPassword = g_new (wchar_t, ++file->url.dwPasswordLength);
123 file->url.lpszUrlPath = g_new (wchar_t, ++file->url.dwUrlPathLength);
124 file->url.lpszExtraInfo = g_new (wchar_t, ++file->url.dwExtraInfoLength);
126 if (!G_WINHTTP_VFS_GET_CLASS (vfs)->funcs->pWinHttpCrackUrl (wuri, 0, 0, &file->url))
128 g_free (file->url.lpszScheme);
129 g_free (file->url.lpszHostName);
130 g_free (file->url.lpszUserName);
131 g_free (file->url.lpszPassword);
132 g_free (file->url.lpszUrlPath);
133 g_free (file->url.lpszExtraInfo);
139 return G_FILE (file);
143 g_winhttp_file_is_native (GFile *file)
149 g_winhttp_file_has_uri_scheme (GFile *file,
150 const char *uri_scheme)
152 return (g_ascii_strcasecmp (uri_scheme, "http") == 0 ||
153 g_ascii_strcasecmp (uri_scheme, "https") == 0);
157 g_winhttp_file_get_uri_scheme (GFile *file)
159 GWinHttpFile *winhttp_file = G_WINHTTP_FILE (file);
161 return g_utf16_to_utf8 (winhttp_file->url.lpszScheme, -1, NULL, NULL, NULL);
165 g_winhttp_file_get_basename (GFile *file)
167 GWinHttpFile *winhttp_file = G_WINHTTP_FILE (file);
172 basename = g_utf16_to_utf8 (winhttp_file->url.lpszUrlPath, -1, NULL, NULL, NULL);
173 last_slash = strrchr (basename, '/');
174 /* If no slash, or only "/" fallback to full path part of URI */
175 if (last_slash == NULL || last_slash[1] == '\0')
178 retval = g_strdup (last_slash + 1);
185 g_winhttp_file_get_path (GFile *file)
191 g_winhttp_file_get_uri (GFile *file)
193 GWinHttpFile *winhttp_file = G_WINHTTP_FILE (file);
199 if (!G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->funcs->pWinHttpCreateUrl (&winhttp_file->url, ICU_ESCAPE, NULL, &len) &&
200 GetLastError () != ERROR_INSUFFICIENT_BUFFER)
203 wuri = g_new (wchar_t, ++len);
205 if (!G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->funcs->pWinHttpCreateUrl (&winhttp_file->url, ICU_ESCAPE, wuri, &len))
211 retval = g_utf16_to_utf8 (wuri, -1, NULL, NULL, NULL);
214 if (g_str_has_prefix (retval, "http://:@"))
216 memmove (retval + 7, retval + 9, strlen (retval) - 9);
217 retval[strlen (retval) - 2] = '\0';
219 else if (g_str_has_prefix (retval, "https://:@"))
221 memmove (retval + 8, retval + 10, strlen (retval) - 10);
222 retval[strlen (retval) - 2] = '\0';
229 g_winhttp_file_get_parse_name (GFile *file)
231 /* FIXME: More hair surely needed */
233 return g_winhttp_file_get_uri (file);
237 g_winhttp_file_get_parent (GFile *file)
239 GWinHttpFile *winhttp_file;
244 winhttp_file = G_WINHTTP_FILE (file);
246 uri = g_winhttp_file_get_uri (file);
250 last_slash = strrchr (uri, '/');
251 if (last_slash == NULL || *(last_slash+1) == 0)
257 while (last_slash > uri && *last_slash == '/')
260 last_slash[1] = '\0';
262 parent = _g_winhttp_file_new (winhttp_file->vfs, uri);
269 g_winhttp_file_dup (GFile *file)
271 GWinHttpFile *winhttp_file = G_WINHTTP_FILE (file);
272 char *uri = g_winhttp_file_get_uri (file);
273 GFile *retval = _g_winhttp_file_new (winhttp_file->vfs, uri);
281 g_winhttp_file_hash (GFile *file)
283 char *uri = g_winhttp_file_get_uri (file);
284 guint retval = g_str_hash (uri);
292 g_winhttp_file_equal (GFile *file1,
295 char *uri1 = g_winhttp_file_get_uri (file1);
296 char *uri2 = g_winhttp_file_get_uri (file2);
297 gboolean retval = g_str_equal (uri1, uri2);
306 match_prefix (const char *path,
311 prefix_len = strlen (prefix);
312 if (strncmp (path, prefix, prefix_len) != 0)
315 if (prefix_len > 0 && prefix[prefix_len-1] == '/')
318 return path + prefix_len;
322 g_winhttp_file_prefix_matches (GFile *parent,
325 char *parent_uri = g_winhttp_file_get_uri (parent);
326 char *descendant_uri = g_winhttp_file_get_uri (descendant);
327 const char *remainder;
330 remainder = match_prefix (descendant_uri, parent_uri);
332 if (remainder != NULL && *remainder == '/')
338 g_free (descendant_uri);
344 g_winhttp_file_get_relative_path (GFile *parent,
347 char *parent_uri = g_winhttp_file_get_uri (parent);
348 char *descendant_uri = g_winhttp_file_get_uri (descendant);
349 const char *remainder;
352 remainder = match_prefix (descendant_uri, parent_uri);
354 if (remainder != NULL && *remainder == '/')
355 retval = g_strdup (remainder + 1);
360 g_free (descendant_uri);
366 g_winhttp_file_resolve_relative_path (GFile *file,
367 const char *relative_path)
369 GWinHttpFile *winhttp_file = G_WINHTTP_FILE (file);
371 wchar_t *wnew_path = g_utf8_to_utf16 (relative_path, -1, NULL, NULL, NULL);
373 if (wnew_path == NULL)
376 if (*wnew_path != '/')
379 int trailing_slash = winhttp_file->url.lpszUrlPath[winhttp_file->url.dwUrlPathLength-1] == L'/'? 1 : 0;
382 tmp = g_new (wchar_t, wcslen (winhttp_file->url.lpszUrlPath) + wcslen (wnew_path) + 1);
383 wcscpy (tmp, winhttp_file->url.lpszUrlPath);
387 tmp = g_new (wchar_t, wcslen (winhttp_file->url.lpszUrlPath) + 1 + wcslen (wnew_path) + 1);
388 wcscpy (tmp, winhttp_file->url.lpszUrlPath);
391 wcscat (tmp, wnew_path);
397 child = g_object_new (G_TYPE_WINHTTP_FILE, NULL);
398 child->vfs = winhttp_file->vfs;
399 child->url = winhttp_file->url;
400 child->url.lpszScheme = g_memdup (winhttp_file->url.lpszScheme, (winhttp_file->url.dwSchemeLength+1)*2);
401 child->url.lpszHostName = g_memdup (winhttp_file->url.lpszHostName, (winhttp_file->url.dwHostNameLength+1)*2);
402 child->url.lpszUserName = g_memdup (winhttp_file->url.lpszUserName, (winhttp_file->url.dwUserNameLength+1)*2);
403 child->url.lpszPassword = g_memdup (winhttp_file->url.lpszPassword, (winhttp_file->url.dwPasswordLength+1)*2);
404 child->url.lpszUrlPath = wnew_path;
405 child->url.dwUrlPathLength = wcslen (wnew_path);
406 child->url.lpszExtraInfo = NULL;
407 child->url.dwExtraInfoLength = 0;
409 return (GFile *) child;
413 g_winhttp_file_get_child_for_display_name (GFile *file,
414 const char *display_name,
420 basename = g_locale_from_utf8 (display_name, -1, NULL, NULL, NULL);
421 if (basename == NULL)
423 g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_FILENAME,
424 _("Invalid filename %s"), display_name);
428 new_file = g_file_get_child (file, basename);
435 g_winhttp_file_set_display_name (GFile *file,
436 const char *display_name,
437 GCancellable *cancellable,
440 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
441 _("Operation not supported"));
447 mktime_utc (SYSTEMTIME *t)
451 static const gint days_before[] =
453 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
456 if (t->wMonth < 1 || t->wMonth > 12)
459 retval = (t->wYear - 1970) * 365;
460 retval += (t->wYear - 1968) / 4;
461 retval += days_before[t->wMonth-1] + t->wDay - 1;
463 if (t->wYear % 4 == 0 && t->wMonth < 3)
466 retval = ((((retval * 24) + t->wHour) * 60) + t->wMinute) * 60 + t->wSecond;
472 g_winhttp_file_query_info (GFile *file,
473 const char *attributes,
474 GFileQueryInfoFlags flags,
475 GCancellable *cancellable,
478 GWinHttpFile *winhttp_file = G_WINHTTP_FILE (file);
479 HINTERNET connection, request;
480 const wchar_t *accept_types[] =
486 GFileAttributeMatcher *matcher;
488 wchar_t *content_length;
489 wchar_t *content_type;
490 SYSTEMTIME last_modified;
491 DWORD last_modified_len;
493 connection = G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->funcs->pWinHttpConnect
494 (G_WINHTTP_VFS (winhttp_file->vfs)->session,
495 winhttp_file->url.lpszHostName,
496 winhttp_file->url.nPort,
499 if (connection == NULL)
501 _g_winhttp_set_error (error, GetLastError (), "HTTP connection");
506 request = G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->funcs->pWinHttpOpenRequest
509 winhttp_file->url.lpszUrlPath,
513 winhttp_file->url.nScheme == INTERNET_SCHEME_HTTPS ? WINHTTP_FLAG_SECURE : 0);
517 _g_winhttp_set_error (error, GetLastError (), "HEAD request");
522 if (!G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->funcs->pWinHttpSendRequest
529 _g_winhttp_set_error (error, GetLastError (), "HEAD request");
534 if (!_g_winhttp_response (winhttp_file->vfs, request, error, "HEAD request"))
537 matcher = g_file_attribute_matcher_new (attributes);
538 info = g_file_info_new ();
539 g_file_info_set_attribute_mask (info, matcher);
541 basename = g_winhttp_file_get_basename (file);
542 g_file_info_set_name (info, basename);
545 content_length = NULL;
546 if (_g_winhttp_query_header (winhttp_file->vfs,
549 WINHTTP_QUERY_CONTENT_LENGTH,
556 if (swscanf (content_length, L"%I64d%n", &cl, &n) == 1 &&
557 n == wcslen (content_length))
558 g_file_info_set_size (info, cl);
560 g_free (content_length);
567 if (_g_winhttp_query_header (winhttp_file->vfs,
570 WINHTTP_QUERY_CONTENT_TYPE,
574 char *ct = g_utf16_to_utf8 (content_type, -1, NULL, NULL, NULL);
578 char *p = strchr (ct, ';');
582 char *tmp = g_strndup (ct, p - ct);
584 g_file_info_set_content_type (info, tmp);
588 g_file_info_set_content_type (info, ct);
594 last_modified_len = sizeof (last_modified);
595 if (G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->funcs->pWinHttpQueryHeaders
597 WINHTTP_QUERY_LAST_MODIFIED | WINHTTP_QUERY_FLAG_SYSTEMTIME,
602 last_modified_len == sizeof (last_modified) &&
603 /* Don't bother comparing to the exact Y2038 moment */
604 last_modified.wYear >= 1970 &&
605 last_modified.wYear < 2038)
609 tv.tv_sec = mktime_utc (&last_modified);
610 tv.tv_usec = last_modified.wMilliseconds * 1000;
612 g_file_info_set_modification_time (info, &tv);
615 g_file_attribute_matcher_unref (matcher);
620 static GFileInputStream *
621 g_winhttp_file_read (GFile *file,
622 GCancellable *cancellable,
625 GWinHttpFile *winhttp_file = G_WINHTTP_FILE (file);
626 HINTERNET connection, request;
627 const wchar_t *accept_types[] =
633 connection = G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->funcs->pWinHttpConnect
634 (G_WINHTTP_VFS (winhttp_file->vfs)->session,
635 winhttp_file->url.lpszHostName,
636 winhttp_file->url.nPort,
639 if (connection == NULL)
641 _g_winhttp_set_error (error, GetLastError (), "HTTP connection");
646 request = G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->funcs->pWinHttpOpenRequest
649 winhttp_file->url.lpszUrlPath,
653 winhttp_file->url.nScheme == INTERNET_SCHEME_HTTPS ? WINHTTP_FLAG_SECURE : 0);
657 _g_winhttp_set_error (error, GetLastError (), "GET request");
662 return _g_winhttp_file_input_stream_new (winhttp_file, connection, request);
665 static GFileOutputStream *
666 g_winhttp_file_create (GFile *file,
667 GFileCreateFlags flags,
668 GCancellable *cancellable,
671 GWinHttpFile *winhttp_file = G_WINHTTP_FILE (file);
672 HINTERNET connection;
674 connection = G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->funcs->pWinHttpConnect
675 (G_WINHTTP_VFS (winhttp_file->vfs)->session,
676 winhttp_file->url.lpszHostName,
677 winhttp_file->url.nPort,
680 if (connection == NULL)
682 _g_winhttp_set_error (error, GetLastError (), "HTTP connection");
687 return _g_winhttp_file_output_stream_new (winhttp_file, connection);
692 static GFileOutputStream *
693 g_winhttp_file_replace (GFile *file,
695 gboolean make_backup,
696 GFileCreateFlags flags,
697 GCancellable *cancellable,
700 /* FIXME: Implement */
707 g_winhttp_file_delete (GFile *file,
708 GCancellable *cancellable,
711 /* FIXME: Implement */
717 g_winhttp_file_make_directory (GFile *file,
718 GCancellable *cancellable,
721 /* FIXME: Implement */
727 g_winhttp_file_copy (GFile *source,
729 GFileCopyFlags flags,
730 GCancellable *cancellable,
731 GFileProgressCallback progress_callback,
732 gpointer progress_callback_data,
735 /* Fall back to default copy?? */
736 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
737 "Copy not supported");
743 g_winhttp_file_move (GFile *source,
745 GFileCopyFlags flags,
746 GCancellable *cancellable,
747 GFileProgressCallback progress_callback,
748 gpointer progress_callback_data,
751 /* FIXME: Implement */
759 g_winhttp_file_file_iface_init (GFileIface *iface)
761 iface->dup = g_winhttp_file_dup;
762 iface->hash = g_winhttp_file_hash;
763 iface->equal = g_winhttp_file_equal;
764 iface->is_native = g_winhttp_file_is_native;
765 iface->has_uri_scheme = g_winhttp_file_has_uri_scheme;
766 iface->get_uri_scheme = g_winhttp_file_get_uri_scheme;
767 iface->get_basename = g_winhttp_file_get_basename;
768 iface->get_path = g_winhttp_file_get_path;
769 iface->get_uri = g_winhttp_file_get_uri;
770 iface->get_parse_name = g_winhttp_file_get_parse_name;
771 iface->get_parent = g_winhttp_file_get_parent;
772 iface->prefix_matches = g_winhttp_file_prefix_matches;
773 iface->get_relative_path = g_winhttp_file_get_relative_path;
774 iface->resolve_relative_path = g_winhttp_file_resolve_relative_path;
775 iface->get_child_for_display_name = g_winhttp_file_get_child_for_display_name;
776 iface->set_display_name = g_winhttp_file_set_display_name;
777 iface->query_info = g_winhttp_file_query_info;
778 iface->read_fn = g_winhttp_file_read;
779 iface->create = g_winhttp_file_create;
781 iface->replace = g_winhttp_file_replace;
782 iface->delete_file = g_winhttp_file_delete;
783 iface->make_directory = g_winhttp_file_make_directory;
784 iface->copy = g_winhttp_file_copy;
785 iface->move = g_winhttp_file_move;