mirror of https://gitee.com/openkylin/gvfs.git
753 lines
20 KiB
C
753 lines
20 KiB
C
|
/* GIO - GLib Input, Output and Streaming Library
|
||
|
*
|
||
|
* Copyright (C) 2008 Red Hat, Inc.
|
||
|
*
|
||
|
* This library is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU Lesser General Public
|
||
|
* License as published by the Free Software Foundation; either
|
||
|
* version 2 of the License, or (at your option) any later version.
|
||
|
*
|
||
|
* This library is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
* Lesser General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU Lesser General
|
||
|
* Public License along with this library; if not, write to the
|
||
|
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||
|
* Boston, MA 02110-1301, USA.
|
||
|
*
|
||
|
* Author: Christian Kellner <gicmo@gnome.org>
|
||
|
*/
|
||
|
|
||
|
#include <config.h>
|
||
|
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <errno.h>
|
||
|
#include <unistd.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include <glib/gstdio.h>
|
||
|
#include <glib/gi18n.h>
|
||
|
#include <gio/gio.h>
|
||
|
|
||
|
#include <libsoup/soup.h>
|
||
|
|
||
|
#include "gvfsbackendhttp.h"
|
||
|
#include "gvfshttpinputstream.h"
|
||
|
#include "gvfsjobopenforread.h"
|
||
|
#include "gvfsjobread.h"
|
||
|
#include "gvfsjobseekread.h"
|
||
|
#include "gvfsjobopenforwrite.h"
|
||
|
#include "gvfsjobwrite.h"
|
||
|
#include "gvfsjobseekwrite.h"
|
||
|
#include "gvfsjobsetdisplayname.h"
|
||
|
#include "gvfsjobqueryinfo.h"
|
||
|
#include "gvfsjobqueryfsinfo.h"
|
||
|
#include "gvfsjobqueryattributes.h"
|
||
|
#include "gvfsjobenumerate.h"
|
||
|
#include "gvfsdaemonprotocol.h"
|
||
|
#include "gvfsdaemonutils.h"
|
||
|
|
||
|
static SoupSession *the_session;
|
||
|
|
||
|
G_DEFINE_TYPE (GVfsBackendHttp, g_vfs_backend_http, G_VFS_TYPE_BACKEND)
|
||
|
|
||
|
static void
|
||
|
g_vfs_backend_http_finalize (GObject *object)
|
||
|
{
|
||
|
GVfsBackendHttp *backend;
|
||
|
|
||
|
backend = G_VFS_BACKEND_HTTP (object);
|
||
|
|
||
|
if (backend->mount_base)
|
||
|
soup_uri_free (backend->mount_base);
|
||
|
|
||
|
g_object_unref (backend->session);
|
||
|
|
||
|
|
||
|
if (G_OBJECT_CLASS (g_vfs_backend_http_parent_class)->finalize)
|
||
|
(*G_OBJECT_CLASS (g_vfs_backend_http_parent_class)->finalize) (object);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
g_vfs_backend_http_init (GVfsBackendHttp *backend)
|
||
|
{
|
||
|
g_vfs_backend_set_user_visible (G_VFS_BACKEND (backend), FALSE);
|
||
|
|
||
|
backend->session = g_object_ref (the_session);
|
||
|
}
|
||
|
|
||
|
/* ************************************************************************* */
|
||
|
/* public utility functions */
|
||
|
|
||
|
SoupURI *
|
||
|
http_backend_get_mount_base (GVfsBackend *backend)
|
||
|
{
|
||
|
return G_VFS_BACKEND_HTTP (backend)->mount_base;
|
||
|
}
|
||
|
|
||
|
char *
|
||
|
http_path_get_basename (const char *path)
|
||
|
{
|
||
|
const char *parent;
|
||
|
char *basename;
|
||
|
size_t len;
|
||
|
|
||
|
if (path == NULL || *path == '\0')
|
||
|
return NULL;
|
||
|
|
||
|
/* remove any leading slashes */
|
||
|
while (*path != '\0' && *path == '/')
|
||
|
path++;
|
||
|
|
||
|
len = strlen (path);
|
||
|
if (len == 0)
|
||
|
return g_strdup ("/");
|
||
|
|
||
|
/* remove any trailing slashes */
|
||
|
while (len)
|
||
|
{
|
||
|
char c = path[len - 1];
|
||
|
if (c != '/')
|
||
|
break;
|
||
|
|
||
|
len--;
|
||
|
}
|
||
|
|
||
|
parent = g_strrstr_len (path, len, "/");
|
||
|
|
||
|
if (parent)
|
||
|
{
|
||
|
parent++; /* skip the found / char */
|
||
|
basename = g_strndup (parent, (len - (parent - path)));
|
||
|
}
|
||
|
else
|
||
|
basename = g_strndup (path, len);
|
||
|
|
||
|
return basename;
|
||
|
}
|
||
|
|
||
|
char *
|
||
|
http_uri_get_basename (const char *uri_str)
|
||
|
{
|
||
|
char *decoded;
|
||
|
char *basename;
|
||
|
|
||
|
basename = http_path_get_basename (uri_str);
|
||
|
|
||
|
decoded = soup_uri_decode (basename);
|
||
|
g_free (basename);
|
||
|
|
||
|
return decoded;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
http_error_code_from_status (guint status)
|
||
|
{
|
||
|
switch (status) {
|
||
|
|
||
|
case SOUP_STATUS_CANT_RESOLVE:
|
||
|
case SOUP_STATUS_CANT_RESOLVE_PROXY:
|
||
|
return G_IO_ERROR_HOST_NOT_FOUND;
|
||
|
|
||
|
case SOUP_STATUS_CANCELLED:
|
||
|
return G_IO_ERROR_CANCELLED;
|
||
|
|
||
|
case SOUP_STATUS_UNAUTHORIZED:
|
||
|
case SOUP_STATUS_PAYMENT_REQUIRED:
|
||
|
case SOUP_STATUS_FORBIDDEN:
|
||
|
return G_IO_ERROR_PERMISSION_DENIED;
|
||
|
|
||
|
case SOUP_STATUS_NOT_FOUND:
|
||
|
case SOUP_STATUS_GONE:
|
||
|
return G_IO_ERROR_NOT_FOUND;
|
||
|
|
||
|
case SOUP_STATUS_GATEWAY_TIMEOUT:
|
||
|
case SOUP_STATUS_REQUEST_TIMEOUT:
|
||
|
return G_IO_ERROR_TIMED_OUT;
|
||
|
|
||
|
case SOUP_STATUS_NOT_IMPLEMENTED:
|
||
|
return G_IO_ERROR_NOT_SUPPORTED;
|
||
|
|
||
|
case SOUP_STATUS_INSUFFICIENT_STORAGE:
|
||
|
return G_IO_ERROR_NO_SPACE;
|
||
|
|
||
|
}
|
||
|
|
||
|
return G_IO_ERROR_FAILED;
|
||
|
}
|
||
|
|
||
|
|
||
|
void
|
||
|
http_job_failed (GVfsJob *job, SoupMessage *msg)
|
||
|
{
|
||
|
switch (msg->status_code) {
|
||
|
|
||
|
case SOUP_STATUS_NOT_FOUND:
|
||
|
g_vfs_job_failed_literal (job, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
||
|
msg->reason_phrase);
|
||
|
break;
|
||
|
|
||
|
case SOUP_STATUS_UNAUTHORIZED:
|
||
|
case SOUP_STATUS_PAYMENT_REQUIRED:
|
||
|
case SOUP_STATUS_FORBIDDEN:
|
||
|
g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
|
||
|
_("HTTP Client Error: %s"), msg->reason_phrase);
|
||
|
break;
|
||
|
default:
|
||
|
g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||
|
_("HTTP Error: %s"), msg->reason_phrase);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
guint
|
||
|
http_backend_send_message (GVfsBackend *backend,
|
||
|
SoupMessage *msg)
|
||
|
{
|
||
|
GVfsBackendHttp *op_backend = G_VFS_BACKEND_HTTP (backend);
|
||
|
|
||
|
return soup_session_send_message (op_backend->session, msg);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
http_backend_queue_message (GVfsBackend *backend,
|
||
|
SoupMessage *msg,
|
||
|
SoupSessionCallback callback,
|
||
|
gpointer user_data)
|
||
|
{
|
||
|
GVfsBackendHttp *op_backend = G_VFS_BACKEND_HTTP (backend);
|
||
|
|
||
|
soup_session_queue_message (op_backend->session, msg,
|
||
|
callback, user_data);
|
||
|
}
|
||
|
/* ************************************************************************* */
|
||
|
/* virtual functions overrides */
|
||
|
|
||
|
static gboolean
|
||
|
try_mount (GVfsBackend *backend,
|
||
|
GVfsJobMount *job,
|
||
|
GMountSpec *mount_spec,
|
||
|
GMountSource *mount_source,
|
||
|
gboolean is_automount)
|
||
|
{
|
||
|
GVfsBackendHttp *op_backend;
|
||
|
const char *uri_str;
|
||
|
char *path;
|
||
|
SoupURI *uri;
|
||
|
GMountSpec *real_mount_spec;
|
||
|
|
||
|
op_backend = G_VFS_BACKEND_HTTP (backend);
|
||
|
|
||
|
uri = NULL;
|
||
|
uri_str = g_mount_spec_get (mount_spec, "uri");
|
||
|
|
||
|
if (uri_str)
|
||
|
uri = soup_uri_new (uri_str);
|
||
|
|
||
|
g_debug ("+ try_mount: %s\n", uri_str ? uri_str : "(null)");
|
||
|
|
||
|
if (uri == NULL)
|
||
|
{
|
||
|
g_vfs_job_failed (G_VFS_JOB (job),
|
||
|
G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
|
||
|
_("Invalid mount spec"));
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
real_mount_spec = g_mount_spec_new ("http");
|
||
|
g_mount_spec_set (real_mount_spec, "uri", uri_str);
|
||
|
|
||
|
if (uri->path != NULL)
|
||
|
{
|
||
|
path = g_uri_unescape_string (uri->path, "/");
|
||
|
g_free (real_mount_spec->mount_prefix);
|
||
|
real_mount_spec->mount_prefix = g_mount_spec_canonicalize_path (path);
|
||
|
g_free (path);
|
||
|
}
|
||
|
|
||
|
g_vfs_backend_set_mount_spec (backend, real_mount_spec);
|
||
|
|
||
|
op_backend->mount_base = uri;
|
||
|
|
||
|
g_vfs_job_succeeded (G_VFS_JOB (job));
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/* *** open_read () *** */
|
||
|
static void
|
||
|
open_for_read_ready (GObject *source_object,
|
||
|
GAsyncResult *result,
|
||
|
gpointer user_data)
|
||
|
{
|
||
|
GInputStream *stream;
|
||
|
GVfsJob *job;
|
||
|
SoupMessage *msg;
|
||
|
gboolean res;
|
||
|
gboolean can_seek;
|
||
|
GError *error;
|
||
|
|
||
|
stream = G_INPUT_STREAM (source_object);
|
||
|
error = NULL;
|
||
|
job = G_VFS_JOB (user_data);
|
||
|
|
||
|
res = g_vfs_http_input_stream_send_finish (stream,
|
||
|
result,
|
||
|
&error);
|
||
|
if (res == FALSE)
|
||
|
{
|
||
|
g_vfs_job_failed_literal (G_VFS_JOB (job),
|
||
|
error->domain,
|
||
|
error->code,
|
||
|
error->message);
|
||
|
|
||
|
g_error_free (error);
|
||
|
g_object_unref (stream);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
msg = g_vfs_http_input_stream_get_message (stream);
|
||
|
if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
|
||
|
{
|
||
|
http_job_failed (G_VFS_JOB (job), msg);
|
||
|
g_object_unref (msg);
|
||
|
g_object_unref (stream);
|
||
|
return;
|
||
|
}
|
||
|
g_object_unref (msg);
|
||
|
|
||
|
can_seek = G_IS_SEEKABLE (stream) && g_seekable_can_seek (G_SEEKABLE (stream));
|
||
|
|
||
|
g_vfs_job_open_for_read_set_can_seek (G_VFS_JOB_OPEN_FOR_READ (job), can_seek);
|
||
|
g_vfs_job_open_for_read_set_handle (G_VFS_JOB_OPEN_FOR_READ (job), stream);
|
||
|
g_vfs_job_succeeded (job);
|
||
|
}
|
||
|
|
||
|
static gboolean
|
||
|
try_open_for_read (GVfsBackend *backend,
|
||
|
GVfsJobOpenForRead *job,
|
||
|
const char *filename)
|
||
|
{
|
||
|
SoupURI *uri;
|
||
|
|
||
|
uri = http_backend_get_mount_base (backend);
|
||
|
http_backend_open_for_read (backend, G_VFS_JOB (job), uri);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
http_backend_open_for_read (GVfsBackend *backend,
|
||
|
GVfsJob *job,
|
||
|
SoupURI *uri)
|
||
|
{
|
||
|
GVfsBackendHttp *op_backend;
|
||
|
GInputStream *stream;
|
||
|
|
||
|
op_backend = G_VFS_BACKEND_HTTP (backend);
|
||
|
|
||
|
stream = g_vfs_http_input_stream_new (op_backend->session, uri);
|
||
|
|
||
|
g_vfs_http_input_stream_send_async (stream,
|
||
|
G_PRIORITY_DEFAULT,
|
||
|
job->cancellable,
|
||
|
open_for_read_ready,
|
||
|
job);
|
||
|
}
|
||
|
|
||
|
/* *** read () *** */
|
||
|
static void
|
||
|
read_ready (GObject *source_object,
|
||
|
GAsyncResult *result,
|
||
|
gpointer user_data)
|
||
|
{
|
||
|
GInputStream *stream;
|
||
|
GVfsJob *job;
|
||
|
GError *error;
|
||
|
gssize nread;
|
||
|
|
||
|
stream = G_INPUT_STREAM (source_object);
|
||
|
error = NULL;
|
||
|
job = G_VFS_JOB (user_data);
|
||
|
|
||
|
nread = g_input_stream_read_finish (stream, result, &error);
|
||
|
|
||
|
if (nread < 0)
|
||
|
{
|
||
|
g_vfs_job_failed_literal (G_VFS_JOB (job),
|
||
|
error->domain,
|
||
|
error->code,
|
||
|
error->message);
|
||
|
|
||
|
g_error_free (error);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
g_vfs_job_read_set_size (G_VFS_JOB_READ (job), nread);
|
||
|
g_vfs_job_succeeded (job);
|
||
|
|
||
|
}
|
||
|
|
||
|
static gboolean
|
||
|
try_read (GVfsBackend *backend,
|
||
|
GVfsJobRead *job,
|
||
|
GVfsBackendHandle handle,
|
||
|
char *buffer,
|
||
|
gsize bytes_requested)
|
||
|
{
|
||
|
GInputStream *stream;
|
||
|
|
||
|
stream = G_INPUT_STREAM (handle);
|
||
|
|
||
|
g_input_stream_read_async (stream,
|
||
|
buffer,
|
||
|
bytes_requested,
|
||
|
G_PRIORITY_DEFAULT,
|
||
|
G_VFS_JOB (job)->cancellable,
|
||
|
read_ready,
|
||
|
job);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
static gboolean
|
||
|
try_seek_on_read (GVfsBackend *backend,
|
||
|
GVfsJobSeekRead *job,
|
||
|
GVfsBackendHandle handle,
|
||
|
goffset offset,
|
||
|
GSeekType type)
|
||
|
{
|
||
|
GInputStream *stream;
|
||
|
GError *error = NULL;
|
||
|
|
||
|
stream = G_INPUT_STREAM (handle);
|
||
|
|
||
|
if (!g_seekable_seek (G_SEEKABLE (stream), offset, type,
|
||
|
G_VFS_JOB (job)->cancellable, &error))
|
||
|
{
|
||
|
g_vfs_job_failed_literal (G_VFS_JOB (job),
|
||
|
error->domain,
|
||
|
error->code,
|
||
|
error->message);
|
||
|
g_error_free (error);
|
||
|
return FALSE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
g_vfs_job_seek_read_set_offset (job, g_seekable_tell (G_SEEKABLE (stream)));
|
||
|
g_vfs_job_succeeded (G_VFS_JOB (job));
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/* *** read_close () *** */
|
||
|
static void
|
||
|
close_read_ready (GObject *source_object,
|
||
|
GAsyncResult *result,
|
||
|
gpointer user_data)
|
||
|
{
|
||
|
GInputStream *stream;
|
||
|
GVfsJob *job;
|
||
|
GError *error = NULL;
|
||
|
gboolean res;
|
||
|
|
||
|
job = G_VFS_JOB (user_data);
|
||
|
stream = G_INPUT_STREAM (source_object);
|
||
|
res = g_input_stream_close_finish (stream,
|
||
|
result,
|
||
|
&error);
|
||
|
if (res == FALSE)
|
||
|
{
|
||
|
g_vfs_job_failed_literal (G_VFS_JOB (job),
|
||
|
error->domain,
|
||
|
error->code,
|
||
|
error->message);
|
||
|
|
||
|
g_error_free (error);
|
||
|
}
|
||
|
else
|
||
|
g_vfs_job_succeeded (job);
|
||
|
|
||
|
g_object_unref (stream);
|
||
|
}
|
||
|
|
||
|
static gboolean
|
||
|
try_close_read (GVfsBackend *backend,
|
||
|
GVfsJobCloseRead *job,
|
||
|
GVfsBackendHandle handle)
|
||
|
{
|
||
|
GInputStream *stream;
|
||
|
|
||
|
stream = G_INPUT_STREAM (handle);
|
||
|
|
||
|
g_input_stream_close_async (stream,
|
||
|
G_PRIORITY_DEFAULT,
|
||
|
G_VFS_JOB (job)->cancellable,
|
||
|
close_read_ready,
|
||
|
job);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* *** query_info () *** */
|
||
|
|
||
|
static void
|
||
|
file_info_from_message (SoupMessage *msg,
|
||
|
GFileInfo *info,
|
||
|
GFileAttributeMatcher *matcher)
|
||
|
{
|
||
|
const char *text;
|
||
|
GHashTable *params;
|
||
|
char *basename;
|
||
|
char *ed_name;
|
||
|
|
||
|
basename = ed_name = NULL;
|
||
|
|
||
|
/* prefer the filename from the Content-Disposition (rfc2183) header
|
||
|
if one if present. See bug 551298. */
|
||
|
if (soup_message_headers_get_content_disposition (msg->response_headers,
|
||
|
NULL, ¶ms))
|
||
|
{
|
||
|
const char *name = g_hash_table_lookup (params, "filename");
|
||
|
|
||
|
if (name)
|
||
|
basename = g_strdup (name);
|
||
|
|
||
|
g_hash_table_destroy (params);
|
||
|
}
|
||
|
|
||
|
if (basename == NULL)
|
||
|
{
|
||
|
const SoupURI *uri;
|
||
|
|
||
|
uri = soup_message_get_uri (msg);
|
||
|
basename = http_uri_get_basename (uri->path);
|
||
|
}
|
||
|
|
||
|
g_debug ("basename:%s\n", basename);
|
||
|
|
||
|
/* read http/1.1 rfc, until then we copy the local files
|
||
|
* behaviour */
|
||
|
if (basename != NULL &&
|
||
|
(g_file_attribute_matcher_matches (matcher,
|
||
|
G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME) ||
|
||
|
g_file_attribute_matcher_matches (matcher,
|
||
|
G_FILE_ATTRIBUTE_STANDARD_EDIT_NAME)))
|
||
|
ed_name = gvfs_file_info_populate_names_as_local (info, basename);
|
||
|
|
||
|
g_free (basename);
|
||
|
g_free (ed_name);
|
||
|
|
||
|
if (soup_message_headers_get_encoding (msg->response_headers) == SOUP_ENCODING_CONTENT_LENGTH)
|
||
|
{
|
||
|
goffset start, end, length;
|
||
|
gboolean ret;
|
||
|
|
||
|
ret = soup_message_headers_get_content_range (msg->response_headers,
|
||
|
&start, &end, &length);
|
||
|
if (ret && length != -1)
|
||
|
{
|
||
|
g_file_info_set_size (info, length);
|
||
|
}
|
||
|
else if (!ret)
|
||
|
{
|
||
|
length = soup_message_headers_get_content_length (msg->response_headers);
|
||
|
g_file_info_set_size (info, length);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
g_file_info_set_file_type (info, G_FILE_TYPE_REGULAR);
|
||
|
|
||
|
text = soup_message_headers_get_content_type (msg->response_headers, NULL);
|
||
|
if (text)
|
||
|
{
|
||
|
GIcon *icon;
|
||
|
|
||
|
g_file_info_set_content_type (info, text);
|
||
|
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE, text);
|
||
|
|
||
|
icon = g_content_type_get_icon (text);
|
||
|
g_file_info_set_icon (info, icon);
|
||
|
g_object_unref (icon);
|
||
|
|
||
|
icon = g_content_type_get_symbolic_icon (text);
|
||
|
g_file_info_set_symbolic_icon (info, icon);
|
||
|
g_object_unref (icon);
|
||
|
}
|
||
|
|
||
|
|
||
|
text = soup_message_headers_get_one (msg->response_headers,
|
||
|
"Last-Modified");
|
||
|
if (text)
|
||
|
{
|
||
|
SoupDate *sd;
|
||
|
|
||
|
sd = soup_date_new_from_string(text);
|
||
|
if (sd)
|
||
|
{
|
||
|
g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED, soup_date_to_time_t (sd));
|
||
|
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC, 0);
|
||
|
soup_date_free (sd);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
text = soup_message_headers_get_one (msg->response_headers,
|
||
|
"ETag");
|
||
|
if (text)
|
||
|
{
|
||
|
g_file_info_set_attribute_string (info,
|
||
|
G_FILE_ATTRIBUTE_ETAG_VALUE,
|
||
|
text);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
query_info_ready (SoupSession *session,
|
||
|
SoupMessage *msg,
|
||
|
gpointer user_data)
|
||
|
{
|
||
|
GFileAttributeMatcher *matcher;
|
||
|
GVfsJobQueryInfo *job;
|
||
|
GFileInfo *info;
|
||
|
|
||
|
job = G_VFS_JOB_QUERY_INFO (user_data);
|
||
|
info = job->file_info;
|
||
|
matcher = job->attribute_matcher;
|
||
|
|
||
|
if (! SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
|
||
|
{
|
||
|
http_job_failed (G_VFS_JOB (job), msg);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
file_info_from_message (msg, info, matcher);
|
||
|
|
||
|
g_vfs_job_succeeded (G_VFS_JOB (job));
|
||
|
}
|
||
|
|
||
|
|
||
|
static gboolean
|
||
|
try_query_info (GVfsBackend *backend,
|
||
|
GVfsJobQueryInfo *job,
|
||
|
const char *filename,
|
||
|
GFileQueryInfoFlags flags,
|
||
|
GFileInfo *info,
|
||
|
GFileAttributeMatcher *attribute_matcher)
|
||
|
{
|
||
|
SoupMessage *msg;
|
||
|
SoupURI *uri;
|
||
|
|
||
|
if (g_file_attribute_matcher_matches_only (attribute_matcher,
|
||
|
G_FILE_ATTRIBUTE_THUMBNAIL_PATH))
|
||
|
{
|
||
|
g_vfs_job_succeeded (G_VFS_JOB (job));
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
uri = http_backend_get_mount_base (backend);
|
||
|
msg = soup_message_new_from_uri (SOUP_METHOD_HEAD, uri);
|
||
|
|
||
|
http_backend_queue_message (backend, msg, query_info_ready, job);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
static gboolean
|
||
|
try_query_info_on_read (GVfsBackend *backend,
|
||
|
GVfsJobQueryInfoRead *job,
|
||
|
GVfsBackendHandle handle,
|
||
|
GFileInfo *info,
|
||
|
GFileAttributeMatcher *attribute_matcher)
|
||
|
{
|
||
|
SoupMessage *msg = g_vfs_http_input_stream_get_message (G_INPUT_STREAM (handle));
|
||
|
|
||
|
file_info_from_message (msg, info, attribute_matcher);
|
||
|
g_object_unref (msg);
|
||
|
|
||
|
g_vfs_job_succeeded (G_VFS_JOB (job));
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
static gboolean
|
||
|
try_query_fs_info (GVfsBackend *backend,
|
||
|
GVfsJobQueryFsInfo *job,
|
||
|
const char *filename,
|
||
|
GFileInfo *info,
|
||
|
GFileAttributeMatcher *matcher)
|
||
|
{
|
||
|
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "http");
|
||
|
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE);
|
||
|
g_vfs_job_succeeded (G_VFS_JOB (job));
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
#define DEBUG_MAX_BODY_SIZE (100 * 1024 * 1024)
|
||
|
|
||
|
static void
|
||
|
g_vfs_backend_http_class_init (GVfsBackendHttpClass *klass)
|
||
|
{
|
||
|
const char *debug;
|
||
|
SoupSessionFeature *cookie_jar;
|
||
|
|
||
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||
|
GVfsBackendClass *backend_class;
|
||
|
|
||
|
gobject_class->finalize = g_vfs_backend_http_finalize;
|
||
|
|
||
|
backend_class = G_VFS_BACKEND_CLASS (klass);
|
||
|
|
||
|
backend_class->try_mount = try_mount;
|
||
|
backend_class->try_open_for_read = try_open_for_read;
|
||
|
backend_class->try_read = try_read;
|
||
|
backend_class->try_seek_on_read = try_seek_on_read;
|
||
|
backend_class->try_close_read = try_close_read;
|
||
|
backend_class->try_query_info = try_query_info;
|
||
|
backend_class->try_query_info_on_read = try_query_info_on_read;
|
||
|
backend_class->try_query_fs_info = try_query_fs_info;
|
||
|
|
||
|
/* Initialize the SoupSession, common to all backend instances */
|
||
|
the_session = soup_session_new_with_options ("user-agent",
|
||
|
"gvfs/" VERSION,
|
||
|
NULL);
|
||
|
|
||
|
g_object_set (the_session, "ssl-strict", FALSE, NULL);
|
||
|
|
||
|
/* Cookie handling - stored temporarlly in memory, mostly useful for
|
||
|
* authentication in WebDAV. */
|
||
|
cookie_jar = g_object_new (SOUP_TYPE_COOKIE_JAR, NULL);
|
||
|
soup_session_add_feature (the_session, cookie_jar);
|
||
|
g_object_unref (cookie_jar);
|
||
|
|
||
|
/* Send Accept-Language header (see bug 166795) */
|
||
|
g_object_set (the_session, "accept-language-auto", TRUE, NULL);
|
||
|
|
||
|
/* Prevent connection timeouts during long operations like COPY. */
|
||
|
g_object_set (the_session, "timeout", 0, NULL);
|
||
|
|
||
|
/* Logging */
|
||
|
debug = g_getenv ("GVFS_HTTP_DEBUG");
|
||
|
if (debug)
|
||
|
{
|
||
|
SoupLogger *logger;
|
||
|
SoupLoggerLogLevel level;
|
||
|
|
||
|
if (g_ascii_strcasecmp (debug, "all") == 0 ||
|
||
|
g_ascii_strcasecmp (debug, "body") == 0)
|
||
|
level = SOUP_LOGGER_LOG_BODY;
|
||
|
else if (g_ascii_strcasecmp (debug, "header") == 0)
|
||
|
level = SOUP_LOGGER_LOG_HEADERS;
|
||
|
else
|
||
|
level = SOUP_LOGGER_LOG_MINIMAL;
|
||
|
|
||
|
logger = soup_logger_new (level, DEBUG_MAX_BODY_SIZE);
|
||
|
soup_session_add_feature (the_session, SOUP_SESSION_FEATURE (logger));
|
||
|
g_object_unref (logger);
|
||
|
}
|
||
|
}
|