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 "gwinhttpfile.h"
33 #include "gwinhttpfileinputstream.h"
34 #include "gwinhttpfileoutputstream.h"
35 #include "gio/gioerror.h"
39 static void g_winhttp_file_file_iface_init (GFileIface
*iface
);
41 #define g_winhttp_file_get_type _g_winhttp_file_get_type
42 G_DEFINE_TYPE_WITH_CODE (GWinHttpFile
, g_winhttp_file
, G_TYPE_OBJECT
,
43 G_IMPLEMENT_INTERFACE (G_TYPE_FILE
,
44 g_winhttp_file_file_iface_init
))
47 g_winhttp_file_finalize (GObject
*object
)
51 file
= G_WINHTTP_FILE (object
);
53 g_free (file
->url
.lpszScheme
);
54 g_free (file
->url
.lpszHostName
);
55 g_free (file
->url
.lpszUserName
);
56 g_free (file
->url
.lpszPassword
);
57 g_free (file
->url
.lpszUrlPath
);
58 g_free (file
->url
.lpszExtraInfo
);
60 g_object_unref (file
->vfs
);
62 G_OBJECT_CLASS (g_winhttp_file_parent_class
)->finalize (object
);
66 g_winhttp_file_class_init (GWinHttpFileClass
*klass
)
68 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
70 gobject_class
->finalize
= g_winhttp_file_finalize
;
74 g_winhttp_file_init (GWinHttpFile
*winhttp
)
79 * _g_winhttp_file_new:
80 * @vfs: GWinHttpVfs to use
81 * @uri: URI of the GWinHttpFile to create.
83 * Returns: new winhttp #GFile.
86 _g_winhttp_file_new (GWinHttpVfs
*vfs
,
92 wuri
= g_utf8_to_utf16 (uri
, -1, NULL
, NULL
, NULL
);
97 file
= g_object_new (G_TYPE_WINHTTP_FILE
, NULL
);
98 file
->vfs
= g_object_ref (vfs
);
100 memset (&file
->url
, 0, sizeof (file
->url
));
101 file
->url
.dwStructSize
= sizeof (file
->url
);
102 file
->url
.dwSchemeLength
= 1;
103 file
->url
.dwHostNameLength
= 1;
104 file
->url
.dwUserNameLength
= 1;
105 file
->url
.dwPasswordLength
= 1;
106 file
->url
.dwUrlPathLength
= 1;
107 file
->url
.dwExtraInfoLength
= 1;
109 if (!G_WINHTTP_VFS_GET_CLASS (vfs
)->funcs
->pWinHttpCrackUrl (wuri
, 0, 0, &file
->url
))
115 file
->url
.lpszScheme
= g_new (wchar_t, ++file
->url
.dwSchemeLength
);
116 file
->url
.lpszHostName
= g_new (wchar_t, ++file
->url
.dwHostNameLength
);
117 file
->url
.lpszUserName
= g_new (wchar_t, ++file
->url
.dwUserNameLength
);
118 file
->url
.lpszPassword
= g_new (wchar_t, ++file
->url
.dwPasswordLength
);
119 file
->url
.lpszUrlPath
= g_new (wchar_t, ++file
->url
.dwUrlPathLength
);
120 file
->url
.lpszExtraInfo
= g_new (wchar_t, ++file
->url
.dwExtraInfoLength
);
122 if (!G_WINHTTP_VFS_GET_CLASS (vfs
)->funcs
->pWinHttpCrackUrl (wuri
, 0, 0, &file
->url
))
124 g_free (file
->url
.lpszScheme
);
125 g_free (file
->url
.lpszHostName
);
126 g_free (file
->url
.lpszUserName
);
127 g_free (file
->url
.lpszPassword
);
128 g_free (file
->url
.lpszUrlPath
);
129 g_free (file
->url
.lpszExtraInfo
);
135 return G_FILE (file
);
139 g_winhttp_file_is_native (GFile
*file
)
145 g_winhttp_file_has_uri_scheme (GFile
*file
,
146 const char *uri_scheme
)
148 return (g_ascii_strcasecmp (uri_scheme
, "http") == 0 ||
149 g_ascii_strcasecmp (uri_scheme
, "https") == 0);
153 g_winhttp_file_get_uri_scheme (GFile
*file
)
155 GWinHttpFile
*winhttp_file
= G_WINHTTP_FILE (file
);
157 return g_utf16_to_utf8 (winhttp_file
->url
.lpszScheme
, -1, NULL
, NULL
, NULL
);
161 g_winhttp_file_get_basename (GFile
*file
)
163 GWinHttpFile
*winhttp_file
= G_WINHTTP_FILE (file
);
168 basename
= g_utf16_to_utf8 (winhttp_file
->url
.lpszUrlPath
, -1, NULL
, NULL
, NULL
);
169 last_slash
= strrchr (basename
, '/');
170 /* If no slash, or only "/" fallback to full path part of URI */
171 if (last_slash
== NULL
|| last_slash
[1] == '\0')
174 retval
= g_strdup (last_slash
+ 1);
181 g_winhttp_file_get_path (GFile
*file
)
187 g_winhttp_file_get_uri (GFile
*file
)
189 GWinHttpFile
*winhttp_file
= G_WINHTTP_FILE (file
);
195 if (!G_WINHTTP_VFS_GET_CLASS (winhttp_file
->vfs
)->funcs
->pWinHttpCreateUrl (&winhttp_file
->url
, ICU_ESCAPE
, NULL
, &len
) &&
196 GetLastError () != ERROR_INSUFFICIENT_BUFFER
)
199 wuri
= g_new (wchar_t, ++len
);
201 if (!G_WINHTTP_VFS_GET_CLASS (winhttp_file
->vfs
)->funcs
->pWinHttpCreateUrl (&winhttp_file
->url
, ICU_ESCAPE
, wuri
, &len
))
207 retval
= g_utf16_to_utf8 (wuri
, -1, NULL
, NULL
, NULL
);
210 if (g_str_has_prefix (retval
, "http://:@"))
212 memmove (retval
+ 7, retval
+ 9, strlen (retval
) - 9);
213 retval
[strlen (retval
) - 2] = '\0';
215 else if (g_str_has_prefix (retval
, "https://:@"))
217 memmove (retval
+ 8, retval
+ 10, strlen (retval
) - 10);
218 retval
[strlen (retval
) - 2] = '\0';
225 g_winhttp_file_get_parse_name (GFile
*file
)
227 /* FIXME: More hair surely needed */
229 return g_winhttp_file_get_uri (file
);
233 g_winhttp_file_get_parent (GFile
*file
)
235 GWinHttpFile
*winhttp_file
;
240 winhttp_file
= G_WINHTTP_FILE (file
);
242 uri
= g_winhttp_file_get_uri (file
);
246 last_slash
= strrchr (uri
, '/');
247 if (last_slash
== NULL
|| *(last_slash
+1) == 0)
253 while (last_slash
> uri
&& *last_slash
== '/')
256 last_slash
[1] = '\0';
258 parent
= _g_winhttp_file_new (winhttp_file
->vfs
, uri
);
265 g_winhttp_file_dup (GFile
*file
)
267 GWinHttpFile
*winhttp_file
= G_WINHTTP_FILE (file
);
268 char *uri
= g_winhttp_file_get_uri (file
);
269 GFile
*retval
= _g_winhttp_file_new (winhttp_file
->vfs
, uri
);
277 g_winhttp_file_hash (GFile
*file
)
279 char *uri
= g_winhttp_file_get_uri (file
);
280 guint retval
= g_str_hash (uri
);
288 g_winhttp_file_equal (GFile
*file1
,
291 char *uri1
= g_winhttp_file_get_uri (file1
);
292 char *uri2
= g_winhttp_file_get_uri (file2
);
293 gboolean retval
= g_str_equal (uri1
, uri2
);
302 match_prefix (const char *path
,
307 prefix_len
= strlen (prefix
);
308 if (strncmp (path
, prefix
, prefix_len
) != 0)
311 if (prefix_len
> 0 && prefix
[prefix_len
-1] == '/')
314 return path
+ prefix_len
;
318 g_winhttp_file_prefix_matches (GFile
*parent
,
321 char *parent_uri
= g_winhttp_file_get_uri (parent
);
322 char *descendant_uri
= g_winhttp_file_get_uri (descendant
);
323 const char *remainder
;
326 remainder
= match_prefix (descendant_uri
, parent_uri
);
328 if (remainder
!= NULL
&& *remainder
== '/')
334 g_free (descendant_uri
);
340 g_winhttp_file_get_relative_path (GFile
*parent
,
343 char *parent_uri
= g_winhttp_file_get_uri (parent
);
344 char *descendant_uri
= g_winhttp_file_get_uri (descendant
);
345 const char *remainder
;
348 remainder
= match_prefix (descendant_uri
, parent_uri
);
350 if (remainder
!= NULL
&& *remainder
== '/')
351 retval
= g_strdup (remainder
+ 1);
356 g_free (descendant_uri
);
362 g_winhttp_file_resolve_relative_path (GFile
*file
,
363 const char *relative_path
)
365 GWinHttpFile
*winhttp_file
= G_WINHTTP_FILE (file
);
367 wchar_t *wnew_path
= g_utf8_to_utf16 (relative_path
, -1, NULL
, NULL
, NULL
);
369 if (wnew_path
== NULL
)
372 if (*wnew_path
!= '/')
375 int trailing_slash
= winhttp_file
->url
.lpszUrlPath
[winhttp_file
->url
.dwUrlPathLength
-1] == L
'/'? 1 : 0;
378 tmp
= g_new (wchar_t, wcslen (winhttp_file
->url
.lpszUrlPath
) + wcslen (wnew_path
) + 1);
379 wcscpy (tmp
, winhttp_file
->url
.lpszUrlPath
);
383 tmp
= g_new (wchar_t, wcslen (winhttp_file
->url
.lpszUrlPath
) + 1 + wcslen (wnew_path
) + 1);
384 wcscpy (tmp
, winhttp_file
->url
.lpszUrlPath
);
387 wcscat (tmp
, wnew_path
);
393 child
= g_object_new (G_TYPE_WINHTTP_FILE
, NULL
);
394 child
->vfs
= winhttp_file
->vfs
;
395 child
->url
= winhttp_file
->url
;
396 child
->url
.lpszScheme
= g_memdup (winhttp_file
->url
.lpszScheme
, (winhttp_file
->url
.dwSchemeLength
+1)*2);
397 child
->url
.lpszHostName
= g_memdup (winhttp_file
->url
.lpszHostName
, (winhttp_file
->url
.dwHostNameLength
+1)*2);
398 child
->url
.lpszUserName
= g_memdup (winhttp_file
->url
.lpszUserName
, (winhttp_file
->url
.dwUserNameLength
+1)*2);
399 child
->url
.lpszPassword
= g_memdup (winhttp_file
->url
.lpszPassword
, (winhttp_file
->url
.dwPasswordLength
+1)*2);
400 child
->url
.lpszUrlPath
= wnew_path
;
401 child
->url
.dwUrlPathLength
= wcslen (wnew_path
);
402 child
->url
.lpszExtraInfo
= NULL
;
403 child
->url
.dwExtraInfoLength
= 0;
405 return (GFile
*) child
;
409 g_winhttp_file_get_child_for_display_name (GFile
*file
,
410 const char *display_name
,
416 basename
= g_locale_from_utf8 (display_name
, -1, NULL
, NULL
, NULL
);
417 if (basename
== NULL
)
419 g_set_error (error
, G_IO_ERROR
, G_IO_ERROR_INVALID_FILENAME
,
420 _("Invalid filename %s"), display_name
);
424 new_file
= g_file_get_child (file
, basename
);
431 g_winhttp_file_set_display_name (GFile
*file
,
432 const char *display_name
,
433 GCancellable
*cancellable
,
436 g_set_error_literal (error
, G_IO_ERROR
, G_IO_ERROR_NOT_SUPPORTED
,
437 _("Operation not supported"));
443 mktime_utc (SYSTEMTIME
*t
)
447 static const gint days_before
[] =
449 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
452 if (t
->wMonth
< 1 || t
->wMonth
> 12)
455 retval
= (t
->wYear
- 1970) * 365;
456 retval
+= (t
->wYear
- 1968) / 4;
457 retval
+= days_before
[t
->wMonth
-1] + t
->wDay
- 1;
459 if (t
->wYear
% 4 == 0 && t
->wMonth
< 3)
462 retval
= ((((retval
* 24) + t
->wHour
) * 60) + t
->wMinute
) * 60 + t
->wSecond
;
468 g_winhttp_file_query_info (GFile
*file
,
469 const char *attributes
,
470 GFileQueryInfoFlags flags
,
471 GCancellable
*cancellable
,
474 GWinHttpFile
*winhttp_file
= G_WINHTTP_FILE (file
);
475 HINTERNET connection
, request
;
476 const wchar_t *accept_types
[] =
482 GFileAttributeMatcher
*matcher
;
484 wchar_t *content_length
;
485 wchar_t *content_type
;
486 SYSTEMTIME last_modified
;
487 DWORD last_modified_len
;
489 connection
= G_WINHTTP_VFS_GET_CLASS (winhttp_file
->vfs
)->funcs
->pWinHttpConnect
490 (G_WINHTTP_VFS (winhttp_file
->vfs
)->session
,
491 winhttp_file
->url
.lpszHostName
,
492 winhttp_file
->url
.nPort
,
495 if (connection
== NULL
)
497 _g_winhttp_set_error (error
, GetLastError (), "HTTP connection");
502 request
= G_WINHTTP_VFS_GET_CLASS (winhttp_file
->vfs
)->funcs
->pWinHttpOpenRequest
505 winhttp_file
->url
.lpszUrlPath
,
509 winhttp_file
->url
.nScheme
== INTERNET_SCHEME_HTTPS
? WINHTTP_FLAG_SECURE
: 0);
513 _g_winhttp_set_error (error
, GetLastError (), "HEAD request");
518 if (!G_WINHTTP_VFS_GET_CLASS (winhttp_file
->vfs
)->funcs
->pWinHttpSendRequest
525 _g_winhttp_set_error (error
, GetLastError (), "HEAD request");
530 if (!_g_winhttp_response (winhttp_file
->vfs
, request
, error
, "HEAD request"))
533 matcher
= g_file_attribute_matcher_new (attributes
);
534 info
= g_file_info_new ();
535 g_file_info_set_attribute_mask (info
, matcher
);
537 basename
= g_winhttp_file_get_basename (file
);
538 g_file_info_set_name (info
, basename
);
541 content_length
= NULL
;
542 if (_g_winhttp_query_header (winhttp_file
->vfs
,
545 WINHTTP_QUERY_CONTENT_LENGTH
,
551 const char *gint64_format
= "%"G_GINT64_FORMAT
"%n";
552 wchar_t *gint64_format_w
= g_utf8_to_utf16 (gint64_format
, -1, NULL
, NULL
, NULL
);
554 if (swscanf (content_length
, gint64_format_w
, &cl
, &n
) == 1 &&
555 n
== wcslen (content_length
))
556 g_file_info_set_size (info
, cl
);
558 g_free (content_length
);
559 g_free (gint64_format_w
);
566 if (_g_winhttp_query_header (winhttp_file
->vfs
,
569 WINHTTP_QUERY_CONTENT_TYPE
,
573 char *ct
= g_utf16_to_utf8 (content_type
, -1, NULL
, NULL
, NULL
);
577 char *p
= strchr (ct
, ';');
581 char *tmp
= g_strndup (ct
, p
- ct
);
583 g_file_info_set_content_type (info
, tmp
);
587 g_file_info_set_content_type (info
, ct
);
593 last_modified_len
= sizeof (last_modified
);
594 if (G_WINHTTP_VFS_GET_CLASS (winhttp_file
->vfs
)->funcs
->pWinHttpQueryHeaders
596 WINHTTP_QUERY_LAST_MODIFIED
| WINHTTP_QUERY_FLAG_SYSTEMTIME
,
601 last_modified_len
== sizeof (last_modified
) &&
602 /* Don't bother comparing to the exact Y2038 moment */
603 last_modified
.wYear
>= 1970 &&
604 last_modified
.wYear
< 2038)
608 tv
.tv_sec
= mktime_utc (&last_modified
);
609 tv
.tv_usec
= last_modified
.wMilliseconds
* 1000;
611 g_file_info_set_modification_time (info
, &tv
);
614 g_file_attribute_matcher_unref (matcher
);
619 static GFileInputStream
*
620 g_winhttp_file_read (GFile
*file
,
621 GCancellable
*cancellable
,
624 GWinHttpFile
*winhttp_file
= G_WINHTTP_FILE (file
);
625 HINTERNET connection
, request
;
626 const wchar_t *accept_types
[] =
632 connection
= G_WINHTTP_VFS_GET_CLASS (winhttp_file
->vfs
)->funcs
->pWinHttpConnect
633 (G_WINHTTP_VFS (winhttp_file
->vfs
)->session
,
634 winhttp_file
->url
.lpszHostName
,
635 winhttp_file
->url
.nPort
,
638 if (connection
== NULL
)
640 _g_winhttp_set_error (error
, GetLastError (), "HTTP connection");
645 request
= G_WINHTTP_VFS_GET_CLASS (winhttp_file
->vfs
)->funcs
->pWinHttpOpenRequest
648 winhttp_file
->url
.lpszUrlPath
,
652 winhttp_file
->url
.nScheme
== INTERNET_SCHEME_HTTPS
? WINHTTP_FLAG_SECURE
: 0);
656 _g_winhttp_set_error (error
, GetLastError (), "GET request");
661 return _g_winhttp_file_input_stream_new (winhttp_file
, connection
, request
);
664 static GFileOutputStream
*
665 g_winhttp_file_create (GFile
*file
,
666 GFileCreateFlags flags
,
667 GCancellable
*cancellable
,
670 GWinHttpFile
*winhttp_file
= G_WINHTTP_FILE (file
);
671 HINTERNET connection
;
673 connection
= G_WINHTTP_VFS_GET_CLASS (winhttp_file
->vfs
)->funcs
->pWinHttpConnect
674 (G_WINHTTP_VFS (winhttp_file
->vfs
)->session
,
675 winhttp_file
->url
.lpszHostName
,
676 winhttp_file
->url
.nPort
,
679 if (connection
== NULL
)
681 _g_winhttp_set_error (error
, GetLastError (), "HTTP connection");
686 return _g_winhttp_file_output_stream_new (winhttp_file
, connection
);
691 static GFileOutputStream
*
692 g_winhttp_file_replace (GFile
*file
,
694 gboolean make_backup
,
695 GFileCreateFlags flags
,
696 GCancellable
*cancellable
,
699 /* FIXME: Implement */
706 g_winhttp_file_delete (GFile
*file
,
707 GCancellable
*cancellable
,
710 /* FIXME: Implement */
716 g_winhttp_file_make_directory (GFile
*file
,
717 GCancellable
*cancellable
,
720 /* FIXME: Implement */
726 g_winhttp_file_copy (GFile
*source
,
728 GFileCopyFlags flags
,
729 GCancellable
*cancellable
,
730 GFileProgressCallback progress_callback
,
731 gpointer progress_callback_data
,
734 /* Fall back to default copy?? */
735 g_set_error_literal (error
, G_IO_ERROR
, G_IO_ERROR_NOT_SUPPORTED
,
736 "Copy not supported");
742 g_winhttp_file_move (GFile
*source
,
744 GFileCopyFlags flags
,
745 GCancellable
*cancellable
,
746 GFileProgressCallback progress_callback
,
747 gpointer progress_callback_data
,
750 /* FIXME: Implement */
758 g_winhttp_file_file_iface_init (GFileIface
*iface
)
760 iface
->dup
= g_winhttp_file_dup
;
761 iface
->hash
= g_winhttp_file_hash
;
762 iface
->equal
= g_winhttp_file_equal
;
763 iface
->is_native
= g_winhttp_file_is_native
;
764 iface
->has_uri_scheme
= g_winhttp_file_has_uri_scheme
;
765 iface
->get_uri_scheme
= g_winhttp_file_get_uri_scheme
;
766 iface
->get_basename
= g_winhttp_file_get_basename
;
767 iface
->get_path
= g_winhttp_file_get_path
;
768 iface
->get_uri
= g_winhttp_file_get_uri
;
769 iface
->get_parse_name
= g_winhttp_file_get_parse_name
;
770 iface
->get_parent
= g_winhttp_file_get_parent
;
771 iface
->prefix_matches
= g_winhttp_file_prefix_matches
;
772 iface
->get_relative_path
= g_winhttp_file_get_relative_path
;
773 iface
->resolve_relative_path
= g_winhttp_file_resolve_relative_path
;
774 iface
->get_child_for_display_name
= g_winhttp_file_get_child_for_display_name
;
775 iface
->set_display_name
= g_winhttp_file_set_display_name
;
776 iface
->query_info
= g_winhttp_file_query_info
;
777 iface
->read_fn
= g_winhttp_file_read
;
778 iface
->create
= g_winhttp_file_create
;
780 iface
->replace
= g_winhttp_file_replace
;
781 iface
->delete_file
= g_winhttp_file_delete
;
782 iface
->make_directory
= g_winhttp_file_make_directory
;
783 iface
->copy
= g_winhttp_file_copy
;
784 iface
->move
= g_winhttp_file_move
;