menu threaded test: run the mainloop after export
[glib.git] / gio / win32 / gwinhttpfile.c
blob84d5d7737df5db9e60fae3d43a8e8df5cd8f1364
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>
25 #include "config.h"
27 #include <stdio.h>
28 #include <string.h>
29 #include <wchar.h>
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"
39 #include "glibintl.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))
48 static void
49 g_winhttp_file_finalize (GObject *object)
51 GWinHttpFile *file;
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);
67 static void
68 g_winhttp_file_class_init (GWinHttpFileClass *klass)
70 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
72 gobject_class->finalize = g_winhttp_file_finalize;
75 static void
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.
87 GFile *
88 _g_winhttp_file_new (GWinHttpVfs *vfs,
89 const char *uri)
91 wchar_t *wuri;
92 GWinHttpFile *file;
94 wuri = g_utf8_to_utf16 (uri, -1, NULL, NULL, NULL);
96 if (wuri == NULL)
97 return 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))
113 g_free (wuri);
114 return NULL;
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);
132 g_free (wuri);
133 return NULL;
136 g_free (wuri);
137 return G_FILE (file);
140 static gboolean
141 g_winhttp_file_is_native (GFile *file)
143 return FALSE;
146 static gboolean
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);
154 static char *
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);
162 static char *
163 g_winhttp_file_get_basename (GFile *file)
165 GWinHttpFile *winhttp_file = G_WINHTTP_FILE (file);
166 char *basename;
167 char *last_slash;
168 char *retval;
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')
174 return basename;
176 retval = g_strdup (last_slash + 1);
177 g_free (basename);
179 return retval;
182 static char *
183 g_winhttp_file_get_path (GFile *file)
185 return NULL;
188 static char *
189 g_winhttp_file_get_uri (GFile *file)
191 GWinHttpFile *winhttp_file = G_WINHTTP_FILE (file);
192 DWORD len;
193 wchar_t *wuri;
194 char *retval;
196 len = 0;
197 if (!G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->funcs->pWinHttpCreateUrl (&winhttp_file->url, ICU_ESCAPE, NULL, &len) &&
198 GetLastError () != ERROR_INSUFFICIENT_BUFFER)
199 return NULL;
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))
205 g_free (wuri);
206 return NULL;
209 retval = g_utf16_to_utf8 (wuri, -1, NULL, NULL, NULL);
210 g_free (wuri);
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';
223 return retval;
226 static char *
227 g_winhttp_file_get_parse_name (GFile *file)
229 /* FIXME: More hair surely needed */
231 return g_winhttp_file_get_uri (file);
234 static GFile *
235 g_winhttp_file_get_parent (GFile *file)
237 GWinHttpFile *winhttp_file;
238 char *uri;
239 char *last_slash;
240 GFile *parent;
242 winhttp_file = G_WINHTTP_FILE (file);
244 uri = g_winhttp_file_get_uri (file);
245 if (uri == NULL)
246 return NULL;
248 last_slash = strrchr (uri, '/');
249 if (last_slash == NULL || *(last_slash+1) == 0)
251 g_free (uri);
252 return NULL;
255 while (last_slash > uri && *last_slash == '/')
256 last_slash--;
258 last_slash[1] = '\0';
260 parent = _g_winhttp_file_new (winhttp_file->vfs, uri);
261 g_free (uri);
263 return parent;
266 static GFile *
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);
273 g_free (uri);
275 return retval;
278 static guint
279 g_winhttp_file_hash (GFile *file)
281 char *uri = g_winhttp_file_get_uri (file);
282 guint retval = g_str_hash (uri);
284 g_free (uri);
286 return retval;
289 static gboolean
290 g_winhttp_file_equal (GFile *file1,
291 GFile *file2)
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);
297 g_free (uri1);
298 g_free (uri2);
300 return retval;
303 static const char *
304 match_prefix (const char *path,
305 const char *prefix)
307 int prefix_len;
309 prefix_len = strlen (prefix);
310 if (strncmp (path, prefix, prefix_len) != 0)
311 return NULL;
313 if (prefix_len > 0 && prefix[prefix_len-1] == '/')
314 prefix_len--;
316 return path + prefix_len;
319 static gboolean
320 g_winhttp_file_prefix_matches (GFile *parent,
321 GFile *descendant)
323 char *parent_uri = g_winhttp_file_get_uri (parent);
324 char *descendant_uri = g_winhttp_file_get_uri (descendant);
325 const char *remainder;
326 gboolean retval;
328 remainder = match_prefix (descendant_uri, parent_uri);
330 if (remainder != NULL && *remainder == '/')
331 retval = TRUE;
332 else
333 retval = FALSE;
335 g_free (parent_uri);
336 g_free (descendant_uri);
338 return retval;
341 static char *
342 g_winhttp_file_get_relative_path (GFile *parent,
343 GFile *descendant)
345 char *parent_uri = g_winhttp_file_get_uri (parent);
346 char *descendant_uri = g_winhttp_file_get_uri (descendant);
347 const char *remainder;
348 char *retval;
350 remainder = match_prefix (descendant_uri, parent_uri);
352 if (remainder != NULL && *remainder == '/')
353 retval = g_strdup (remainder + 1);
354 else
355 retval = NULL;
357 g_free (parent_uri);
358 g_free (descendant_uri);
360 return retval;
363 static GFile *
364 g_winhttp_file_resolve_relative_path (GFile *file,
365 const char *relative_path)
367 GWinHttpFile *winhttp_file = G_WINHTTP_FILE (file);
368 GWinHttpFile *child;
369 wchar_t *wnew_path = g_utf8_to_utf16 (relative_path, -1, NULL, NULL, NULL);
371 if (wnew_path == NULL)
372 return NULL;
374 if (*wnew_path != '/')
376 wchar_t *tmp = NULL;
377 int trailing_slash = winhttp_file->url.lpszUrlPath[winhttp_file->url.dwUrlPathLength-1] == L'/'? 1 : 0;
378 if (trailing_slash)
380 tmp = g_new (wchar_t, wcslen (winhttp_file->url.lpszUrlPath) + wcslen (wnew_path) + 1);
381 wcscpy (tmp, winhttp_file->url.lpszUrlPath);
383 else
385 tmp = g_new (wchar_t, wcslen (winhttp_file->url.lpszUrlPath) + 1 + wcslen (wnew_path) + 1);
386 wcscpy (tmp, winhttp_file->url.lpszUrlPath);
387 wcscat (tmp, L"/");
389 wcscat (tmp, wnew_path);
391 g_free (wnew_path);
392 wnew_path = tmp;
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;
410 static GFile *
411 g_winhttp_file_get_child_for_display_name (GFile *file,
412 const char *display_name,
413 GError **error)
415 GFile *new_file;
416 char *basename;
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);
423 return NULL;
426 new_file = g_file_get_child (file, basename);
427 g_free (basename);
429 return new_file;
432 static GFile *
433 g_winhttp_file_set_display_name (GFile *file,
434 const char *display_name,
435 GCancellable *cancellable,
436 GError **error)
438 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
439 _("Operation not supported"));
441 return NULL;
444 static time_t
445 mktime_utc (SYSTEMTIME *t)
447 time_t retval;
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)
455 return (time_t) -1;
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)
462 retval -= 1;
464 retval = ((((retval * 24) + t->wHour) * 60) + t->wMinute) * 60 + t->wSecond;
466 return retval;
469 static GFileInfo *
470 g_winhttp_file_query_info (GFile *file,
471 const char *attributes,
472 GFileQueryInfoFlags flags,
473 GCancellable *cancellable,
474 GError **error)
476 GWinHttpFile *winhttp_file = G_WINHTTP_FILE (file);
477 HINTERNET connection, request;
478 const wchar_t *accept_types[] =
480 L"*/*",
481 NULL,
483 GFileInfo *info;
484 GFileAttributeMatcher *matcher;
485 char *basename;
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");
501 return NULL;
504 request = G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->funcs->pWinHttpOpenRequest
505 (connection,
506 L"HEAD",
507 winhttp_file->url.lpszUrlPath,
508 NULL,
509 WINHTTP_NO_REFERER,
510 accept_types,
511 winhttp_file->url.nScheme == INTERNET_SCHEME_HTTPS ? WINHTTP_FLAG_SECURE : 0);
513 if (request == NULL)
515 _g_winhttp_set_error (error, GetLastError (), "HEAD request");
517 return NULL;
520 if (!G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->funcs->pWinHttpSendRequest
521 (request,
522 NULL, 0,
523 NULL, 0,
527 _g_winhttp_set_error (error, GetLastError (), "HEAD request");
529 return NULL;
532 if (!_g_winhttp_response (winhttp_file->vfs, request, error, "HEAD request"))
533 return NULL;
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);
541 g_free (basename);
543 content_length = NULL;
544 if (_g_winhttp_query_header (winhttp_file->vfs,
545 request,
546 "HEAD request",
547 WINHTTP_QUERY_CONTENT_LENGTH,
548 &content_length,
549 NULL))
551 gint64 cl;
552 int n;
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);
561 if (matcher == NULL)
562 return info;
564 content_type = NULL;
565 if (_g_winhttp_query_header (winhttp_file->vfs,
566 request,
567 "HEAD request",
568 WINHTTP_QUERY_CONTENT_TYPE,
569 &content_type,
570 NULL))
572 char *ct = g_utf16_to_utf8 (content_type, -1, NULL, NULL, NULL);
574 if (ct != NULL)
576 char *p = strchr (ct, ';');
578 if (p != NULL)
580 char *tmp = g_strndup (ct, p - ct);
582 g_file_info_set_content_type (info, tmp);
583 g_free (tmp);
585 else
586 g_file_info_set_content_type (info, ct);
589 g_free (ct);
592 last_modified_len = sizeof (last_modified);
593 if (G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->funcs->pWinHttpQueryHeaders
594 (request,
595 WINHTTP_QUERY_LAST_MODIFIED | WINHTTP_QUERY_FLAG_SYSTEMTIME,
596 NULL,
597 &last_modified,
598 &last_modified_len,
599 NULL) &&
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)
605 GTimeVal tv;
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);
615 return info;
618 static GFileInputStream *
619 g_winhttp_file_read (GFile *file,
620 GCancellable *cancellable,
621 GError **error)
623 GWinHttpFile *winhttp_file = G_WINHTTP_FILE (file);
624 HINTERNET connection, request;
625 const wchar_t *accept_types[] =
627 L"*/*",
628 NULL,
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");
641 return NULL;
644 request = G_WINHTTP_VFS_GET_CLASS (winhttp_file->vfs)->funcs->pWinHttpOpenRequest
645 (connection,
646 L"GET",
647 winhttp_file->url.lpszUrlPath,
648 NULL,
649 WINHTTP_NO_REFERER,
650 accept_types,
651 winhttp_file->url.nScheme == INTERNET_SCHEME_HTTPS ? WINHTTP_FLAG_SECURE : 0);
653 if (request == NULL)
655 _g_winhttp_set_error (error, GetLastError (), "GET request");
657 return NULL;
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,
667 GError **error)
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");
682 return NULL;
685 return _g_winhttp_file_output_stream_new (winhttp_file, connection);
688 #if 0
690 static GFileOutputStream *
691 g_winhttp_file_replace (GFile *file,
692 const char *etag,
693 gboolean make_backup,
694 GFileCreateFlags flags,
695 GCancellable *cancellable,
696 GError **error)
698 /* FIXME: Implement */
700 return NULL;
704 static gboolean
705 g_winhttp_file_delete (GFile *file,
706 GCancellable *cancellable,
707 GError **error)
709 /* FIXME: Implement */
711 return FALSE;
714 static gboolean
715 g_winhttp_file_make_directory (GFile *file,
716 GCancellable *cancellable,
717 GError **error)
719 /* FIXME: Implement */
721 return FALSE;
724 static gboolean
725 g_winhttp_file_copy (GFile *source,
726 GFile *destination,
727 GFileCopyFlags flags,
728 GCancellable *cancellable,
729 GFileProgressCallback progress_callback,
730 gpointer progress_callback_data,
731 GError **error)
733 /* Fall back to default copy?? */
734 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
735 "Copy not supported");
737 return FALSE;
740 static gboolean
741 g_winhttp_file_move (GFile *source,
742 GFile *destination,
743 GFileCopyFlags flags,
744 GCancellable *cancellable,
745 GFileProgressCallback progress_callback,
746 gpointer progress_callback_data,
747 GError **error)
749 /* FIXME: Implement */
751 return FALSE;
754 #endif
756 static void
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;
778 #if 0
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;
784 #endif