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.1 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, see <http://www.gnu.org/licenses/>.
19 * Author: Alexander Larsson <alexl@redhat.com>
20 * Author: Tor Lillqvist <tml@novell.com>
29 #include "gio/gfile.h"
30 #include "gio/gfileattribute.h"
31 #include "gio/gfileinfo.h"
32 #include "gio/gfileinfo-priv.h"
33 #include "gwinhttpfile.h"
34 #include "gwinhttpfileinputstream.h"
35 #include "gwinhttpfileoutputstream.h"
36 #include "gio/gioerror.h"
40 static void g_winhttp_file_file_iface_init (GFileIface *iface);
42 #define g_winhttp_file_get_type _g_winhttp_file_get_type
43 G_DEFINE_TYPE_WITH_CODE (GWinHttpFile, g_winhttp_file, G_TYPE_OBJECT,
44 G_IMPLEMENT_INTERFACE (G_TYPE_FILE,
45 g_winhttp_file_file_iface_init))
48 g_winhttp_file_finalize (GObject *object)
52 file = G_WINHTTP_FILE (object);
54 g_free (file->url.lpszScheme);
55 g_free (file->url.lpszHostName);
56 g_free (file->url.lpszUserName);
57 g_free (file->url.lpszPassword);
58 g_free (file->url.lpszUrlPath);
59 g_free (file->url.lpszExtraInfo);
61 g_object_unref (file->vfs);
63 G_OBJECT_CLASS (g_winhttp_file_parent_class)->finalize (object);
67 g_winhttp_file_class_init (GWinHttpFileClass *klass)
69 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
71 gobject_class->finalize = g_winhttp_file_finalize;
75 g_winhttp_file_init (GWinHttpFile *winhttp)
80 * _g_winhttp_file_new:
81 * @vfs: GWinHttpVfs to use
82 * @uri: URI of the GWinHttpFile to create.
84 * Returns: (nullable): new winhttp #GFile, or %NULL if there was an error constructing it.
87 _g_winhttp_file_new (GWinHttpVfs *vfs,
93 wuri = g_utf8_to_utf16 (uri, -1, NULL, NULL, NULL);
98 file = g_object_new (G_TYPE_WINHTTP_FILE, NULL);
99 file->vfs = g_object_ref (vfs);
101 memset (&file->url, 0, sizeof (file->url));
102 file->url.dwStructSize = sizeof (file->url);
103 file->url.dwSchemeLength = 1;
104 file->url.dwHostNameLength = 1;
105 file->url.dwUserNameLength = 1;
106 file->url.dwPasswordLength = 1;
107 file->url.dwUrlPathLength = 1;
108 file->url.dwExtraInfoLength = 1;
110 if (!G_WINHTTP_VFS_GET_CLASS (vfs)->funcs->pWinHttpCrackUrl (wuri, 0, 0, &file->url))
116 file->url.lpszScheme = g_new (wchar_t, ++file->url.dwSchemeLength);
117 file->url.lpszHostName = g_new (wchar_t, ++file->url.dwHostNameLength);
118 file->url.lpszUserName = g_new (wchar_t, ++file->url.dwUserNameLength);
119 file->url.lpszPassword = g_new (wchar_t, ++file->url.dwPasswordLength);
120 file->url.lpszUrlPath = g_new (wchar_t, ++file->url.dwUrlPathLength);
121 file->url.lpszExtraInfo = g_new (wchar_t, ++file->url.dwExtraInfoLength);
123 if (!G_WINHTTP_VFS_GET_CLASS (vfs)->funcs->pWinHttpCrackUrl (wuri, 0, 0, &file->url))
125 g_free (file->url.lpszScheme);
126 g_free (file->url.lpszHostName);
127 g_free (file->url.lpszUserName);
128 g_free (file->url.lpszPassword);
129 g_free (file->url.lpszUrlPath);
130 g_free (file->url.lpszExtraInfo);
136 return G_FILE (file);
140 g_winhttp_file_is_native (GFile *file)
146 g_winhttp_file_has_uri_scheme (GFile *file,
147 const char *uri_scheme)
149 return (g_ascii_strcasecmp (uri_scheme, "http") == 0 ||
150 g_ascii_strcasecmp (uri_scheme, "https") == 0);
154 g_winhttp_file_get_uri_scheme (GFile *file)
156 GWinHttpFile *winhttp_file = G_WINHTTP_FILE (file);
158 return g_utf16_to_utf8 (winhttp_file->url.lpszScheme, -1, NULL, NULL, NULL);
162 g_winhttp_file_get_basename (GFile *file)
164 GWinHttpFile *winhttp_file = G_WINHTTP_FILE (file);
169 basename = g_utf16_to_utf8 (winhttp_file->url.lpszUrlPath, -1, NULL, NULL, NULL);
170 last_slash = strrchr (basename, '/');
171 /* If no slash, or only "/" fallback to full path part of URI */
172 if (last_slash == NULL || last_slash[1] == '\0')
175 retval = g_strdup (last_slash + 1);
182 g_winhttp_file_get_display_name (GFile *file)
186 /* FIXME: This could be improved by using a new g_utf16_make_valid() function
187 * to recover what we can from the URI, and then suffixing it with
188 * “ (invalid encoding)” as per g_filename_display_basename(). */
189 basename = g_winhttp_file_get_basename (file);
191 return g_strdup (_(" (invalid encoding)"));
193 return g_steal_pointer (&basename);
197 g_winhttp_file_get_path (GFile *file)
203 g_winhttp_file_get_uri (GFile *file)
205 GWinHttpFile *winhttp_file = G_WINHTTP_FILE (file);
211 if (!G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->funcs->pWinHttpCreateUrl (&winhttp_file->url, ICU_ESCAPE, NULL, &len) &&
212 GetLastError () != ERROR_INSUFFICIENT_BUFFER)
215 wuri = g_new (wchar_t, ++len);
217 if (!G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->funcs->pWinHttpCreateUrl (&winhttp_file->url, ICU_ESCAPE, wuri, &len))
223 retval = g_utf16_to_utf8 (wuri, -1, NULL, NULL, NULL);
226 if (g_str_has_prefix (retval, "http://:@"))
228 memmove (retval + 7, retval + 9, strlen (retval) - 9);
229 retval[strlen (retval) - 2] = '\0';
231 else if (g_str_has_prefix (retval, "https://:@"))
233 memmove (retval + 8, retval + 10, strlen (retval) - 10);
234 retval[strlen (retval) - 2] = '\0';
241 g_winhttp_file_get_parse_name (GFile *file)
243 /* FIXME: More hair surely needed */
245 return g_winhttp_file_get_uri (file);
249 g_winhttp_file_get_parent (GFile *file)
251 GWinHttpFile *winhttp_file;
256 winhttp_file = G_WINHTTP_FILE (file);
258 uri = g_winhttp_file_get_uri (file);
262 last_slash = strrchr (uri, '/');
263 if (last_slash == NULL || *(last_slash+1) == 0)
269 while (last_slash > uri && *last_slash == '/')
272 last_slash[1] = '\0';
274 parent = _g_winhttp_file_new (winhttp_file->vfs, uri);
281 g_winhttp_file_dup (GFile *file)
283 GWinHttpFile *winhttp_file = G_WINHTTP_FILE (file);
284 char *uri = g_winhttp_file_get_uri (file);
285 GFile *retval = _g_winhttp_file_new (winhttp_file->vfs, uri);
293 g_winhttp_file_hash (GFile *file)
295 char *uri = g_winhttp_file_get_uri (file);
296 guint retval = g_str_hash (uri);
304 g_winhttp_file_equal (GFile *file1,
307 char *uri1 = g_winhttp_file_get_uri (file1);
308 char *uri2 = g_winhttp_file_get_uri (file2);
309 gboolean retval = g_str_equal (uri1, uri2);
318 match_prefix (const char *path,
323 prefix_len = strlen (prefix);
324 if (strncmp (path, prefix, prefix_len) != 0)
327 if (prefix_len > 0 && prefix[prefix_len-1] == '/')
330 return path + prefix_len;
334 g_winhttp_file_prefix_matches (GFile *parent,
337 char *parent_uri = g_winhttp_file_get_uri (parent);
338 char *descendant_uri = g_winhttp_file_get_uri (descendant);
339 const char *remainder;
342 remainder = match_prefix (descendant_uri, parent_uri);
344 if (remainder != NULL && *remainder == '/')
350 g_free (descendant_uri);
356 g_winhttp_file_get_relative_path (GFile *parent,
359 char *parent_uri = g_winhttp_file_get_uri (parent);
360 char *descendant_uri = g_winhttp_file_get_uri (descendant);
361 const char *remainder;
364 remainder = match_prefix (descendant_uri, parent_uri);
366 if (remainder != NULL && *remainder == '/')
367 retval = g_strdup (remainder + 1);
372 g_free (descendant_uri);
378 g_winhttp_file_resolve_relative_path (GFile *file,
379 const char *relative_path)
381 GWinHttpFile *winhttp_file = G_WINHTTP_FILE (file);
383 wchar_t *wnew_path = g_utf8_to_utf16 (relative_path, -1, NULL, NULL, NULL);
385 if (wnew_path == NULL)
388 if (*wnew_path != '/')
391 int trailing_slash = winhttp_file->url.lpszUrlPath[winhttp_file->url.dwUrlPathLength-1] == L'/'? 1 : 0;
394 tmp = g_new (wchar_t, wcslen (winhttp_file->url.lpszUrlPath) + wcslen (wnew_path) + 1);
395 wcscpy (tmp, winhttp_file->url.lpszUrlPath);
399 tmp = g_new (wchar_t, wcslen (winhttp_file->url.lpszUrlPath) + 1 + wcslen (wnew_path) + 1);
400 wcscpy (tmp, winhttp_file->url.lpszUrlPath);
403 wcscat (tmp, wnew_path);
409 child = g_object_new (G_TYPE_WINHTTP_FILE, NULL);
410 child->vfs = winhttp_file->vfs;
411 child->url = winhttp_file->url;
412 child->url.lpszScheme = g_memdup2 (winhttp_file->url.lpszScheme, ((gsize) winhttp_file->url.dwSchemeLength + 1) * 2);
413 child->url.lpszHostName = g_memdup2 (winhttp_file->url.lpszHostName, ((gsize) winhttp_file->url.dwHostNameLength + 1) * 2);
414 child->url.lpszUserName = g_memdup2 (winhttp_file->url.lpszUserName, ((gsize) winhttp_file->url.dwUserNameLength + 1) * 2);
415 child->url.lpszPassword = g_memdup2 (winhttp_file->url.lpszPassword, ((gsize) winhttp_file->url.dwPasswordLength + 1) * 2);
416 child->url.lpszUrlPath = wnew_path;
417 child->url.dwUrlPathLength = wcslen (wnew_path);
418 child->url.lpszExtraInfo = NULL;
419 child->url.dwExtraInfoLength = 0;
421 return (GFile *) child;
425 g_winhttp_file_get_child_for_display_name (GFile *file,
426 const char *display_name,
432 basename = g_locale_from_utf8 (display_name, -1, NULL, NULL, NULL);
433 if (basename == NULL)
435 g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_FILENAME,
436 _("Invalid filename %s"), display_name);
440 new_file = g_file_get_child (file, basename);
447 g_winhttp_file_set_display_name (GFile *file,
448 const char *display_name,
449 GCancellable *cancellable,
452 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
453 _("Operation not supported"));
459 g_winhttp_file_query_info (GFile *file,
460 const char *attributes,
461 GFileQueryInfoFlags flags,
462 GCancellable *cancellable,
465 GWinHttpFile *winhttp_file = G_WINHTTP_FILE (file);
466 HINTERNET connection, request;
467 const wchar_t *accept_types[] =
473 GFileAttributeMatcher *matcher;
475 wchar_t *content_length;
476 wchar_t *content_type;
477 SYSTEMTIME last_modified;
478 DWORD last_modified_len;
480 matcher = g_file_attribute_matcher_new (attributes);
481 info = g_file_info_new ();
482 g_file_info_set_attribute_mask (info, matcher);
484 basename = g_winhttp_file_get_basename (file);
485 g_file_info_set_name (info, basename);
488 if (_g_file_attribute_matcher_matches_id (matcher,
489 G_FILE_ATTRIBUTE_ID_STANDARD_DISPLAY_NAME))
491 char *display_name = g_winhttp_file_get_display_name (file);
492 g_file_info_set_display_name (info, display_name);
493 g_free (display_name);
496 if (_g_file_attribute_matcher_matches_id (matcher, G_FILE_ATTRIBUTE_ID_STANDARD_TYPE))
497 g_file_info_set_file_type (info, G_FILE_TYPE_REGULAR);
499 if (!(_g_file_attribute_matcher_matches_id (matcher, G_FILE_ATTRIBUTE_ID_STANDARD_SIZE) ||
500 _g_file_attribute_matcher_matches_id (matcher, G_FILE_ATTRIBUTE_ID_STANDARD_CONTENT_TYPE) ||
501 _g_file_attribute_matcher_matches_id (matcher, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED) ||
502 _g_file_attribute_matcher_matches_id (matcher, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED_NSEC) ||
503 _g_file_attribute_matcher_matches_id (matcher, G_FILE_ATTRIBUTE_ID_TIME_MODIFIED_USEC)))
508 connection = G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->funcs->pWinHttpConnect
509 (G_WINHTTP_VFS (winhttp_file->vfs)->session,
510 winhttp_file->url.lpszHostName,
511 winhttp_file->url.nPort,
514 if (connection == NULL)
516 _g_winhttp_set_error (error, GetLastError (), "HTTP connection");
521 request = G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->funcs->pWinHttpOpenRequest
524 winhttp_file->url.lpszUrlPath,
528 winhttp_file->url.nScheme == INTERNET_SCHEME_HTTPS ? WINHTTP_FLAG_SECURE : 0);
532 _g_winhttp_set_error (error, GetLastError (), "HEAD request");
537 if (!G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->funcs->pWinHttpSendRequest
544 _g_winhttp_set_error (error, GetLastError (), "HEAD request");
549 if (!_g_winhttp_response (winhttp_file->vfs, request, error, "HEAD request"))
552 content_length = NULL;
553 if (_g_winhttp_query_header (winhttp_file->vfs,
556 WINHTTP_QUERY_CONTENT_LENGTH,
562 const char *gint64_format = "%"G_GINT64_FORMAT"%n";
563 wchar_t *gint64_format_w = g_utf8_to_utf16 (gint64_format, -1, NULL, NULL, NULL);
565 if (swscanf (content_length, gint64_format_w, &cl, &n) == 1 &&
566 n == wcslen (content_length))
567 g_file_info_set_size (info, cl);
569 g_free (content_length);
570 g_free (gint64_format_w);
577 if (_g_winhttp_query_header (winhttp_file->vfs,
580 WINHTTP_QUERY_CONTENT_TYPE,
584 char *ct = g_utf16_to_utf8 (content_type, -1, NULL, NULL, NULL);
588 char *p = strchr (ct, ';');
592 char *tmp = g_strndup (ct, p - ct);
594 g_file_info_set_content_type (info, tmp);
598 g_file_info_set_content_type (info, ct);
604 last_modified_len = sizeof (last_modified);
605 if (G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->funcs->pWinHttpQueryHeaders
607 WINHTTP_QUERY_LAST_MODIFIED | WINHTTP_QUERY_FLAG_SYSTEMTIME,
612 last_modified_len == sizeof (last_modified) &&
613 /* Don't bother comparing to the exact Y2038 moment */
614 last_modified.wYear >= 1970 &&
615 last_modified.wYear < 2038)
617 GDateTime *dt = NULL, *dt2 = NULL;
619 dt = g_date_time_new_from_unix_utc (last_modified.wMilliseconds / 1000);
620 dt2 = g_date_time_add_seconds (dt, (last_modified.wMilliseconds % 1000) / 1000);
622 g_file_info_set_modification_date_time (info, dt2);
624 g_date_time_unref (dt2);
625 g_date_time_unref (dt);
628 g_file_attribute_matcher_unref (matcher);
633 static GFileInputStream *
634 g_winhttp_file_read (GFile *file,
635 GCancellable *cancellable,
638 GWinHttpFile *winhttp_file = G_WINHTTP_FILE (file);
639 HINTERNET connection, request;
640 const wchar_t *accept_types[] =
646 connection = G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->funcs->pWinHttpConnect
647 (G_WINHTTP_VFS (winhttp_file->vfs)->session,
648 winhttp_file->url.lpszHostName,
649 winhttp_file->url.nPort,
652 if (connection == NULL)
654 _g_winhttp_set_error (error, GetLastError (), "HTTP connection");
659 request = G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->funcs->pWinHttpOpenRequest
662 winhttp_file->url.lpszUrlPath,
666 winhttp_file->url.nScheme == INTERNET_SCHEME_HTTPS ? WINHTTP_FLAG_SECURE : 0);
670 _g_winhttp_set_error (error, GetLastError (), "GET request");
675 return _g_winhttp_file_input_stream_new (winhttp_file, connection, request);
678 static GFileOutputStream *
679 g_winhttp_file_create (GFile *file,
680 GFileCreateFlags flags,
681 GCancellable *cancellable,
684 GWinHttpFile *winhttp_file = G_WINHTTP_FILE (file);
685 HINTERNET connection;
687 connection = G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->funcs->pWinHttpConnect
688 (G_WINHTTP_VFS (winhttp_file->vfs)->session,
689 winhttp_file->url.lpszHostName,
690 winhttp_file->url.nPort,
693 if (connection == NULL)
695 _g_winhttp_set_error (error, GetLastError (), "HTTP connection");
700 return _g_winhttp_file_output_stream_new (winhttp_file, connection);
705 static GFileOutputStream *
706 g_winhttp_file_replace (GFile *file,
708 gboolean make_backup,
709 GFileCreateFlags flags,
710 GCancellable *cancellable,
713 /* FIXME: Implement */
720 g_winhttp_file_delete (GFile *file,
721 GCancellable *cancellable,
724 /* FIXME: Implement */
730 g_winhttp_file_make_directory (GFile *file,
731 GCancellable *cancellable,
734 /* FIXME: Implement */
740 g_winhttp_file_copy (GFile *source,
742 GFileCopyFlags flags,
743 GCancellable *cancellable,
744 GFileProgressCallback progress_callback,
745 gpointer progress_callback_data,
748 /* Fall back to default copy?? */
749 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
750 "Copy not supported");
756 g_winhttp_file_move (GFile *source,
758 GFileCopyFlags flags,
759 GCancellable *cancellable,
760 GFileProgressCallback progress_callback,
761 gpointer progress_callback_data,
764 /* FIXME: Implement */
772 g_winhttp_file_file_iface_init (GFileIface *iface)
774 iface->dup = g_winhttp_file_dup;
775 iface->hash = g_winhttp_file_hash;
776 iface->equal = g_winhttp_file_equal;
777 iface->is_native = g_winhttp_file_is_native;
778 iface->has_uri_scheme = g_winhttp_file_has_uri_scheme;
779 iface->get_uri_scheme = g_winhttp_file_get_uri_scheme;
780 iface->get_basename = g_winhttp_file_get_basename;
781 iface->get_path = g_winhttp_file_get_path;
782 iface->get_uri = g_winhttp_file_get_uri;
783 iface->get_parse_name = g_winhttp_file_get_parse_name;
784 iface->get_parent = g_winhttp_file_get_parent;
785 iface->prefix_matches = g_winhttp_file_prefix_matches;
786 iface->get_relative_path = g_winhttp_file_get_relative_path;
787 iface->resolve_relative_path = g_winhttp_file_resolve_relative_path;
788 iface->get_child_for_display_name = g_winhttp_file_get_child_for_display_name;
789 iface->set_display_name = g_winhttp_file_set_display_name;
790 iface->query_info = g_winhttp_file_query_info;
791 iface->read_fn = g_winhttp_file_read;
792 iface->create = g_winhttp_file_create;
794 iface->replace = g_winhttp_file_replace;
795 iface->delete_file = g_winhttp_file_delete;
796 iface->make_directory = g_winhttp_file_make_directory;
797 iface->copy = g_winhttp_file_copy;
798 iface->move = g_winhttp_file_move;