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>
31 #include "gio/gfile.h"
32 #include "gio/gfileattribute.h"
33 #include "gio/gfileinfo.h"
34 #include "gwinhttpfile.h"
35 #include "gwinhttpfileinputstream.h"
36 #include "gwinhttpfileoutputstream.h"
37 #include "gio/gioerror.h"
41 static void g_winhttp_file_file_iface_init (GFileIface
*iface
);
43 #define g_winhttp_file_get_type _g_winhttp_file_get_type
44 G_DEFINE_TYPE_WITH_CODE (GWinHttpFile
, g_winhttp_file
, G_TYPE_OBJECT
,
45 G_IMPLEMENT_INTERFACE (G_TYPE_FILE
,
46 g_winhttp_file_file_iface_init
))
49 g_winhttp_file_finalize (GObject
*object
)
53 file
= G_WINHTTP_FILE (object
);
55 g_free (file
->url
.lpszScheme
);
56 g_free (file
->url
.lpszHostName
);
57 g_free (file
->url
.lpszUserName
);
58 g_free (file
->url
.lpszPassword
);
59 g_free (file
->url
.lpszUrlPath
);
60 g_free (file
->url
.lpszExtraInfo
);
62 g_object_unref (file
->vfs
);
64 G_OBJECT_CLASS (g_winhttp_file_parent_class
)->finalize (object
);
68 g_winhttp_file_class_init (GWinHttpFileClass
*klass
)
70 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
72 gobject_class
->finalize
= g_winhttp_file_finalize
;
76 g_winhttp_file_init (GWinHttpFile
*winhttp
)
81 * _g_winhttp_file_new:
82 * @vfs: GWinHttpVfs to use
83 * @uri: URI of the GWinHttpFile to create.
85 * Returns: new winhttp #GFile.
88 _g_winhttp_file_new (GWinHttpVfs
*vfs
,
94 wuri
= g_utf8_to_utf16 (uri
, -1, NULL
, NULL
, NULL
);
99 file
= g_object_new (G_TYPE_WINHTTP_FILE
, NULL
);
100 file
->vfs
= g_object_ref (vfs
);
102 memset (&file
->url
, 0, sizeof (file
->url
));
103 file
->url
.dwStructSize
= sizeof (file
->url
);
104 file
->url
.dwSchemeLength
= 1;
105 file
->url
.dwHostNameLength
= 1;
106 file
->url
.dwUserNameLength
= 1;
107 file
->url
.dwPasswordLength
= 1;
108 file
->url
.dwUrlPathLength
= 1;
109 file
->url
.dwExtraInfoLength
= 1;
111 if (!G_WINHTTP_VFS_GET_CLASS (vfs
)->funcs
->pWinHttpCrackUrl (wuri
, 0, 0, &file
->url
))
117 file
->url
.lpszScheme
= g_new (wchar_t, ++file
->url
.dwSchemeLength
);
118 file
->url
.lpszHostName
= g_new (wchar_t, ++file
->url
.dwHostNameLength
);
119 file
->url
.lpszUserName
= g_new (wchar_t, ++file
->url
.dwUserNameLength
);
120 file
->url
.lpszPassword
= g_new (wchar_t, ++file
->url
.dwPasswordLength
);
121 file
->url
.lpszUrlPath
= g_new (wchar_t, ++file
->url
.dwUrlPathLength
);
122 file
->url
.lpszExtraInfo
= g_new (wchar_t, ++file
->url
.dwExtraInfoLength
);
124 if (!G_WINHTTP_VFS_GET_CLASS (vfs
)->funcs
->pWinHttpCrackUrl (wuri
, 0, 0, &file
->url
))
126 g_free (file
->url
.lpszScheme
);
127 g_free (file
->url
.lpszHostName
);
128 g_free (file
->url
.lpszUserName
);
129 g_free (file
->url
.lpszPassword
);
130 g_free (file
->url
.lpszUrlPath
);
131 g_free (file
->url
.lpszExtraInfo
);
137 return G_FILE (file
);
141 g_winhttp_file_is_native (GFile
*file
)
147 g_winhttp_file_has_uri_scheme (GFile
*file
,
148 const char *uri_scheme
)
150 return (g_ascii_strcasecmp (uri_scheme
, "http") == 0 ||
151 g_ascii_strcasecmp (uri_scheme
, "https") == 0);
155 g_winhttp_file_get_uri_scheme (GFile
*file
)
157 GWinHttpFile
*winhttp_file
= G_WINHTTP_FILE (file
);
159 return g_utf16_to_utf8 (winhttp_file
->url
.lpszScheme
, -1, NULL
, NULL
, NULL
);
163 g_winhttp_file_get_basename (GFile
*file
)
165 GWinHttpFile
*winhttp_file
= G_WINHTTP_FILE (file
);
170 basename
= g_utf16_to_utf8 (winhttp_file
->url
.lpszUrlPath
, -1, NULL
, NULL
, NULL
);
171 last_slash
= strrchr (basename
, '/');
172 /* If no slash, or only "/" fallback to full path part of URI */
173 if (last_slash
== NULL
|| last_slash
[1] == '\0')
176 retval
= g_strdup (last_slash
+ 1);
183 g_winhttp_file_get_path (GFile
*file
)
189 g_winhttp_file_get_uri (GFile
*file
)
191 GWinHttpFile
*winhttp_file
= G_WINHTTP_FILE (file
);
197 if (!G_WINHTTP_VFS_GET_CLASS (winhttp_file
->vfs
)->funcs
->pWinHttpCreateUrl (&winhttp_file
->url
, ICU_ESCAPE
, NULL
, &len
) &&
198 GetLastError () != ERROR_INSUFFICIENT_BUFFER
)
201 wuri
= g_new (wchar_t, ++len
);
203 if (!G_WINHTTP_VFS_GET_CLASS (winhttp_file
->vfs
)->funcs
->pWinHttpCreateUrl (&winhttp_file
->url
, ICU_ESCAPE
, wuri
, &len
))
209 retval
= g_utf16_to_utf8 (wuri
, -1, NULL
, NULL
, NULL
);
212 if (g_str_has_prefix (retval
, "http://:@"))
214 memmove (retval
+ 7, retval
+ 9, strlen (retval
) - 9);
215 retval
[strlen (retval
) - 2] = '\0';
217 else if (g_str_has_prefix (retval
, "https://:@"))
219 memmove (retval
+ 8, retval
+ 10, strlen (retval
) - 10);
220 retval
[strlen (retval
) - 2] = '\0';
227 g_winhttp_file_get_parse_name (GFile
*file
)
229 /* FIXME: More hair surely needed */
231 return g_winhttp_file_get_uri (file
);
235 g_winhttp_file_get_parent (GFile
*file
)
237 GWinHttpFile
*winhttp_file
;
242 winhttp_file
= G_WINHTTP_FILE (file
);
244 uri
= g_winhttp_file_get_uri (file
);
248 last_slash
= strrchr (uri
, '/');
249 if (last_slash
== NULL
|| *(last_slash
+1) == 0)
255 while (last_slash
> uri
&& *last_slash
== '/')
258 last_slash
[1] = '\0';
260 parent
= _g_winhttp_file_new (winhttp_file
->vfs
, uri
);
267 g_winhttp_file_dup (GFile
*file
)
269 GWinHttpFile
*winhttp_file
= G_WINHTTP_FILE (file
);
270 char *uri
= g_winhttp_file_get_uri (file
);
271 GFile
*retval
= _g_winhttp_file_new (winhttp_file
->vfs
, uri
);
279 g_winhttp_file_hash (GFile
*file
)
281 char *uri
= g_winhttp_file_get_uri (file
);
282 guint retval
= g_str_hash (uri
);
290 g_winhttp_file_equal (GFile
*file1
,
293 char *uri1
= g_winhttp_file_get_uri (file1
);
294 char *uri2
= g_winhttp_file_get_uri (file2
);
295 gboolean retval
= g_str_equal (uri1
, uri2
);
304 match_prefix (const char *path
,
309 prefix_len
= strlen (prefix
);
310 if (strncmp (path
, prefix
, prefix_len
) != 0)
313 if (prefix_len
> 0 && prefix
[prefix_len
-1] == '/')
316 return path
+ prefix_len
;
320 g_winhttp_file_prefix_matches (GFile
*parent
,
323 char *parent_uri
= g_winhttp_file_get_uri (parent
);
324 char *descendant_uri
= g_winhttp_file_get_uri (descendant
);
325 const char *remainder
;
328 remainder
= match_prefix (descendant_uri
, parent_uri
);
330 if (remainder
!= NULL
&& *remainder
== '/')
336 g_free (descendant_uri
);
342 g_winhttp_file_get_relative_path (GFile
*parent
,
345 char *parent_uri
= g_winhttp_file_get_uri (parent
);
346 char *descendant_uri
= g_winhttp_file_get_uri (descendant
);
347 const char *remainder
;
350 remainder
= match_prefix (descendant_uri
, parent_uri
);
352 if (remainder
!= NULL
&& *remainder
== '/')
353 retval
= g_strdup (remainder
+ 1);
358 g_free (descendant_uri
);
364 g_winhttp_file_resolve_relative_path (GFile
*file
,
365 const char *relative_path
)
367 GWinHttpFile
*winhttp_file
= G_WINHTTP_FILE (file
);
369 wchar_t *wnew_path
= g_utf8_to_utf16 (relative_path
, -1, NULL
, NULL
, NULL
);
371 if (wnew_path
== NULL
)
374 if (*wnew_path
!= '/')
377 int trailing_slash
= winhttp_file
->url
.lpszUrlPath
[winhttp_file
->url
.dwUrlPathLength
-1] == L
'/'? 1 : 0;
380 tmp
= g_new (wchar_t, wcslen (winhttp_file
->url
.lpszUrlPath
) + wcslen (wnew_path
) + 1);
381 wcscpy (tmp
, winhttp_file
->url
.lpszUrlPath
);
385 tmp
= g_new (wchar_t, wcslen (winhttp_file
->url
.lpszUrlPath
) + 1 + wcslen (wnew_path
) + 1);
386 wcscpy (tmp
, winhttp_file
->url
.lpszUrlPath
);
389 wcscat (tmp
, wnew_path
);
395 child
= g_object_new (G_TYPE_WINHTTP_FILE
, NULL
);
396 child
->vfs
= winhttp_file
->vfs
;
397 child
->url
= winhttp_file
->url
;
398 child
->url
.lpszScheme
= g_memdup (winhttp_file
->url
.lpszScheme
, (winhttp_file
->url
.dwSchemeLength
+1)*2);
399 child
->url
.lpszHostName
= g_memdup (winhttp_file
->url
.lpszHostName
, (winhttp_file
->url
.dwHostNameLength
+1)*2);
400 child
->url
.lpszUserName
= g_memdup (winhttp_file
->url
.lpszUserName
, (winhttp_file
->url
.dwUserNameLength
+1)*2);
401 child
->url
.lpszPassword
= g_memdup (winhttp_file
->url
.lpszPassword
, (winhttp_file
->url
.dwPasswordLength
+1)*2);
402 child
->url
.lpszUrlPath
= wnew_path
;
403 child
->url
.dwUrlPathLength
= wcslen (wnew_path
);
404 child
->url
.lpszExtraInfo
= NULL
;
405 child
->url
.dwExtraInfoLength
= 0;
407 return (GFile
*) child
;
411 g_winhttp_file_get_child_for_display_name (GFile
*file
,
412 const char *display_name
,
418 basename
= g_locale_from_utf8 (display_name
, -1, NULL
, NULL
, NULL
);
419 if (basename
== NULL
)
421 g_set_error (error
, G_IO_ERROR
, G_IO_ERROR_INVALID_FILENAME
,
422 _("Invalid filename %s"), display_name
);
426 new_file
= g_file_get_child (file
, basename
);
433 g_winhttp_file_set_display_name (GFile
*file
,
434 const char *display_name
,
435 GCancellable
*cancellable
,
438 g_set_error_literal (error
, G_IO_ERROR
, G_IO_ERROR_NOT_SUPPORTED
,
439 _("Operation not supported"));
445 mktime_utc (SYSTEMTIME
*t
)
449 static const gint days_before
[] =
451 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
454 if (t
->wMonth
< 1 || t
->wMonth
> 12)
457 retval
= (t
->wYear
- 1970) * 365;
458 retval
+= (t
->wYear
- 1968) / 4;
459 retval
+= days_before
[t
->wMonth
-1] + t
->wDay
- 1;
461 if (t
->wYear
% 4 == 0 && t
->wMonth
< 3)
464 retval
= ((((retval
* 24) + t
->wHour
) * 60) + t
->wMinute
) * 60 + t
->wSecond
;
470 g_winhttp_file_query_info (GFile
*file
,
471 const char *attributes
,
472 GFileQueryInfoFlags flags
,
473 GCancellable
*cancellable
,
476 GWinHttpFile
*winhttp_file
= G_WINHTTP_FILE (file
);
477 HINTERNET connection
, request
;
478 const wchar_t *accept_types
[] =
484 GFileAttributeMatcher
*matcher
;
486 wchar_t *content_length
;
487 wchar_t *content_type
;
488 SYSTEMTIME last_modified
;
489 DWORD last_modified_len
;
491 connection
= G_WINHTTP_VFS_GET_CLASS (winhttp_file
->vfs
)->funcs
->pWinHttpConnect
492 (G_WINHTTP_VFS (winhttp_file
->vfs
)->session
,
493 winhttp_file
->url
.lpszHostName
,
494 winhttp_file
->url
.nPort
,
497 if (connection
== NULL
)
499 _g_winhttp_set_error (error
, GetLastError (), "HTTP connection");
504 request
= G_WINHTTP_VFS_GET_CLASS (winhttp_file
->vfs
)->funcs
->pWinHttpOpenRequest
507 winhttp_file
->url
.lpszUrlPath
,
511 winhttp_file
->url
.nScheme
== INTERNET_SCHEME_HTTPS
? WINHTTP_FLAG_SECURE
: 0);
515 _g_winhttp_set_error (error
, GetLastError (), "HEAD request");
520 if (!G_WINHTTP_VFS_GET_CLASS (winhttp_file
->vfs
)->funcs
->pWinHttpSendRequest
527 _g_winhttp_set_error (error
, GetLastError (), "HEAD request");
532 if (!_g_winhttp_response (winhttp_file
->vfs
, request
, error
, "HEAD request"))
535 matcher
= g_file_attribute_matcher_new (attributes
);
536 info
= g_file_info_new ();
537 g_file_info_set_attribute_mask (info
, matcher
);
539 basename
= g_winhttp_file_get_basename (file
);
540 g_file_info_set_name (info
, basename
);
543 content_length
= NULL
;
544 if (_g_winhttp_query_header (winhttp_file
->vfs
,
547 WINHTTP_QUERY_CONTENT_LENGTH
,
554 if (swscanf (content_length
, L
"%I64d%n", &cl
, &n
) == 1 &&
555 n
== wcslen (content_length
))
556 g_file_info_set_size (info
, cl
);
558 g_free (content_length
);
565 if (_g_winhttp_query_header (winhttp_file
->vfs
,
568 WINHTTP_QUERY_CONTENT_TYPE
,
572 char *ct
= g_utf16_to_utf8 (content_type
, -1, NULL
, NULL
, NULL
);
576 char *p
= strchr (ct
, ';');
580 char *tmp
= g_strndup (ct
, p
- ct
);
582 g_file_info_set_content_type (info
, tmp
);
586 g_file_info_set_content_type (info
, ct
);
592 last_modified_len
= sizeof (last_modified
);
593 if (G_WINHTTP_VFS_GET_CLASS (winhttp_file
->vfs
)->funcs
->pWinHttpQueryHeaders
595 WINHTTP_QUERY_LAST_MODIFIED
| WINHTTP_QUERY_FLAG_SYSTEMTIME
,
600 last_modified_len
== sizeof (last_modified
) &&
601 /* Don't bother comparing to the exact Y2038 moment */
602 last_modified
.wYear
>= 1970 &&
603 last_modified
.wYear
< 2038)
607 tv
.tv_sec
= mktime_utc (&last_modified
);
608 tv
.tv_usec
= last_modified
.wMilliseconds
* 1000;
610 g_file_info_set_modification_time (info
, &tv
);
613 g_file_attribute_matcher_unref (matcher
);
618 static GFileInputStream
*
619 g_winhttp_file_read (GFile
*file
,
620 GCancellable
*cancellable
,
623 GWinHttpFile
*winhttp_file
= G_WINHTTP_FILE (file
);
624 HINTERNET connection
, request
;
625 const wchar_t *accept_types
[] =
631 connection
= G_WINHTTP_VFS_GET_CLASS (winhttp_file
->vfs
)->funcs
->pWinHttpConnect
632 (G_WINHTTP_VFS (winhttp_file
->vfs
)->session
,
633 winhttp_file
->url
.lpszHostName
,
634 winhttp_file
->url
.nPort
,
637 if (connection
== NULL
)
639 _g_winhttp_set_error (error
, GetLastError (), "HTTP connection");
644 request
= G_WINHTTP_VFS_GET_CLASS (winhttp_file
->vfs
)->funcs
->pWinHttpOpenRequest
647 winhttp_file
->url
.lpszUrlPath
,
651 winhttp_file
->url
.nScheme
== INTERNET_SCHEME_HTTPS
? WINHTTP_FLAG_SECURE
: 0);
655 _g_winhttp_set_error (error
, GetLastError (), "GET request");
660 return _g_winhttp_file_input_stream_new (winhttp_file
, connection
, request
);
663 static GFileOutputStream
*
664 g_winhttp_file_create (GFile
*file
,
665 GFileCreateFlags flags
,
666 GCancellable
*cancellable
,
669 GWinHttpFile
*winhttp_file
= G_WINHTTP_FILE (file
);
670 HINTERNET connection
;
672 connection
= G_WINHTTP_VFS_GET_CLASS (winhttp_file
->vfs
)->funcs
->pWinHttpConnect
673 (G_WINHTTP_VFS (winhttp_file
->vfs
)->session
,
674 winhttp_file
->url
.lpszHostName
,
675 winhttp_file
->url
.nPort
,
678 if (connection
== NULL
)
680 _g_winhttp_set_error (error
, GetLastError (), "HTTP connection");
685 return _g_winhttp_file_output_stream_new (winhttp_file
, connection
);
690 static GFileOutputStream
*
691 g_winhttp_file_replace (GFile
*file
,
693 gboolean make_backup
,
694 GFileCreateFlags flags
,
695 GCancellable
*cancellable
,
698 /* FIXME: Implement */
705 g_winhttp_file_delete (GFile
*file
,
706 GCancellable
*cancellable
,
709 /* FIXME: Implement */
715 g_winhttp_file_make_directory (GFile
*file
,
716 GCancellable
*cancellable
,
719 /* FIXME: Implement */
725 g_winhttp_file_copy (GFile
*source
,
727 GFileCopyFlags flags
,
728 GCancellable
*cancellable
,
729 GFileProgressCallback progress_callback
,
730 gpointer progress_callback_data
,
733 /* Fall back to default copy?? */
734 g_set_error_literal (error
, G_IO_ERROR
, G_IO_ERROR_NOT_SUPPORTED
,
735 "Copy not supported");
741 g_winhttp_file_move (GFile
*source
,
743 GFileCopyFlags flags
,
744 GCancellable
*cancellable
,
745 GFileProgressCallback progress_callback
,
746 gpointer progress_callback_data
,
749 /* FIXME: Implement */
757 g_winhttp_file_file_iface_init (GFileIface
*iface
)
759 iface
->dup
= g_winhttp_file_dup
;
760 iface
->hash
= g_winhttp_file_hash
;
761 iface
->equal
= g_winhttp_file_equal
;
762 iface
->is_native
= g_winhttp_file_is_native
;
763 iface
->has_uri_scheme
= g_winhttp_file_has_uri_scheme
;
764 iface
->get_uri_scheme
= g_winhttp_file_get_uri_scheme
;
765 iface
->get_basename
= g_winhttp_file_get_basename
;
766 iface
->get_path
= g_winhttp_file_get_path
;
767 iface
->get_uri
= g_winhttp_file_get_uri
;
768 iface
->get_parse_name
= g_winhttp_file_get_parse_name
;
769 iface
->get_parent
= g_winhttp_file_get_parent
;
770 iface
->prefix_matches
= g_winhttp_file_prefix_matches
;
771 iface
->get_relative_path
= g_winhttp_file_get_relative_path
;
772 iface
->resolve_relative_path
= g_winhttp_file_resolve_relative_path
;
773 iface
->get_child_for_display_name
= g_winhttp_file_get_child_for_display_name
;
774 iface
->set_display_name
= g_winhttp_file_set_display_name
;
775 iface
->query_info
= g_winhttp_file_query_info
;
776 iface
->read_fn
= g_winhttp_file_read
;
777 iface
->create
= g_winhttp_file_create
;
779 iface
->replace
= g_winhttp_file_replace
;
780 iface
->delete_file
= g_winhttp_file_delete
;
781 iface
->make_directory
= g_winhttp_file_make_directory
;
782 iface
->copy
= g_winhttp_file_copy
;
783 iface
->move
= g_winhttp_file_move
;