summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/Makefile.am.inc7
-rw-r--r--common/flatpak-appdata.c2
-rw-r--r--common/flatpak-auth.c2
-rw-r--r--common/flatpak-bundle-ref.c2
-rw-r--r--common/flatpak-bwrap.c8
-rw-r--r--common/flatpak-context-private.h1
-rw-r--r--common/flatpak-context.c46
-rw-r--r--common/flatpak-dir-private.h11
-rw-r--r--common/flatpak-dir.c602
-rw-r--r--common/flatpak-enum-types.c.template2
-rw-r--r--common/flatpak-enum-types.h.template2
-rw-r--r--common/flatpak-exports.c172
-rw-r--r--common/flatpak-installation.c16
-rw-r--r--common/flatpak-installed-ref.c2
-rw-r--r--common/flatpak-instance.c41
-rw-r--r--common/flatpak-json-oci.c2
-rw-r--r--common/flatpak-json.c2
-rw-r--r--common/flatpak-oci-registry-private.h26
-rw-r--r--common/flatpak-oci-registry.c381
-rw-r--r--common/flatpak-parental-controls.c10
-rw-r--r--common/flatpak-progress.c4
-rw-r--r--common/flatpak-prune.c24
-rw-r--r--common/flatpak-ref-utils.c6
-rw-r--r--common/flatpak-ref.c2
-rw-r--r--common/flatpak-related-ref.c2
-rw-r--r--common/flatpak-remote-ref.c4
-rw-r--r--common/flatpak-remote.c7
-rw-r--r--common/flatpak-run.c203
-rw-r--r--common/flatpak-transaction.c204
-rw-r--r--common/flatpak-transaction.h5
-rw-r--r--common/flatpak-uri-private.h125
-rw-r--r--common/flatpak-uri.c1763
-rw-r--r--common/flatpak-utils-base.c2
-rw-r--r--common/flatpak-utils-http-private.h30
-rw-r--r--common/flatpak-utils-http.c1528
-rw-r--r--common/flatpak-utils-private.h23
-rw-r--r--common/flatpak-utils.c135
-rw-r--r--common/meson.build263
38 files changed, 4492 insertions, 1175 deletions
diff --git a/common/Makefile.am.inc b/common/Makefile.am.inc
index 09e4bb68..aa4dc6e2 100644
--- a/common/Makefile.am.inc
+++ b/common/Makefile.am.inc
@@ -36,6 +36,7 @@ common/flatpak-enum-types.c: $(flatpakinclude_HEADERS) common/flatpak-enum-types
common/flatpak-enum-types.c.tmp && mv common/flatpak-enum-types.c.tmp common/flatpak-enum-types.c
EXTRA_DIST += common/flatpak-enum-types.c.template common/flatpak-enum-types.h.template
+EXTRA_DIST += common/meson.build
common/flatpak-dbus-generated.c: data/org.freedesktop.Flatpak.xml data/org.freedesktop.Flatpak.Authenticator.xml Makefile
mkdir -p $(builddir)/common
@@ -168,6 +169,8 @@ libflatpak_common_la_SOURCES = \
common/flatpak-utils-http.c \
common/flatpak-utils-private.h \
common/flatpak-utils.c \
+ common/flatpak-uri-private.h \
+ common/flatpak-uri.c \
common/flatpak-prune.c \
common/flatpak-prune-private.h \
common/flatpak-zstd-decompressor.c \
@@ -197,6 +200,7 @@ libflatpak_common_la_CFLAGS = \
$(MALCONTENT_CFLAGS) \
$(OSTREE_CFLAGS) \
$(POLKIT_CFLAGS) \
+ $(CURL_CFLAGS) \
$(SOUP_CFLAGS) \
$(SYSTEMD_CFLAGS) \
$(XAUTH_CFLAGS) \
@@ -214,6 +218,7 @@ libflatpak_common_la_LIBADD = \
$(MALCONTENT_LIBS) \
$(OSTREE_LIBS) \
$(POLKIT_LIBS) \
+ $(CURL_LIBS) \
$(SOUP_LIBS) \
$(SYSTEMD_LIBS) \
$(XAUTH_LIBS) \
@@ -233,6 +238,7 @@ libflatpak_la_CFLAGS = \
$(AM_CFLAGS) \
$(BASE_CFLAGS) \
$(OSTREE_CFLAGS) \
+ $(CURL_CFLAGS) \
$(SOUP_CFLAGS) \
$(JSON_CFLAGS) \
$(NULL)
@@ -251,6 +257,7 @@ libflatpak_la_LIBADD = \
libglnx.la \
$(BASE_LIBS) \
$(OSTREE_LIBS) \
+ $(CURL_LIBS) \
$(SOUP_LIBS) \
$(JSON_LIBS) \
$(NULL)
diff --git a/common/flatpak-appdata.c b/common/flatpak-appdata.c
index 393776fa..5a14b1a0 100644
--- a/common/flatpak-appdata.c
+++ b/common/flatpak-appdata.c
@@ -1,4 +1,4 @@
-/*
+/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
* Copyright © 2019 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
diff --git a/common/flatpak-auth.c b/common/flatpak-auth.c
index 9e45da41..aac2a115 100644
--- a/common/flatpak-auth.c
+++ b/common/flatpak-auth.c
@@ -1,4 +1,4 @@
-/*
+/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
* Copyright © 2019 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
diff --git a/common/flatpak-bundle-ref.c b/common/flatpak-bundle-ref.c
index 7ce4429c..db97bc04 100644
--- a/common/flatpak-bundle-ref.c
+++ b/common/flatpak-bundle-ref.c
@@ -1,4 +1,4 @@
-/*
+/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
* Copyright © 2015 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
diff --git a/common/flatpak-bwrap.c b/common/flatpak-bwrap.c
index 4367201b..6949c0be 100644
--- a/common/flatpak-bwrap.c
+++ b/common/flatpak-bwrap.c
@@ -1,4 +1,4 @@
-/*
+/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
* Copyright © 2014-2018 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
@@ -372,7 +372,7 @@ flatpak_bwrap_bundle_args (FlatpakBwrap *bwrap,
fd = glnx_steal_fd (&args_tmpf.fd);
- flatpak_debug2 ("bwrap --args %d = ...", fd);
+ g_debug ("bwrap --args %d = ...", fd);
for (i = start; i < end; i++)
{
@@ -380,11 +380,11 @@ flatpak_bwrap_bundle_args (FlatpakBwrap *bwrap,
{
g_autofree char *quoted = g_shell_quote (bwrap->argv->pdata[i]);
- flatpak_debug2 (" %s", quoted);
+ g_debug (" %s", quoted);
}
else
{
- flatpak_debug2 (" %s", (const char *) bwrap->argv->pdata[i]);
+ g_debug (" %s", (const char *) bwrap->argv->pdata[i]);
}
}
diff --git a/common/flatpak-context-private.h b/common/flatpak-context-private.h
index 099b6275..d9a79777 100644
--- a/common/flatpak-context-private.h
+++ b/common/flatpak-context-private.h
@@ -49,6 +49,7 @@ typedef enum {
FLATPAK_CONTEXT_SOCKET_SSH_AUTH = 1 << 6,
FLATPAK_CONTEXT_SOCKET_PCSC = 1 << 7,
FLATPAK_CONTEXT_SOCKET_CUPS = 1 << 8,
+ FLATPAK_CONTEXT_SOCKET_GPG_AGENT = 1 << 9,
} FlatpakContextSockets;
typedef enum {
diff --git a/common/flatpak-context.c b/common/flatpak-context.c
index dfec7ffa..9e028fb5 100644
--- a/common/flatpak-context.c
+++ b/common/flatpak-context.c
@@ -1,4 +1,4 @@
-/*
+/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
* Copyright © 2014-2018 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
@@ -61,6 +61,7 @@ const char *flatpak_context_sockets[] = {
"ssh-auth",
"pcsc",
"cups",
+ "gpg-agent",
NULL
};
@@ -1612,7 +1613,7 @@ flatpak_context_load_metadata (FlatpakContext *context,
share = flatpak_context_share_from_string (parse_negated (shares[i], &remove), NULL);
if (share == 0)
- g_debug ("Unknown share type %s", shares[i]);
+ g_info ("Unknown share type %s", shares[i]);
else
{
if (remove)
@@ -1634,7 +1635,7 @@ flatpak_context_load_metadata (FlatpakContext *context,
{
FlatpakContextSockets socket = flatpak_context_socket_from_string (parse_negated (sockets[i], &remove), NULL);
if (socket == 0)
- g_debug ("Unknown socket type %s", sockets[i]);
+ g_info ("Unknown socket type %s", sockets[i]);
else
{
if (remove)
@@ -1657,7 +1658,7 @@ flatpak_context_load_metadata (FlatpakContext *context,
{
FlatpakContextDevices device = flatpak_context_device_from_string (parse_negated (devices[i], &remove), NULL);
if (device == 0)
- g_debug ("Unknown device type %s", devices[i]);
+ g_info ("Unknown device type %s", devices[i]);
else
{
if (remove)
@@ -1680,7 +1681,7 @@ flatpak_context_load_metadata (FlatpakContext *context,
{
FlatpakContextFeatures feature = flatpak_context_feature_from_string (parse_negated (features[i], &remove), NULL);
if (feature == 0)
- g_debug ("Unknown feature type %s", features[i]);
+ g_info ("Unknown feature type %s", features[i]);
else
{
if (remove)
@@ -1706,7 +1707,7 @@ flatpak_context_load_metadata (FlatpakContext *context,
if (!flatpak_context_parse_filesystem (fs, remove,
&filesystem, &mode, NULL))
- g_debug ("Unknown filesystem type %s", filesystems[i]);
+ g_info ("Unknown filesystem type %s", filesystems[i]);
else
{
g_assert (mode == FLATPAK_FILESYSTEM_MODE_NONE || !remove);
@@ -2361,7 +2362,10 @@ flatpak_context_add_bus_filters (FlatpakContext *context,
flatpak_bwrap_add_arg_printf (bwrap, "--own=org.mpris.MediaPlayer2.%s.*", app_id);
}
else
- flatpak_bwrap_add_arg_printf (bwrap, "--own=%s.Sandboxed.*", app_id);
+ {
+ flatpak_bwrap_add_arg_printf (bwrap, "--own=%s.Sandboxed.*", app_id);
+ flatpak_bwrap_add_arg_printf (bwrap, "--own=org.mpris.MediaPlayer2.%s.Sandboxed.*", app_id);
+ }
}
if (session_bus)
@@ -2431,8 +2435,8 @@ flatpak_context_make_sandboxed (FlatpakContext *context)
}
const char *dont_mount_in_root[] = {
- ".", "..", "lib", "lib32", "lib64", "bin", "sbin", "usr", "boot", "root",
- "tmp", "etc", "app", "run", "proc", "sys", "dev", "var", NULL
+ ".", "..", "lib", "lib32", "lib64", "bin", "sbin", "usr", "boot", "efi",
+ "root", "tmp", "etc", "app", "run", "proc", "sys", "dev", "var", NULL
};
static void
@@ -2459,7 +2463,7 @@ flatpak_context_export (FlatpakContext *context,
DIR *dir;
struct dirent *dirent;
- g_debug ("Allowing host-fs access");
+ g_info ("Allowing host-fs access");
home_access = TRUE;
/* Bind mount most dirs in / into the new root */
@@ -2496,7 +2500,7 @@ flatpak_context_export (FlatpakContext *context,
home_mode = GPOINTER_TO_INT (g_hash_table_lookup (context->filesystems, "home"));
if (home_mode != FLATPAK_FILESYSTEM_MODE_NONE)
{
- g_debug ("Allowing homedir access");
+ g_info ("Allowing homedir access");
home_access = TRUE;
flatpak_exports_add_path_expose (exports, MAX (home_mode, fs_mode), g_get_home_dir ());
@@ -2531,14 +2535,17 @@ flatpak_context_export (FlatpakContext *context,
/* xdg-user-dirs sets disabled dirs to $HOME, and its in general not a good
idea to set full access to $HOME other than explicitly, so we ignore
these */
- g_debug ("Xdg dir %s is $HOME (i.e. disabled), ignoring", filesystem);
+ g_info ("Xdg dir %s is $HOME (i.e. disabled), ignoring", filesystem);
continue;
}
subpath = g_build_filename (path, rest, NULL);
if (mode == FLATPAK_FILESYSTEM_MODE_CREATE && do_create)
- g_mkdir_with_parents (subpath, 0755);
+ {
+ if (g_mkdir_with_parents (subpath, 0755) != 0)
+ g_info ("Unable to create directory %s", subpath);
+ }
if (g_file_test (subpath, G_FILE_TEST_EXISTS))
{
@@ -2556,7 +2563,10 @@ flatpak_context_export (FlatpakContext *context,
path = g_build_filename (g_get_home_dir (), filesystem + 2, NULL);
if (mode == FLATPAK_FILESYSTEM_MODE_CREATE && do_create)
- g_mkdir_with_parents (path, 0755);
+ {
+ if (g_mkdir_with_parents (path, 0755) != 0)
+ g_info ("Unable to create directory %s", path);
+ }
if (g_file_test (path, G_FILE_TEST_EXISTS))
flatpak_exports_add_path_expose_or_hide (exports, mode, path);
@@ -2564,7 +2574,10 @@ flatpak_context_export (FlatpakContext *context,
else if (g_str_has_prefix (filesystem, "/"))
{
if (mode == FLATPAK_FILESYSTEM_MODE_CREATE && do_create)
- g_mkdir_with_parents (filesystem, 0755);
+ {
+ if (g_mkdir_with_parents (filesystem, 0755) != 0)
+ g_info ("Unable to create directory %s", filesystem);
+ }
if (g_file_test (filesystem, G_FILE_TEST_EXISTS))
flatpak_exports_add_path_expose_or_hide (exports, mode, filesystem);
@@ -2693,7 +2706,8 @@ flatpak_context_append_bwrap_filesystem (FlatpakContext *context,
g_autofree char *src = g_build_filename (g_get_home_dir (), ".var/app", app_id, persist, NULL);
g_autofree char *dest = g_build_filename (g_get_home_dir (), persist, NULL);
- g_mkdir_with_parents (src, 0755);
+ if (g_mkdir_with_parents (src, 0755) != 0)
+ g_info ("Unable to create directory %s", src);
flatpak_bwrap_add_bind_arg (bwrap, "--bind", src, dest);
}
diff --git a/common/flatpak-dir-private.h b/common/flatpak-dir-private.h
index 74d2e974..950944b3 100644
--- a/common/flatpak-dir-private.h
+++ b/common/flatpak-dir-private.h
@@ -70,6 +70,7 @@ GType flatpak_deploy_get_type (void);
#define FLATPAK_REF_BRANCH_KEY "Branch"
#define FLATPAK_REF_COLLECTION_ID_KEY "CollectionID"
#define FLATPAK_REF_DEPLOY_COLLECTION_ID_KEY "DeployCollectionID"
+#define FLATPAK_REF_DEPLOY_SIDELOAD_COLLECTION_ID_KEY "DeploySideloadCollectionID"
#define FLATPAK_REPO_GROUP "Flatpak Repo"
#define FLATPAK_REPO_VERSION_KEY "Version"
@@ -89,6 +90,7 @@ GType flatpak_deploy_get_type (void);
#define FLATPAK_REPO_COLLECTION_ID_KEY "CollectionID"
#define FLATPAK_REPO_DEPLOY_COLLECTION_ID_KEY "DeployCollectionID"
+#define FLATPAK_REPO_DEPLOY_SIDELOAD_COLLECTION_ID_KEY "DeploySideloadCollectionID"
#define FLATPAK_CLI_UPDATE_INTERVAL_MS 300
@@ -646,10 +648,19 @@ GPtrArray * flatpak_dir_list_refs (Fla
FlatpakKinds kinds,
GCancellable *cancellable,
GError **error);
+gboolean flatpak_dir_is_runtime_extension (FlatpakDir *self,
+ FlatpakDecomposed *ref);
GPtrArray * flatpak_dir_list_app_refs_with_runtime (FlatpakDir *self,
+ GHashTable **runtime_app_map,
FlatpakDecomposed *runtime_ref,
GCancellable *cancellable,
GError **error);
+GPtrArray * flatpak_dir_list_app_refs_with_runtime_extension (FlatpakDir *self,
+ GHashTable **runtime_app_map,
+ GHashTable **extension_app_map,
+ FlatpakDecomposed *runtime_ext_ref,
+ GCancellable *cancellable,
+ GError **error);
GVariant * flatpak_dir_read_latest_commit (FlatpakDir *self,
const char *remote,
FlatpakDecomposed *ref,
diff --git a/common/flatpak-dir.c b/common/flatpak-dir.c
index 7471f529..927f26b6 100644
--- a/common/flatpak-dir.c
+++ b/common/flatpak-dir.c
@@ -1,4 +1,5 @@
-/*
+/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
+ *
* Copyright © 2014-2019 Red Hat, Inc
* Copyright © 2017 Endless Mobile, Inc.
*
@@ -165,7 +166,7 @@ static gboolean flatpak_dir_lookup_remote_filter (FlatpakDir *self,
GRegex **deny_regex,
GError **error);
-static void ensure_soup_session (FlatpakDir *self);
+static void ensure_http_session (FlatpakDir *self);
static void flatpak_dir_log (FlatpakDir *self,
const char *file,
@@ -241,7 +242,7 @@ struct FlatpakDir
GRegex *masked;
GRegex *pinned;
- SoupSession *soup_session;
+ FlatpakHttpSession *http_session;
};
G_LOCK_DEFINE_STATIC (config_cache);
@@ -430,14 +431,14 @@ flatpak_remote_state_add_sideload_repo (FlatpakRemoteState *self,
/* We expect to hit this code path when the repo is providing things
* from other remotes
*/
- g_debug ("Sideload repo at path %s not valid for remote %s: %s",
- flatpak_file_get_path_cached (dir), self->remote_name, local_error->message);
+ g_info ("Sideload repo at path %s not valid for remote %s: %s",
+ flatpak_file_get_path_cached (dir), self->remote_name, local_error->message);
flatpak_sideload_state_free (ss);
}
else
{
g_ptr_array_add (self->sideload_repos, ss);
- g_debug ("Using sideloaded repo %s for remote %s", flatpak_file_get_path_cached (dir), self->remote_name);
+ g_info ("Using sideloaded repo %s for remote %s", flatpak_file_get_path_cached (dir), self->remote_name);
}
}
}
@@ -808,7 +809,7 @@ flatpak_remote_state_match_subrefs (FlatpakRemoteState *self,
if (self->summary == NULL && self->index == NULL)
{
- g_debug ("flatpak_remote_state_match_subrefs with no summary");
+ g_info ("flatpak_remote_state_match_subrefs with no summary");
return g_ptr_array_new_with_free_func ((GDestroyNotify)flatpak_decomposed_unref);
}
@@ -1160,14 +1161,14 @@ flatpak_remote_state_fetch_commit_object (FlatpakRemoteState *self,
if (!ostree_repo_remote_get_url (dir->repo, self->remote_name, &base_url, error))
return NULL;
- ensure_soup_session (dir);
+ ensure_http_session (dir);
part1 = g_strndup (checksum, 2);
part2 = g_strdup_printf ("%s.commit", checksum + 2);
object_url = g_build_filename (base_url, "objects", part1, part2, NULL);
- bytes = flatpak_load_uri (dir->soup_session, object_url, 0, token,
+ bytes = flatpak_load_uri (dir->http_session, object_url, 0, token,
NULL, NULL, NULL,
cancellable, error);
if (bytes == NULL)
@@ -1643,7 +1644,7 @@ append_locations_from_config_file (GPtrArray *locations,
if (!g_key_file_load_from_file (keyfile, file_path, G_KEY_FILE_NONE, &my_error))
{
- g_debug ("Could not get list of system installations from '%s': %s", file_path, my_error->message);
+ g_info ("Could not get list of system installations from '%s': %s", file_path, my_error->message);
g_propagate_error (error, g_steal_pointer (&my_error));
goto out;
}
@@ -1689,7 +1690,7 @@ append_locations_from_config_file (GPtrArray *locations,
path = g_key_file_get_string (keyfile, groups[i], "Path", &my_error);
if (path == NULL)
{
- g_debug ("While reading '%s': Unable to get path for installation '%s': %s", file_path, id, my_error->message);
+ g_info ("While reading '%s': Unable to get path for installation '%s': %s", file_path, id, my_error->message);
g_propagate_error (error, g_steal_pointer (&my_error));
goto out;
}
@@ -1757,7 +1758,7 @@ system_locations_from_configuration (GCancellable *cancellable,
if (!g_file_test (config_dir, G_FILE_TEST_IS_DIR))
{
- g_debug ("No installations directory in %s. Skipping", config_dir);
+ g_info ("No installations directory in %s. Skipping", config_dir);
goto out;
}
@@ -1768,8 +1769,8 @@ system_locations_from_configuration (GCancellable *cancellable,
cancellable, &my_error);
if (my_error != NULL)
{
- g_debug ("Unexpected error retrieving extra installations in %s: %s",
- config_dir, my_error->message);
+ g_info ("Unexpected error retrieving extra installations in %s: %s",
+ config_dir, my_error->message);
g_propagate_error (error, g_steal_pointer (&my_error));
goto out;
}
@@ -1784,8 +1785,8 @@ system_locations_from_configuration (GCancellable *cancellable,
if (!g_file_enumerator_iterate (dir_enum, &file_info, &path,
cancellable, &my_error))
{
- g_debug ("Unexpected error reading file in %s: %s",
- config_dir, my_error->message);
+ g_info ("Unexpected error reading file in %s: %s",
+ config_dir, my_error->message);
g_propagate_error (error, g_steal_pointer (&my_error));
goto out;
}
@@ -2172,7 +2173,7 @@ flatpak_dir_system_helper_call (FlatpakDir *self,
return NULL;
}
- g_debug ("Calling system helper: %s", method_name);
+ g_info ("Calling system helper: %s", method_name);
res = g_dbus_connection_call_with_unix_fd_list_sync (self->system_helper_bus,
FLATPAK_SYSTEM_HELPER_BUS_NAME,
FLATPAK_SYSTEM_HELPER_PATH,
@@ -2477,7 +2478,7 @@ flatpak_dir_system_helper_call_cancel_pull (FlatpakDir *self,
if (flatpak_dir_get_no_interaction (self))
arg_flags |= FLATPAK_HELPER_CANCEL_PULL_FLAGS_NO_INTERACTION;
- g_debug ("Calling system helper: CancelPull");
+ g_info ("Calling system helper: CancelPull");
g_autoptr(GVariant) ret =
flatpak_dir_system_helper_call (self, "CancelPull",
@@ -2507,7 +2508,7 @@ flatpak_dir_system_helper_call_get_revokefs_fd (FlatpakDir *self,
if (flatpak_dir_get_no_interaction (self))
arg_flags |= FLATPAK_HELPER_GET_REVOKEFS_FD_FLAGS_NO_INTERACTION;
- g_debug ("Calling system helper: GetRevokefsFd");
+ g_info ("Calling system helper: GetRevokefsFd");
g_autoptr(GVariant) ret =
flatpak_dir_system_helper_call (self, "GetRevokefsFd",
@@ -2586,7 +2587,7 @@ flatpak_dir_finalize (GObject *object)
if (self->system_helper_bus != (gpointer) 1)
g_clear_object (&self->system_helper_bus);
- g_clear_object (&self->soup_session);
+ g_clear_pointer (&self->http_session, flatpak_http_session_free);
g_clear_pointer (&self->summary_cache, g_hash_table_unref);
g_clear_pointer (&self->remote_filters, g_hash_table_unref);
g_clear_pointer (&self->masked, g_regex_unref);
@@ -3789,7 +3790,7 @@ flatpak_dir_migrate_config (FlatpakDir *self,
if (config == NULL)
config = ostree_repo_copy_config (flatpak_dir_get_repo (self));
- g_debug ("Migrating remote '%s' to gpg-verify-summary", remote);
+ g_info ("Migrating remote '%s' to gpg-verify-summary", remote);
g_key_file_set_boolean (config, group, "gpg-verify-summary", TRUE);
}
}
@@ -3805,7 +3806,7 @@ flatpak_dir_migrate_config (FlatpakDir *self,
FLATPAK_HELPER_ENSURE_REPO_FLAGS_NONE,
installation ? installation : "",
NULL, &local_error))
- g_debug ("Failed to migrate system config: %s", local_error->message);
+ g_info ("Failed to migrate system config: %s", local_error->message);
}
else
{
@@ -3915,8 +3916,8 @@ _flatpak_dir_find_new_flatpakrepos (FlatpakDir *self, OstreeRepo *repo)
if (!g_file_enumerator_iterate (dir_enum, &file_info, &path,
NULL, &my_error))
{
- g_debug ("Unexpected error reading file in %s: %s",
- config_dir, my_error->message);
+ g_info ("Unexpected error reading file in %s: %s",
+ config_dir, my_error->message);
break;
}
@@ -4004,7 +4005,7 @@ apply_new_flatpakrepo (const char *remote_name,
NULL, &imported, NULL, error))
return FALSE;
- g_debug ("Imported %u GPG key%s to remote \"%s\"", imported, (imported == 1) ? "" : "s", remote_name);
+ g_info ("Imported %u GPG key%s to remote \"%s\"", imported, (imported == 1) ? "" : "s", remote_name);
}
return TRUE;
@@ -4557,6 +4558,67 @@ remove_old_appstream_tmpdirs (GFile *dir)
tmp = g_file_get_child (dir, dent->d_name);
/* We ignore errors here, no need to worry anyone */
+ g_info ("Deleting stale appstream deploy tmpdir %s", flatpak_file_get_path_cached (tmp));
+ (void)flatpak_rm_rf (tmp, NULL, NULL);
+ }
+}
+
+/* Like the function above, this looks for old temporary directories created by
+ * previous versions of flatpak_dir_deploy().
+ * These are all directories starting with a dot. Such directories can be from a
+ * concurrent deploy, so we only remove directories older than a day to avoid
+ * races.
+*/
+static void
+remove_old_deploy_tmpdirs (GFile *dir)
+{
+ g_auto(GLnxDirFdIterator) dir_iter = { 0 };
+ time_t now = time (NULL);
+
+ if (!glnx_dirfd_iterator_init_at (AT_FDCWD, flatpak_file_get_path_cached (dir),
+ FALSE, &dir_iter, NULL))
+ return;
+
+ while (TRUE)
+ {
+ struct stat stbuf;
+ struct dirent *dent;
+ g_autoptr(GFile) tmp = NULL;
+
+ if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dir_iter, &dent, NULL, NULL))
+ break;
+
+ if (dent == NULL)
+ break;
+
+ /* We ignore non-dotfiles and .timestamps as they are not tempfiles */
+ if (dent->d_name[0] != '.' ||
+ strcmp (dent->d_name, ".timestamp") == 0)
+ continue;
+
+ /* Check for right types and names. The format we’re looking for is:
+ * .[0-9a-f]{64}-[0-9A-Z]{6} */
+ if (dent->d_type == DT_DIR)
+ {
+ if (strlen (dent->d_name) != 72 ||
+ dent->d_name[65] != '-')
+ continue;
+ }
+ else
+ continue;
+
+ /* Check that the file is at least a day old to avoid races */
+ if (!glnx_fstatat (dir_iter.fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW, NULL))
+ continue;
+
+ if (stbuf.st_mtime >= now ||
+ now - stbuf.st_mtime < SECS_PER_DAY)
+ continue;
+
+ tmp = g_file_get_child (dir, dent->d_name);
+
+ /* We ignore errors here, no need to worry anyone */
+ g_info ("Deleting stale deploy tmpdir %s", flatpak_file_get_path_cached (tmp));
(void)flatpak_rm_rf (tmp, NULL, NULL);
}
}
@@ -4941,7 +5003,7 @@ flatpak_dir_update_oci_index (FlatpakDir *self,
if (index_cache == NULL)
return NULL;
- ensure_soup_session (self);
+ ensure_http_session (self);
if (!ostree_repo_remote_get_url (self->repo,
remote,
@@ -4949,7 +5011,7 @@ flatpak_dir_update_oci_index (FlatpakDir *self,
error))
return NULL;
- if (!flatpak_oci_index_ensure_cached (self->soup_session, oci_uri,
+ if (!flatpak_oci_index_ensure_cached (self->http_session, oci_uri,
index_cache, index_uri_out,
cancellable, &local_error))
{
@@ -5047,9 +5109,9 @@ flatpak_dir_update_appstream_oci (FlatpakDir *self,
FALSE, &icons_dfd, error))
return FALSE;
- ensure_soup_session (self);
+ ensure_http_session (self);
- appstream = flatpak_oci_index_make_appstream (self->soup_session,
+ appstream = flatpak_oci_index_make_appstream (self->http_session,
index_cache,
index_uri,
arch,
@@ -5405,7 +5467,7 @@ repo_pull (OstreeRepo *self,
sideload_url = g_file_get_uri (sideload_repo);
- g_debug ("Sideloading %s from %s in pull", ref_to_fetch, sideload_url);
+ g_info ("Sideloading %s from %s in pull", ref_to_fetch, sideload_url);
g_assert (state->collection_id != NULL);
@@ -5480,15 +5542,15 @@ repo_pull (OstreeRepo *self,
}
static void
-ensure_soup_session (FlatpakDir *self)
+ensure_http_session (FlatpakDir *self)
{
- if (g_once_init_enter (&self->soup_session))
+ if (g_once_init_enter (&self->http_session))
{
- SoupSession *soup_session;
+ FlatpakHttpSession *http_session;
- soup_session = flatpak_create_soup_session (PACKAGE_STRING);
+ http_session = flatpak_create_http_session (PACKAGE_STRING);
- g_once_init_leave (&self->soup_session, soup_session);
+ g_once_init_leave (&self->http_session, http_session);
}
}
@@ -5676,7 +5738,7 @@ flatpak_dir_pull_extra_data (FlatpakDir *self,
extra_local_file = flatpak_build_file (base_dir, "extra-data", extra_data_sha256, extra_data_name, NULL);
if (g_file_query_exists (extra_local_file, cancellable))
{
- g_debug ("Loading extra-data from local file %s", flatpak_file_get_path_cached (extra_local_file));
+ g_info ("Loading extra-data from local file %s", flatpak_file_get_path_cached (extra_local_file));
gsize extra_local_size;
g_autofree char *extra_local_contents = NULL;
g_autoptr(GError) my_error = NULL;
@@ -5691,8 +5753,8 @@ flatpak_dir_pull_extra_data (FlatpakDir *self,
}
else
{
- ensure_soup_session (self);
- bytes = flatpak_load_uri (self->soup_session, extra_data_uri, 0, NULL,
+ ensure_http_session (self);
+ bytes = flatpak_load_uri (self->http_session, extra_data_uri, 0, NULL,
extra_data_progress_report, progress, NULL,
cancellable, error);
}
@@ -5827,7 +5889,7 @@ flatpak_dir_mirror_oci (FlatpakDir *self,
flatpak_progress_start_oci_pull (progress);
- g_debug ("Mirroring OCI image %s", oci_digest);
+ g_info ("Mirroring OCI image %s", oci_digest);
res = flatpak_mirror_image_from_oci (dst_registry, registry, oci_repository, oci_digest, state->remote_name, ref, delta_url, self->repo, oci_pull_progress_cb,
progress, cancellable, error);
@@ -5908,7 +5970,7 @@ flatpak_dir_pull_oci (FlatpakDir *self,
flatpak_progress_start_oci_pull (progress);
- g_debug ("Pulling OCI image %s", oci_digest);
+ g_info ("Pulling OCI image %s", oci_digest);
checksum = flatpak_pull_from_oci (repo, registry, oci_repository, oci_digest, delta_url, FLATPAK_OCI_MANIFEST (versioned), image_config,
state->remote_name, ref, flatpak_flags, oci_pull_progress_cb, progress, cancellable, error);
@@ -5916,7 +5978,7 @@ flatpak_dir_pull_oci (FlatpakDir *self,
if (checksum == NULL)
return FALSE;
- g_debug ("Imported OCI image as checksum %s", checksum);
+ g_info ("Imported OCI image as checksum %s", checksum);
if (repo == self->repo)
name = flatpak_dir_get_name (self);
@@ -5996,11 +6058,11 @@ flatpak_dir_pull (FlatpakDir *self,
return FALSE;
}
- g_debug ("%s: Using commit %s for pull of ref %s from remote %s%s%s",
- G_STRFUNC, rev, ref, state->remote_name,
- sideload_repo ? "sideloaded from " : "",
- sideload_repo ? flatpak_file_get_path_cached (sideload_repo) : ""
- );
+ g_info ("%s: Using commit %s for pull of ref %s from remote %s%s%s",
+ G_STRFUNC, rev, ref, state->remote_name,
+ sideload_repo ? "sideloaded from " : "",
+ sideload_repo ? flatpak_file_get_path_cached (sideload_repo) : ""
+ );
if (repo == NULL)
repo = self->repo;
@@ -6442,6 +6504,7 @@ flatpak_dir_make_current_ref (FlatpakDir *self,
GCancellable *cancellable,
GError **error)
{
+ g_autoptr(GError) local_error = NULL;
g_autoptr(GFile) base = NULL;
g_autoptr(GFile) dir = NULL;
g_autoptr(GFile) current_link = NULL;
@@ -6458,7 +6521,12 @@ flatpak_dir_make_current_ref (FlatpakDir *self,
current_link = g_file_get_child (dir, "current");
- g_file_delete (current_link, cancellable, NULL);
+ if (!g_file_delete (current_link, cancellable, &local_error) &&
+ !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+ {
+ g_propagate_error (error, g_steal_pointer (&local_error));
+ return FALSE;
+ }
rest = flatpak_decomposed_peek_arch (ref, NULL);
if (!g_file_make_symbolic_link (current_link, rest, cancellable, error))
@@ -6684,32 +6752,185 @@ flatpak_dir_list_refs (FlatpakDir *self,
return g_steal_pointer (&refs);
}
-GPtrArray *
-flatpak_dir_list_app_refs_with_runtime (FlatpakDir *self,
- FlatpakDecomposed *runtime_ref,
- GCancellable *cancellable,
- GError **error)
+gboolean
+flatpak_dir_is_runtime_extension (FlatpakDir *self,
+ FlatpakDecomposed *ref)
{
+ g_autoptr(GBytes) ext_deploy_data = NULL;
+
+ if (!flatpak_decomposed_is_runtime (ref))
+ return FALSE;
+
+ /* deploy v4 guarantees extension-of info */
+ ext_deploy_data = flatpak_dir_get_deploy_data (self, ref, 4, NULL, NULL);
+ if (ext_deploy_data && flatpak_deploy_data_get_extension_of (ext_deploy_data) != NULL)
+ return TRUE;
+
+ return FALSE;
+}
+
+static GHashTable *
+flatpak_dir_get_runtime_app_map (FlatpakDir *self,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autoptr(GHashTable) runtime_app_map = g_hash_table_new_full ((GHashFunc)flatpak_decomposed_hash,
+ (GEqualFunc)flatpak_decomposed_equal,
+ (GDestroyNotify)flatpak_decomposed_unref,
+ (GDestroyNotify)g_ptr_array_unref);
g_autoptr(GPtrArray) app_refs = NULL;
- const char *runtime_pref = flatpak_decomposed_get_pref (runtime_ref);
- g_autoptr(GPtrArray) apps = g_ptr_array_new_with_free_func ((GDestroyNotify)flatpak_decomposed_unref);
- app_refs = flatpak_dir_list_refs (self, FLATPAK_KINDS_APP, NULL, NULL);
- for (int i = 0; app_refs != NULL && i < app_refs->len; i++)
+ app_refs = flatpak_dir_list_refs (self, FLATPAK_KINDS_APP, cancellable, error);
+ if (app_refs == NULL)
+ return NULL;
+
+ for (guint i = 0; i < app_refs->len; i++)
{
FlatpakDecomposed *app_ref = g_ptr_array_index (app_refs, i);
/* deploy v4 guarantees runtime info */
g_autoptr(GBytes) app_deploy_data = flatpak_dir_get_deploy_data (self, app_ref, 4, NULL, NULL);
+ g_autoptr(FlatpakDecomposed) runtime_decomposed = NULL;
+ g_autoptr(GPtrArray) runtime_apps = NULL;
+ const char *runtime_pref;
+
+ if (app_deploy_data == NULL)
+ continue;
+
+ runtime_pref = flatpak_deploy_data_get_runtime (app_deploy_data);
+ runtime_decomposed = flatpak_decomposed_new_from_pref (FLATPAK_KINDS_RUNTIME, runtime_pref, error);
+ if (runtime_decomposed == NULL)
+ return NULL;
+
+ runtime_apps = g_hash_table_lookup (runtime_app_map, runtime_decomposed);
+ if (runtime_apps == NULL)
+ {
+ runtime_apps = g_ptr_array_new_with_free_func ((GDestroyNotify)flatpak_decomposed_unref);
+ g_hash_table_insert (runtime_app_map, flatpak_decomposed_ref (runtime_decomposed), g_ptr_array_ref (runtime_apps));
+ }
+ else
+ g_ptr_array_ref (runtime_apps);
+
+ g_ptr_array_add (runtime_apps, flatpak_decomposed_ref (app_ref));
+ }
- if (app_deploy_data)
+ return g_steal_pointer (&runtime_app_map);
+}
+
+GPtrArray *
+flatpak_dir_list_app_refs_with_runtime (FlatpakDir *self,
+ GHashTable **runtime_app_map,
+ FlatpakDecomposed *runtime_ref,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GPtrArray *apps;
+
+ g_assert (runtime_app_map != NULL);
+
+ if (*runtime_app_map == NULL)
+ *runtime_app_map = flatpak_dir_get_runtime_app_map (self, cancellable, error);
+
+ if (*runtime_app_map == NULL)
+ return NULL;
+
+ apps = g_hash_table_lookup (*runtime_app_map, runtime_ref);
+ if (apps == NULL) /* unused runtime */
+ return g_ptr_array_new_with_free_func ((GDestroyNotify)flatpak_decomposed_unref);
+
+ return g_ptr_array_ref (apps);
+}
+
+static GHashTable *
+flatpak_dir_get_extension_app_map (FlatpakDir *self,
+ GHashTable *runtime_app_map,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autoptr(GHashTable) extension_app_map = g_hash_table_new_full ((GHashFunc)flatpak_decomposed_hash,
+ (GEqualFunc)flatpak_decomposed_equal,
+ (GDestroyNotify)flatpak_decomposed_unref,
+ (GDestroyNotify)g_ptr_array_unref);
+ g_autoptr(GPtrArray) all_refs = NULL;
+
+ g_assert (runtime_app_map != NULL);
+
+ all_refs = flatpak_dir_list_refs (self, FLATPAK_KINDS_RUNTIME | FLATPAK_KINDS_APP, NULL, NULL);
+ for (guint i = 0; all_refs != NULL && i < all_refs->len; i++)
+ {
+ FlatpakDecomposed *ref = g_ptr_array_index (all_refs, i);
+ g_autoptr(GPtrArray) related = NULL;
+ GPtrArray *runtime_apps = NULL;
+
+ if (flatpak_decomposed_id_is_subref (ref))
+ continue;
+
+ if (flatpak_decomposed_is_runtime (ref))
+ {
+ runtime_apps = g_hash_table_lookup (runtime_app_map, ref);
+ if (runtime_apps == NULL)
+ continue;
+ }
+
+ related = flatpak_dir_find_local_related (self, ref, NULL, TRUE, cancellable, error);
+ if (related == NULL)
+ return NULL;
+
+ for (guint j = 0; j < related->len; j++)
{
- const char *app_runtime = flatpak_deploy_data_get_runtime (app_deploy_data);
- if (g_strcmp0 (app_runtime, runtime_pref) == 0)
- g_ptr_array_add (apps, flatpak_decomposed_ref (app_ref));
+ FlatpakRelated *rel = g_ptr_array_index (related, j);
+ g_autoptr(GPtrArray) extension_apps = g_hash_table_lookup (extension_app_map, rel->ref);
+ if (extension_apps == NULL)
+ {
+ extension_apps = g_ptr_array_new_with_free_func ((GDestroyNotify)flatpak_decomposed_unref);
+ g_hash_table_insert (extension_app_map, flatpak_decomposed_ref (rel->ref), g_ptr_array_ref (extension_apps));
+ }
+ else
+ g_ptr_array_ref (extension_apps);
+
+ if (flatpak_decomposed_is_runtime (ref))
+ {
+ g_assert (runtime_apps);
+ for (guint k = 0; runtime_apps && k < runtime_apps->len; k++)
+ g_ptr_array_add (extension_apps, flatpak_decomposed_ref (g_ptr_array_index (runtime_apps, k)));
+ }
+ else
+ g_ptr_array_add (extension_apps, flatpak_decomposed_ref (ref));
}
}
- return g_steal_pointer (&apps);
+ return g_steal_pointer (&extension_app_map);
+}
+
+GPtrArray *
+flatpak_dir_list_app_refs_with_runtime_extension (FlatpakDir *self,
+ GHashTable **runtime_app_map,
+ GHashTable **extension_app_map,
+ FlatpakDecomposed *runtime_ext_ref,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GPtrArray *apps;
+
+ g_assert (runtime_app_map != NULL);
+ g_assert (extension_app_map != NULL);
+
+ if (*runtime_app_map == NULL)
+ *runtime_app_map = flatpak_dir_get_runtime_app_map (self, cancellable, error);
+
+ if (*runtime_app_map == NULL)
+ return NULL;
+
+ if (*extension_app_map == NULL)
+ *extension_app_map = flatpak_dir_get_extension_app_map (self, *runtime_app_map, cancellable, error);
+
+ if (*extension_app_map == NULL)
+ return NULL;
+
+ apps = g_hash_table_lookup (*extension_app_map, runtime_ext_ref);
+ if (apps == NULL) /* unused extension */
+ return g_ptr_array_new_with_free_func ((GDestroyNotify)flatpak_decomposed_unref);
+
+ return g_ptr_array_ref (apps);
}
GVariant *
@@ -6865,7 +7086,7 @@ flatpak_dir_run_triggers (FlatpakDir *self,
if (triggerspath == NULL)
triggerspath = FLATPAK_TRIGGERDIR;
- g_debug ("running triggers from %s", triggerspath);
+ g_info ("running triggers from %s", triggerspath);
triggersdir = g_file_new_for_path (triggerspath);
@@ -6895,7 +7116,7 @@ flatpak_dir_run_triggers (FlatpakDir *self,
g_autoptr(FlatpakBwrap) bwrap = NULL;
g_autofree char *commandline = NULL;
- g_debug ("running trigger %s", name);
+ g_info ("running trigger %s", name);
bwrap = flatpak_bwrap_new (NULL);
@@ -6918,7 +7139,7 @@ flatpak_dir_run_triggers (FlatpakDir *self,
flatpak_bwrap_finish (bwrap);
commandline = flatpak_quote_argv ((const char **) bwrap->argv->pdata, -1);
- g_debug ("Running '%s'", commandline);
+ g_info ("Running '%s'", commandline);
/* We use LEAVE_DESCRIPTORS_OPEN to work around dead-lock, see flatpak_close_fds_workaround */
if (!g_spawn_sync ("/",
@@ -7875,7 +8096,7 @@ extract_extra_data (FlatpakDir *self,
if (n_extra_data_sources == 0)
return TRUE;
- g_debug ("extracting extra data to %s", flatpak_file_get_path_cached (extradir));
+ g_info ("extracting extra data to %s", flatpak_file_get_path_cached (extradir));
if (!ostree_repo_read_commit_detached_metadata (self->repo, checksum, &detached_metadata,
cancellable, error))
@@ -8029,6 +8250,7 @@ apply_extra_data (FlatpakDir *self,
int exit_status;
const char *group = FLATPAK_METADATA_GROUP_APPLICATION;
g_autoptr(GError) local_error = NULL;
+ FlatpakRunFlags run_flags;
apply_extra_file = g_file_resolve_relative_path (checkoutdir, "files/bin/apply_extra");
if (!g_file_query_exists (apply_extra_file, cancellable))
@@ -8105,20 +8327,22 @@ apply_extra_data (FlatpakDir *self,
"--cap-drop", "ALL",
NULL);
+ /* Might need multiarch in apply_extra (see e.g. #3742).
+ * Should be pretty safe in this limited context */
+ run_flags = (FLATPAK_RUN_FLAG_MULTIARCH |
+ FLATPAK_RUN_FLAG_NO_SESSION_HELPER |
+ FLATPAK_RUN_FLAG_NO_PROC |
+ FLATPAK_RUN_FLAG_NO_SESSION_BUS_PROXY |
+ FLATPAK_RUN_FLAG_NO_SYSTEM_BUS_PROXY |
+ FLATPAK_RUN_FLAG_NO_A11Y_BUS_PROXY);
+
if (!flatpak_run_setup_base_argv (bwrap, runtime_files, NULL, runtime_arch,
- /* Might need multiarch in apply_extra (see e.g. #3742). Should be pretty safe in this limited context */
- FLATPAK_RUN_FLAG_MULTIARCH |
- FLATPAK_RUN_FLAG_NO_SESSION_HELPER | FLATPAK_RUN_FLAG_NO_PROC,
- error))
+ run_flags, error))
return FALSE;
app_context = flatpak_context_new ();
- if (!flatpak_run_add_environment_args (bwrap, NULL,
- FLATPAK_RUN_FLAG_NO_SESSION_BUS_PROXY |
- FLATPAK_RUN_FLAG_NO_SYSTEM_BUS_PROXY |
- FLATPAK_RUN_FLAG_NO_A11Y_BUS_PROXY,
- id,
+ if (!flatpak_run_add_environment_args (bwrap, NULL, run_flags, id,
app_context, NULL, NULL, -1,
NULL, cancellable, error))
return FALSE;
@@ -8131,7 +8355,7 @@ apply_extra_data (FlatpakDir *self,
flatpak_bwrap_finish (bwrap);
- g_debug ("Running /app/bin/apply_extra ");
+ g_info ("Running /app/bin/apply_extra ");
/* We run the sandbox without caps, but it can still create files owned by itself with
* arbitrary permissions, including setuid myself. This is extra risky in the case where
@@ -8219,7 +8443,7 @@ flatpak_dir_check_parental_controls (FlatpakDir *self,
* system-helper, self->source_pid is non-zero. */
if (self->source_pid == 0 && getuid () == 0)
{
- g_debug ("Skipping parental controls check for %s due to running as root", ref);
+ g_info ("Skipping parental controls check for %s due to running as root", ref);
return TRUE;
}
@@ -8229,7 +8453,7 @@ flatpak_dir_check_parental_controls (FlatpakDir *self,
if (!g_str_has_prefix (ref, "app/"))
return TRUE;
- g_debug ("Getting parental controls details for %s from %s",
+ g_info ("Getting parental controls details for %s from %s",
ref, flatpak_deploy_data_get_origin (deploy_data));
if (on_session != NULL)
@@ -8237,8 +8461,8 @@ flatpak_dir_check_parental_controls (FlatpakDir *self,
/* FIXME: Instead of skipping the parental controls check in the test
* environment, make a mock service for it.
* https://github.com/flatpak/flatpak/issues/2993 */
- g_debug ("Skipping parental controls check for %s since the "
- "system bus is unavailable in the test environment", ref);
+ g_info ("Skipping parental controls check for %s since the "
+ "system bus is unavailable in the test environment", ref);
return TRUE;
}
@@ -8272,15 +8496,15 @@ flatpak_dir_check_parental_controls (FlatpakDir *self,
cancellable, &local_error);
if (g_error_matches (local_error, MCT_APP_FILTER_ERROR, MCT_APP_FILTER_ERROR_DISABLED))
{
- g_debug ("Skipping parental controls check for %s since parental "
- "controls are disabled globally", ref);
+ g_info ("Skipping parental controls check for %s since parental "
+ "controls are disabled globally", ref);
return TRUE;
}
else if (g_error_matches (local_error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN) ||
g_error_matches (local_error, G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER))
{
- g_debug ("Skipping parental controls check for %s since a required "
- "service was not found", ref);
+ g_info ("Skipping parental controls check for %s since a required "
+ "service was not found", ref);
return TRUE;
}
else if (local_error != NULL)
@@ -8301,7 +8525,7 @@ flatpak_dir_check_parental_controls (FlatpakDir *self,
if (repo_installation_allowed && app_is_appropriate)
{
- g_debug ("Parental controls policy satisfied for %s", ref);
+ g_info ("Parental controls policy satisfied for %s", ref);
return TRUE;
}
@@ -8332,7 +8556,7 @@ flatpak_dir_check_parental_controls (FlatpakDir *self,
_("Installing %s is not allowed by the policy set by your administrator"),
ref);
- g_debug ("Parental controls policy overridden by polkit for %s", ref);
+ g_info ("Parental controls policy overridden by polkit for %s", ref);
#endif /* USE_SYSTEM_HELPER */
#endif /* HAVE_LIBMALCONTENT */
@@ -8369,9 +8593,11 @@ flatpak_dir_deploy (FlatpakDir *self,
g_autofree char *ref_id = NULL;
g_autoptr(GFile) root = NULL;
g_autoptr(GFile) deploy_base = NULL;
+ glnx_autofd int deploy_base_dfd = -1;
g_autoptr(GFile) checkoutdir = NULL;
g_autoptr(GFile) bindir = NULL;
g_autofree char *checkoutdirpath = NULL;
+ const char *checkoutdir_basename;
g_autoptr(GFile) real_checkoutdir = NULL;
g_autoptr(GFile) dotref = NULL;
g_autoptr(GFile) files_etc = NULL;
@@ -8385,8 +8611,6 @@ flatpak_dir_deploy (FlatpakDir *self,
OstreeRepoCheckoutAtOptions options = { 0, };
const char *checksum;
glnx_autofd int checkoutdir_dfd = -1;
- g_autoptr(GFile) tmp_dir_template = NULL;
- g_autofree char *tmp_dir_path = NULL;
const char *xa_ref = NULL;
g_autofree char *checkout_basename = NULL;
gboolean created_extra_data = FALSE;
@@ -8396,6 +8620,7 @@ flatpak_dir_deploy (FlatpakDir *self,
g_autofree char *metadata_contents = NULL;
gsize metadata_size = 0;
const char *flatpak;
+ g_auto(GLnxTmpDir) tmp_dir_handle = { 0, };
if (!flatpak_dir_ensure_repo (self, cancellable, error))
return FALSE;
@@ -8410,9 +8635,18 @@ flatpak_dir_deploy (FlatpakDir *self,
deploy_base = flatpak_dir_get_deploy_dir (self, ref);
+ if (!glnx_opendirat (AT_FDCWD, flatpak_file_get_path_cached (deploy_base), TRUE, &deploy_base_dfd, error))
+ return FALSE;
+
+ /* There used to be a bug here where temporary files beneath @deploy_base were not removed,
+ * which could use quite a lot of space over time, so we check for these and remove them.
+ * We only do so for the current app to avoid every deploy operation iterating over
+ * every app directory and all their immediate descendents. That would be a bit much I/O. */
+ remove_old_deploy_tmpdirs (deploy_base);
+
if (checksum_or_latest == NULL)
{
- g_debug ("No checksum specified, getting tip of %s from origin %s", flatpak_decomposed_get_ref (ref), origin);
+ g_info ("No checksum specified, getting tip of %s from origin %s", flatpak_decomposed_get_ref (ref), origin);
resolved_ref = flatpak_dir_read_latest (self, origin, flatpak_decomposed_get_ref (ref), NULL, cancellable, error);
if (resolved_ref == NULL)
@@ -8422,12 +8656,12 @@ flatpak_dir_deploy (FlatpakDir *self,
}
checksum = resolved_ref;
- g_debug ("tip resolved to: %s", checksum);
+ g_info ("tip resolved to: %s", checksum);
}
else
{
checksum = checksum_or_latest;
- g_debug ("Looking for checksum %s in local repo", checksum);
+ g_info ("Looking for checksum %s in local repo", checksum);
if (!ostree_repo_read_commit (self->repo, checksum, NULL, NULL, cancellable, NULL))
return flatpak_fail_error (error, FLATPAK_ERROR_INVALID_DATA, _("%s is not available"), flatpak_decomposed_get_ref (ref));
}
@@ -8444,17 +8678,15 @@ flatpak_dir_deploy (FlatpakDir *self,
_("%s commit %s already installed"), flatpak_decomposed_get_ref (ref), checksum);
g_autofree char *template = g_strdup_printf (".%s-XXXXXX", checkout_basename);
- tmp_dir_template = g_file_get_child (deploy_base, template);
- tmp_dir_path = g_file_get_path (tmp_dir_template);
- if (g_mkdtemp_full (tmp_dir_path, 0755) == NULL)
+ if (!glnx_mkdtempat (deploy_base_dfd, template, 0755, &tmp_dir_handle, NULL))
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
_("Can't create deploy directory"));
return FALSE;
}
- checkoutdir = g_file_new_for_path (tmp_dir_path);
+ checkoutdir = g_file_get_child (deploy_base, tmp_dir_handle.path);
if (!ostree_repo_read_commit (self->repo, checksum, &root, NULL, cancellable, error))
{
@@ -8470,11 +8702,12 @@ flatpak_dir_deploy (FlatpakDir *self,
options.enable_fsync = FALSE; /* We checkout to a temp dir and sync before moving it in place */
options.bareuseronly_dirs = TRUE; /* https://github.com/ostreedev/ostree/pull/927 */
checkoutdirpath = g_file_get_path (checkoutdir);
+ checkoutdir_basename = tmp_dir_handle.path; /* so checkoutdirpath = deploy_base_dfd / checkoutdir_basename */
if (subpaths == NULL || *subpaths == NULL)
{
if (!ostree_repo_checkout_at (self->repo, &options,
- AT_FDCWD, checkoutdirpath,
+ deploy_base_dfd, checkoutdir_basename,
checksum,
cancellable, error))
{
@@ -8493,7 +8726,7 @@ flatpak_dir_deploy (FlatpakDir *self,
options.subpath = "metadata";
if (!ostree_repo_checkout_at (self->repo, &options,
- AT_FDCWD, checkoutdirpath,
+ deploy_base_dfd, checkoutdir_basename,
checksum,
cancellable, error))
{
@@ -8506,13 +8739,14 @@ flatpak_dir_deploy (FlatpakDir *self,
g_autofree char *subpath = g_build_filename ("files", subpaths[i], NULL);
g_autofree char *dstpath = g_build_filename (checkoutdirpath, "/files", subpaths[i], NULL);
g_autofree char *dstpath_parent = g_path_get_dirname (dstpath);
+ g_autofree char *dstpath_relative_to_deploy_base = g_build_filename (checkoutdir_basename, "/files", subpaths[i], NULL);
g_autoptr(GFile) child = NULL;
child = g_file_resolve_relative_path (root, subpath);
if (!g_file_query_exists (child, cancellable))
{
- g_debug ("subpath %s not in tree", subpaths[i]);
+ g_info ("subpath %s not in tree", subpaths[i]);
continue;
}
@@ -8524,7 +8758,7 @@ flatpak_dir_deploy (FlatpakDir *self,
options.subpath = subpath;
if (!ostree_repo_checkout_at (self->repo, &options,
- AT_FDCWD, dstpath,
+ deploy_base_dfd, dstpath_relative_to_deploy_base,
checksum,
cancellable, error))
{
@@ -8740,7 +8974,7 @@ flatpak_dir_deploy (FlatpakDir *self,
if (!flatpak_bytes_save (deploy_data_file, deploy_data, cancellable, error))
return FALSE;
- if (!glnx_opendirat (AT_FDCWD, checkoutdirpath, TRUE, &checkoutdir_dfd, error))
+ if (!glnx_opendirat (deploy_base_dfd, checkoutdir_basename, TRUE, &checkoutdir_dfd, error))
return FALSE;
if (syncfs (checkoutdir_dfd) != 0)
@@ -8753,6 +8987,8 @@ flatpak_dir_deploy (FlatpakDir *self,
cancellable, NULL, NULL, error))
return FALSE;
+ glnx_tmpdir_unset (&tmp_dir_handle);
+
if (!flatpak_dir_set_active (self, ref, checkout_basename, cancellable, error))
return FALSE;
@@ -8836,7 +9072,7 @@ flatpak_dir_deploy_install (FlatpakDir *self,
if (strcmp (old_origin, origin) != 0)
remove_ref_from_remote = g_strdup (old_origin);
- g_debug ("Removing old deployment for reinstall");
+ g_info ("Removing old deployment for reinstall");
if (!flatpak_dir_undeploy (self, ref, old_active,
TRUE, FALSE,
cancellable, error))
@@ -9035,7 +9271,7 @@ rewrite_one_dynamic_launcher (const char *portal_desktop_dir,
}
if (!g_key_file_has_key (old_key_file, G_KEY_FILE_DESKTOP_GROUP, "X-Flatpak", NULL))
{
- g_debug ("Ignoring non-Flatpak dynamic launcher: %s", desktop_path);
+ g_info ("Ignoring non-Flatpak dynamic launcher: %s", desktop_path);
return;
}
@@ -9069,7 +9305,13 @@ rewrite_one_dynamic_launcher (const char *portal_desktop_dir,
/* Fix symlink */
link_file = g_file_new_build_filename (g_get_user_data_dir (), "applications", desktop_name, NULL);
relative_path = g_build_filename ("..", "xdg-desktop-portal", "applications", new_desktop, NULL);
- g_file_delete (link_file, NULL, NULL);
+ if (!g_file_delete (link_file, NULL, &local_error) &&
+ !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+ {
+ g_info ("Unable to delete desktop file link %s: %s", desktop_name, local_error->message);
+ g_clear_error (&local_error);
+ }
+
new_link_file = g_file_new_build_filename (g_get_user_data_dir (), "applications", new_desktop, NULL);
if (!g_file_make_symbolic_link (new_link_file, relative_path, NULL, &local_error))
{
@@ -9414,7 +9656,7 @@ flatpak_dir_setup_revokefs_fuse_mount (FlatpakDir *self,
&local_error))
{
if (g_error_matches (local_error, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED))
- g_debug ("revokefs-fuse not supported on your installation: %s", local_error->message);
+ g_info ("revokefs-fuse not supported on your installation: %s", local_error->message);
else
g_warning ("Failed to get revokefs-fuse socket from system-helper: %s", local_error->message);
@@ -10512,10 +10754,11 @@ flatpak_dir_uninstall (FlatpakDir *self,
if (flatpak_decomposed_is_runtime (ref) && !force_remove)
{
+ g_autoptr(GHashTable) runtime_app_map = NULL;
g_autoptr(GPtrArray) blocking = NULL;
/* Look for apps that need this runtime */
- blocking = flatpak_dir_list_app_refs_with_runtime (self, ref, cancellable, error);
+ blocking = flatpak_dir_list_app_refs_with_runtime (self, &runtime_app_map, ref, cancellable, error);
if (blocking == NULL)
return FALSE;
@@ -10538,7 +10781,7 @@ flatpak_dir_uninstall (FlatpakDir *self,
old_active = g_strdup (flatpak_deploy_data_get_commit (deploy_data));
- g_debug ("dropping active ref");
+ g_info ("dropping active ref");
if (!flatpak_dir_set_active (self, ref, NULL, cancellable, error))
return FALSE;
@@ -10548,7 +10791,7 @@ flatpak_dir_uninstall (FlatpakDir *self,
if (current_ref != NULL &&
flatpak_decomposed_equal (ref, current_ref))
{
- g_debug ("dropping current ref");
+ g_info ("dropping current ref");
if (!flatpak_dir_drop_current_ref (self, name, cancellable, error))
return FALSE;
}
@@ -10965,7 +11208,7 @@ flatpak_dir_undeploy_all (FlatpakDir *self,
for (i = 0; deployed[i] != NULL; i++)
{
- g_debug ("undeploying %s", deployed[i]);
+ g_info ("undeploying %s", deployed[i]);
if (!flatpak_dir_undeploy (self, ref, deployed[i], FALSE, force_remove, cancellable, error))
return FALSE;
}
@@ -10974,12 +11217,12 @@ flatpak_dir_undeploy_all (FlatpakDir *self,
was_deployed = g_file_query_exists (deploy_base, cancellable);
if (was_deployed)
{
- g_debug ("removing deploy base");
+ g_info ("removing deploy base");
if (!flatpak_rm_rf (deploy_base, cancellable, error))
return FALSE;
}
- g_debug ("cleaning up empty directories");
+ g_info ("cleaning up empty directories");
arch_dir = g_file_get_parent (deploy_base);
if (g_file_query_exists (arch_dir, cancellable) &&
!g_file_delete (arch_dir, cancellable, &temp_error))
@@ -11149,7 +11392,7 @@ flatpak_dir_prune (FlatpakDir *self,
the shared lock operation is released and we will do a prune then */
if (g_error_matches (lock_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
{
- g_debug ("Skipping prune due to in progress operation");
+ g_info ("Skipping prune due to in progress operation");
return TRUE;
}
@@ -11157,7 +11400,7 @@ flatpak_dir_prune (FlatpakDir *self,
return FALSE;
}
- g_debug ("Pruning repo");
+ g_info ("Pruning repo");
if (!ostree_repo_prune (self->repo,
OSTREE_REPO_PRUNE_FLAGS_REFS_ONLY,
0,
@@ -11168,7 +11411,7 @@ flatpak_dir_prune (FlatpakDir *self,
goto out;
formatted_freed_size = g_format_size_full (pruned_object_size_total, 0);
- g_debug ("Pruned %d/%d objects, size %s", objects_total, objects_pruned, formatted_freed_size);
+ g_info ("Pruned %d/%d objects, size %s", objects_total, objects_pruned, formatted_freed_size);
ret = TRUE;
@@ -11208,7 +11451,7 @@ flatpak_dir_update_summary (FlatpakDir *self,
g_autoptr(GError) local_error = NULL;
g_autoptr(GFile) summary_file = NULL;
- g_debug ("Deleting summary");
+ g_info ("Deleting summary");
summary_file = g_file_get_child (ostree_repo_get_path (self->repo), "summary");
@@ -11224,7 +11467,7 @@ flatpak_dir_update_summary (FlatpakDir *self,
{
g_auto(GLnxLockFile) lock = { 0, };
- g_debug ("Updating summary");
+ g_info ("Updating summary");
/* Keep a shared repo lock to avoid prunes removing objects we're relying on
* while generating the summary. */
@@ -11526,7 +11769,7 @@ flatpak_dir_lookup_cached_summary (FlatpakDir *self,
if ((now - summary->time) / G_USEC_PER_SEC < SUMMARY_CACHE_TIMEOUT_SEC &&
strcmp (url, summary->url) == 0)
{
- /* g_debug ("Using cached summary for remote %s", name); */
+ /* g_info ("Using cached summary for remote %s", name); */
*bytes_out = g_bytes_ref (summary->bytes);
if (bytes_sig_out)
{
@@ -11800,7 +12043,7 @@ flatpak_dir_remote_clear_cached_summary (FlatpakDir *self,
GCancellable *cancellable,
GError **error)
{
- g_debug ("Clearing cached summaries for remote %s", remote);
+ g_info ("Clearing cached summaries for remote %s", remote);
if (!_flatpak_dir_remote_clear_cached_summary (self, remote, NULL, cancellable, error))
return FALSE;
if (!_flatpak_dir_remote_clear_cached_summary (self, remote, ".sig", cancellable, error))
@@ -11902,9 +12145,26 @@ flatpak_dir_remote_load_cached_summary (FlatpakDir *self,
sha256 = g_compute_checksum_for_bytes (G_CHECKSUM_SHA256, mfile_bytes);
if (strcmp (sha256, checksum) != 0)
{
- g_file_delete (main_cache_file, NULL, NULL);
+ g_autoptr(GError) local_error = NULL;
+
+ if (!g_file_delete (main_cache_file, NULL, &local_error) &&
+ !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+ {
+ g_autofree char *path = g_file_get_path (main_cache_file);
+ g_info ("Unable to delete file %s: %s", path, local_error->message);
+ g_clear_error (&local_error);
+ }
+
if (sig_ext)
- g_file_delete (sig_cache_file, NULL, NULL);
+ {
+ if (!g_file_delete (sig_cache_file, NULL, &local_error) &&
+ !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+ {
+ g_autofree char *path = g_file_get_path (sig_cache_file);
+ g_info ("Unable to delete file %s: %s", path, local_error->message);
+ g_clear_error (&local_error);
+ }
+ }
return flatpak_fail_error (error, FLATPAK_ERROR_INVALID_DATA,
_("Invalid checksum for indexed summary %s read from %s"),
@@ -11966,11 +12226,11 @@ flatpak_dir_remote_fetch_summary (FlatpakDir *self,
if (!flatpak_dir_remote_load_cached_summary (self, name_or_uri, NULL, NULL, ".sig",
&summary, &summary_sig, cancellable, error))
return FALSE;
- g_debug ("Loaded summary from cache for remote ‘%s’", name_or_uri);
+ g_info ("Loaded summary from cache for remote ‘%s’", name_or_uri);
}
else
{
- g_debug ("Fetching summary file for remote ‘%s’", name_or_uri);
+ g_info ("Fetching summary file for remote ‘%s’", name_or_uri);
if (!ostree_repo_remote_fetch_summary (self->repo, name_or_uri,
&summary, &summary_sig,
cancellable,
@@ -12045,7 +12305,7 @@ remote_verify_signature (OstreeRepo *repo,
}
static GBytes *
-load_uri_with_fallback (SoupSession *soup_session,
+load_uri_with_fallback (FlatpakHttpSession *http_session,
const char *uri,
const char *uri2,
FlatpakHTTPFlags flags,
@@ -12056,7 +12316,7 @@ load_uri_with_fallback (SoupSession *soup_session,
g_autoptr(GError) local_error = NULL;
GBytes *res;
- res = flatpak_load_uri (soup_session, uri, flags, token,
+ res = flatpak_load_uri (http_session, uri, flags, token,
NULL, NULL, NULL,
cancellable, &local_error);
if (res)
@@ -12068,7 +12328,7 @@ load_uri_with_fallback (SoupSession *soup_session,
return NULL;
}
- return flatpak_load_uri (soup_session, uri2, flags, token,
+ return flatpak_load_uri (http_session, uri2, flags, token,
NULL, NULL, NULL,
cancellable, error);
}
@@ -12092,7 +12352,7 @@ flatpak_dir_remote_fetch_summary_index (FlatpakDir *self,
g_autoptr(GBytes) index_sig = NULL;
gboolean gpg_verify_summary;
- ensure_soup_session (self);
+ ensure_http_session (self);
if (!ostree_repo_remote_get_url (self->repo, name_or_uri, &url, error))
return FALSE;
@@ -12130,7 +12390,7 @@ flatpak_dir_remote_fetch_summary_index (FlatpakDir *self,
g_propagate_error (error, g_steal_pointer (&cache_error));
return FALSE;
}
- g_debug ("Loaded summary index from cache for remote ‘%s’", name_or_uri);
+ g_info ("Loaded summary index from cache for remote ‘%s’", name_or_uri);
index = g_steal_pointer (&cached_index);
if (gpg_verify_summary)
@@ -12142,9 +12402,9 @@ flatpak_dir_remote_fetch_summary_index (FlatpakDir *self,
g_autoptr(GBytes) dl_index = NULL;
gboolean used_download = FALSE;
- g_debug ("Fetching summary index file for remote ‘%s’", name_or_uri);
+ g_info ("Fetching summary index file for remote ‘%s’", name_or_uri);
- dl_index = flatpak_load_uri (self->soup_session, index_url, 0, NULL,
+ dl_index = flatpak_load_uri (self->http_session, index_url, 0, NULL,
NULL, NULL, NULL,
cancellable, error);
if (dl_index == NULL)
@@ -12173,7 +12433,7 @@ flatpak_dir_remote_fetch_summary_index (FlatpakDir *self,
g_autoptr(GError) dl_sig_error = NULL;
g_autoptr (GBytes) dl_index_sig = NULL;
- dl_index_sig = load_uri_with_fallback (self->soup_session, index_sig_url, index_sig_url2, 0, NULL,
+ dl_index_sig = load_uri_with_fallback (self->http_session, index_sig_url, index_sig_url2, 0, NULL,
cancellable, &dl_sig_error);
if (dl_index_sig == NULL)
{
@@ -12242,7 +12502,7 @@ flatpak_dir_remote_fetch_indexed_summary (FlatpakDir *self,
g_autofree char *checksum = NULL;
g_autofree char *cache_name = NULL;
- ensure_soup_session (self);
+ ensure_http_session (self);
if (!ostree_repo_remote_get_url (self->repo, name_or_uri, &url, error))
return FALSE;
@@ -12313,13 +12573,13 @@ flatpak_dir_remote_fetch_indexed_summary (FlatpakDir *self,
g_autofree char *delta_filename = g_strconcat (old_checksum, "-", checksum, ".delta", NULL);
g_autofree char *delta_url = g_build_filename (url, "summaries", delta_filename, NULL);
- g_debug ("Fetching indexed summary delta %s for remote ‘%s’", delta_filename, name_or_uri);
+ g_info ("Fetching indexed summary delta %s for remote ‘%s’", delta_filename, name_or_uri);
- g_autoptr(GBytes) delta = flatpak_load_uri (self->soup_session, delta_url, 0, NULL,
+ g_autoptr(GBytes) delta = flatpak_load_uri (self->http_session, delta_url, 0, NULL,
NULL, NULL, NULL,
cancellable, &delta_error);
if (delta == NULL)
- g_debug ("Failed to load delta, falling back: %s", delta_error->message);
+ g_info ("Failed to load delta, falling back: %s", delta_error->message);
else
{
g_autoptr(GBytes) applied = flatpak_summary_apply_diff (old_summary, delta, &delta_error);
@@ -12340,9 +12600,9 @@ flatpak_dir_remote_fetch_indexed_summary (FlatpakDir *self,
if (summary == NULL)
{
g_autofree char *filename = g_strconcat (checksum, ".gz", NULL);
- g_debug ("Fetching indexed summary file %s for remote ‘%s’", filename, name_or_uri);
+ g_info ("Fetching indexed summary file %s for remote ‘%s’", filename, name_or_uri);
g_autofree char *subsummary_url = g_build_filename (url, "summaries", filename, NULL);
- summary_z = flatpak_load_uri (self->soup_session, subsummary_url, 0, NULL,
+ summary_z = flatpak_load_uri (self->http_session, subsummary_url, 0, NULL,
NULL, NULL, NULL,
cancellable, error);
if (summary_z == NULL)
@@ -12372,7 +12632,7 @@ flatpak_dir_remote_fetch_indexed_summary (FlatpakDir *self,
}
}
else
- g_debug ("Loaded indexed summary file %s from cache for remote ‘%s’", checksum, name_or_uri);
+ g_info ("Loaded indexed summary file %s from cache for remote ‘%s’", checksum, name_or_uri);
/* Cache in memory */
if (!is_local && !only_cached)
@@ -12508,7 +12768,7 @@ _flatpak_dir_get_remote_state (FlatpakDir *self,
got_summary = TRUE;
if (optional && !g_cancellable_is_cancelled (cancellable))
{
- g_debug ("Failed to download optional summary index: %s", local_error->message);
+ g_info ("Failed to download optional summary index: %s", local_error->message);
state->summary_fetch_error = g_steal_pointer (&local_error);
}
else
@@ -12534,7 +12794,7 @@ _flatpak_dir_get_remote_state (FlatpakDir *self,
{
if (optional && !g_cancellable_is_cancelled (cancellable))
{
- g_debug ("Failed to download optional summary: %s", local_error->message);
+ g_info ("Failed to download optional summary: %s", local_error->message);
state->summary_fetch_error = g_steal_pointer (&local_error);
}
else
@@ -12599,7 +12859,7 @@ _flatpak_dir_get_remote_state (FlatpakDir *self,
var_subsummary_peek_checksum (subsummary, &checksum_bytes_len);
if (G_UNLIKELY (checksum_bytes_len != OSTREE_SHA256_DIGEST_LEN))
{
- g_debug ("Invalid checksum for digested summary, not using cache");
+ g_info ("Invalid checksum for digested summary, not using cache");
continue;
}
@@ -12749,7 +13009,7 @@ populate_hash_table_from_refs_map (GHashTable *ret_all_refs,
continue; /* New timestamp is older, skip this commit */
}
- new_timestamp = g_memdup (&timestamp, sizeof (guint64));
+ new_timestamp = g_memdup2 (&timestamp, sizeof (guint64));
}
g_hash_table_replace (ret_all_refs, g_steal_pointer (&decomposed), ostree_checksum_from_bytes (csum_bytes));
@@ -14216,18 +14476,27 @@ parse_ref_file (GKeyFile *keyfile,
gpg_data = g_bytes_new_take (g_steal_pointer (&decoded), decoded_len);
}
- collection_id = g_key_file_get_string (keyfile, FLATPAK_REF_GROUP,
- FLATPAK_REF_DEPLOY_COLLECTION_ID_KEY, NULL);
+ /* We have a hierarchy of keys for setting the collection ID, which all have
+ * the same effect. The only difference is which versions of Flatpak support
+ * them, and therefore what P2P implementation is enabled by them:
+ * DeploySideloadCollectionID: supported by Flatpak >= 1.12.8 (1.7.1
+ * introduced sideload support but this key was added late)
+ * DeployCollectionID: supported by Flatpak >= 1.0.6
+ * CollectionID: supported by Flatpak >= 0.9.8
+ */
+ collection_id = flatpak_keyfile_get_string_non_empty (keyfile, FLATPAK_REF_GROUP,
+ FLATPAK_REF_DEPLOY_SIDELOAD_COLLECTION_ID_KEY);
- if (collection_id != NULL && *collection_id == '\0')
- g_clear_pointer (&collection_id, g_free);
if (collection_id == NULL)
{
- collection_id = g_key_file_get_string (keyfile, FLATPAK_REF_GROUP,
- FLATPAK_REF_COLLECTION_ID_KEY, NULL);
+ collection_id = flatpak_keyfile_get_string_non_empty (keyfile, FLATPAK_REF_GROUP,
+ FLATPAK_REF_DEPLOY_COLLECTION_ID_KEY);
+ }
+ if (collection_id == NULL)
+ {
+ collection_id = flatpak_keyfile_get_string_non_empty (keyfile, FLATPAK_REF_GROUP,
+ FLATPAK_REF_COLLECTION_ID_KEY);
}
- if (collection_id != NULL && *collection_id == '\0')
- g_clear_pointer (&collection_id, g_free);
if (collection_id != NULL && gpg_data == NULL)
return flatpak_fail_error (error, FLATPAK_ERROR_INVALID_DATA, _("Collection ID requires GPG key to be provided"));
@@ -14682,8 +14951,8 @@ flatpak_dir_modify_remote (FlatpakDir *self,
return FALSE;
/* XXX If we ever add internationalization, use ngettext() here. */
- g_debug ("Imported %u GPG key%s to remote \"%s\"",
- imported, (imported == 1) ? "" : "s", remote_name);
+ g_info ("Imported %u GPG key%s to remote \"%s\"",
+ imported, (imported == 1) ? "" : "s", remote_name);
}
filter_path = g_key_file_get_value (new_config, group, "xa.filter", NULL);
@@ -14704,11 +14973,11 @@ flatpak_dir_modify_remote (FlatpakDir *self,
if (!g_file_replace_contents (filter_copy, backup_data_copy, strlen (backup_data_copy),
NULL, FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, cancellable, &local_error))
- g_debug ("Failed to save backup copy of filter file %s: %s\n", filter_path, local_error->message);
+ g_info ("Failed to save backup copy of filter file %s: %s\n", filter_path, local_error->message);
}
else
{
- g_debug ("Failed to read filter %s file while making a backup copy: %s\n", filter_path, local_error->message);
+ g_info ("Failed to read filter %s file while making a backup copy: %s\n", filter_path, local_error->message);
}
}
@@ -15031,14 +15300,14 @@ flatpak_dir_update_remote_configuration (FlatpakDir *self,
if (!gpg_verify_summary || !gpg_verify)
{
- g_debug ("Ignoring automatic updates for system-helper remotes without gpg signatures");
+ g_info ("Ignoring automatic updates for system-helper remotes without gpg signatures");
return TRUE;
}
if ((state->summary != NULL && state->summary_sig_bytes == NULL) ||
(state->index != NULL && state->index_sig_bytes == NULL))
{
- g_debug ("Can't update remote configuration as user, no GPG signature");
+ g_info ("Can't update remote configuration as user, no GPG signature");
return TRUE;
}
@@ -15175,9 +15444,9 @@ add_related (FlatpakDir *self,
flatpak_find_unmaintained_extension_dir_if_exists (id, arch, branch, NULL);
if (unmaintained_path != NULL && deploy_data == NULL)
{
- g_debug ("Skipping related extension ‘%s’ because it is already "
- "installed as an unmaintained extension in ‘%s’.",
- id, flatpak_file_get_path_cached (unmaintained_path));
+ g_info ("Skipping related extension ‘%s’ because it is already "
+ "installed as an unmaintained extension in ‘%s’.",
+ id, flatpak_file_get_path_cached (unmaintained_path));
download = FALSE;
}
@@ -15490,7 +15759,7 @@ flatpak_dir_find_remote_related (FlatpakDir *self,
metadata_file = g_file_get_child (deploy_dir, "metadata");
if (!g_file_load_contents (metadata_file, cancellable, &metadata, NULL, NULL, NULL))
{
- g_debug ("No metadata in local deploy");
+ g_info ("No metadata in local deploy");
/* No metadata => no related, but no error */
}
}
@@ -15777,11 +16046,14 @@ flatpak_dir_find_local_related (FlatpakDir *self,
if (deploy_data == NULL)
return NULL;
- metadata = g_file_get_child (deploy_dir, "metadata");
- if (!g_file_load_contents (metadata, cancellable, &metadata_contents, NULL, NULL, NULL))
+ if (flatpak_deploy_data_get_extension_of (deploy_data) == NULL)
{
- g_debug ("No metadata in local deploy");
- /* No metadata => no related, but no error */
+ metadata = g_file_get_child (deploy_dir, "metadata");
+ if (!g_file_load_contents (metadata, cancellable, &metadata_contents, NULL, NULL, NULL))
+ {
+ g_info ("No metadata in local deploy");
+ /* No metadata => no related, but no error */
+ }
}
}
else
@@ -15793,7 +16065,7 @@ flatpak_dir_find_local_related (FlatpakDir *self,
g_autoptr(GVariant) commit_metadata = g_variant_get_child_value (commit_data, 0);
g_variant_lookup (commit_metadata, "xa.metadata", "s", &metadata_contents);
if (metadata_contents == NULL)
- g_debug ("No xa.metadata in local commit %s ref %s", checksum, flatpak_decomposed_get_ref (ref));
+ g_info ("No xa.metadata in local commit %s ref %s", checksum, flatpak_decomposed_get_ref (ref));
}
}
@@ -15819,7 +16091,7 @@ flatpak_dir_get_remote_auto_install_authenticator_ref (FlatpakDir *self,
g_autoptr(GError) local_error = NULL;
ref = flatpak_decomposed_new_from_parts (FLATPAK_KINDS_APP, authenticator_name, flatpak_get_arch (), "autoinstall", &local_error);
if (ref == NULL)
- g_debug ("Invalid authenticator ref: %s\n", local_error->message);
+ g_info ("Invalid authenticator ref: %s\n", local_error->message);
}
return g_steal_pointer (&ref);
@@ -16243,8 +16515,8 @@ flatpak_dir_delete_mirror_refs (FlatpakDir *self,
{
if (g_strv_contains ((const char * const *)ignore_collections->pdata, c_r->collection_id))
{
- g_debug ("Ignoring collection-ref (%s, %s) since its remote is disabled or it matches the repo collection ID",
- c_r->collection_id, c_r->ref_name);
+ g_info ("Ignoring collection-ref (%s, %s) since its remote is disabled or it matches the repo collection ID",
+ c_r->collection_id, c_r->ref_name);
continue;
}
diff --git a/common/flatpak-enum-types.c.template b/common/flatpak-enum-types.c.template
index 5ce936c7..74c70869 100644
--- a/common/flatpak-enum-types.c.template
+++ b/common/flatpak-enum-types.c.template
@@ -8,7 +8,7 @@
/*** END file-header ***/
/*** BEGIN file-production ***/
-/* enumerations from "@filename@" */
+/* enumerations from "@basename@" */
/*** END file-production ***/
/*** BEGIN value-header ***/
diff --git a/common/flatpak-enum-types.h.template b/common/flatpak-enum-types.h.template
index 5b67b9c0..9b343088 100644
--- a/common/flatpak-enum-types.h.template
+++ b/common/flatpak-enum-types.h.template
@@ -9,7 +9,7 @@ G_BEGIN_DECLS
/*** BEGIN file-production ***/
-/* enumerations from "@filename@" */
+/* enumerations from "@basename@" */
/*** END file-production ***/
/*** BEGIN value-header ***/
diff --git a/common/flatpak-exports.c b/common/flatpak-exports.c
index 6ac9dd46..15acc5b8 100644
--- a/common/flatpak-exports.c
+++ b/common/flatpak-exports.c
@@ -1,4 +1,4 @@
-/*
+/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
* Copyright © 2014-2019 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
@@ -93,6 +93,40 @@ is_export_mode (int mode)
|| mode == FAKE_MODE_SYMLINK);
}
+static inline const char *
+export_mode_to_verb (int mode)
+{
+ switch (mode)
+ {
+ case FAKE_MODE_DIR:
+ return "ensure existence of directory";
+
+ case FAKE_MODE_SYMLINK:
+ return "create symbolic link";
+
+ default:
+ break;
+ }
+
+ switch ((FlatpakFilesystemMode) mode)
+ {
+ case FLATPAK_FILESYSTEM_MODE_READ_ONLY:
+ return "export read-only";
+
+ case FLATPAK_FILESYSTEM_MODE_CREATE:
+ return "create and export read/write";
+
+ case FLATPAK_FILESYSTEM_MODE_READ_WRITE:
+ return "export read/write";
+
+ case FLATPAK_FILESYSTEM_MODE_NONE:
+ return "replace with tmpfs";
+
+ default:
+ return "[use unknown/invalid mode?]";
+ }
+}
+
typedef struct
{
char *path;
@@ -394,6 +428,8 @@ flatpak_exports_append_bwrap_args (FlatpakExports *exports,
g_qsort_with_data (keys, n_keys, sizeof (char *), (GCompareDataFunc) flatpak_strcmp0_ptr, NULL);
+ g_debug ("Converting FlatpakExports to bwrap arguments...");
+
for (l = eps; l != NULL; l = l->next)
{
ExportedPath *ep = l->data;
@@ -403,7 +439,14 @@ flatpak_exports_append_bwrap_args (FlatpakExports *exports,
if (ep->mode == FAKE_MODE_SYMLINK)
{
- if (!path_parent_is_mapped (keys, n_keys, exports->hash, path))
+ g_debug ("\"%s\" is meant to be a symlink", path);
+
+ if (path_parent_is_mapped (keys, n_keys, exports->hash, path))
+ {
+ g_debug ("Not creating \"%s\" as symlink because its parent is "
+ "already mapped", path);
+ }
+ else
{
g_autofree char *resolved = flatpak_exports_resolve_link_in_host (exports,
path,
@@ -412,30 +455,60 @@ flatpak_exports_append_bwrap_args (FlatpakExports *exports,
{
g_autofree char *parent = g_path_get_dirname (path);
g_autofree char *relative = make_relative (parent, resolved);
+
+ g_debug ("Resolved \"%s\" to \"%s\" in host", path, resolved);
+ g_debug ("Creating \"%s\" -> \"%s\" in sandbox", path, relative);
flatpak_bwrap_add_args (bwrap, "--symlink", relative, path, NULL);
}
+ else
+ {
+ g_debug ("Unable to resolve \"%s\" in host, skipping", path);
+ }
}
}
else if (ep->mode == FAKE_MODE_TMPFS)
{
+ g_debug ("\"%s\" is meant to be a tmpfs or empty directory", path);
+
/* Mount a tmpfs to hide the subdirectory, but only if there
is a pre-existing dir we can mount the path on. */
if (path_is_dir (exports, path))
{
if (!path_parent_is_mapped (keys, n_keys, exports->hash, path))
/* If the parent is not mapped, it will be a tmpfs, no need to mount another one */
- flatpak_bwrap_add_args (bwrap, "--dir", path, NULL);
+ {
+ g_debug ("Parent of \"%s\" is not mapped, creating empty directory", path);
+ flatpak_bwrap_add_args (bwrap, "--dir", path, NULL);
+ }
else
- flatpak_bwrap_add_args (bwrap, "--tmpfs", path, NULL);
+ {
+ g_debug ("Parent of \"%s\" is mapped, creating tmpfs to shadow it", path);
+ flatpak_bwrap_add_args (bwrap, "--tmpfs", path, NULL);
+ }
+ }
+ else
+ {
+ g_debug ("Not a directory, skipping: \"%s\"", path);
}
}
else if (ep->mode == FAKE_MODE_DIR)
{
+ g_debug ("\"%s\" is meant to be a directory", path);
+
if (path_is_dir (exports, path))
- flatpak_bwrap_add_args (bwrap, "--dir", path, NULL);
+ {
+ g_debug ("Ensuring \"%s\" is created as a directory", path);
+ flatpak_bwrap_add_args (bwrap, "--dir", path, NULL);
+ }
+ else
+ {
+ g_debug ("Not a directory, skipping: \"%s\"", path);
+ }
}
else
{
+ g_debug ("\"%s\" is meant to be shared (ro or rw) with the container",
+ path);
flatpak_bwrap_add_args (bwrap,
(ep->mode == FLATPAK_FILESYSTEM_MODE_READ_ONLY) ? "--ro-bind" : "--bind",
path, path, NULL);
@@ -679,9 +752,29 @@ do_export_path (FlatpakExports *exports,
ep->path = g_strdup (path);
if (old_ep != NULL)
- ep->mode = MAX (old_ep->mode, mode);
+ {
+ if (old_ep->mode < mode)
+ {
+ g_debug ("Increasing export mode from \"%s\" to \"%s\": %s",
+ export_mode_to_verb (old_ep->mode),
+ export_mode_to_verb (mode),
+ path);
+ ep->mode = mode;
+ }
+ else
+ {
+ g_debug ("Not changing export mode from \"%s\" to \"%s\": %s",
+ export_mode_to_verb (old_ep->mode),
+ export_mode_to_verb (mode),
+ path);
+ ep->mode = old_ep->mode;
+ }
+ }
else
- ep->mode = mode;
+ {
+ g_debug ("Will %s: %s", export_mode_to_verb (mode), path);
+ ep->mode = mode;
+ }
g_hash_table_replace (exports->hash, ep->path, ep);
}
@@ -784,36 +877,53 @@ _exports_path_expose (FlatpakExports *exports,
g_return_val_if_fail (is_export_mode (mode), FALSE);
+ g_debug ("Trying to %s: %s", export_mode_to_verb (mode), path);
+
if (level > 40) /* 40 is the current kernel ELOOP check */
{
- g_debug ("Expose too deep, bail");
+ g_info ("Expose too deep, bail");
return FALSE;
}
if (!g_path_is_absolute (path))
{
- g_debug ("Not exposing relative path %s", path);
+ g_info ("Not exposing relative path %s", path);
return FALSE;
}
/* Check if it exists at all */
o_path_fd = flatpak_exports_open_in_host (exports, path, O_PATH | O_NOFOLLOW);
+
if (o_path_fd == -1)
- return FALSE;
+ {
+ g_info ("Unable to open path %s to %s: %s",
+ path, export_mode_to_verb (mode), g_strerror (errno));
+ return FALSE;
+ }
if (fstat (o_path_fd, &st) != 0)
- return FALSE;
+ {
+ g_info ("Unable to get file type of %s: %s", path, g_strerror (errno));
+ return FALSE;
+ }
/* Don't expose weird things */
if (!(S_ISDIR (st.st_mode) ||
S_ISREG (st.st_mode) ||
S_ISLNK (st.st_mode) ||
S_ISSOCK (st.st_mode)))
- return FALSE;
+ {
+ g_info ("%s has unsupported file type 0o%o", path, st.st_mode & S_IFMT);
+ return FALSE;
+ }
/* O_PATH + fstatfs is the magic that we need to statfs without automounting the target */
if (fstatfs (o_path_fd, &stfs) != 0)
- return FALSE;
+ {
+ g_info ("Unable to get filesystem information for %s: %s",
+ path, g_strerror (errno));
+ return FALSE;
+ }
if (stfs.f_type == AUTOFS_SUPER_MAGIC ||
(G_UNLIKELY (exports->test_flags & FLATPAK_EXPORTS_TEST_FLAGS_AUTOFS) &&
@@ -821,7 +931,7 @@ _exports_path_expose (FlatpakExports *exports,
{
if (!check_if_autofs_works (exports, path))
{
- g_debug ("ignoring blocking autofs path %s", path);
+ g_info ("ignoring blocking autofs path %s", path);
return FALSE;
}
}
@@ -836,7 +946,7 @@ _exports_path_expose (FlatpakExports *exports,
create the parents for them anyway */
if (flatpak_has_path_prefix (path, dont_export_in[i]))
{
- g_debug ("skipping export for path %s", path);
+ g_info ("skipping export for path %s in unsupported prefix", path);
return FALSE;
}
}
@@ -846,7 +956,7 @@ _exports_path_expose (FlatpakExports *exports,
/* Same as /usr, but for the directories that get merged into /usr */
if (flatpak_has_path_prefix (path, flatpak_abs_usrmerged_dirs[i]))
{
- g_debug ("skipping export for path %s", path);
+ g_info ("skipping export for path %s in a /usr-merged directory", path);
return FALSE;
}
}
@@ -860,27 +970,49 @@ _exports_path_expose (FlatpakExports *exports,
if (slash)
*slash = 0;
- if (path_is_symlink (exports, path) && !never_export_as_symlink (path))
+ if (!path_is_symlink (exports, path))
+ {
+ g_debug ("%s is not a symlink", path);
+ }
+ else if (never_export_as_symlink (path))
+ {
+ g_debug ("%s is a symlink, but we avoid exporting it as such", path);
+ }
+ else
{
- g_autofree char *resolved = flatpak_exports_resolve_link_in_host (exports, path, NULL);
+ g_autoptr(GError) error = NULL;
+ g_autofree char *resolved = flatpak_exports_resolve_link_in_host (exports, path, &error);
g_autofree char *new_target = NULL;
if (resolved)
{
+ g_debug ("%s is a symlink, resolved to %s", path, resolved);
+
if (slash)
new_target = g_build_filename (resolved, slash + 1, NULL);
else
new_target = g_strdup (resolved);
+ g_debug ("Trying to export the target instead: %s", new_target);
+
if (_exports_path_expose (exports, mode, new_target, level + 1))
{
do_export_path (exports, path, FAKE_MODE_SYMLINK);
return TRUE;
}
- }
- return FALSE;
+ g_debug ("Could not export target %s, so ignoring %s",
+ new_target, path);
+ return FALSE;
+ }
+ else
+ {
+ g_debug ("%s is a symlink but we were unable to resolve it: %s",
+ path, error->message);
+ return FALSE;
+ }
}
+
if (slash)
*slash = '/';
}
diff --git a/common/flatpak-installation.c b/common/flatpak-installation.c
index aa11ebec..c19ef6bc 100644
--- a/common/flatpak-installation.c
+++ b/common/flatpak-installation.c
@@ -1,4 +1,4 @@
-/*
+/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
* Copyright © 2015 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
@@ -320,11 +320,11 @@ flatpak_installation_new_system_with_id (const char *id,
&local_error);
if (installation == NULL)
{
- g_debug ("Error creating Flatpak installation: %s", local_error->message);
+ g_info ("Error creating Flatpak installation: %s", local_error->message);
g_propagate_error (error, g_steal_pointer (&local_error));
}
- g_debug ("Found Flatpak installation for '%s'", id);
+ g_info ("Found Flatpak installation for '%s'", id);
return g_steal_pointer (&installation);
}
@@ -1013,7 +1013,7 @@ transaction_ready (FlatpakTransaction *transaction,
if (type == FLATPAK_TRANSACTION_OPERATION_UNINSTALL)
{
const char *ref = flatpak_transaction_operation_get_ref (op);
- g_debug ("Update transaction wants to uninstall %s", ref);
+ g_info ("Update transaction wants to uninstall %s", ref);
continue;
}
@@ -1105,7 +1105,7 @@ flatpak_installation_list_installed_refs_for_update (FlatpakInstallation *self,
if (g_error_matches (local_error, FLATPAK_ERROR, FLATPAK_ERROR_REMOTE_NOT_FOUND))
{
- g_debug ("%s: Unable to update %s: %s", G_STRFUNC, ref, local_error->message);
+ g_info ("%s: Unable to update %s: %s", G_STRFUNC, ref, local_error->message);
g_clear_error (&local_error);
}
else
@@ -1159,7 +1159,7 @@ flatpak_installation_list_installed_refs_for_update (FlatpakInstallation *self,
if (!g_hash_table_contains (installed_refs_for_update_set, op_ref))
{
g_hash_table_add (installed_refs_for_update_set, (char *)op_ref);
- g_debug ("%s: Installed ref %s needs update", G_STRFUNC, op_ref);
+ g_info ("%s: Installed ref %s needs update", G_STRFUNC, op_ref);
g_ptr_array_add (installed_refs_for_update,
g_object_ref (installed_ref));
}
@@ -1177,7 +1177,7 @@ flatpak_installation_list_installed_refs_for_update (FlatpakInstallation *self,
if (installed_ref != NULL)
{
g_hash_table_add (installed_refs_for_update_set, (char *)related_op_ref);
- g_debug ("%s: Installed ref %s needs update", G_STRFUNC, related_op_ref);
+ g_info ("%s: Installed ref %s needs update", G_STRFUNC, related_op_ref);
g_ptr_array_add (installed_refs_for_update,
g_object_ref (installed_ref));
}
@@ -1200,7 +1200,7 @@ flatpak_installation_list_installed_refs_for_update (FlatpakInstallation *self,
if (!g_hash_table_contains (installed_refs_for_update_set, rebased_ref))
{
g_hash_table_add (installed_refs_for_update_set, (char *)rebased_ref);
- g_debug ("%s: Installed ref %s needs update", G_STRFUNC, rebased_ref);
+ g_info ("%s: Installed ref %s needs update", G_STRFUNC, rebased_ref);
g_ptr_array_add (installed_refs_for_update,
g_object_ref (installed_ref));
}
diff --git a/common/flatpak-installed-ref.c b/common/flatpak-installed-ref.c
index 02c60b67..c3f8048c 100644
--- a/common/flatpak-installed-ref.c
+++ b/common/flatpak-installed-ref.c
@@ -1,4 +1,4 @@
-/*
+/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
* Copyright © 2015 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
diff --git a/common/flatpak-instance.c b/common/flatpak-instance.c
index 234a4b8b..3623043d 100644
--- a/common/flatpak-instance.c
+++ b/common/flatpak-instance.c
@@ -1,4 +1,4 @@
-/*
+/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
* Copyright © 2018 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
@@ -322,7 +322,7 @@ get_instance_info (const char *dir)
key_file = g_key_file_new ();
if (!g_key_file_load_from_file (key_file, file, G_KEY_FILE_NONE, &error))
{
- g_debug ("Failed to load instance info file '%s': %s", file, error->message);
+ g_info ("Failed to load instance info file '%s': %s", file, error->message);
return NULL;
}
@@ -344,21 +344,21 @@ get_child_pid (const char *dir)
if (!g_file_get_contents (file, &contents, &length, &error))
{
- g_debug ("Failed to load bwrapinfo.json file '%s': %s", file, error->message);
+ g_info ("Failed to load bwrapinfo.json file '%s': %s", file, error->message);
return 0;
}
parser = json_parser_new ();
if (!json_parser_load_from_data (parser, contents, length, &error))
{
- g_debug ("Failed to parse bwrapinfo.json file '%s': %s", file, error->message);
+ g_info ("Failed to parse bwrapinfo.json file '%s': %s", file, error->message);
return 0;
}
node = json_parser_get_root (parser);
if (!node)
{
- g_debug ("Failed to parse bwrapinfo.json file '%s': %s", file, "empty");
+ g_info ("Failed to parse bwrapinfo.json file '%s': %s", file, "empty");
return 0;
}
@@ -378,7 +378,7 @@ get_pid (const char *dir)
if (!g_file_get_contents (file, &contents, NULL, &error))
{
- g_debug ("Failed to load pid file '%s': %s", file, error->message);
+ g_info ("Failed to load pid file '%s': %s", file, error->message);
return 0;
}
@@ -730,7 +730,8 @@ flatpak_instance_allocate_id (char **host_dir_out,
g_return_val_if_fail (lock_fd_out != NULL, NULL);
g_return_val_if_fail (*lock_fd_out == -1, NULL);
- g_mkdir_with_parents (base_dir, 0755);
+ if (g_mkdir_with_parents (base_dir, 0755) != 0)
+ return NULL;
flatpak_instance_iterate_all_and_gc (NULL);
@@ -766,7 +767,7 @@ flatpak_instance_allocate_id (char **host_dir_out,
if (lock_fd != -1 && fcntl (lock_fd, F_SETLK, &l) == 0)
{
*lock_fd_out = glnx_steal_fd (&lock_fd);
- g_debug ("Allocated instance id %s", instance_id);
+ g_info ("Allocated instance id %s", instance_id);
*host_dir_out = g_steal_pointer (&instance_dir);
return g_steal_pointer (&instance_id);
}
@@ -989,7 +990,7 @@ flatpak_instance_gc_per_app_dirs (const char *instance_id,
if (statbuf.st_mtime + 3 >= time (NULL))
return glnx_throw (error, "lock file too recent, avoiding race condition");
- g_debug ("Cleaning up per-app-ID state for %s", app_id);
+ g_info ("Cleaning up per-app-ID state for %s", app_id);
/* /dev/shm is offloaded onto the host's /dev/shm to get consistent
* free space behaviour and make sure it's actually in RAM. It could
@@ -1017,13 +1018,13 @@ flatpak_instance_gc_per_app_dirs (const char *instance_id,
g_assert (g_str_has_prefix (path, "/dev/shm/"));
if (unlinkat (per_app_dir_fd, "dev-shm", 0) != 0)
- g_debug ("Unable to clean up %s/%s: %s",
- per_app_dir, "dev-shm", g_strerror (errno));
+ g_info ("Unable to clean up %s/%s: %s",
+ per_app_dir, "dev-shm", g_strerror (errno));
if (!glnx_shutil_rm_rf_at (AT_FDCWD, path, NULL, &local_error))
{
- g_debug ("Unable to clean up %s: %s",
- path, local_error->message);
+ g_info ("Unable to clean up %s: %s",
+ path, local_error->message);
g_clear_error (&local_error);
}
}
@@ -1034,9 +1035,9 @@ flatpak_instance_gc_per_app_dirs (const char *instance_id,
}
else
{
- g_debug ("%s/%s no longer points to the expected directory and "
- "was removed: %s",
- per_app_dir, "dev-shm", local_error->message);
+ g_info ("%s/%s no longer points to the expected directory and "
+ "was removed: %s",
+ per_app_dir, "dev-shm", local_error->message);
g_clear_error (&local_error);
}
}
@@ -1048,8 +1049,8 @@ flatpak_instance_gc_per_app_dirs (const char *instance_id,
* and not a symlink. If it's a symlink, we'll just unlink it. */
if (!glnx_shutil_rm_rf_at (per_app_dir_fd, "tmp", NULL, &local_error))
{
- g_debug ("Unable to clean up %s/tmp: %s", per_app_dir,
- local_error->message);
+ g_info ("Unable to clean up %s/tmp: %s", per_app_dir,
+ local_error->message);
g_clear_error (&local_error);
}
@@ -1103,10 +1104,10 @@ flatpak_instance_iterate_all_and_gc (GPtrArray *out_instances)
g_autoptr(GError) local_error = NULL;
/* The instance is not used, remove it */
- g_debug ("Cleaning up unused container id %s", dent->d_name);
+ g_info ("Cleaning up unused container id %s", dent->d_name);
if (!flatpak_instance_gc_per_app_dirs (dent->d_name, &local_error))
- flatpak_debug2 ("Not cleaning up per-app dir: %s", local_error->message);
+ g_debug ("Not cleaning up per-app dir: %s", local_error->message);
glnx_shutil_rm_rf_at (iter.fd, dent->d_name, NULL, NULL);
continue;
diff --git a/common/flatpak-json-oci.c b/common/flatpak-json-oci.c
index 3b683d6d..4a429efc 100644
--- a/common/flatpak-json-oci.c
+++ b/common/flatpak-json-oci.c
@@ -1,4 +1,4 @@
-/*
+/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
* Copyright (C) 2015 Red Hat, Inc
*
* This file is free software; you can redistribute it and/or modify it
diff --git a/common/flatpak-json.c b/common/flatpak-json.c
index 4d684fac..dfabdbe9 100644
--- a/common/flatpak-json.c
+++ b/common/flatpak-json.c
@@ -1,4 +1,4 @@
-/*
+/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
* Copyright (C) 2015 Red Hat, Inc
*
* This file is free software; you can redistribute it and/or modify it
diff --git a/common/flatpak-oci-registry-private.h b/common/flatpak-oci-registry-private.h
index 66d69f08..83ec8467 100644
--- a/common/flatpak-oci-registry-private.h
+++ b/common/flatpak-oci-registry-private.h
@@ -160,24 +160,24 @@ FlatpakOciSignature *flatpak_oci_verify_signature (OstreeRepo *repo,
GBytes *signature,
GError **error);
-gboolean flatpak_oci_index_ensure_cached (SoupSession *soup_session,
- const char *uri,
- GFile *index,
- char **index_uri_out,
- GCancellable *cancellable,
- GError **error);
+gboolean flatpak_oci_index_ensure_cached (FlatpakHttpSession *http_session,
+ const char *uri,
+ GFile *index,
+ char **index_uri_out,
+ GCancellable *cancellable,
+ GError **error);
GVariant *flatpak_oci_index_make_summary (GFile *index,
const char *index_uri,
GCancellable *cancellable,
GError **error);
-GBytes *flatpak_oci_index_make_appstream (SoupSession *soup_session,
- GFile *index,
- const char *index_uri,
- const char *arch,
- int icons_dfd,
- GCancellable *cancellable,
- GError **error);
+GBytes *flatpak_oci_index_make_appstream (FlatpakHttpSession *http_session,
+ GFile *index,
+ const char *index_uri,
+ const char *arch,
+ int icons_dfd,
+ GCancellable *cancellable,
+ GError **error);
#endif /* __FLATPAK_OCI_REGISTRY_H__ */
diff --git a/common/flatpak-oci-registry.c b/common/flatpak-oci-registry.c
index 77b13901..1c87feee 100644
--- a/common/flatpak-oci-registry.c
+++ b/common/flatpak-oci-registry.c
@@ -1,4 +1,4 @@
-/*
+/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
* Copyright © 2016 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
@@ -27,10 +27,10 @@
#include "libglnx.h"
#include <gpgme.h>
-#include <libsoup/soup.h>
#include "flatpak-oci-registry-private.h"
#include "flatpak-utils-base-private.h"
#include "flatpak-utils-private.h"
+#include "flatpak-uri-private.h"
#include "flatpak-dir-private.h"
#include "flatpak-zstd-decompressor-private.h"
@@ -67,8 +67,8 @@ struct FlatpakOciRegistry
int dfd;
/* Remote repos */
- SoupSession *soup_session;
- SoupURI *base_uri;
+ FlatpakHttpSession *http_session;
+ GUri *base_uri;
};
typedef struct
@@ -88,6 +88,20 @@ G_DEFINE_TYPE_WITH_CODE (FlatpakOciRegistry, flatpak_oci_registry, G_TYPE_OBJECT
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
flatpak_oci_registry_initable_iface_init))
+static gchar *
+parse_relative_uri (GUri *base_uri,
+ const char *subpath,
+ GError **error)
+{
+ g_autoptr(GUri) uri = NULL;
+
+ uri = g_uri_parse_relative (base_uri, subpath, FLATPAK_HTTP_URI_FLAGS | G_URI_FLAGS_PARSE_RELAXED, error);
+ if (uri == NULL)
+ return NULL;
+
+ return g_uri_to_string_partial (uri, G_URI_HIDE_PASSWORD);
+}
+
static void
flatpak_oci_registry_finalize (GObject *object)
{
@@ -96,8 +110,8 @@ flatpak_oci_registry_finalize (GObject *object)
if (self->dfd != -1)
close (self->dfd);
- g_clear_object (&self->soup_session);
- g_clear_pointer (&self->base_uri, soup_uri_free);
+ g_clear_pointer (&self->http_session, flatpak_http_session_free);
+ g_clear_pointer (&self->base_uri, g_uri_unref);
g_free (self->uri);
g_free (self->token);
@@ -312,7 +326,7 @@ local_load_file (int dfd,
/* We just support the first http uri for now */
static char *
-choose_alt_uri (SoupURI *base_uri,
+choose_alt_uri (GUri *base_uri,
const char **alt_uris)
{
int i;
@@ -331,8 +345,8 @@ choose_alt_uri (SoupURI *base_uri,
}
static GBytes *
-remote_load_file (SoupSession *soup_session,
- SoupURI *base,
+remote_load_file (FlatpakHttpSession *http_session,
+ GUri *base,
const char *subpath,
const char **alt_uris,
const char *token,
@@ -340,25 +354,18 @@ remote_load_file (SoupSession *soup_session,
GCancellable *cancellable,
GError **error)
{
- g_autoptr(SoupURI) uri = NULL;
g_autoptr(GBytes) bytes = NULL;
g_autofree char *uri_s = NULL;
uri_s = choose_alt_uri (base, alt_uris);
if (uri_s == NULL)
{
- uri = soup_uri_new_with_base (base, subpath);
- if (uri == NULL)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
- "Invalid relative url %s", subpath);
- return NULL;
- }
-
- uri_s = soup_uri_to_string (uri, FALSE);
+ uri_s = parse_relative_uri (base, subpath, error);
+ if (uri_s == NULL)
+ return NULL;
}
- bytes = flatpak_load_uri (soup_session,
+ bytes = flatpak_load_uri (http_session,
uri_s, FLATPAK_HTTP_FLAGS_ACCEPT_OCI,
token,
NULL, NULL, out_content_type,
@@ -380,7 +387,7 @@ flatpak_oci_registry_load_file (FlatpakOciRegistry *self,
if (self->dfd != -1)
return local_load_file (self->dfd, subpath, cancellable, error);
else
- return remote_load_file (self->soup_session, self->base_uri, subpath, alt_uris, self->token, out_content_type, cancellable, error);
+ return remote_load_file (self->http_session, self->base_uri, subpath, alt_uris, self->token, out_content_type, cancellable, error);
}
static JsonNode *
@@ -532,7 +539,7 @@ flatpak_oci_registry_ensure_remote (FlatpakOciRegistry *self,
GCancellable *cancellable,
GError **error)
{
- g_autoptr(SoupURI) baseuri = NULL;
+ g_autoptr(GUri) baseuri = NULL;
if (for_write)
{
@@ -541,8 +548,8 @@ flatpak_oci_registry_ensure_remote (FlatpakOciRegistry *self,
return FALSE;
}
- self->soup_session = flatpak_create_soup_session (PACKAGE_STRING);
- baseuri = soup_uri_new (self->uri);
+ self->http_session = flatpak_create_http_session (PACKAGE_STRING);
+ baseuri = g_uri_parse (self->uri, FLATPAK_HTTP_URI_FLAGS | G_URI_FLAGS_PARSE_RELAXED, NULL);
if (baseuri == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
@@ -783,7 +790,6 @@ flatpak_oci_registry_download_blob (FlatpakOciRegistry *self,
}
else
{
- g_autoptr(SoupURI) uri = NULL;
g_autofree char *uri_s = NULL;
g_autofree char *checksum = NULL;
g_autofree char *tmpfile_name = g_strdup_printf ("oci-layer-XXXXXX");
@@ -794,15 +800,9 @@ flatpak_oci_registry_download_blob (FlatpakOciRegistry *self,
uri_s = choose_alt_uri (self->base_uri, alt_uris);
if (uri_s == NULL)
{
- uri = soup_uri_new_with_base (self->base_uri, subpath);
- if (uri == NULL)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
- "Invalid relative url %s", subpath);
- return -1;
- }
-
- uri_s = soup_uri_to_string (uri, FALSE);
+ uri_s = parse_relative_uri (self->base_uri, subpath, error);
+ if (uri_s == NULL)
+ return -1;
}
@@ -816,7 +816,7 @@ flatpak_oci_registry_download_blob (FlatpakOciRegistry *self,
if (fd == -1)
return -1;
- if (!flatpak_download_http_uri (self->soup_session, uri_s,
+ if (!flatpak_download_http_uri (self->http_session, uri_s,
FLATPAK_HTTP_FLAGS_ACCEPT_OCI,
out_stream,
self->token,
@@ -902,21 +902,13 @@ flatpak_oci_registry_mirror_blob (FlatpakOciRegistry *self,
}
else
{
- g_autoptr(SoupURI) uri = NULL;
- g_autofree char *uri_s = NULL;
-
- uri = soup_uri_new_with_base (source_registry->base_uri, src_subpath);
- if (uri == NULL)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
- "Invalid relative url %s", src_subpath);
- return FALSE;
- }
+ g_autofree char *uri_s = parse_relative_uri (source_registry->base_uri, src_subpath, error);
+ if (uri_s == NULL)
+ return FALSE;
out_stream = g_unix_output_stream_new (tmpf.fd, FALSE);
- uri_s = soup_uri_to_string (uri, FALSE);
- if (!flatpak_download_http_uri (source_registry->soup_session, uri_s,
+ if (!flatpak_download_http_uri (source_registry->http_session, uri_s,
FLATPAK_HTTP_FLAGS_ACCEPT_OCI, out_stream,
self->token,
progress_cb, user_data,
@@ -967,6 +959,17 @@ object_get_string_member_with_default (JsonNode *json,
return json_node_get_string (node);
}
+static const char *
+object_find_error_string (JsonNode *json)
+{
+ const char *error_detail = NULL;
+ error_detail = object_get_string_member_with_default (json, "details", NULL);
+ if (error_detail == NULL)
+ error_detail = object_get_string_member_with_default (json, "message", NULL);
+ if (error_detail == NULL)
+ error_detail = object_get_string_member_with_default (json, "error", NULL);
+ return error_detail;
+}
static char *
get_token_for_www_auth (FlatpakOciRegistry *self,
@@ -976,15 +979,16 @@ get_token_for_www_auth (FlatpakOciRegistry *self,
GCancellable *cancellable,
GError **error)
{
- g_autoptr(GInputStream) auth_stream = NULL;
- g_autoptr(SoupMessage) auth_msg = NULL;
g_autoptr(GHashTable) params = NULL;
- g_autoptr(GHashTable) args = NULL;
+ g_autoptr(GString) args = NULL;
const char *realm, *service, *scope, *token, *body_data;
g_autofree char *default_scope = NULL;
- g_autoptr(SoupURI) auth_uri = NULL;
+ g_autoptr(GUri) auth_uri = NULL;
+ g_autofree char *auth_uri_s = NULL;
g_autoptr(GBytes) body = NULL;
g_autoptr(JsonNode) json = NULL;
+ GUri *tmp_uri;
+ int http_status;
if (g_ascii_strncasecmp (www_authenticate, "Bearer ", strlen ("Bearer ")) != 0)
{
@@ -992,7 +996,7 @@ get_token_for_www_auth (FlatpakOciRegistry *self,
return NULL;
}
- params = soup_header_parse_param_list (www_authenticate + strlen ("Bearer "));
+ params = flatpak_parse_http_header_param_list (www_authenticate + strlen ("Bearer "));
realm = g_hash_table_lookup (params, "realm");
if (realm == NULL)
@@ -1001,59 +1005,76 @@ get_token_for_www_auth (FlatpakOciRegistry *self,
return NULL;
}
- auth_uri = soup_uri_new (realm);
+ auth_uri = g_uri_parse (realm, FLATPAK_HTTP_URI_FLAGS | G_URI_FLAGS_PARSE_RELAXED, NULL);
if (auth_uri == NULL)
{
flatpak_fail (error, _("Invalid realm in authentication request"));
return NULL;
}
- args = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
+ args = g_string_new (NULL);
+
service = g_hash_table_lookup (params, "service");
if (service)
- g_hash_table_insert (args, "service", (char *)service);
+ flatpak_uri_encode_query_arg (args, "service", (char *)service);
scope = g_hash_table_lookup (params, "scope");
if (scope == NULL)
scope = default_scope = g_strdup_printf("repository:%s:pull", repository);
- g_hash_table_insert (args, "scope", (char *)scope);
-
- soup_uri_set_query_from_form (auth_uri, args);
-
- auth_msg = soup_message_new_from_uri ("GET", auth_uri);
-
- if (auth)
- {
- g_autofree char *basic_auth = g_strdup_printf ("Basic %s", auth);
- soup_message_headers_replace (auth_msg->request_headers, "Authorization", basic_auth);
- }
-
- auth_stream = soup_session_send (self->soup_session, auth_msg, NULL, error);
- if (auth_stream == NULL)
- return NULL;
- body = flatpak_read_stream (auth_stream, TRUE, error);
+ flatpak_uri_encode_query_arg (args, "scope", (char *)scope);
+
+ tmp_uri = g_uri_build (g_uri_get_flags (auth_uri) | G_URI_FLAGS_ENCODED_QUERY,
+ g_uri_get_scheme (auth_uri),
+ g_uri_get_userinfo (auth_uri),
+ g_uri_get_host (auth_uri),
+ g_uri_get_port (auth_uri),
+ g_uri_get_path (auth_uri),
+ args->str,
+ g_uri_get_fragment (auth_uri));
+ g_uri_unref (auth_uri);
+ auth_uri = tmp_uri;
+ auth_uri_s = g_uri_to_string_partial (auth_uri, G_URI_HIDE_PASSWORD);
+
+ body = flatpak_load_uri_full (self->http_session,
+ auth_uri_s,
+ FLATPAK_HTTP_FLAGS_NOCHECK_STATUS,
+ auth, NULL,
+ NULL, NULL,
+ &http_status, NULL, NULL,
+ cancellable, error);
if (body == NULL)
return NULL;
body_data = (char *)g_bytes_get_data (body, NULL);
- if (!SOUP_STATUS_IS_SUCCESSFUL (auth_msg->status_code))
+ if (http_status < 200 || http_status >= 300)
{
const char *error_detail = NULL;
json = json_from_string (body_data, NULL);
if (json)
{
- error_detail = object_get_string_member_with_default (json, "details", NULL);
- if (error_detail == NULL)
- error_detail = object_get_string_member_with_default (json, "message", NULL);
- if (error_detail == NULL)
- error_detail = object_get_string_member_with_default (json, "error", NULL);
+ error_detail = object_find_error_string (json);
+ if (error_detail == NULL && JSON_NODE_HOLDS_OBJECT(json))
+ {
+ JsonNode *errors = json_object_get_member (json_node_get_object (json), "errors");
+ if (errors && JSON_NODE_HOLDS_ARRAY (errors))
+ {
+ JsonArray *array = json_node_get_array (errors);
+ for (int i = 0; i < json_array_get_length (array); i++)
+ {
+ error_detail = object_find_error_string (json_array_get_element (array, i));
+ if (error_detail != 0)
+ break;
+ }
+ }
+ }
}
+
if (error_detail == NULL)
- g_debug ("Unhandled error body format: %s", body_data);
+ g_info ("Unhandled error body format: %s", body_data);
- if (auth_msg->status_code == SOUP_STATUS_UNAUTHORIZED)
+ if (http_status == 401 /* UNAUTHORIZED */)
{
if (error_detail)
flatpak_fail_error (error, FLATPAK_ERROR_NOT_AUTHORIZED, _("Authorization failed: %s"), error_detail);
@@ -1062,7 +1083,7 @@ get_token_for_www_auth (FlatpakOciRegistry *self,
return NULL;
}
- flatpak_fail (error, _("Unexpected response status %d when requesting token: %s"), auth_msg->status_code, (char *)g_bytes_get_data (body, NULL));
+ flatpak_fail (error, _("Unexpected response status %d when requesting token: %s"), http_status, (char *)g_bytes_get_data (body, NULL));
return NULL;
}
@@ -1089,11 +1110,11 @@ flatpak_oci_registry_get_token (FlatpakOciRegistry *self,
GError **error)
{
g_autofree char *subpath = NULL;
- g_autoptr(SoupURI) uri = NULL;
- g_autoptr(GInputStream) stream = NULL;
- g_autoptr(SoupMessage) msg = NULL;
+ g_autofree char *uri_s = NULL;
g_autofree char *www_authenticate = NULL;
g_autofree char *token = NULL;
+ g_autoptr(GBytes) body = NULL;
+ int http_status;
g_assert (self->valid);
@@ -1104,38 +1125,32 @@ flatpak_oci_registry_get_token (FlatpakOciRegistry *self,
if (self->dfd != -1)
return g_strdup (""); // No tokens for local repos
- uri = soup_uri_new_with_base (self->base_uri, subpath);
- if (uri == NULL)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
- "Invalid relative url %s", subpath);
- return NULL;
- }
-
- msg = soup_message_new_from_uri ("HEAD", uri);
-
- soup_message_headers_replace (msg->request_headers, "Accept",
- FLATPAK_OCI_MEDIA_TYPE_IMAGE_MANIFEST ", " FLATPAK_DOCKER_MEDIA_TYPE_IMAGE_MANIFEST2);
+ uri_s = parse_relative_uri (self->base_uri, subpath, error);
+ if (uri_s == NULL)
+ return NULL;
- stream = soup_session_send (self->soup_session, msg, NULL, error);
- if (stream == NULL)
+ body = flatpak_load_uri_full (self->http_session, uri_s,
+ FLATPAK_HTTP_FLAGS_ACCEPT_OCI | FLATPAK_HTTP_FLAGS_HEAD | FLATPAK_HTTP_FLAGS_NOCHECK_STATUS,
+ NULL, NULL,
+ NULL, NULL,
+ &http_status, NULL, &www_authenticate,
+ cancellable, error);
+ if (body == NULL)
return NULL;
- if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
- {
- return g_strdup ("");
- }
- else if (msg->status_code != SOUP_STATUS_UNAUTHORIZED)
+ if (http_status >= 200 && http_status < 300)
+ return g_strdup ("");
+
+ if (http_status != 401 /* UNAUTHORIZED */)
{
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unexpected response status %d from repo", msg->status_code);
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unexpected response status %d from repo", http_status);
return NULL;
}
/* Need www-authenticated header */
- www_authenticate = g_strdup (soup_message_headers_get_one (msg->response_headers, "WWW-Authenticate"));
if (www_authenticate == NULL)
{
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Now WWW-Authenticate header from repo");
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "No WWW-Authenticate header from repo");
return NULL;
}
@@ -2044,14 +2059,15 @@ flatpak_oci_registry_find_delta_manifest (FlatpakOciRegistry *registry,
if (delta_manifest_url != NULL)
{
- g_autoptr(SoupURI) soup_uri = soup_uri_new_with_base (registry->base_uri, delta_manifest_url);
- g_autofree char *uri_s = soup_uri_to_string (soup_uri, FALSE);
+ g_autoptr(GBytes) bytes = NULL;
+ g_autofree char *uri_s = parse_relative_uri (registry->base_uri, delta_manifest_url, NULL);
- g_autoptr(GBytes) bytes = flatpak_load_uri (registry->soup_session,
- uri_s, FLATPAK_HTTP_FLAGS_ACCEPT_OCI,
- registry->token,
- NULL, NULL, NULL,
- cancellable, NULL);
+ if (uri_s != NULL)
+ bytes = flatpak_load_uri (registry->http_session,
+ uri_s, FLATPAK_HTTP_FLAGS_ACCEPT_OCI,
+ registry->token,
+ NULL, NULL, NULL,
+ cancellable, NULL);
if (bytes != NULL)
{
g_autoptr(FlatpakOciVersioned) versioned =
@@ -2711,22 +2727,24 @@ compare_image_by_ref (ImageInfo *a,
}
gboolean
-flatpak_oci_index_ensure_cached (SoupSession *soup_session,
- const char *uri,
- GFile *index,
- char **index_uri_out,
- GCancellable *cancellable,
- GError **error)
+flatpak_oci_index_ensure_cached (FlatpakHttpSession *http_session,
+ const char *uri,
+ GFile *index,
+ char **index_uri_out,
+ GCancellable *cancellable,
+ GError **error)
{
g_autofree char *index_path = g_file_get_path (index);
- g_autoptr(SoupURI) base_uri = NULL;
- g_autoptr(SoupURI) query_uri = NULL;
+ g_autoptr(GUri) base_uri = NULL;
+ g_autoptr(GUri) query_uri = NULL;
g_autofree char *query_uri_s = NULL;
+ g_autoptr(GString) query = NULL;
g_autoptr(GString) path = NULL;
g_autofree char *tag = NULL;
const char *oci_arch = NULL;
gboolean success = FALSE;
g_autoptr(GError) local_error = NULL;
+ GUri *tmp_uri;
if (!g_str_has_prefix (uri, "oci+http:") && !g_str_has_prefix (uri, "oci+https:"))
{
@@ -2735,7 +2753,7 @@ flatpak_oci_index_ensure_cached (SoupSession *soup_session,
return FALSE;
}
- base_uri = soup_uri_new (uri + 4);
+ base_uri = g_uri_parse (uri + 4, FLATPAK_HTTP_URI_FLAGS | G_URI_FLAGS_PARSE_RELAXED, NULL);
if (base_uri == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
@@ -2743,7 +2761,7 @@ flatpak_oci_index_ensure_cached (SoupSession *soup_session,
return FALSE;
}
- path = g_string_new (soup_uri_get_path (base_uri));
+ path = g_string_new (g_uri_get_path (base_uri));
/* Append /index/static or /static to the path.
*/
@@ -2755,33 +2773,61 @@ flatpak_oci_index_ensure_cached (SoupSession *soup_session,
g_string_append (path, "static");
- soup_uri_set_path (base_uri, path->str);
+ /* Replace path */
+ tmp_uri = g_uri_build (g_uri_get_flags (base_uri),
+ g_uri_get_scheme (base_uri),
+ g_uri_get_userinfo (base_uri),
+ g_uri_get_host (base_uri),
+ g_uri_get_port (base_uri),
+ path->str,
+ g_uri_get_query (base_uri),
+ g_uri_get_fragment (base_uri));
+ g_uri_unref (base_uri);
+ base_uri = tmp_uri;
/* The fragment of the URI defines a tag to look for; if absent
* or empty, we use 'latest'
*/
- tag = g_strdup (soup_uri_get_fragment (base_uri));
+ tag = g_strdup (g_uri_get_fragment (base_uri));
if (tag == NULL || tag[0] == '\0')
{
g_clear_pointer (&tag, g_free);
tag = g_strdup ("latest");
}
- soup_uri_set_fragment (base_uri, NULL);
-
- query_uri = soup_uri_copy (base_uri);
+ /* Remove fragment */
+ tmp_uri = g_uri_build (g_uri_get_flags (base_uri),
+ g_uri_get_scheme (base_uri),
+ g_uri_get_userinfo (base_uri),
+ g_uri_get_host (base_uri),
+ g_uri_get_port (base_uri),
+ g_uri_get_path (base_uri),
+ g_uri_get_query (base_uri),
+ NULL);
+ g_uri_unref (base_uri);
+ base_uri = tmp_uri;
oci_arch = flatpak_arch_to_oci_arch (flatpak_get_arch ());
- soup_uri_set_query_from_fields (query_uri,
- "label:org.flatpak.ref:exists", "1",
- "architecture", oci_arch,
- "os", "linux",
- "tag", tag,
- NULL);
- query_uri_s = soup_uri_to_string (query_uri, FALSE);
- success = flatpak_cache_http_uri (soup_session,
+ query = g_string_new (NULL);
+ flatpak_uri_encode_query_arg (query, "label:org.flatpak.ref:exists", "1");
+ flatpak_uri_encode_query_arg (query, "architecture", oci_arch);
+ flatpak_uri_encode_query_arg (query, "os", "linux");
+ flatpak_uri_encode_query_arg (query, "tag", tag);
+
+ query_uri = g_uri_build (g_uri_get_flags (base_uri) | G_URI_FLAGS_ENCODED_QUERY,
+ g_uri_get_scheme (base_uri),
+ g_uri_get_userinfo (base_uri),
+ g_uri_get_host (base_uri),
+ g_uri_get_port (base_uri),
+ g_uri_get_path (base_uri),
+ query->str,
+ g_uri_get_fragment (base_uri));
+
+ query_uri_s = g_uri_to_string_partial (query_uri, G_URI_HIDE_PASSWORD);
+
+ success = flatpak_cache_http_uri (http_session,
query_uri_s,
FLATPAK_HTTP_FLAGS_STORE_COMPRESSED,
AT_FDCWD, index_path,
@@ -2792,7 +2838,7 @@ flatpak_oci_index_ensure_cached (SoupSession *soup_session,
g_error_matches (local_error, FLATPAK_HTTP_ERROR, FLATPAK_HTTP_ERROR_NOT_CHANGED))
{
if (index_uri_out)
- *index_uri_out = soup_uri_to_string (base_uri, FALSE);
+ *index_uri_out = g_uri_to_string_partial (base_uri, G_URI_HIDE_PASSWORD);
}
else
{
@@ -2857,7 +2903,6 @@ flatpak_oci_index_make_summary (GFile *index,
GError **error)
{
g_autoptr(FlatpakOciIndexResponse) response = NULL;
- g_autoptr(SoupURI) registry_uri = NULL;
g_autofree char *registry_uri_s = NULL;
int i;
g_autoptr(GArray) images = g_array_new (FALSE, TRUE, sizeof (ImageInfo));
@@ -2867,15 +2912,16 @@ flatpak_oci_index_make_summary (GFile *index,
g_autoptr(GVariantBuilder) summary_builder = NULL;
g_autoptr(GVariant) summary = NULL;
g_autoptr(GVariantBuilder) ref_data_builder = NULL;
- g_autoptr(SoupURI) soup_uri = NULL;
+ g_autoptr(GUri) uri = NULL;
response = load_oci_index (index, cancellable, error);
if (!response)
return NULL;
- soup_uri = soup_uri_new (index_uri);
- registry_uri = soup_uri_new_with_base (soup_uri, response->registry);
- registry_uri_s = soup_uri_to_string (registry_uri, FALSE);
+ uri = g_uri_parse (index_uri, FLATPAK_HTTP_URI_FLAGS | G_URI_FLAGS_PARSE_RELAXED, NULL);
+ registry_uri_s = parse_relative_uri (uri, response->registry, error);
+ if (registry_uri_s == NULL)
+ return NULL;
for (i = 0; response->results != NULL && response->results[i] != NULL; i++)
{
@@ -2939,7 +2985,7 @@ flatpak_oci_index_make_summary (GFile *index,
if (!g_str_has_prefix (image->digest, "sha256:"))
{
- g_debug ("Ignoring digest type %s", image->digest);
+ g_info ("Ignoring digest type %s", image->digest);
continue;
}
@@ -3017,15 +3063,15 @@ flatpak_oci_index_make_summary (GFile *index,
}
static gboolean
-add_icon_image (SoupSession *soup_session,
- const char *index_uri,
- int icons_dfd,
- GHashTable *used_icons,
- const char *subdir,
- const char *id,
- const char *icon_data,
- GCancellable *cancellable,
- GError **error)
+add_icon_image (FlatpakHttpSession *http_session,
+ const char *index_uri,
+ int icons_dfd,
+ GHashTable *used_icons,
+ const char *subdir,
+ const char *id,
+ const char *icon_data,
+ GCancellable *cancellable,
+ GError **error)
{
g_autofree char *icon_name = g_strconcat (id, ".png", NULL);
g_autofree char *icon_path = g_build_filename (subdir, icon_name, NULL);
@@ -3061,12 +3107,15 @@ add_icon_image (SoupSession *soup_session,
}
else
{
- g_autoptr(SoupURI) base_uri = soup_uri_new (index_uri);
- g_autoptr(SoupURI) icon_uri = soup_uri_new_with_base (base_uri, icon_data);
- g_autofree char *icon_uri_s = soup_uri_to_string (icon_uri, FALSE);
+ g_autoptr(GUri) base_uri = g_uri_parse (index_uri, FLATPAK_HTTP_URI_FLAGS | G_URI_FLAGS_PARSE_RELAXED, NULL);
+ g_autofree char *icon_uri_s = NULL;
g_autoptr(GError) local_error = NULL;
- if (!flatpak_cache_http_uri (soup_session, icon_uri_s,
+ icon_uri_s = parse_relative_uri (base_uri, icon_data, error);
+ if (icon_uri_s == NULL)
+ return FALSE;
+
+ if (!flatpak_cache_http_uri (http_session, icon_uri_s,
0 /* flags */,
icons_dfd, icon_path,
NULL, NULL,
@@ -3084,7 +3133,7 @@ add_icon_image (SoupSession *soup_session,
}
static void
-add_image_to_appstream (SoupSession *soup_session,
+add_image_to_appstream (FlatpakHttpSession *http_session,
const char *index_uri,
FlatpakXml *appstream_root,
int icons_dfd,
@@ -3175,7 +3224,7 @@ add_image_to_appstream (SoupSession *soup_session,
const char *icon_data = get_image_metadata (image, icon_sizes[i].label);
if (icon_data)
{
- if (!add_icon_image (soup_session,
+ if (!add_icon_image (http_session,
index_uri,
icons_dfd,
used_icons,
@@ -3260,13 +3309,13 @@ clean_unused_icons (int icons_dfd,
}
GBytes *
-flatpak_oci_index_make_appstream (SoupSession *soup_session,
- GFile *index,
- const char *index_uri,
- const char *arch,
- int icons_dfd,
- GCancellable *cancellable,
- GError **error)
+flatpak_oci_index_make_appstream (FlatpakHttpSession *http_session,
+ GFile *index,
+ const char *index_uri,
+ const char *arch,
+ int icons_dfd,
+ GCancellable *cancellable,
+ GError **error)
{
g_autoptr(FlatpakOciIndexResponse) response = NULL;
g_autoptr(FlatpakXml) appstream_root = NULL;
@@ -3294,7 +3343,7 @@ flatpak_oci_index_make_appstream (SoupSession *soup_session,
{
FlatpakOciIndexImage *image = r->images[j];
if (g_strcmp0 (image->architecture, oci_arch) == 0)
- add_image_to_appstream (soup_session,
+ add_image_to_appstream (http_session,
index_uri,
appstream_root, icons_dfd, used_icons,
r, image,
@@ -3310,7 +3359,7 @@ flatpak_oci_index_make_appstream (SoupSession *soup_session,
{
FlatpakOciIndexImage *image = list->images[k];
if (g_strcmp0 (image->architecture, oci_arch) == 0)
- add_image_to_appstream (soup_session,
+ add_image_to_appstream (http_session,
index_uri,
appstream_root, icons_dfd, used_icons,
r, image,
diff --git a/common/flatpak-parental-controls.c b/common/flatpak-parental-controls.c
index c7815678..5b9d0c4f 100644
--- a/common/flatpak-parental-controls.c
+++ b/common/flatpak-parental-controls.c
@@ -1,4 +1,4 @@
-/*
+/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
* Copyright © 2018 Endless Mobile, Inc.
*
* This program is free software; you can redistribute it and/or
@@ -129,10 +129,10 @@ flatpak_oars_check_rating (GHashTable *content_rating,
(rating_value != MCT_APP_FILTER_OARS_VALUE_UNKNOWN &&
filter_value == MCT_APP_FILTER_OARS_VALUE_UNKNOWN))
{
- g_debug ("%s: Comparing rating ‘%s’: app has ‘%s’ but policy has ‘%s’ unknown: OARS check failed",
- G_STRFUNC, oars_sections[i],
- app_filter_oars_value_to_string (rating_value),
- app_filter_oars_value_to_string (filter_value));
+ g_info ("%s: Comparing rating ‘%s’: app has ‘%s’ but policy has ‘%s’ unknown: OARS check failed",
+ G_STRFUNC, oars_sections[i],
+ app_filter_oars_value_to_string (rating_value),
+ app_filter_oars_value_to_string (filter_value));
return FALSE;
}
}
diff --git a/common/flatpak-progress.c b/common/flatpak-progress.c
index 1e8842ae..1ca4b046 100644
--- a/common/flatpak-progress.c
+++ b/common/flatpak-progress.c
@@ -1,4 +1,4 @@
-/*
+/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
* Copyright © 2019 Endless Mobile, Inc
*
* This program is free software; you can redistribute it and/or
@@ -314,7 +314,7 @@ out:
if (new_progress > 100)
{
if (!self->reported_overflow)
- g_debug ("Unexpectedly got > 100%% progress, limiting");
+ g_info ("Unexpectedly got > 100%% progress, limiting");
self->reported_overflow = TRUE;
new_progress = 100;
}
diff --git a/common/flatpak-prune.c b/common/flatpak-prune.c
index 78e397fd..0e23609f 100644
--- a/common/flatpak-prune.c
+++ b/common/flatpak-prune.c
@@ -1,4 +1,4 @@
-/*
+/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
* Copyright © 2021 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
@@ -517,7 +517,7 @@ traverse_reachable_refs_unlocked (OstreeRepo *repo,
if (object_name_bag_contains (reachable, &commit_name))
continue;
- flatpak_debug2 ("Finding objects to keep for commit %s", checksum);
+ g_debug ("Finding objects to keep for commit %s", checksum);
if (!load_extra_commitmeta (repo, checksum, &extra_commitmeta, cancellable, error))
return FALSE;
@@ -608,8 +608,8 @@ prune_loose_object (OtPruneData *data,
{
guint64 storage_size = 0;
- flatpak_debug2 ("Pruning unneeded object %s.%s", checksum,
- ostree_object_type_to_string (objtype));
+ g_debug ("Pruning unneeded object %s.%s", checksum,
+ ostree_object_type_to_string (objtype));
if (!ostree_repo_query_object_storage_size (data->repo, objtype, checksum,
&storage_size, cancellable, error))
@@ -767,14 +767,14 @@ flatpak_repo_prune (OstreeRepo *repo,
return FALSE;
timer = g_timer_new ();
- g_debug ("Finding reachable objects, unlocked (depth=%d)", depth);
+ g_info ("Finding reachable objects, unlocked (depth=%d)", depth);
g_timer_start (timer);
if (!traverse_reachable_refs_unlocked (repo, depth, reachable, cancellable, error))
return FALSE;
g_timer_stop (timer);
- g_debug ("Elapsed time: %.1f sec", g_timer_elapsed (timer, NULL));
+ g_info ("Elapsed time: %.1f sec", g_timer_elapsed (timer, NULL));
}
{
@@ -785,7 +785,7 @@ flatpak_repo_prune (OstreeRepo *repo,
return FALSE;
timer = g_timer_new ();
- g_debug ("Finding reachable objects, locked (depth=%d)", depth);
+ g_info ("Finding reachable objects, locked (depth=%d)", depth);
g_timer_start (timer);
if (!traverse_reachable_refs_unlocked (repo, depth, reachable, cancellable, error))
@@ -796,29 +796,29 @@ flatpak_repo_prune (OstreeRepo *repo,
data.dont_prune = dry_run;
g_timer_stop (timer);
- g_debug ("Elapsed time: %.1f sec", g_timer_elapsed (timer, NULL));
+ g_info ("Elapsed time: %.1f sec", g_timer_elapsed (timer, NULL));
- g_debug ("Pruning unreachable objects");
+ g_info ("Pruning unreachable objects");
g_timer_start (timer);
if (!prune_unreachable_loose_objects (repo, &data, cancellable, error))
return FALSE;
g_timer_stop (timer);
- g_debug ("Elapsed time: %.1f sec", g_timer_elapsed (timer, NULL));
+ g_info ("Elapsed time: %.1f sec", g_timer_elapsed (timer, NULL));
}
/* Prune static deltas outside lock to avoid conflict with its exclusive lock */
if (!dry_run)
{
- g_debug ("Pruning static deltas");
+ g_info ("Pruning static deltas");
g_timer_start (timer);
if (!ostree_repo_prune_static_deltas (repo, NULL, cancellable, error))
return FALSE;
g_timer_stop (timer);
- g_debug ("Elapsed time: %.1f sec", g_timer_elapsed (timer, NULL));
+ g_info ("Elapsed time: %.1f sec", g_timer_elapsed (timer, NULL));
}
*out_objects_total = data.n_reachable + data.n_unreachable;
diff --git a/common/flatpak-ref-utils.c b/common/flatpak-ref-utils.c
index 37abafb3..45b1405d 100644
--- a/common/flatpak-ref-utils.c
+++ b/common/flatpak-ref-utils.c
@@ -1,4 +1,4 @@
-/*
+/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
* Copyright © 2014-2020 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
@@ -94,8 +94,6 @@ find_last_char (const char *str, gsize len, int c)
* 2) DBus names require only two elements
*
* Returns: %TRUE if valid, %FALSE otherwise.
- *
- * Since: 2.26
*/
gboolean
flatpak_is_valid_name (const char *string,
@@ -362,8 +360,6 @@ is_valid_branch_character (gint c)
* Branch names must contain at least one character.
*
* Returns: %TRUE if valid, %FALSE otherwise.
- *
- * Since: 2.26
*/
gboolean
flatpak_is_valid_branch (const char *string,
diff --git a/common/flatpak-ref.c b/common/flatpak-ref.c
index 84773548..1c7fda7b 100644
--- a/common/flatpak-ref.c
+++ b/common/flatpak-ref.c
@@ -1,4 +1,4 @@
-/*
+/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
* Copyright © 2015 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
diff --git a/common/flatpak-related-ref.c b/common/flatpak-related-ref.c
index ec9cb491..40764640 100644
--- a/common/flatpak-related-ref.c
+++ b/common/flatpak-related-ref.c
@@ -1,4 +1,4 @@
-/*
+/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
* Copyright © 2015 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
diff --git a/common/flatpak-remote-ref.c b/common/flatpak-remote-ref.c
index 332d28d0..1868c725 100644
--- a/common/flatpak-remote-ref.c
+++ b/common/flatpak-remote-ref.c
@@ -1,4 +1,4 @@
-/*
+/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
* Copyright © 2015 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
@@ -341,7 +341,7 @@ flatpak_remote_ref_new (FlatpakDecomposed *decomposed,
&download_size, &installed_size, &metadata,
NULL))
{
- g_debug ("Can't find metadata for ref %s", flatpak_decomposed_get_ref (decomposed));
+ g_info ("Can't find metadata for ref %s", flatpak_decomposed_get_ref (decomposed));
}
if (metadata)
diff --git a/common/flatpak-remote.c b/common/flatpak-remote.c
index 42afcc06..c13d6c29 100644
--- a/common/flatpak-remote.c
+++ b/common/flatpak-remote.c
@@ -1,4 +1,4 @@
-/*
+/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
* Copyright © 2015 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
@@ -120,6 +120,11 @@ flatpak_remote_finalize (GObject *object)
g_free (priv->local_title);
g_free (priv->local_default_branch);
g_free (priv->local_main_ref);
+ g_free (priv->local_filter);
+ g_free (priv->local_comment);
+ g_free (priv->local_description);
+ g_free (priv->local_homepage);
+ g_free (priv->local_icon);
G_OBJECT_CLASS (flatpak_remote_parent_class)->finalize (object);
}
diff --git a/common/flatpak-run.c b/common/flatpak-run.c
index e4391019..8d613165 100644
--- a/common/flatpak-run.c
+++ b/common/flatpak-run.c
@@ -1,4 +1,4 @@
-/*
+/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
* Copyright © 2014-2019 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
@@ -163,7 +163,6 @@ static void
write_xauth (int family,
const char *remote_host,
const char *number,
- const char *replace_number,
FILE *output)
{
Xauth *xa, local_xa;
@@ -191,11 +190,6 @@ write_xauth (int family,
unames.nodename, number))
{
local_xa = *xa;
- if (local_xa.number != NULL && replace_number != NULL)
- {
- local_xa.number = (char *) replace_number;
- local_xa.number_length = strlen (replace_number);
- }
if (local_xa.family == FamilyLocal &&
!auth_streq (unames.nodename, local_xa.address, local_xa.address_length))
@@ -319,19 +313,18 @@ flatpak_run_add_x11_args (FlatpakBwrap *bwrap,
return;
}
- g_debug ("Allowing x11 access");
+ g_info ("Allowing x11 access");
display = g_getenv ("DISPLAY");
if (display != NULL)
{
g_autofree char *remote_host = NULL;
- g_autofree char *original_display_nr = NULL;
- const char *replace_display_nr = NULL;
+ g_autofree char *display_nr = NULL;
int family = -1;
if (!flatpak_run_parse_x11_display (display, &family, &x11_socket,
- &remote_host, &original_display_nr,
+ &remote_host, &display_nr,
&local_error))
{
g_warning ("%s", local_error->message);
@@ -339,16 +332,16 @@ flatpak_run_add_x11_args (FlatpakBwrap *bwrap,
return;
}
- g_assert (original_display_nr != NULL);
+ g_assert (display_nr != NULL);
if (x11_socket != NULL
&& g_file_test (x11_socket, G_FILE_TEST_EXISTS))
{
+ g_assert (g_str_has_prefix (x11_socket, "/tmp/.X11-unix/X"));
flatpak_bwrap_add_args (bwrap,
- "--ro-bind", x11_socket, "/tmp/.X11-unix/X99",
+ "--ro-bind", x11_socket, x11_socket,
NULL);
- flatpak_bwrap_set_env (bwrap, "DISPLAY", ":99.0", TRUE);
- replace_display_nr = "99";
+ flatpak_bwrap_set_env (bwrap, "DISPLAY", display, TRUE);
}
else if ((shares & FLATPAK_CONTEXT_SHARED_NETWORK) == 0)
{
@@ -376,7 +369,7 @@ flatpak_run_add_x11_args (FlatpakBwrap *bwrap,
}
else
{
- flatpak_debug2 ("Assuming --share=network gives access to remote X11");
+ g_debug ("Assuming --share=network gives access to remote X11");
}
#ifdef ENABLE_XAUTH
@@ -393,8 +386,7 @@ flatpak_run_add_x11_args (FlatpakBwrap *bwrap,
{
static const char dest[] = "/run/flatpak/Xauthority";
- write_xauth (family, remote_host, original_display_nr,
- replace_display_nr, output);
+ write_xauth (family, remote_host, display_nr, output);
flatpak_bwrap_add_args_data_fd (bwrap, "--ro-bind-data", tmp_fd, dest);
flatpak_bwrap_set_env (bwrap, "XAUTHORITY", dest, TRUE);
@@ -528,7 +520,7 @@ flatpak_run_get_cups_server_name_config (const char *path)
input_stream = g_file_read (file, NULL, &my_error);
if (my_error)
{
- g_debug ("CUPS configuration file '%s': %s", path, my_error->message);
+ g_info ("CUPS configuration file '%s': %s", path, my_error->message);
return NULL;
}
@@ -600,7 +592,7 @@ flatpak_run_add_cups_args (FlatpakBwrap *bwrap)
if (!g_file_test (cups_server_name, G_FILE_TEST_EXISTS))
{
- g_debug ("Could not find CUPS server");
+ g_info ("Could not find CUPS server");
return;
}
@@ -609,6 +601,46 @@ flatpak_run_add_cups_args (FlatpakBwrap *bwrap)
NULL);
}
+static void
+flatpak_run_add_gpg_agent_args (FlatpakBwrap *bwrap)
+{
+ const char * agent_socket;
+ g_autofree char * sandbox_agent_socket = NULL;
+ g_autoptr(GError) gpgconf_error = NULL;
+ g_autoptr(GSubprocess) process = NULL;
+ g_autoptr(GInputStream) base_stream = NULL;
+ g_autoptr(GDataInputStream) data_stream = NULL;
+
+ process = g_subprocess_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE,
+ &gpgconf_error,
+ "gpgconf", "--list-dir", "agent-socket", NULL);
+
+ if (gpgconf_error)
+ {
+ g_info ("GPG-Agent directories: %s", gpgconf_error->message);
+ return;
+ }
+
+ base_stream = g_subprocess_get_stdout_pipe (process);
+ data_stream = g_data_input_stream_new (base_stream);
+
+ agent_socket = g_data_input_stream_read_line (data_stream,
+ NULL, NULL,
+ &gpgconf_error);
+
+ if (!agent_socket || gpgconf_error)
+ {
+ g_info ("GPG-Agent directories: %s", gpgconf_error->message);
+ return;
+ }
+
+ sandbox_agent_socket = g_strdup_printf ("/run/user/%d/gnupg/S.gpg-agent", getuid ());
+
+ flatpak_bwrap_add_args (bwrap,
+ "--ro-bind-try", agent_socket, sandbox_agent_socket,
+ NULL);
+}
+
/* Try to find a default server from a pulseaudio confguration file */
static char *
flatpak_run_get_pulseaudio_server_user_config (const char *path)
@@ -622,7 +654,7 @@ flatpak_run_get_pulseaudio_server_user_config (const char *path)
input_stream = g_file_read (file, NULL, &my_error);
if (my_error)
{
- g_debug ("Pulseaudio user configuration file '%s': %s", path, my_error->message);
+ g_info ("Pulseaudio user configuration file '%s': %s", path, my_error->message);
return NULL;
}
@@ -661,7 +693,7 @@ flatpak_run_get_pulseaudio_server_user_config (const char *path)
if (strcmp ("default-server", tokens[0]) == 0)
{
g_strstrip (tokens[1]);
- g_debug ("Found pulseaudio socket from configuration file '%s': %s", path, tokens[1]);
+ g_info ("Found pulseaudio socket from configuration file '%s': %s", path, tokens[1]);
return g_strdup (tokens[1]);
}
}
@@ -888,7 +920,7 @@ flatpak_run_add_pulseaudio_args (FlatpakBwrap *bwrap,
g_warning ("PulseAudio access will require --share=network permission.");
}
- g_debug ("Using remote PulseAudio server \"%s\"", pulseaudio_server);
+ g_info ("Using remote PulseAudio server \"%s\"", pulseaudio_server);
flatpak_bwrap_set_env (bwrap, "PULSE_SERVER", pulseaudio_server, TRUE);
}
else if (pulseaudio_socket && g_file_test (pulseaudio_socket, G_FILE_TEST_EXISTS))
@@ -912,7 +944,7 @@ flatpak_run_add_pulseaudio_args (FlatpakBwrap *bwrap,
flatpak_bwrap_add_runtime_dir_member (bwrap, "pulse");
}
else
- g_debug ("Could not find pulseaudio socket");
+ g_info ("Could not find pulseaudio socket");
/* Also allow ALSA access. This was added in 1.8, and is not ideally named. However,
* since the practical permission of ALSA and PulseAudio are essentially the same, and
@@ -924,6 +956,19 @@ flatpak_run_add_pulseaudio_args (FlatpakBwrap *bwrap,
}
static void
+flatpak_run_add_gssproxy_args (FlatpakBwrap *bwrap)
+{
+ /* We only expose the gssproxy user service. The gssproxy system service is
+ * not intended to be exposed to sandboxed environments.
+ */
+ g_autofree char *gssproxy_host_dir = g_build_filename (g_get_user_runtime_dir (), "gssproxy", NULL);
+ const char *gssproxy_sandboxed_dir = "/run/flatpak/gssproxy/";
+
+ if (g_file_test (gssproxy_host_dir, G_FILE_TEST_EXISTS))
+ flatpak_bwrap_add_args (bwrap, "--ro-bind", gssproxy_host_dir, gssproxy_sandboxed_dir, NULL);
+}
+
+static void
flatpak_run_add_resolved_args (FlatpakBwrap *bwrap)
{
const char *resolved_socket = "/run/systemd/resolve/io.systemd.Resolve";
@@ -985,7 +1030,7 @@ flatpak_run_add_system_dbus_args (FlatpakBwrap *app_bwrap,
unrestricted = (context->sockets & FLATPAK_CONTEXT_SOCKET_SYSTEM_BUS) != 0;
if (unrestricted)
- g_debug ("Allowing system-dbus access");
+ g_info ("Allowing system-dbus access");
no_proxy = (flags & FLATPAK_RUN_FLAG_NO_SYSTEM_BUS_PROXY) != 0;
@@ -1066,7 +1111,7 @@ flatpak_run_add_session_dbus_args (FlatpakBwrap *app_bwrap,
}
if (unrestricted)
- g_debug ("Allowing session-dbus access");
+ g_info ("Allowing session-dbus access");
no_proxy = (flags & FLATPAK_RUN_FLAG_NO_SESSION_BUS_PROXY) != 0;
@@ -1319,7 +1364,7 @@ start_dbus_proxy (FlatpakBwrap *app_bwrap,
flatpak_bwrap_finish (proxy_bwrap);
commandline = flatpak_quote_argv ((const char **) proxy_bwrap->argv->pdata, -1);
- g_debug ("Running '%s'", commandline);
+ g_info ("Running '%s'", commandline);
/* We use LEAVE_DESCRIPTORS_OPEN to work around dead-lock, see flatpak_close_fds_workaround */
if (!g_spawn_async (NULL,
@@ -1552,13 +1597,13 @@ flatpak_run_add_environment_args (FlatpakBwrap *bwrap,
if ((context->shares & FLATPAK_CONTEXT_SHARED_IPC) == 0)
{
- g_debug ("Disallowing ipc access");
+ g_info ("Disallowing ipc access");
flatpak_bwrap_add_args (bwrap, "--unshare-ipc", NULL);
}
if ((context->shares & FLATPAK_CONTEXT_SHARED_NETWORK) == 0)
{
- g_debug ("Disallowing network access");
+ g_info ("Disallowing network access");
flatpak_bwrap_add_args (bwrap, "--unshare-net", NULL);
}
@@ -1655,7 +1700,7 @@ flatpak_run_add_environment_args (FlatpakBwrap *bwrap,
NULL);
if (context->devices & FLATPAK_CONTEXT_DEVICE_DRI)
{
- g_debug ("Allowing dri access");
+ g_info ("Allowing dri access");
int i;
char *dri_devices[] = {
"/dev/dri",
@@ -1690,7 +1735,7 @@ flatpak_run_add_environment_args (FlatpakBwrap *bwrap,
if (context->devices & FLATPAK_CONTEXT_DEVICE_KVM)
{
- g_debug ("Allowing kvm access");
+ g_info ("Allowing kvm access");
if (g_file_test ("/dev/kvm", G_FILE_TEST_EXISTS))
flatpak_bwrap_add_args (bwrap, "--dev-bind", "/dev/kvm", "/dev/kvm", NULL);
}
@@ -1700,7 +1745,7 @@ flatpak_run_add_environment_args (FlatpakBwrap *bwrap,
/* This is a symlink to /run/shm on debian, so bind to real target */
g_autofree char *real_dev_shm = realpath ("/dev/shm", NULL);
- g_debug ("Allowing /dev/shm access (as %s)", real_dev_shm);
+ g_info ("Allowing /dev/shm access (as %s)", real_dev_shm);
if (real_dev_shm != NULL)
flatpak_bwrap_add_args (bwrap, "--bind", real_dev_shm, "/dev/shm", NULL);
}
@@ -1759,7 +1804,7 @@ flatpak_run_add_environment_args (FlatpakBwrap *bwrap,
if (context->sockets & FLATPAK_CONTEXT_SOCKET_WAYLAND)
{
- g_debug ("Allowing wayland access");
+ g_info ("Allowing wayland access");
has_wayland = flatpak_run_add_wayland_args (bwrap);
}
@@ -1777,7 +1822,7 @@ flatpak_run_add_environment_args (FlatpakBwrap *bwrap,
if (context->sockets & FLATPAK_CONTEXT_SOCKET_PULSEAUDIO)
{
- g_debug ("Allowing pulseaudio access");
+ g_info ("Allowing pulseaudio access");
flatpak_run_add_pulseaudio_args (bwrap, context->shares);
}
@@ -1791,6 +1836,11 @@ flatpak_run_add_environment_args (FlatpakBwrap *bwrap,
flatpak_run_add_cups_args (bwrap);
}
+ if (context->sockets & FLATPAK_CONTEXT_SOCKET_GPG_AGENT)
+ {
+ flatpak_run_add_gpg_agent_args (bwrap);
+ }
+
flatpak_run_add_session_dbus_args (bwrap, proxy_arg_bwrap, context, flags, app_id);
flatpak_run_add_system_dbus_args (bwrap, proxy_arg_bwrap, context, flags);
flatpak_run_add_a11y_dbus_args (bwrap, proxy_arg_bwrap, context, flags);
@@ -1801,7 +1851,7 @@ flatpak_run_add_environment_args (FlatpakBwrap *bwrap,
{
/* We still run along even if we don't get a cgroup, as nothing
really depends on it. Its just nice to have */
- g_debug ("Failed to run in transient scope: %s", my_error->message);
+ g_info ("Failed to run in transient scope: %s", my_error->message);
g_clear_error (&my_error);
}
@@ -1830,14 +1880,19 @@ static const ExportData default_exports[] = {
{"XDG_CONFIG_DIRS", "/app/etc/xdg:/etc/xdg"},
{"XDG_DATA_DIRS", "/app/share:/usr/share"},
{"SHELL", "/bin/sh"},
- {"TMPDIR", NULL}, /* Unset TMPDIR as it may not exist in the sandbox */
+ /* Unset temporary file paths as they may not exist in the sandbox */
+ {"TEMP", NULL},
+ {"TEMPDIR", NULL},
+ {"TMP", NULL},
+ {"TMPDIR", NULL},
/* We always use /run/user/UID, even if the user's XDG_RUNTIME_DIR
* outside the sandbox is somewhere else. Don't allow a different
* setting from outside the sandbox to overwrite this. */
{"XDG_RUNTIME_DIR", NULL},
/* Some env vars are common enough and will affect the sandbox badly
- if set on the host. We clear these always. */
+ if set on the host. We clear these always. If updating this list,
+ also update the list in flatpak-run.xml. */
{"PYTHONPATH", NULL},
{"PERLLIB", NULL},
{"PERL5LIB", NULL},
@@ -1854,6 +1909,9 @@ static const ExportData default_exports[] = {
{"GST_PTP_HELPER", NULL},
{"GST_PTP_HELPER_1_0", NULL},
{"GST_INSTALL_PLUGINS_HELPER", NULL},
+ {"KRB5CCNAME", NULL},
+ {"XKB_CONFIG_ROOT", NULL},
+ {"GIO_EXTRA_MODULES", NULL},
};
static const ExportData no_ld_so_cache_exports[] = {
@@ -2456,17 +2514,17 @@ get_dconf_data (const char *app_id,
if (migrate_path)
{
- g_debug ("Add values in dir '%s', prefix is '%s'", migrate_path, prefix);
+ g_info ("Add values in dir '%s', prefix is '%s'", migrate_path, prefix);
if (flatpak_dconf_path_is_similar (migrate_path, prefix))
add_dconf_dir_to_keyfile (values_data, client, migrate_path, DCONF_READ_USER_VALUE);
else
g_warning ("Ignoring D-Conf migrate-path setting %s", migrate_path);
}
- g_debug ("Add defaults in dir %s", prefix);
+ g_info ("Add defaults in dir %s", prefix);
add_dconf_dir_to_keyfile (defaults_data, client, prefix, DCONF_READ_DEFAULT_VALUE);
- g_debug ("Add locks in dir %s", prefix);
+ g_info ("Add locks in dir %s", prefix);
add_dconf_locks_to_list (locks_data, client, prefix);
/* We allow extra paths for defaults and locks, but not for user values */
@@ -2477,15 +2535,15 @@ get_dconf_data (const char *app_id,
{
if (dconf_is_dir (paths[i], NULL))
{
- g_debug ("Add defaults in dir %s", paths[i]);
+ g_info ("Add defaults in dir %s", paths[i]);
add_dconf_dir_to_keyfile (defaults_data, client, paths[i], DCONF_READ_DEFAULT_VALUE);
- g_debug ("Add locks in dir %s", paths[i]);
+ g_info ("Add locks in dir %s", paths[i]);
add_dconf_locks_to_list (locks_data, client, paths[i]);
}
else if (dconf_is_key (paths[i], NULL))
{
- g_debug ("Add individual key %s", paths[i]);
+ g_info ("Add individual key %s", paths[i]);
add_dconf_key_to_keyfile (defaults_data, client, paths[i], DCONF_READ_DEFAULT_VALUE);
add_dconf_key_to_keyfile (values_data, client, paths[i], DCONF_READ_USER_VALUE);
}
@@ -2569,7 +2627,7 @@ flatpak_run_add_dconf_args (FlatpakBwrap *bwrap,
"config/glib-2.0/settings/keyfile",
NULL);
- g_debug ("writing D-Conf values to %s", filename);
+ g_info ("writing D-Conf values to %s", filename);
if (values_size != 0 && !g_file_test (filename, G_FILE_TEST_EXISTS))
{
@@ -3000,7 +3058,7 @@ add_document_portal_args (FlatpakBwrap *bwrap,
if (g_dbus_message_to_gerror (reply, &local_error))
{
if (g_error_matches (local_error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN))
- g_debug ("Document portal not available, not mounting /run/flatpak/doc");
+ g_info ("Document portal not available, not mounting /run/flatpak/doc");
else
g_message ("Can't get document portal: %s", local_error->message);
}
@@ -3127,9 +3185,6 @@ setup_seccomp (FlatpakBwrap *bwrap,
{SCMP_SYS (uselib), EPERM},
/* Don't allow disabling accounting */
{SCMP_SYS (acct), EPERM},
- /* 16-bit code is unnecessary in the sandbox, and modify_ldt is a
- historic source of interesting information leaks. */
- {SCMP_SYS (modify_ldt), EPERM},
/* Don't allow reading current quota use */
{SCMP_SYS (quotactl), EPERM},
@@ -3295,8 +3350,25 @@ setup_seccomp (FlatpakBwrap *bwrap,
* libseccomp cannot map the syscall number to a name and back to a
* number for the non-native architecture. */
if (r == -EFAULT)
- flatpak_debug2 ("Unable to block syscall %d: syscall not known to libseccomp?",
- scall);
+ g_debug ("Unable to block syscall %d: syscall not known to libseccomp?",
+ scall);
+ else if (r < 0)
+ return flatpak_fail_error (error, FLATPAK_ERROR_SETUP_FAILED, _("Failed to block syscall %d: %s"), scall, flatpak_seccomp_strerror (r));
+ }
+
+ if (!multiarch)
+ {
+ /* modify_ldt is a historic source of interesting information leaks,
+ * so it's disabled as a hardening measure.
+ * However, it is required to run old 16-bit applications
+ * as well as some Wine patches, so it's allowed in multiarch. */
+ int scall = SCMP_SYS (modify_ldt);
+ r = seccomp_rule_add (seccomp, SCMP_ACT_ERRNO (EPERM), scall, 0);
+
+ /* See above for the meaning of EFAULT. */
+ if (r == -EFAULT)
+ g_debug ("Unable to block syscall %d: syscall not known to libseccomp?",
+ scall);
else if (r < 0)
return flatpak_fail_error (error, FLATPAK_ERROR_SETUP_FAILED, _("Failed to block syscall %d: %s"), scall, flatpak_seccomp_strerror (r));
}
@@ -3317,8 +3389,8 @@ setup_seccomp (FlatpakBwrap *bwrap,
/* See above for the meaning of EFAULT. */
if (r == -EFAULT)
- flatpak_debug2 ("Unable to block syscall %d: syscall not known to libseccomp?",
- scall);
+ g_debug ("Unable to block syscall %d: syscall not known to libseccomp?",
+ scall);
else if (r < 0)
return flatpak_fail_error (error, FLATPAK_ERROR_SETUP_FAILED, _("Failed to block syscall %d: %s"), scall, flatpak_seccomp_strerror (r));
}
@@ -3399,8 +3471,8 @@ flatpak_run_setup_usr_links (FlatpakBwrap *bwrap,
}
else
{
- g_debug ("%s does not exist",
- flatpak_file_get_path_cached (runtime_subdir));
+ g_info ("%s does not exist",
+ flatpak_file_get_path_cached (runtime_subdir));
}
}
}
@@ -3572,7 +3644,7 @@ flatpak_run_setup_base_argv (FlatpakBwrap *bwrap,
if ((flags & FLATPAK_RUN_FLAG_SET_PERSONALITY) &&
flatpak_is_linux32_arch (arch))
{
- g_debug ("Setting personality linux32");
+ g_info ("Setting personality linux32");
pers = PER_LINUX32;
}
@@ -3726,7 +3798,7 @@ add_rest_args (FlatpakBwrap *bwrap,
g_assert (doc_path != NULL);
}
- g_debug ("Forwarding file '%s' as '%s' to %s", args[i], doc_path, app_id);
+ g_info ("Forwarding file '%s' as '%s' to %s", args[i], doc_path, app_id);
flatpak_bwrap_add_arg (bwrap, doc_path);
}
else
@@ -3822,7 +3894,7 @@ regenerate_ld_cache (GPtrArray *base_argv_array,
if (ld_so_fd >= 0)
return glnx_steal_fd (&ld_so_fd);
- g_debug ("Regenerating ld.so.cache %s", flatpak_file_get_path_cached (ld_so_cache));
+ g_info ("Regenerating ld.so.cache %s", flatpak_file_get_path_cached (ld_so_cache));
if (!flatpak_mkdir_p (ld_so_dir, cancellable, error))
return FALSE;
@@ -3870,7 +3942,7 @@ regenerate_ld_cache (GPtrArray *base_argv_array,
flatpak_bwrap_finish (bwrap);
commandline = flatpak_quote_argv ((const char **) bwrap->argv->pdata, -1);
- g_debug ("Running: '%s'", commandline);
+ g_info ("Running: '%s'", commandline);
combined_fd_array = g_array_new (FALSE, TRUE, sizeof (int));
g_array_append_vals (combined_fd_array, base_fd_array->data, base_fd_array->len);
@@ -3953,15 +4025,15 @@ check_parental_controls (FlatpakDecomposed *app_ref,
cancellable, &local_error);
if (g_error_matches (local_error, MCT_APP_FILTER_ERROR, MCT_APP_FILTER_ERROR_DISABLED))
{
- g_debug ("Skipping parental controls check for %s since parental "
- "controls are disabled globally", flatpak_decomposed_get_ref (app_ref));
+ g_info ("Skipping parental controls check for %s since parental "
+ "controls are disabled globally", flatpak_decomposed_get_ref (app_ref));
return TRUE;
}
else if (g_error_matches (local_error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN) ||
g_error_matches (local_error, G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER))
{
- g_debug ("Skipping parental controls check for %s since a required "
- "service was not found", flatpak_decomposed_get_ref (app_ref));
+ g_info ("Skipping parental controls check for %s since a required "
+ "service was not found", flatpak_decomposed_get_ref (app_ref));
return TRUE;
}
else if (local_error != NULL)
@@ -4560,7 +4632,10 @@ flatpak_run_app (FlatpakDecomposed *app_ref,
}
if ((app_context->shares & FLATPAK_CONTEXT_SHARED_NETWORK) != 0)
- flatpak_run_add_resolved_args (bwrap);
+ {
+ flatpak_run_add_gssproxy_args (bwrap);
+ flatpak_run_add_resolved_args (bwrap);
+ }
flatpak_run_add_journal_args (bwrap);
add_font_path_args (bwrap);
@@ -4644,7 +4719,7 @@ flatpak_run_app (FlatpakDecomposed *app_ref,
flatpak_bwrap_finish (bwrap);
commandline = flatpak_quote_argv ((const char **) bwrap->argv->pdata, -1);
- g_debug ("Running '%s'", commandline);
+ g_info ("Running '%s'", commandline);
if ((flags & (FLATPAK_RUN_FLAG_BACKGROUND)) != 0 ||
g_getenv ("FLATPAK_TEST_COVERAGE") != NULL)
diff --git a/common/flatpak-transaction.c b/common/flatpak-transaction.c
index 2239ea2d..97d79bbc 100644
--- a/common/flatpak-transaction.c
+++ b/common/flatpak-transaction.c
@@ -1,4 +1,4 @@
-/*
+/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
* Copyright © 2016 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
@@ -22,7 +22,6 @@
#include <stdio.h>
#include <glib/gi18n-lib.h>
-#include <libsoup/soup.h>
#include "flatpak-auth-private.h"
#include "flatpak-error.h"
@@ -30,6 +29,7 @@
#include "flatpak-progress-private.h"
#include "flatpak-transaction-private.h"
#include "flatpak-utils-private.h"
+#include "flatpak-uri-private.h"
#include "flatpak-variant-impl-private.h"
/**
@@ -704,12 +704,13 @@ flatpak_transaction_operation_get_decomposed (FlatpakTransactionOperation *self)
* flatpak_transaction_operation_get_related_to_ops:
* @self: a #FlatpakTransactionOperation
*
- * Gets the operations which caused this operation to be added to the
- * transaction. In the case of a runtime, it's the apps whose runtime it is (and
- * this could be multiple apps, if they all require the same runtime). In
- * the case of a related ref such as an extension, it's the main app or
- * runtime. In the case of a main app or something added to the transaction by
- * flatpak_transaction_add_ref(), %NULL or an empty array will be returned.
+ * Gets the operation(s) which caused this operation to be added to the
+ * transaction. In the case of a runtime, it's the app(s) whose runtime it is,
+ * and/or a runtime extension in the special case of an extra-data extension
+ * that doesn't define the "NoRuntime" key. In the case of a related ref such
+ * as an extension, it's the main app or runtime. In the case of a main app or
+ * something added to the transaction by e.g. flatpak_transaction_add_install()
+ * and which is not otherwise needed, %NULL or an empty array will be returned.
*
* Note that an op will be returned even if it’s marked as to be skipped when
* the transaction is run. Check that using
@@ -1317,15 +1318,17 @@ flatpak_transaction_class_init (FlatpakTransactionClass *klass)
* FlatpakTransaction::ready-pre-auth:
* @object: A #FlatpakTransaction
*
- * The ::ready-pre-auth signal is emitted when all the refs involved in the transaction
- * have been resolved to commits, but we might not necessarily have asked for authenticaion
- * for all their required operations. This is very similar to the ::ready signal, and you can
- * chose which one (or both) to use depending on how you want to handle authentication in your user
+ * The ::ready-pre-auth signal is emitted when all the refs involved in the
+ * transaction have been resolved to commits, but we might not necessarily
+ * have asked for authentication for all their required operations. This is
+ * very similar to the ::ready signal, and you can choose which one (or both)
+ * to use depending on how you want to handle authentication in your user
* interface.
*
- * At this point flatpak_transaction_get_operations() will return all the operations
- * that will be executed as part of the transaction. You can call flatpak_transaction_operation_get_requires_authentication()
- * to see which will require authentication.
+ * At this point flatpak_transaction_get_operations() will return all the
+ * operations that will be executed as part of the transaction. You can call
+ * flatpak_transaction_operation_get_requires_authentication() to see which
+ * will require authentication.
*
* Returns: %TRUE to carry on with the transaction, %FALSE to abort
*
@@ -2118,11 +2121,11 @@ flatpak_transaction_add_op (FlatpakTransaction *self,
g_autofree char *subpaths_str = NULL;
subpaths_str = subpaths_to_string (subpaths);
- g_debug ("Transaction: %s %s:%s%s%s%s",
- kind_to_str (kind), remote, flatpak_decomposed_get_ref (ref),
- commit != NULL ? "@" : "",
- commit != NULL ? commit : "",
- subpaths_str);
+ g_info ("Transaction: %s %s:%s%s%s%s",
+ kind_to_str (kind), remote, flatpak_decomposed_get_ref (ref),
+ commit != NULL ? "@" : "",
+ commit != NULL ? commit : "",
+ subpaths_str);
op = flatpak_transaction_get_last_op_for_ref (self, ref);
/* If previous_ids is given, then this is a rebase operation. */
@@ -2189,7 +2192,7 @@ op_get_related (FlatpakTransaction *self,
if (op->resolved_metakey == NULL)
{
- g_debug ("no resolved metadata for related to %s", flatpak_decomposed_get_ref (op->ref));
+ g_info ("no resolved metadata for related to %s", flatpak_decomposed_get_ref (op->ref));
return TRUE;
}
@@ -2342,7 +2345,7 @@ search_for_dependency (FlatpakTransaction *self,
state = flatpak_transaction_ensure_remote_state (self, FLATPAK_TRANSACTION_OPERATION_INSTALL, remote, arch, &local_error);
if (state == NULL)
{
- g_debug ("Can't get state for remote %s, ignoring: %s", remote, local_error->message);
+ g_info ("Can't get state for remote %s, ignoring: %s", remote, local_error->message);
continue;
}
@@ -2465,7 +2468,7 @@ op_get_runtime_ref (FlatpakTransactionOperation *op)
decomposed = flatpak_decomposed_new_from_pref (FLATPAK_KINDS_RUNTIME, runtime_pref, NULL);
if (decomposed == NULL)
- g_debug ("Invalid runtime ref %s in metadata", runtime_pref);
+ g_info ("Invalid runtime ref %s in metadata", runtime_pref);
return decomposed;
}
@@ -2485,7 +2488,7 @@ op_get_sdk_ref (FlatpakTransactionOperation *op)
decomposed = flatpak_decomposed_new_from_pref (FLATPAK_KINDS_RUNTIME, sdk_pref, NULL);
if (decomposed == NULL)
- g_debug ("Invalid runtime ref %s in metadata", sdk_pref);
+ g_info ("Invalid runtime ref %s in metadata", sdk_pref);
return decomposed;
}
@@ -2502,8 +2505,8 @@ add_new_dep_op (FlatpakTransaction *self,
if (!ref_is_installed (self, dep_ref))
{
- g_debug ("Installing dependency %s of %s", flatpak_decomposed_get_pref (dep_ref),
- flatpak_decomposed_get_pref (op->ref));
+ g_info ("Installing dependency %s of %s", flatpak_decomposed_get_pref (dep_ref),
+ flatpak_decomposed_get_pref (op->ref));
dep_remote = find_runtime_remote (self, op->ref, op->remote, dep_ref, op->kind, NULL, error);
if (dep_remote == NULL)
return FALSE;
@@ -2518,8 +2521,8 @@ add_new_dep_op (FlatpakTransaction *self,
/* Update if in same dir */
if (dir_ref_is_installed (priv->dir, dep_ref, &dep_remote, NULL))
{
- g_debug ("Updating dependency %s of %s", flatpak_decomposed_get_pref (dep_ref),
- flatpak_decomposed_get_pref (op->ref));
+ g_info ("Updating dependency %s of %s", flatpak_decomposed_get_pref (dep_ref),
+ flatpak_decomposed_get_pref (op->ref));
*dep_op = flatpak_transaction_add_op (self, dep_remote, dep_ref, NULL, NULL, NULL, NULL,
FLATPAK_TRANSACTION_OPERATION_UPDATE, FALSE, error);
if (*dep_op == NULL)
@@ -2665,7 +2668,7 @@ flatpak_transaction_add_ref (FlatpakTransaction *self,
if (flatpak_dir_get_remote_disabled (priv->dir, origin))
{
- g_debug (_("Remote %s disabled, ignoring %s update"), origin, pref);
+ g_info (_("Remote %s disabled, ignoring %s update"), origin, pref);
return TRUE;
}
remote = origin;
@@ -3003,13 +3006,13 @@ flatpak_transaction_update_metadata (FlatpakTransaction *self,
g_autoptr(GError) my_error = NULL;
g_autoptr(FlatpakRemoteState) state = flatpak_transaction_ensure_remote_state (self, FLATPAK_TRANSACTION_OPERATION_UPDATE, remote, NULL, NULL);
- g_debug ("Looking for remote metadata updates for %s", remote);
+ g_info ("Looking for remote metadata updates for %s", remote);
if (!flatpak_dir_update_remote_configuration (priv->dir, remote, state, &updated, cancellable, &my_error))
- g_debug (_("Error updating remote metadata for '%s': %s"), remote, my_error->message);
+ g_info (_("Error updating remote metadata for '%s': %s"), remote, my_error->message);
if (updated)
{
- g_debug ("Got updated metadata for %s", remote);
+ g_info ("Got updated metadata for %s", remote);
some_updated = TRUE;
}
}
@@ -3067,13 +3070,13 @@ flatpak_transaction_add_auto_install (FlatpakTransaction *self,
if (state != NULL &&
flatpak_remote_state_lookup_ref (state, flatpak_decomposed_get_ref (auto_install_ref), NULL, NULL, NULL, NULL, NULL))
{
- g_debug ("Auto adding install of %s from remote %s", flatpak_decomposed_get_ref (auto_install_ref), remote);
+ g_info ("Auto adding install of %s from remote %s", flatpak_decomposed_get_ref (auto_install_ref), remote);
if (!flatpak_transaction_add_ref (self, remote, auto_install_ref, NULL, NULL, NULL,
FLATPAK_TRANSACTION_OPERATION_INSTALL_OR_UPDATE,
NULL, NULL, FALSE,
&local_error))
- g_debug ("Failed to add auto-install ref %s: %s", flatpak_decomposed_get_ref (auto_install_ref),
+ g_info ("Failed to add auto-install ref %s: %s", flatpak_decomposed_get_ref (auto_install_ref),
local_error->message);
}
}
@@ -3141,7 +3144,7 @@ load_deployed_metadata (FlatpakTransaction *self, FlatpakDecomposed *ref, char *
if (!g_file_load_contents (metadata_file, NULL, &metadata_contents, &metadata_contents_length, NULL, NULL))
{
- g_debug ("No metadata in local deploy of %s", flatpak_decomposed_get_ref (ref));
+ g_info ("No metadata in local deploy of %s", flatpak_decomposed_get_ref (ref));
return NULL;
}
@@ -3173,7 +3176,7 @@ mark_op_resolved (FlatpakTransactionOperation *op,
GBytes *old_metadata,
GError **error)
{
- g_debug ("marking op %s:%s resolved to %s", kind_to_str (op->kind), flatpak_decomposed_get_ref (op->ref), commit ? commit : "-");
+ g_info ("marking op %s:%s resolved to %s", kind_to_str (op->kind), flatpak_decomposed_get_ref (op->ref), commit ? commit : "-");
g_assert (op != NULL);
@@ -3267,6 +3270,19 @@ resolve_op_from_commit (FlatpakTransaction *self,
g_variant_lookup (commit_metadata, OSTREE_COMMIT_META_KEY_ENDOFLIFE, "s", &op->eol);
g_variant_lookup (commit_metadata, OSTREE_COMMIT_META_KEY_ENDOFLIFE_REBASE, "s", &op->eol_rebase);
+ if (op->eol_rebase)
+ {
+ g_autoptr(FlatpakDecomposed) eolr_decomposed = NULL;
+ eolr_decomposed = flatpak_decomposed_new_from_ref (op->eol_rebase, error);
+ if (!eolr_decomposed)
+ return FALSE;
+ if (flatpak_decomposed_get_kind (op->ref) != flatpak_decomposed_get_kind (eolr_decomposed))
+ return flatpak_fail_error (error, FLATPAK_ERROR_INVALID_DATA,
+ "end-of-life-rebase on commit %s has the wrong type (%s -> %s)",
+ checksum, flatpak_decomposed_get_ref (op->ref),
+ flatpak_decomposed_get_ref (eolr_decomposed));
+ }
+
return resolve_op_end (self, op, checksum, sideload_path, metadata_bytes, error);
}
@@ -3317,6 +3333,19 @@ try_resolve_op_from_metadata (FlatpakTransaction *self,
op->eol = g_strdup (var_metadata_lookup_string (sparse_cache, FLATPAK_SPARSE_CACHE_KEY_ENDOFLINE, NULL));
op->eol_rebase = g_strdup (var_metadata_lookup_string (sparse_cache, FLATPAK_SPARSE_CACHE_KEY_ENDOFLINE_REBASE, NULL));
op->token_type = GINT32_FROM_LE (var_metadata_lookup_int32 (sparse_cache, FLATPAK_SPARSE_CACHE_KEY_TOKEN_TYPE, op->token_type));
+
+ if (op->eol_rebase)
+ {
+ g_autoptr(FlatpakDecomposed) eolr_decomposed = NULL;
+ eolr_decomposed = flatpak_decomposed_new_from_ref (op->eol_rebase, error);
+ if (!eolr_decomposed)
+ return FALSE;
+ if (flatpak_decomposed_get_kind (op->ref) != flatpak_decomposed_get_kind (eolr_decomposed))
+ return flatpak_fail_error (error, FLATPAK_ERROR_INVALID_DATA,
+ "end-of-life-rebase on commit %s has the wrong type (%s -> %s)",
+ checksum, flatpak_decomposed_get_ref (op->ref),
+ flatpak_decomposed_get_ref (eolr_decomposed));
+ }
}
return resolve_op_end (self, op, checksum, sideload_path, metadata_bytes, error);
@@ -3449,7 +3478,7 @@ resolve_ops (FlatpakTransaction *self,
if (latest_sideload_path != NULL && local_commit_data && latest_timestamp != 0 &&
ostree_commit_get_timestamp (local_commit_data) > latest_timestamp)
{
- g_debug ("Installed commit %s newer than sideloaded %s, ignoring", local_checksum, latest_checksum);
+ g_info ("Installed commit %s newer than sideloaded %s, ignoring", local_checksum, latest_checksum);
checksum = g_steal_pointer (&local_checksum);
}
else
@@ -3507,7 +3536,7 @@ resolve_ops (FlatpakTransaction *self,
if (g_error_matches (local_error, FLATPAK_HTTP_ERROR, FLATPAK_HTTP_ERROR_UNAUTHORIZED) && !op->requested_token)
{
- g_debug ("Unauthorized access during resolve by commit of %s, retrying with token", flatpak_decomposed_get_ref (op->ref));
+ g_info ("Unauthorized access during resolve by commit of %s, retrying with token", flatpak_decomposed_get_ref (op->ref));
priv->needs_resolve = TRUE;
priv->needs_tokens = TRUE;
@@ -3593,7 +3622,7 @@ request_tokens_webflow (FlatpakAuthenticatorRequest *object,
g_assert (priv->active_request_id == 0);
priv->active_request_id = ++priv->next_request_id;
- g_debug ("Webflow start %s", arg_uri);
+ g_info ("Webflow start %s", arg_uri);
g_signal_emit (transaction, signals[WEBFLOW_START], 0, data->remote, arg_uri, options, priv->active_request_id, &retval);
if (!retval)
{
@@ -3603,7 +3632,7 @@ request_tokens_webflow (FlatpakAuthenticatorRequest *object,
/* We didn't handle the uri, cancel the auth op. */
if (!flatpak_authenticator_request_call_close_sync (data->request, NULL, &local_error))
- g_debug ("Failed to close auth request: %s", local_error->message);
+ g_info ("Failed to close auth request: %s", local_error->message);
}
}
@@ -3623,7 +3652,7 @@ request_tokens_webflow_done (FlatpakAuthenticatorRequest *object,
id = priv->active_request_id;
priv->active_request_id = 0;
- g_debug ("Webflow done");
+ g_info ("Webflow done");
g_signal_emit (transaction, signals[WEBFLOW_DONE], 0, options, id);
}
@@ -3643,7 +3672,7 @@ request_tokens_basic_auth (FlatpakAuthenticatorRequest *object,
g_assert (priv->active_request_id == 0);
priv->active_request_id = ++priv->next_request_id;
- g_debug ("BasicAuth start %s", arg_realm);
+ g_info ("BasicAuth start %s", arg_realm);
g_signal_emit (transaction, signals[BASIC_AUTH_START], 0, data->remote, arg_realm, options, priv->active_request_id, &retval);
if (!retval)
{
@@ -3653,7 +3682,7 @@ request_tokens_basic_auth (FlatpakAuthenticatorRequest *object,
/* We didn't handle the request, cancel the auth op. */
if (!flatpak_authenticator_request_call_close_sync (data->request, NULL, &local_error))
- g_debug ("Failed to close auth request: %s", local_error->message);
+ g_info ("Failed to close auth request: %s", local_error->message);
}
}
@@ -3690,7 +3719,7 @@ flatpak_transaction_abort_webflow (FlatpakTransaction *self,
if (!data->done)
{
if (!flatpak_authenticator_request_call_close_sync (data->request, NULL, &local_error))
- g_debug ("Failed to close auth request: %s", local_error->message);
+ g_info ("Failed to close auth request: %s", local_error->message);
}
}
}
@@ -3734,7 +3763,7 @@ flatpak_transaction_complete_basic_auth (FlatpakTransaction *self,
if (user == NULL)
{
if (!flatpak_authenticator_request_call_close_sync (data->request, NULL, &local_error))
- g_debug ("Failed to abort basic auth request: %s", local_error->message);
+ g_info ("Failed to abort basic auth request: %s", local_error->message);
}
else
{
@@ -3742,7 +3771,7 @@ flatpak_transaction_complete_basic_auth (FlatpakTransaction *self,
user, password,
options,
NULL, &local_error))
- g_debug ("Failed to reply to basic auth request: %s", local_error->message);
+ g_info ("Failed to reply to basic auth request: %s", local_error->message);
}
}
}
@@ -3829,7 +3858,7 @@ request_tokens_for_remote (FlatpakTransaction *self,
g_string_append (refs_as_str, ", ");
}
- g_debug ("Requesting tokens for remote %s: %s", remote, refs_as_str->str);
+ g_info ("Requesting tokens for remote %s: %s", remote, refs_as_str->str);
refs = g_variant_ref_sink (g_variant_builder_end (&refs_builder));
extra_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
@@ -3875,7 +3904,7 @@ request_tokens_for_remote (FlatpakTransaction *self,
{
g_autofree char *results_str = results != NULL ? g_variant_print (results, FALSE) : g_strdup ("NULL");
- g_debug ("Response from request_tokens: %d - %s\n", data.response, results_str);
+ g_info ("Response from request_tokens: %d - %s\n", data.response, results_str);
}
if (data.response == FLATPAK_AUTH_RESPONSE_CANCELLED)
@@ -4116,6 +4145,71 @@ flatpak_transaction_get_current_operation (FlatpakTransaction *self)
}
/**
+ * flatpak_transaction_get_operation_for_ref:
+ * @self: a #FlatpakTransaction
+ * @remote: (nullable): a remote name
+ * @ref: a ref
+ * @error: return location for an error
+ *
+ * Gets the operation for @ref, if any match. If @remote is non-%NULL, only an
+ * operation for that remote will be returned. If remote is %NULL and the
+ * transaction has more than one operation for @ref from different remotes, an
+ * error will be returned.
+ *
+ * Returns: (transfer full): the #FlatpakTransactionOperation for @ref, or
+ * %NULL with @error set
+ * Since: 1.13.3
+ */
+FlatpakTransactionOperation *
+flatpak_transaction_get_operation_for_ref (FlatpakTransaction *self,
+ const char *remote,
+ const char *ref,
+ GError **error)
+{
+ FlatpakTransactionPrivate *priv = flatpak_transaction_get_instance_private (self);
+ g_autoptr(FlatpakDecomposed) decomposed_ref = NULL;
+ g_autoptr(FlatpakTransactionOperation) matching_op = NULL;
+ GList *l;
+
+ g_return_val_if_fail (ref != NULL, NULL);
+
+ decomposed_ref = flatpak_decomposed_new_from_ref (ref, error);
+ if (decomposed_ref == NULL)
+ return NULL;
+
+ for (l = priv->ops; l != NULL; l = l->next)
+ {
+ FlatpakTransactionOperation *op = l->data;
+
+ if (remote != NULL && g_strcmp0 (remote, op->remote) != 0)
+ continue;
+
+ if (flatpak_decomposed_equal (op->ref, decomposed_ref))
+ {
+ if (matching_op == NULL)
+ matching_op = g_object_ref (op);
+ else
+ {
+ flatpak_fail_error (error, FLATPAK_ERROR_INVALID_DATA,
+ _("Ref %s from %s matches more than one transaction operation"),
+ ref, remote ? remote : _("any remote"));
+ return NULL;
+ }
+ }
+ }
+
+ if (matching_op == NULL)
+ {
+ flatpak_fail_error (error, FLATPAK_ERROR_REF_NOT_FOUND,
+ _("No transaction operation found for ref %s from %s"),
+ ref, remote ? remote : _("any remote"));
+ return NULL;
+ }
+
+ return g_steal_pointer (&matching_op);
+}
+
+/**
* flatpak_transaction_get_installation:
* @self: a #FlatpakTransactionOperation
*
@@ -4222,7 +4316,7 @@ load_flatpakrepo_file (FlatpakTransaction *self,
g_autoptr(GBytes) dep_data = NULL;
g_autoptr(GKeyFile) dep_keyfile = g_key_file_new ();
g_autoptr(GError) local_error = NULL;
- g_autoptr(SoupSession) soup_session = NULL;
+ g_autoptr(FlatpakHttpSession) http_session = NULL;
if (priv->disable_deps)
return TRUE;
@@ -4232,8 +4326,8 @@ load_flatpakrepo_file (FlatpakTransaction *self,
!g_str_has_prefix (dep_url, "file:"))
return flatpak_fail_error (error, FLATPAK_ERROR_INVALID_DATA, _("Flatpakrepo URL %s not file, HTTP or HTTPS"), dep_url);
- soup_session = flatpak_create_soup_session (PACKAGE_STRING);
- dep_data = flatpak_load_uri (soup_session, dep_url, 0, NULL, NULL, NULL, NULL, cancellable, error);
+ http_session = flatpak_create_http_session (PACKAGE_STRING);
+ dep_data = flatpak_load_uri (http_session, dep_url, 0, NULL, NULL, NULL, NULL, cancellable, error);
if (dep_data == NULL)
{
g_prefix_error (error, _("Can't load dependent file %s: "), dep_url);
@@ -4264,7 +4358,7 @@ handle_runtime_repo_deps (FlatpakTransaction *self,
g_autofree char *runtime_url = NULL;
g_autofree char *new_remote = NULL;
g_autofree char *basename = NULL;
- g_autoptr(SoupURI) uri = NULL;
+ g_autoptr(GUri) uri = NULL;
g_auto(GStrv) remotes = NULL;
g_autoptr(GKeyFile) config = NULL;
g_autoptr(GBytes) gpg_key = NULL;
@@ -4278,8 +4372,8 @@ handle_runtime_repo_deps (FlatpakTransaction *self,
g_assert (dep_keyfile != NULL);
- uri = soup_uri_new (dep_url);
- basename = g_path_get_basename (soup_uri_get_path (uri));
+ uri = g_uri_parse (dep_url, FLATPAK_HTTP_URI_FLAGS | G_URI_FLAGS_PARSE_RELAXED, NULL);
+ basename = g_path_get_basename (g_uri_get_path (uri));
/* Strip suffix */
t = strchr (basename, '.');
if (t != NULL)
@@ -4661,7 +4755,7 @@ _run_op_kind (FlatpakTransaction *self,
}
}
else
- g_debug ("%s need no update", flatpak_decomposed_get_ref (op->ref));
+ g_info ("%s need no update", flatpak_decomposed_get_ref (op->ref));
}
else if (op->kind == FLATPAK_TRANSACTION_OPERATION_INSTALL_BUNDLE)
{
diff --git a/common/flatpak-transaction.h b/common/flatpak-transaction.h
index 6f3ec544..c5832dc8 100644
--- a/common/flatpak-transaction.h
+++ b/common/flatpak-transaction.h
@@ -280,6 +280,11 @@ gboolean flatpak_transaction_run (FlatpakTransaction *transaction,
FLATPAK_EXTERN
FlatpakTransactionOperation *flatpak_transaction_get_current_operation (FlatpakTransaction *self);
FLATPAK_EXTERN
+FlatpakTransactionOperation *flatpak_transaction_get_operation_for_ref (FlatpakTransaction *self,
+ const char *remote,
+ const char *ref,
+ GError **error);
+FLATPAK_EXTERN
FlatpakInstallation *flatpak_transaction_get_installation (FlatpakTransaction *self);
FLATPAK_EXTERN
GList *flatpak_transaction_get_operations (FlatpakTransaction *self);
diff --git a/common/flatpak-uri-private.h b/common/flatpak-uri-private.h
new file mode 100644
index 00000000..fa3afee5
--- /dev/null
+++ b/common/flatpak-uri-private.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright © 2022 Red Hat, Inc
+ *
+ * This program 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.1 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __FLATPAK_URI_PRIVATE_H__
+#define __FLATPAK_URI_PRIVATE_H__
+
+#include <string.h>
+
+#include "flatpak-utils-private.h"
+
+/* This file is mainly a backport of GUri for older versions of glib, and some helpers around it */
+
+void flatpak_uri_encode_query_arg (GString *query,
+ const char *key,
+ const char *value);
+GHashTable *flatpak_parse_http_header_param_list (const char *header);
+GDateTime * flatpak_parse_http_time (const char *date_string);
+char * flatpak_format_http_date (GDateTime *date);
+
+/* Same as SOUP_HTTP_URI_FLAGS, means all possible flags for http uris */
+#if GLIB_CHECK_VERSION (2, 68, 0) || !GLIB_CHECK_VERSION (2, 66, 0)
+#define FLATPAK_HTTP_URI_FLAGS (G_URI_FLAGS_HAS_PASSWORD | G_URI_FLAGS_ENCODED_PATH | G_URI_FLAGS_ENCODED_QUERY | G_URI_FLAGS_ENCODED_FRAGMENT | G_URI_FLAGS_SCHEME_NORMALIZE)
+#else
+/* GLib 2.66 didn't support scheme-based normalization */
+#define FLATPAK_HTTP_URI_FLAGS (G_URI_FLAGS_HAS_PASSWORD | G_URI_FLAGS_ENCODED_PATH | G_URI_FLAGS_ENCODED_QUERY | G_URI_FLAGS_ENCODED_FRAGMENT)
+#endif
+
+#if !GLIB_CHECK_VERSION (2, 66, 0)
+
+typedef enum {
+ G_URI_FLAGS_NONE = 0,
+ G_URI_FLAGS_PARSE_RELAXED = 1 << 0,
+ G_URI_FLAGS_HAS_PASSWORD = 1 << 1,
+ G_URI_FLAGS_HAS_AUTH_PARAMS = 1 << 2,
+ G_URI_FLAGS_ENCODED = 1 << 3,
+ G_URI_FLAGS_NON_DNS = 1 << 4,
+ G_URI_FLAGS_ENCODED_QUERY = 1 << 5,
+ G_URI_FLAGS_ENCODED_PATH = 1 << 6,
+ G_URI_FLAGS_ENCODED_FRAGMENT = 1 << 7,
+ G_URI_FLAGS_SCHEME_NORMALIZE = 1 << 8,
+} GUriFlags;
+
+typedef enum {
+ G_URI_HIDE_NONE = 0,
+ G_URI_HIDE_USERINFO = 1 << 0,
+ G_URI_HIDE_PASSWORD = 1 << 1,
+ G_URI_HIDE_AUTH_PARAMS = 1 << 2,
+ G_URI_HIDE_QUERY = 1 << 3,
+ G_URI_HIDE_FRAGMENT = 1 << 4,
+} GUriHideFlags;
+
+typedef struct _GUri GUri;
+
+GUri * flatpak_g_uri_ref (GUri *uri);
+void flatpak_g_uri_unref (GUri *uri);
+GUri * flatpak_g_uri_parse (const gchar *uri_string,
+ GUriFlags flags,
+ GError **error);
+GUri * flatpak_g_uri_parse_relative (GUri *base_uri,
+ const gchar *uri_ref,
+ GUriFlags flags,
+ GError **error);
+char * flatpak_g_uri_to_string_partial (GUri *uri,
+ GUriHideFlags flags);
+GUri * flatpak_g_uri_build (GUriFlags flags,
+ const gchar *scheme,
+ const gchar *userinfo,
+ const gchar *host,
+ gint port,
+ const gchar *path,
+ const gchar *query,
+ const gchar *fragment);
+const gchar *flatpak_g_uri_get_scheme (GUri *uri);
+const gchar *flatpak_g_uri_get_userinfo (GUri *uri);
+const gchar *flatpak_g_uri_get_user (GUri *uri);
+const gchar *flatpak_g_uri_get_password (GUri *uri);
+const gchar *flatpak_g_uri_get_auth_params (GUri *uri);
+const gchar *flatpak_g_uri_get_host (GUri *uri);
+gint flatpak_g_uri_get_port (GUri *uri);
+const gchar *flatpak_g_uri_get_path (GUri *uri);
+const gchar *flatpak_g_uri_get_query (GUri *uri);
+const gchar *flatpak_g_uri_get_fragment (GUri *uri);
+GUriFlags flatpak_g_uri_get_flags (GUri *uri);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GUri, flatpak_g_uri_unref)
+
+#define g_uri_ref flatpak_g_uri_ref
+#define g_uri_unref flatpak_g_uri_unref
+#define g_uri_parse flatpak_g_uri_parse
+#define g_uri_parse_relative flatpak_g_uri_parse_relative
+#define g_uri_to_string_partial flatpak_g_uri_to_string_partial
+#define g_uri_build flatpak_g_uri_build
+#define g_uri_get_scheme flatpak_g_uri_get_scheme
+#define g_uri_get_userinfo flatpak_g_uri_get_userinfo
+#define g_uri_get_user flatpak_g_uri_get_user
+#define g_uri_get_password flatpak_g_uri_get_password
+#define g_uri_get_auth_params flatpak_g_uri_get_auth_params
+#define g_uri_get_host flatpak_g_uri_get_host
+#define g_uri_get_port flatpak_g_uri_get_port
+#define g_uri_get_path flatpak_g_uri_get_path
+#define g_uri_get_query flatpak_g_uri_get_query
+#define g_uri_get_fragment flatpak_g_uri_get_fragment
+#define g_uri_get_flags flatpak_g_uri_get_flags
+
+#endif
+
+
+#endif /* __FLATPAK_URI_PRIVATE_H__ */
diff --git a/common/flatpak-uri.c b/common/flatpak-uri.c
new file mode 100644
index 00000000..7ec6606e
--- /dev/null
+++ b/common/flatpak-uri.c
@@ -0,0 +1,1763 @@
+/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
+ * Copyright © 1995-1998 Free Software Foundation, Inc.
+ * Copyright © 2014-2019 Red Hat, Inc
+ *
+ * This program 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.1 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Alexander Larsson <alexl@redhat.com>
+ */
+
+#include "config.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "flatpak-uri-private.h"
+
+#if !GLIB_CHECK_VERSION (2, 66, 0)
+
+struct _GUri {
+ gchar *scheme;
+ gchar *userinfo;
+ gchar *host;
+ gint port;
+ gchar *path;
+ gchar *query;
+ gchar *fragment;
+
+ gchar *user;
+ gchar *password;
+ gchar *auth_params;
+
+ GUriFlags flags;
+ int ref_count;
+};
+
+GUri *
+flatpak_g_uri_ref (GUri *uri)
+{
+ g_return_val_if_fail (uri != NULL, NULL);
+ g_atomic_int_inc (&uri->ref_count);
+ return uri;
+}
+
+void
+flatpak_g_uri_unref (GUri *uri)
+{
+ g_return_if_fail (uri != NULL);
+
+ if (g_atomic_int_dec_and_test (&uri->ref_count))
+ {
+ g_free (uri->scheme);
+ g_free (uri->userinfo);
+ g_free (uri->host);
+ g_free (uri->path);
+ g_free (uri->query);
+ g_free (uri->fragment);
+ g_free (uri->user);
+ g_free (uri->password);
+ g_free (uri->auth_params);
+ g_free (uri);
+ }
+}
+
+static gboolean
+flatpak_g_uri_char_is_unreserved (gchar ch)
+{
+ if (g_ascii_isalnum (ch))
+ return TRUE;
+ return ch == '-' || ch == '.' || ch == '_' || ch == '~';
+}
+
+#define XDIGIT(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - 'A' + 10)
+#define HEXCHAR(s) ((XDIGIT (s[1]) << 4) + XDIGIT (s[2]))
+
+static gssize
+uri_decoder (gchar **out,
+ const gchar *illegal_chars,
+ const gchar *start,
+ gsize length,
+ gboolean just_normalize,
+ gboolean www_form,
+ GUriFlags flags,
+ GError **error)
+{
+ gchar c;
+ GString *decoded;
+ const gchar *invalid, *s, *end;
+ gssize len;
+
+ if (!(flags & G_URI_FLAGS_ENCODED))
+ just_normalize = FALSE;
+
+ decoded = g_string_sized_new (length + 1);
+ for (s = start, end = s + length; s < end; s++)
+ {
+ if (*s == '%')
+ {
+ if (s + 2 >= end ||
+ !g_ascii_isxdigit (s[1]) ||
+ !g_ascii_isxdigit (s[2]))
+ {
+ /* % followed by non-hex or the end of the string; this is an error */
+ if (!(flags & G_URI_FLAGS_PARSE_RELAXED))
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+ /* xgettext: no-c-format */
+ _("Invalid %-encoding in URI"));
+ g_string_free (decoded, TRUE);
+ return -1;
+ }
+
+ /* In non-strict mode, just let it through; we *don't*
+ * fix it to "%25", since that might change the way that
+ * the URI's owner would interpret it.
+ */
+ g_string_append_c (decoded, *s);
+ continue;
+ }
+
+ c = HEXCHAR (s);
+ if (illegal_chars && strchr (illegal_chars, c))
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+ _("Illegal character in URI"));
+ g_string_free (decoded, TRUE);
+ return -1;
+ }
+ if (just_normalize && !flatpak_g_uri_char_is_unreserved (c))
+ {
+ /* Leave the % sequence there but normalize it. */
+ g_string_append_c (decoded, *s);
+ g_string_append_c (decoded, g_ascii_toupper (s[1]));
+ g_string_append_c (decoded, g_ascii_toupper (s[2]));
+ s += 2;
+ }
+ else
+ {
+ g_string_append_c (decoded, c);
+ s += 2;
+ }
+ }
+ else if (www_form && *s == '+')
+ g_string_append_c (decoded, ' ');
+ /* Normalize any illegal characters. */
+ else if (just_normalize && (!g_ascii_isgraph (*s)))
+ g_string_append_printf (decoded, "%%%02X", (guchar)*s);
+ else
+ g_string_append_c (decoded, *s);
+ }
+
+ len = decoded->len;
+ g_assert (len >= 0);
+
+ if (!(flags & G_URI_FLAGS_ENCODED) &&
+ !g_utf8_validate (decoded->str, len, &invalid))
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+ _("Non-UTF-8 characters in URI"));
+ g_string_free (decoded, TRUE);
+ return -1;
+ }
+
+ if (out)
+ *out = g_string_free (decoded, FALSE);
+ else
+ g_string_free (decoded, TRUE);
+
+ return len;
+}
+
+static gboolean
+uri_decode (gchar **out,
+ const gchar *illegal_chars,
+ const gchar *start,
+ gsize length,
+ gboolean www_form,
+ GUriFlags flags,
+ GError **error)
+{
+ return uri_decoder (out, illegal_chars, start, length, FALSE, www_form, flags,
+ error) != -1;
+}
+
+static gboolean
+uri_normalize (gchar **out,
+ const gchar *start,
+ gsize length,
+ GUriFlags flags,
+ GError **error)
+{
+ return uri_decoder (out, NULL, start, length, TRUE, FALSE, flags,
+ error) != -1;
+}
+
+static gboolean
+parse_ip_literal (const gchar *start,
+ gsize length,
+ GUriFlags flags,
+ gchar **out,
+ GError **error)
+{
+ gchar *pct, *zone_id = NULL;
+ gchar *addr = NULL;
+ gsize addr_length = 0;
+ gsize zone_id_length = 0;
+ gchar *decoded_zone_id = NULL;
+
+ if (start[length - 1] != ']')
+ goto bad_ipv6_literal;
+
+ /* Drop the square brackets */
+ addr = g_strndup (start + 1, length - 2);
+ addr_length = length - 2;
+
+ /* If there's an IPv6 scope ID, split out the zone. */
+ pct = strchr (addr, '%');
+ if (pct != NULL)
+ {
+ *pct = '\0';
+
+ if (addr_length - (pct - addr) >= 4 &&
+ *(pct + 1) == '2' && *(pct + 2) == '5')
+ {
+ zone_id = pct + 3;
+ zone_id_length = addr_length - (zone_id - addr);
+ }
+ else if (flags & G_URI_FLAGS_PARSE_RELAXED &&
+ addr_length - (pct - addr) >= 2)
+ {
+ zone_id = pct + 1;
+ zone_id_length = addr_length - (zone_id - addr);
+ }
+ else
+ goto bad_ipv6_literal;
+
+ g_assert (zone_id_length >= 1);
+ }
+
+ /* addr must be an IPv6 address */
+ if (!g_hostname_is_ip_address (addr) || !strchr (addr, ':'))
+ goto bad_ipv6_literal;
+
+ /* Zone ID must be valid. It can contain %-encoded characters. */
+ if (zone_id != NULL &&
+ !uri_decode (&decoded_zone_id, NULL, zone_id, zone_id_length, FALSE,
+ flags, NULL))
+ goto bad_ipv6_literal;
+
+ /* Success */
+ if (out != NULL && decoded_zone_id != NULL)
+ *out = g_strconcat (addr, "%", decoded_zone_id, NULL);
+ else if (out != NULL)
+ *out = g_steal_pointer (&addr);
+
+ g_free (addr);
+ g_free (decoded_zone_id);
+
+ return TRUE;
+
+bad_ipv6_literal:
+ g_free (addr);
+ g_free (decoded_zone_id);
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+ _("Invalid IPv6 address ‘%.*s’ in URI"),
+ (gint)length, start);
+
+ return FALSE;
+}
+
+static gboolean
+parse_host (const gchar *start,
+ gsize length,
+ GUriFlags flags,
+ gchar **out,
+ GError **error)
+{
+ gchar *decoded = NULL, *host;
+ gchar *addr = NULL;
+
+ if (*start == '[')
+ {
+ if (!parse_ip_literal (start, length, flags, &host, error))
+ return FALSE;
+ goto ok;
+ }
+
+ if (g_ascii_isdigit (*start))
+ {
+ addr = g_strndup (start, length);
+ if (g_hostname_is_ip_address (addr))
+ {
+ host = addr;
+ goto ok;
+ }
+ g_free (addr);
+ }
+
+ if (flags & G_URI_FLAGS_NON_DNS)
+ {
+ if (!uri_normalize (&decoded, start, length, flags,
+ error))
+ return FALSE;
+ host = g_steal_pointer (&decoded);
+ goto ok;
+ }
+
+ flags &= ~G_URI_FLAGS_ENCODED;
+ if (!uri_decode (&decoded, NULL, start, length, FALSE, flags,
+ error))
+ return FALSE;
+
+ /* You're not allowed to %-encode an IP address, so if it wasn't
+ * one before, it better not be one now.
+ */
+ if (g_hostname_is_ip_address (decoded))
+ {
+ g_free (decoded);
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+ _("Illegal encoded IP address ‘%.*s’ in URI"),
+ (gint)length, start);
+ return FALSE;
+ }
+
+ if (g_hostname_is_non_ascii (decoded))
+ {
+ host = g_hostname_to_ascii (decoded);
+ if (host == NULL)
+ {
+ g_free (decoded);
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+ _("Illegal internationalized hostname ‘%.*s’ in URI"),
+ (gint) length, start);
+ return FALSE;
+ }
+ }
+ else
+ {
+ host = g_steal_pointer (&decoded);
+ }
+
+ ok:
+ if (out)
+ *out = g_steal_pointer (&host);
+ g_free (host);
+ g_free (decoded);
+
+ return TRUE;
+}
+
+static gboolean
+parse_port (const gchar *start,
+ gsize length,
+ gint *out,
+ GError **error)
+{
+ gchar *end;
+ gulong parsed_port;
+
+ /* strtoul() allows leading + or -, so we have to check this first. */
+ if (!g_ascii_isdigit (*start))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+ _("Could not parse port ‘%.*s’ in URI"),
+ (gint)length, start);
+ return FALSE;
+ }
+
+ /* We know that *(start + length) is either '\0' or a non-numeric
+ * character, so strtoul() won't scan beyond it.
+ */
+ parsed_port = strtoul (start, &end, 10);
+ if (end != start + length)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+ _("Could not parse port ‘%.*s’ in URI"),
+ (gint)length, start);
+ return FALSE;
+ }
+ else if (parsed_port > 65535)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+ _("Port ‘%.*s’ in URI is out of range"),
+ (gint)length, start);
+ return FALSE;
+ }
+
+ if (out)
+ *out = parsed_port;
+ return TRUE;
+}
+
+static gboolean
+parse_userinfo (const gchar *start,
+ gsize length,
+ GUriFlags flags,
+ gchar **user,
+ gchar **password,
+ gchar **auth_params,
+ GError **error)
+{
+ const gchar *user_end = NULL, *password_end = NULL, *auth_params_end;
+
+ auth_params_end = start + length;
+ if (flags & G_URI_FLAGS_HAS_AUTH_PARAMS)
+ password_end = memchr (start, ';', auth_params_end - start);
+ if (!password_end)
+ password_end = auth_params_end;
+ if (flags & G_URI_FLAGS_HAS_PASSWORD)
+ user_end = memchr (start, ':', password_end - start);
+ if (!user_end)
+ user_end = password_end;
+
+ if (!uri_normalize (user, start, user_end - start, flags,
+ error))
+ return FALSE;
+
+ if (*user_end == ':')
+ {
+ start = user_end + 1;
+ if (!uri_normalize (password, start, password_end - start, flags,
+ error))
+ {
+ if (user)
+ g_clear_pointer (user, g_free);
+ return FALSE;
+ }
+ }
+ else if (password)
+ *password = NULL;
+
+ if (*password_end == ';')
+ {
+ start = password_end + 1;
+ if (!uri_normalize (auth_params, start, auth_params_end - start, flags,
+ error))
+ {
+ if (user)
+ g_clear_pointer (user, g_free);
+ if (password)
+ g_clear_pointer (password, g_free);
+ return FALSE;
+ }
+ }
+ else if (auth_params)
+ *auth_params = NULL;
+
+ return TRUE;
+}
+
+static gchar *
+uri_cleanup (const gchar *uri_string)
+{
+ GString *copy;
+ const gchar *end;
+
+ /* Skip leading whitespace */
+ while (g_ascii_isspace (*uri_string))
+ uri_string++;
+
+ /* Ignore trailing whitespace */
+ end = uri_string + strlen (uri_string);
+ while (end > uri_string && g_ascii_isspace (*(end - 1)))
+ end--;
+
+ /* Copy the rest, encoding unencoded spaces and stripping other whitespace */
+ copy = g_string_sized_new (end - uri_string);
+ while (uri_string < end)
+ {
+ if (*uri_string == ' ')
+ g_string_append (copy, "%20");
+ else if (g_ascii_isspace (*uri_string))
+ ;
+ else
+ g_string_append_c (copy, *uri_string);
+ uri_string++;
+ }
+
+ return g_string_free (copy, FALSE);
+}
+
+static gboolean
+should_normalize_empty_path (const char *scheme)
+{
+ const char * const schemes[] = { "https", "http", "wss", "ws" };
+ gsize i;
+ for (i = 0; i < G_N_ELEMENTS (schemes); ++i)
+ {
+ if (!strcmp (schemes[i], scheme))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static int
+normalize_port (const char *scheme,
+ int port)
+{
+ const char *default_schemes[3] = { NULL };
+ int i;
+
+ switch (port)
+ {
+ case 21:
+ default_schemes[0] = "ftp";
+ break;
+ case 80:
+ default_schemes[0] = "http";
+ default_schemes[1] = "ws";
+ break;
+ case 443:
+ default_schemes[0] = "https";
+ default_schemes[1] = "wss";
+ break;
+ default:
+ break;
+ }
+
+ for (i = 0; default_schemes[i]; ++i)
+ {
+ if (!strcmp (scheme, default_schemes[i]))
+ return -1;
+ }
+
+ return port;
+}
+
+static int
+default_scheme_port (const char *scheme)
+{
+ if (strcmp (scheme, "http") == 0 || strcmp (scheme, "ws") == 0)
+ return 80;
+
+ if (strcmp (scheme, "https") == 0 || strcmp (scheme, "wss") == 0)
+ return 443;
+
+ if (strcmp (scheme, "ftp") == 0)
+ return 21;
+
+ return -1;
+}
+
+static gboolean
+flatpak_g_uri_split_internal (const gchar *uri_string,
+ GUriFlags flags,
+ gchar **scheme,
+ gchar **userinfo,
+ gchar **user,
+ gchar **password,
+ gchar **auth_params,
+ gchar **host,
+ gint *port,
+ gchar **path,
+ gchar **query,
+ gchar **fragment,
+ GError **error)
+{
+ const gchar *end, *colon, *at, *path_start, *semi, *question;
+ const gchar *p, *bracket, *hostend;
+ gchar *cleaned_uri_string = NULL;
+ gchar *normalized_scheme = NULL;
+
+ if (scheme)
+ *scheme = NULL;
+ if (userinfo)
+ *userinfo = NULL;
+ if (user)
+ *user = NULL;
+ if (password)
+ *password = NULL;
+ if (auth_params)
+ *auth_params = NULL;
+ if (host)
+ *host = NULL;
+ if (port)
+ *port = -1;
+ if (path)
+ *path = NULL;
+ if (query)
+ *query = NULL;
+ if (fragment)
+ *fragment = NULL;
+
+ if ((flags & G_URI_FLAGS_PARSE_RELAXED) && strpbrk (uri_string, " \t\n\r"))
+ {
+ cleaned_uri_string = uri_cleanup (uri_string);
+ uri_string = cleaned_uri_string;
+ }
+
+ /* Find scheme */
+ p = uri_string;
+ while (*p && (g_ascii_isalpha (*p) ||
+ (p > uri_string && (g_ascii_isdigit (*p) ||
+ *p == '.' || *p == '+' || *p == '-'))))
+ p++;
+
+ if (p > uri_string && *p == ':')
+ {
+ normalized_scheme = g_ascii_strdown (uri_string, p - uri_string);
+ if (scheme)
+ *scheme = g_steal_pointer (&normalized_scheme);
+ p++;
+ }
+ else
+ {
+ if (scheme)
+ *scheme = NULL;
+ p = uri_string;
+ }
+
+ /* Check for authority */
+ if (strncmp (p, "//", 2) == 0)
+ {
+ p += 2;
+
+ path_start = p + strcspn (p, "/?#");
+ at = memchr (p, '@', path_start - p);
+ if (at)
+ {
+ if (flags & G_URI_FLAGS_PARSE_RELAXED)
+ {
+ gchar *next_at;
+
+ /* Any "@"s in the userinfo must be %-encoded, but
+ * people get this wrong sometimes. Since "@"s in the
+ * hostname are unlikely (and also wrong anyway), assume
+ * that if there are extra "@"s, they belong in the
+ * userinfo.
+ */
+ do
+ {
+ next_at = memchr (at + 1, '@', path_start - (at + 1));
+ if (next_at)
+ at = next_at;
+ }
+ while (next_at);
+ }
+
+ if (user || password || auth_params ||
+ (flags & (G_URI_FLAGS_HAS_PASSWORD|G_URI_FLAGS_HAS_AUTH_PARAMS)))
+ {
+ if (!parse_userinfo (p, at - p, flags,
+ user, password, auth_params,
+ error))
+ goto fail;
+ }
+
+ if (!uri_normalize (userinfo, p, at - p, flags,
+ error))
+ goto fail;
+
+ p = at + 1;
+ }
+
+ if (flags & G_URI_FLAGS_PARSE_RELAXED)
+ {
+ semi = strchr (p, ';');
+ if (semi && semi < path_start)
+ {
+ /* Technically, semicolons are allowed in the "host"
+ * production, but no one ever does this, and some
+ * schemes mistakenly use semicolon as a delimiter
+ * marking the start of the path. We have to check this
+ * after checking for userinfo though, because a
+ * semicolon before the "@" must be part of the
+ * userinfo.
+ */
+ path_start = semi;
+ }
+ }
+
+ /* Find host and port. The host may be a bracket-delimited IPv6
+ * address, in which case the colon delimiting the port must come
+ * (immediately) after the close bracket.
+ */
+ if (*p == '[')
+ {
+ bracket = memchr (p, ']', path_start - p);
+ if (bracket && *(bracket + 1) == ':')
+ colon = bracket + 1;
+ else
+ colon = NULL;
+ }
+ else
+ colon = memchr (p, ':', path_start - p);
+
+ hostend = colon ? colon : path_start;
+ if (!parse_host (p, hostend - p, flags, host, error))
+ goto fail;
+
+ if (colon && colon != path_start - 1)
+ {
+ p = colon + 1;
+ if (!parse_port (p, path_start - p, port, error))
+ goto fail;
+ }
+
+ p = path_start;
+ }
+
+ /* Find fragment. */
+ end = p + strcspn (p, "#");
+ if (*end == '#')
+ {
+ if (!uri_normalize (fragment, end + 1, strlen (end + 1),
+ flags | (flags & G_URI_FLAGS_ENCODED_FRAGMENT ? G_URI_FLAGS_ENCODED : 0),
+ error))
+ goto fail;
+ }
+
+ /* Find query */
+ question = memchr (p, '?', end - p);
+ if (question)
+ {
+ if (!uri_normalize (query, question + 1, end - (question + 1),
+ flags | (flags & G_URI_FLAGS_ENCODED_QUERY ? G_URI_FLAGS_ENCODED : 0),
+ error))
+ goto fail;
+ end = question;
+ }
+
+ if (!uri_normalize (path, p, end - p,
+ flags | (flags & G_URI_FLAGS_ENCODED_PATH ? G_URI_FLAGS_ENCODED : 0),
+ error))
+ goto fail;
+
+ /* Scheme-based normalization */
+ if (flags & G_URI_FLAGS_SCHEME_NORMALIZE && ((scheme && *scheme) || normalized_scheme))
+ {
+ const char *scheme_str = scheme && *scheme ? *scheme : normalized_scheme;
+
+ if (should_normalize_empty_path (scheme_str) && path && !**path)
+ {
+ g_free (*path);
+ *path = g_strdup ("/");
+ }
+
+ if (port && *port == -1)
+ *port = default_scheme_port (scheme_str);
+ }
+
+ g_free (normalized_scheme);
+ g_free (cleaned_uri_string);
+ return TRUE;
+
+ fail:
+ if (scheme)
+ g_clear_pointer (scheme, g_free);
+ if (userinfo)
+ g_clear_pointer (userinfo, g_free);
+ if (host)
+ g_clear_pointer (host, g_free);
+ if (port)
+ *port = -1;
+ if (path)
+ g_clear_pointer (path, g_free);
+ if (query)
+ g_clear_pointer (query, g_free);
+ if (fragment)
+ g_clear_pointer (fragment, g_free);
+
+ g_free (normalized_scheme);
+ g_free (cleaned_uri_string);
+ return FALSE;
+}
+
+
+/* Implements the "Remove Dot Segments" algorithm from section 5.2.4 of
+ * RFC 3986.
+ *
+ * See https://tools.ietf.org/html/rfc3986#section-5.2.4
+ */
+static void
+remove_dot_segments (gchar *path)
+{
+ /* The output can be written to the same buffer that the input
+ * is read from, as the output pointer is only ever increased
+ * when the input pointer is increased as well, and the input
+ * pointer is never decreased. */
+ gchar *input = path;
+ gchar *output = path;
+
+ if (!*path)
+ return;
+
+ while (*input)
+ {
+ /* A. If the input buffer begins with a prefix of "../" or "./",
+ * then remove that prefix from the input buffer; otherwise,
+ */
+ if (strncmp (input, "../", 3) == 0)
+ input += 3;
+ else if (strncmp (input, "./", 2) == 0)
+ input += 2;
+
+ /* B. if the input buffer begins with a prefix of "/./" or "/.",
+ * where "." is a complete path segment, then replace that
+ * prefix with "/" in the input buffer; otherwise,
+ */
+ else if (strncmp (input, "/./", 3) == 0)
+ input += 2;
+ else if (strcmp (input, "/.") == 0)
+ input[1] = '\0';
+
+ /* C. if the input buffer begins with a prefix of "/../" or "/..",
+ * where ".." is a complete path segment, then replace that
+ * prefix with "/" in the input buffer and remove the last
+ * segment and its preceding "/" (if any) from the output
+ * buffer; otherwise,
+ */
+ else if (strncmp (input, "/../", 4) == 0)
+ {
+ input += 3;
+ if (output > path)
+ {
+ do
+ {
+ output--;
+ }
+ while (*output != '/' && output > path);
+ }
+ }
+ else if (strcmp (input, "/..") == 0)
+ {
+ input[1] = '\0';
+ if (output > path)
+ {
+ do
+ {
+ output--;
+ }
+ while (*output != '/' && output > path);
+ }
+ }
+
+ /* D. if the input buffer consists only of "." or "..", then remove
+ * that from the input buffer; otherwise,
+ */
+ else if (strcmp (input, "..") == 0 || strcmp (input, ".") == 0)
+ input[0] = '\0';
+
+ /* E. move the first path segment in the input buffer to the end of
+ * the output buffer, including the initial "/" character (if
+ * any) and any subsequent characters up to, but not including,
+ * the next "/" character or the end of the input buffer.
+ */
+ else
+ {
+ *output++ = *input++;
+ while (*input && *input != '/')
+ *output++ = *input++;
+ }
+ }
+ *output = '\0';
+}
+
+GUri *
+flatpak_g_uri_parse (const gchar *uri_string,
+ GUriFlags flags,
+ GError **error)
+{
+ g_return_val_if_fail (uri_string != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ return flatpak_g_uri_parse_relative (NULL, uri_string, flags, error);
+}
+
+GUri *
+flatpak_g_uri_parse_relative (GUri *base_uri,
+ const gchar *uri_ref,
+ GUriFlags flags,
+ GError **error)
+{
+ GUri *uri = NULL;
+
+ g_return_val_if_fail (uri_ref != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+ g_return_val_if_fail (base_uri == NULL || base_uri->scheme != NULL, NULL);
+
+ /* Use GUri struct to construct the return value: there is no guarantee it is
+ * actually correct within the function body. */
+ uri = g_new0 (GUri, 1);
+ uri->ref_count = 1;
+ uri->flags = flags;
+
+ if (!flatpak_g_uri_split_internal (uri_ref, flags,
+ &uri->scheme, &uri->userinfo,
+ &uri->user, &uri->password, &uri->auth_params,
+ &uri->host, &uri->port,
+ &uri->path, &uri->query, &uri->fragment,
+ error))
+ {
+ flatpak_g_uri_unref (uri);
+ return NULL;
+ }
+
+ if (!uri->scheme && !base_uri)
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+ _("URI is not absolute, and no base URI was provided"));
+ flatpak_g_uri_unref (uri);
+ return NULL;
+ }
+
+ if (base_uri)
+ {
+ /* This is section 5.2.2 of RFC 3986, except that we're doing
+ * it in place in @uri rather than copying from R to T.
+ *
+ * See https://tools.ietf.org/html/rfc3986#section-5.2.2
+ */
+ if (uri->scheme)
+ remove_dot_segments (uri->path);
+ else
+ {
+ uri->scheme = g_strdup (base_uri->scheme);
+ if (uri->host)
+ remove_dot_segments (uri->path);
+ else
+ {
+ if (!*uri->path)
+ {
+ g_free (uri->path);
+ uri->path = g_strdup (base_uri->path);
+ if (!uri->query)
+ uri->query = g_strdup (base_uri->query);
+ }
+ else
+ {
+ if (*uri->path == '/')
+ remove_dot_segments (uri->path);
+ else
+ {
+ gchar *newpath, *last;
+
+ last = strrchr (base_uri->path, '/');
+ if (last)
+ {
+ newpath = g_strdup_printf ("%.*s/%s",
+ (gint)(last - base_uri->path),
+ base_uri->path,
+ uri->path);
+ }
+ else
+ newpath = g_strdup_printf ("/%s", uri->path);
+
+ g_free (uri->path);
+ uri->path = g_steal_pointer (&newpath);
+
+ remove_dot_segments (uri->path);
+ }
+ }
+
+ uri->userinfo = g_strdup (base_uri->userinfo);
+ uri->user = g_strdup (base_uri->user);
+ uri->password = g_strdup (base_uri->password);
+ uri->auth_params = g_strdup (base_uri->auth_params);
+ uri->host = g_strdup (base_uri->host);
+ uri->port = base_uri->port;
+ }
+ }
+
+ /* Scheme normalization couldn't have been done earlier
+ * as the relative URI may not have had a scheme */
+ if (flags & G_URI_FLAGS_SCHEME_NORMALIZE)
+ {
+ if (should_normalize_empty_path (uri->scheme) && !*uri->path)
+ {
+ g_free (uri->path);
+ uri->path = g_strdup ("/");
+ }
+
+ uri->port = normalize_port (uri->scheme, uri->port);
+ }
+ }
+ else
+ {
+ remove_dot_segments (uri->path);
+ }
+
+ return g_steal_pointer (&uri);
+}
+
+/* userinfo as a whole can contain sub-delims + ":", but split-out
+ * user can't contain ":" or ";", and split-out password can't contain
+ * ";".
+ */
+#define USERINFO_ALLOWED_CHARS G_URI_RESERVED_CHARS_ALLOWED_IN_USERINFO
+#define USER_ALLOWED_CHARS "!$&'()*+,="
+#define PASSWORD_ALLOWED_CHARS "!$&'()*+,=:"
+#define AUTH_PARAMS_ALLOWED_CHARS USERINFO_ALLOWED_CHARS
+#define IP_ADDR_ALLOWED_CHARS ":"
+#define HOST_ALLOWED_CHARS G_URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS
+#define PATH_ALLOWED_CHARS G_URI_RESERVED_CHARS_ALLOWED_IN_PATH
+#define QUERY_ALLOWED_CHARS G_URI_RESERVED_CHARS_ALLOWED_IN_PATH "?"
+#define FRAGMENT_ALLOWED_CHARS G_URI_RESERVED_CHARS_ALLOWED_IN_PATH "?"
+
+static gchar *
+flatpak_g_uri_join_internal (GUriFlags flags,
+ const gchar *scheme,
+ gboolean userinfo,
+ const gchar *user,
+ const gchar *password,
+ const gchar *auth_params,
+ const gchar *host,
+ gint port,
+ const gchar *path,
+ const gchar *query,
+ const gchar *fragment)
+{
+ gboolean encoded = (flags & G_URI_FLAGS_ENCODED);
+ GString *str;
+ char *normalized_scheme = NULL;
+
+ /* Restrictions on path prefixes. See:
+ * https://tools.ietf.org/html/rfc3986#section-3
+ */
+ g_return_val_if_fail (path != NULL, NULL);
+ g_return_val_if_fail (host == NULL || (path[0] == '\0' || path[0] == '/'), NULL);
+ g_return_val_if_fail (host != NULL || (path[0] != '/' || path[1] != '/'), NULL);
+
+ str = g_string_new (scheme);
+ if (scheme)
+ g_string_append_c (str, ':');
+
+ if (flags & G_URI_FLAGS_SCHEME_NORMALIZE && scheme && ((host && port != -1) || path[0] == '\0'))
+ normalized_scheme = g_ascii_strdown (scheme, -1);
+
+ if (host)
+ {
+ g_string_append (str, "//");
+
+ if (user)
+ {
+ if (encoded)
+ g_string_append (str, user);
+ else
+ {
+ if (userinfo)
+ g_string_append_uri_escaped (str, user, USERINFO_ALLOWED_CHARS, TRUE);
+ else
+ /* Encode ':' and ';' regardless of whether we have a
+ * password or auth params, since it may be parsed later
+ * under the assumption that it does.
+ */
+ g_string_append_uri_escaped (str, user, USER_ALLOWED_CHARS, TRUE);
+ }
+
+ if (password)
+ {
+ g_string_append_c (str, ':');
+ if (encoded)
+ g_string_append (str, password);
+ else
+ g_string_append_uri_escaped (str, password,
+ PASSWORD_ALLOWED_CHARS, TRUE);
+ }
+
+ if (auth_params)
+ {
+ g_string_append_c (str, ';');
+ if (encoded)
+ g_string_append (str, auth_params);
+ else
+ g_string_append_uri_escaped (str, auth_params,
+ AUTH_PARAMS_ALLOWED_CHARS, TRUE);
+ }
+
+ g_string_append_c (str, '@');
+ }
+
+ if (strchr (host, ':') && g_hostname_is_ip_address (host))
+ {
+ g_string_append_c (str, '[');
+ if (encoded)
+ g_string_append (str, host);
+ else
+ g_string_append_uri_escaped (str, host, IP_ADDR_ALLOWED_CHARS, TRUE);
+ g_string_append_c (str, ']');
+ }
+ else
+ {
+ if (encoded)
+ g_string_append (str, host);
+ else
+ g_string_append_uri_escaped (str, host, HOST_ALLOWED_CHARS, TRUE);
+ }
+
+ if (port != -1 && (!normalized_scheme || normalize_port (normalized_scheme, port) != -1))
+ g_string_append_printf (str, ":%d", port);
+ }
+
+ if (path[0] == '\0' && normalized_scheme && should_normalize_empty_path (normalized_scheme))
+ g_string_append (str, "/");
+ else if (encoded || flags & G_URI_FLAGS_ENCODED_PATH)
+ g_string_append (str, path);
+ else
+ g_string_append_uri_escaped (str, path, PATH_ALLOWED_CHARS, TRUE);
+
+ g_free (normalized_scheme);
+
+ if (query)
+ {
+ g_string_append_c (str, '?');
+ if (encoded || flags & G_URI_FLAGS_ENCODED_QUERY)
+ g_string_append (str, query);
+ else
+ g_string_append_uri_escaped (str, query, QUERY_ALLOWED_CHARS, TRUE);
+ }
+ if (fragment)
+ {
+ g_string_append_c (str, '#');
+ if (encoded || flags & G_URI_FLAGS_ENCODED_FRAGMENT)
+ g_string_append (str, fragment);
+ else
+ g_string_append_uri_escaped (str, fragment, FRAGMENT_ALLOWED_CHARS, TRUE);
+ }
+
+ return g_string_free (str, FALSE);
+}
+
+static gchar *
+flatpak_g_uri_join (GUriFlags flags,
+ const gchar *scheme,
+ const gchar *userinfo,
+ const gchar *host,
+ gint port,
+ const gchar *path,
+ const gchar *query,
+ const gchar *fragment)
+{
+ g_return_val_if_fail (port >= -1 && port <= 65535, NULL);
+ g_return_val_if_fail (path != NULL, NULL);
+
+ return flatpak_g_uri_join_internal (flags,
+ scheme,
+ TRUE, userinfo, NULL, NULL,
+ host,
+ port,
+ path,
+ query,
+ fragment);
+}
+
+static gchar *
+flatpak_g_uri_join_with_user (GUriFlags flags,
+ const gchar *scheme,
+ const gchar *user,
+ const gchar *password,
+ const gchar *auth_params,
+ const gchar *host,
+ gint port,
+ const gchar *path,
+ const gchar *query,
+ const gchar *fragment)
+{
+ g_return_val_if_fail (port >= -1 && port <= 65535, NULL);
+ g_return_val_if_fail (path != NULL, NULL);
+
+ return flatpak_g_uri_join_internal (flags,
+ scheme,
+ FALSE, user, password, auth_params,
+ host,
+ port,
+ path,
+ query,
+ fragment);
+}
+
+GUri *
+flatpak_g_uri_build (GUriFlags flags,
+ const gchar *scheme,
+ const gchar *userinfo,
+ const gchar *host,
+ gint port,
+ const gchar *path,
+ const gchar *query,
+ const gchar *fragment)
+{
+ GUri *uri;
+
+ g_return_val_if_fail (scheme != NULL, NULL);
+ g_return_val_if_fail (port >= -1 && port <= 65535, NULL);
+ g_return_val_if_fail (path != NULL, NULL);
+
+ uri = g_new0 (GUri, 1);
+ uri->ref_count = 1;
+ uri->flags = flags;
+ uri->scheme = g_ascii_strdown (scheme, -1);
+ uri->userinfo = g_strdup (userinfo);
+ uri->host = g_strdup (host);
+ uri->port = port;
+ uri->path = g_strdup (path);
+ uri->query = g_strdup (query);
+ uri->fragment = g_strdup (fragment);
+
+ return g_steal_pointer (&uri);
+}
+
+gchar *
+flatpak_g_uri_to_string_partial (GUri *uri,
+ GUriHideFlags flags)
+{
+ gboolean hide_user = (flags & G_URI_HIDE_USERINFO);
+ gboolean hide_password = (flags & (G_URI_HIDE_USERINFO | G_URI_HIDE_PASSWORD));
+ gboolean hide_auth_params = (flags & (G_URI_HIDE_USERINFO | G_URI_HIDE_AUTH_PARAMS));
+ gboolean hide_query = (flags & G_URI_HIDE_QUERY);
+ gboolean hide_fragment = (flags & G_URI_HIDE_FRAGMENT);
+
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ if (uri->flags & (G_URI_FLAGS_HAS_PASSWORD | G_URI_FLAGS_HAS_AUTH_PARAMS))
+ {
+ return flatpak_g_uri_join_with_user (uri->flags,
+ uri->scheme,
+ hide_user ? NULL : uri->user,
+ hide_password ? NULL : uri->password,
+ hide_auth_params ? NULL : uri->auth_params,
+ uri->host,
+ uri->port,
+ uri->path,
+ hide_query ? NULL : uri->query,
+ hide_fragment ? NULL : uri->fragment);
+ }
+
+ return flatpak_g_uri_join (uri->flags,
+ uri->scheme,
+ hide_user ? NULL : uri->userinfo,
+ uri->host,
+ uri->port,
+ uri->path,
+ hide_query ? NULL : uri->query,
+ hide_fragment ? NULL : uri->fragment);
+}
+
+const gchar *
+flatpak_g_uri_get_scheme (GUri *uri)
+{
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ return uri->scheme;
+}
+
+const gchar *
+flatpak_g_uri_get_userinfo (GUri *uri)
+{
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ return uri->userinfo;
+}
+
+const gchar *
+flatpak_g_uri_get_user (GUri *uri)
+{
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ return uri->user;
+}
+
+const gchar *
+flatpak_g_uri_get_password (GUri *uri)
+{
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ return uri->password;
+}
+
+const gchar *
+flatpak_g_uri_get_auth_params (GUri *uri)
+{
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ return uri->auth_params;
+}
+
+const gchar *
+flatpak_g_uri_get_host (GUri *uri)
+{
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ return uri->host;
+}
+
+gint
+flatpak_g_uri_get_port (GUri *uri)
+{
+ g_return_val_if_fail (uri != NULL, -1);
+
+ if (uri->port == -1 && uri->flags & G_URI_FLAGS_SCHEME_NORMALIZE)
+ return default_scheme_port (uri->scheme);
+
+ return uri->port;
+}
+
+const gchar *
+flatpak_g_uri_get_path (GUri *uri)
+{
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ return uri->path;
+}
+
+const gchar *
+flatpak_g_uri_get_query (GUri *uri)
+{
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ return uri->query;
+}
+
+const gchar *
+flatpak_g_uri_get_fragment (GUri *uri)
+{
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ return uri->fragment;
+}
+
+GUriFlags
+flatpak_g_uri_get_flags (GUri *uri)
+{
+ g_return_val_if_fail (uri != NULL, G_URI_FLAGS_NONE);
+
+ return uri->flags;
+}
+
+#endif /* GLIB_CHECK_VERSION (2, 66, 0) */
+
+
+static void
+append_form_encoded (GString *str, const char *in)
+{
+ const unsigned char *s = (const unsigned char *)in;
+
+ while (*s)
+ {
+ if (*s == ' ')
+ {
+ g_string_append_c (str, '+');
+ s++;
+ }
+ else if (!g_ascii_isalnum (*s) && (*s != '-') && (*s != '_')
+ && (*s != '.'))
+ g_string_append_printf (str, "%%%02X", (int)*s++);
+ else
+ g_string_append_c (str, *s++);
+ }
+}
+
+void
+flatpak_uri_encode_query_arg (GString *str,
+ const char *key,
+ const char *value)
+{
+ if (str->len)
+ g_string_append_c (str, '&');
+ append_form_encoded (str, key);
+
+ g_string_append_c (str, '=');
+ append_form_encoded (str, value);
+}
+
+
+/* This is a simplified copy of soup_header_parse_param_list() to avoid a soup dependency */
+
+static const char *
+skip_lws (const char *s)
+{
+ while (g_ascii_isspace (*s))
+ s++;
+ return s;
+}
+
+static const char *
+unskip_lws (const char *s, const char *start)
+{
+ while (s > start && g_ascii_isspace (*(s - 1)))
+ s--;
+ return s;
+}
+
+static const char *
+skip_delims (const char *s, char delim)
+{
+ /* The grammar allows for multiple delimiters */
+ while (g_ascii_isspace (*s) || *s == delim)
+ s++;
+ return s;
+}
+
+static const char *
+skip_item (const char *s, char delim)
+{
+ gboolean quoted = FALSE;
+ const char *start = s;
+
+ /* A list item ends at the last non-whitespace character
+ * before a delimiter which is not inside a quoted-string. Or
+ * at the end of the string.
+ */
+
+ while (*s)
+ {
+ if (*s == '"')
+ quoted = !quoted;
+ else if (quoted)
+ {
+ if (*s == '\\' && *(s + 1))
+ s++;
+ }
+ else
+ {
+ if (*s == delim)
+ break;
+ }
+ s++;
+ }
+
+ return unskip_lws (s, start);
+}
+
+static GSList *
+parse_list (const char *header, char delim)
+{
+ GSList *list = NULL;
+ const char *end;
+
+ header = skip_delims (header, delim);
+ while (*header)
+ {
+ end = skip_item (header, delim);
+ list = g_slist_prepend (list, g_strndup (header, end - header));
+ header = skip_delims (end, delim);
+ }
+
+ return g_slist_reverse (list);
+}
+
+static void
+decode_quoted_string (char *quoted_string)
+{
+ char *src, *dst;
+
+ src = quoted_string + 1;
+ dst = quoted_string;
+ while (*src && *src != '"')
+ {
+ if (*src == '\\' && *(src + 1))
+ src++;
+ *dst++ = *src++;
+ }
+ *dst = '\0';
+}
+
+GHashTable *
+flatpak_parse_http_header_param_list (const char *header)
+{
+ GHashTable *params;
+ GSList *list, *iter;
+ char *eq, *name_end, *value;
+
+ params = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free, g_free);
+
+ list = parse_list (header, ',');
+ for (iter = list; iter; iter = iter->next)
+ {
+ g_autofree char *item = iter->data;
+
+ eq = strchr (item, '=');
+ if (eq)
+ {
+ name_end = (char *)unskip_lws (eq, item);
+ if (name_end == item)
+ continue;
+
+ *name_end = '\0';
+
+ value = (char *)skip_lws (eq + 1);
+ if (*value == '"')
+ decode_quoted_string (value);
+ }
+ else
+ value = NULL;
+
+ g_autofree char *key = g_ascii_strdown (item, -1);
+ if (!g_hash_table_contains (params, key))
+ g_hash_table_replace (params, g_steal_pointer (&key), g_strdup (value));
+ }
+
+ g_slist_free (list);
+ return params;
+}
+
+/* Do not internationalize */
+static const char *const months[] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+/* Do not internationalize */
+static const char *const days[] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+
+char *
+flatpak_format_http_date (GDateTime *date)
+{
+ g_autoptr(GDateTime) utcdate = g_date_time_to_utc (date);
+ g_autofree char *date_format = NULL;
+
+ /* "Sun, 06 Nov 1994 08:49:37 GMT" */
+
+ date_format = g_strdup_printf ("%s, %%d %s %%Y %%T GMT",
+ days[g_date_time_get_day_of_week (utcdate) - 1],
+ months[g_date_time_get_month (utcdate) - 1]);
+
+ return g_date_time_format (utcdate, (const char*)date_format);
+}
+
+
+static inline gboolean
+parse_day (int *day, const char **date_string)
+{
+ char *end;
+
+ *day = strtoul (*date_string, &end, 10);
+ if (end == (char *)*date_string)
+ return FALSE;
+
+ while (*end == ' ' || *end == '-')
+ end++;
+ *date_string = end;
+ return TRUE;
+}
+
+static inline gboolean
+parse_month (int *month, const char **date_string)
+{
+ int i;
+
+ for (i = 0; i < G_N_ELEMENTS (months); i++)
+ {
+ if (!g_ascii_strncasecmp (*date_string, months[i], 3))
+ {
+ *month = i + 1;
+ *date_string += 3;
+ while (**date_string == ' ' || **date_string == '-')
+ (*date_string)++;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static inline gboolean
+parse_year (int *year, const char **date_string)
+{
+ char *end;
+
+ *year = strtoul (*date_string, &end, 10);
+ if (end == (char *)*date_string)
+ return FALSE;
+
+ if (end == (char *)*date_string + 2) {
+ if (*year < 70)
+ *year += 2000;
+ else
+ *year += 1900;
+ } else if (end == (char *)*date_string + 3)
+ *year += 1900;
+
+ while (*end == ' ' || *end == '-')
+ end++;
+ *date_string = end;
+
+ return TRUE;
+}
+
+static inline gboolean
+parse_time (int *hour, int *minute, int *second, const char **date_string)
+{
+ char *p, *end;
+
+ *hour = strtoul (*date_string, &end, 10);
+ if (end == (char *)*date_string || *end++ != ':')
+ return FALSE;
+ p = end;
+ *minute = strtoul (p, &end, 10);
+ if (end == p || *end++ != ':')
+ return FALSE;
+ p = end;
+ *second = strtoul (p, &end, 10);
+ if (end == p)
+ return FALSE;
+ p = end;
+
+ while (*p == ' ')
+ p++;
+ *date_string = p;
+
+ return TRUE;
+}
+
+static inline GTimeZone *
+time_zone_new_offset (gint32 offset)
+{
+#if GLIB_CHECK_VERSION (2, 58, 0)
+ return g_time_zone_new_offset (offset);
+#else
+ g_autofree char *id = NULL;
+ gint hours, minutes;
+ gint seconds = offset;
+ GTimeZone *tz;
+ char sign = '+';
+
+ if (seconds == 0)
+ return g_time_zone_new_utc ();
+
+ if (seconds < 0)
+ {
+ seconds = -seconds;
+ sign = '-';
+ }
+
+ hours = seconds / 3600;
+ seconds = seconds % 3600;
+ minutes = seconds / 60;
+ seconds = seconds % 60;
+
+ id = g_strdup_printf ("%c%02d:%02d:%02d", sign, hours, minutes, seconds);
+ tz = g_time_zone_new (id);
+ /* If this assertion fails, we'll log a critical but still return tz,
+ * which is documented to be UTC if the time zone could not be parsed */
+ g_return_val_if_fail (g_time_zone_get_offset (tz, 0) == offset, tz);
+ return tz;
+#endif
+}
+
+static inline gboolean
+parse_timezone (GTimeZone **timezone_out, const char **date_string)
+{
+ gint32 offset_minutes;
+ gboolean utc;
+
+ if (!**date_string)
+ {
+ utc = FALSE;
+ offset_minutes = 0;
+ }
+ else if (**date_string == '+' || **date_string == '-')
+ {
+ gulong val;
+ int sign = (**date_string == '+') ? 1 : -1;
+ val = strtoul (*date_string + 1, (char **)date_string, 10);
+ if (**date_string == ':')
+ val = 60 * val + strtoul (*date_string + 1, (char **)date_string, 10);
+ else
+ val = 60 * (val / 100) + (val % 100);
+ offset_minutes = sign * val;
+ utc = (sign == -1) && !val;
+ }
+ else if (**date_string == 'Z')
+ {
+ offset_minutes = 0;
+ utc = TRUE;
+ (*date_string)++;
+ }
+ else if (!strcmp (*date_string, "GMT") ||
+ !strcmp (*date_string, "UTC"))
+ {
+ offset_minutes = 0;
+ utc = TRUE;
+ (*date_string) += 3;
+ }
+ else if (strchr ("ECMP", **date_string) &&
+ ((*date_string)[1] == 'D' || (*date_string)[1] == 'S') &&
+ (*date_string)[2] == 'T') {
+ offset_minutes = -60 * (5 * strcspn ("ECMP", *date_string));
+ if ((*date_string)[1] == 'D')
+ offset_minutes += 60;
+ utc = FALSE;
+ }
+ else
+ return FALSE;
+
+ if (utc)
+ *timezone_out = g_time_zone_new_utc ();
+ else
+ *timezone_out = time_zone_new_offset (offset_minutes * 60);
+
+ return TRUE;
+}
+
+GDateTime *
+flatpak_parse_http_time (const char *date_string)
+{
+ int month, day, year, hour, minute, second;
+ g_autoptr(GTimeZone) tz = NULL;
+
+ g_return_val_if_fail (date_string != NULL, NULL);
+
+ while (g_ascii_isspace (*date_string))
+ date_string++;
+
+ /* If it starts with a word, it must be a weekday, which we skip */
+ if (g_ascii_isalpha (*date_string))
+ {
+ while (g_ascii_isalpha (*date_string))
+ date_string++;
+ if (*date_string == ',')
+ date_string++;
+ while (g_ascii_isspace (*date_string))
+ date_string++;
+ }
+
+ /* If there's now another word, this must be an asctime-date */
+ if (g_ascii_isalpha (*date_string))
+ {
+ /* (Sun) Nov 6 08:49:37 1994 */
+ if (!parse_month (&month, &date_string) ||
+ !parse_day (&day, &date_string) ||
+ !parse_time (&hour, &minute, &second, &date_string) ||
+ !parse_year (&year, &date_string))
+ return NULL;
+
+ /* There shouldn't be a timezone, but check anyway */
+ parse_timezone (&tz, &date_string);
+ }
+ else
+ {
+ /* Non-asctime date, so some variation of
+ * (Sun,) 06 Nov 1994 08:49:37 GMT
+ */
+ if (!parse_day (&day, &date_string) ||
+ !parse_month (&month, &date_string) ||
+ !parse_year (&year, &date_string) ||
+ !parse_time (&hour, &minute, &second, &date_string))
+ return NULL;
+
+ /* This time there *should* be a timezone, but we
+ * survive if there isn't.
+ */
+ parse_timezone (&tz, &date_string);
+ }
+
+ if (!tz)
+ tz = g_time_zone_new_utc ();
+
+ return g_date_time_new (tz, year, month, day, hour, minute, second);
+}
diff --git a/common/flatpak-utils-base.c b/common/flatpak-utils-base.c
index d5f19e81..ef91d1b1 100644
--- a/common/flatpak-utils-base.c
+++ b/common/flatpak-utils-base.c
@@ -1,4 +1,4 @@
-/*
+/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
* Copyright © 2019 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
diff --git a/common/flatpak-utils-http-private.h b/common/flatpak-utils-http-private.h
index f1319f0f..2c89ba40 100644
--- a/common/flatpak-utils-http-private.h
+++ b/common/flatpak-utils-http-private.h
@@ -23,8 +23,6 @@
#include <string.h>
-#include <libsoup/soup.h>
-
typedef enum {
FLATPAK_HTTP_ERROR_NOT_CHANGED = 0,
FLATPAK_HTTP_ERROR_UNAUTHORIZED = 1,
@@ -34,19 +32,37 @@ typedef enum {
GQuark flatpak_http_error_quark (void);
+typedef struct FlatpakHttpSession FlatpakHttpSession;
+
+FlatpakHttpSession* flatpak_create_http_session (const char *user_agent);
+void flatpak_http_session_free (FlatpakHttpSession* http_session);
-SoupSession * flatpak_create_soup_session (const char *user_agent);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(FlatpakHttpSession, flatpak_http_session_free)
typedef enum {
FLATPAK_HTTP_FLAGS_NONE = 0,
FLATPAK_HTTP_FLAGS_ACCEPT_OCI = 1 << 0,
- FLATPAK_HTTP_FLAGS_STORE_COMPRESSED = 2 << 0,
+ FLATPAK_HTTP_FLAGS_STORE_COMPRESSED = 1 << 1,
+ FLATPAK_HTTP_FLAGS_NOCHECK_STATUS = 1 << 2,
+ FLATPAK_HTTP_FLAGS_HEAD = 1 << 3,
} FlatpakHTTPFlags;
typedef void (*FlatpakLoadUriProgress) (guint64 downloaded_bytes,
gpointer user_data);
-GBytes * flatpak_load_uri (SoupSession *soup_session,
+GBytes * flatpak_load_uri_full (FlatpakHttpSession *http_session,
+ const char *uri,
+ FlatpakHTTPFlags flags,
+ const char *auth,
+ const char *token,
+ FlatpakLoadUriProgress progress,
+ gpointer user_data,
+ int *out_status,
+ char **out_content_type,
+ char **out_www_authenticate,
+ GCancellable *cancellable,
+ GError **error);
+GBytes * flatpak_load_uri (FlatpakHttpSession *http_session,
const char *uri,
FlatpakHTTPFlags flags,
const char *token,
@@ -55,7 +71,7 @@ GBytes * flatpak_load_uri (SoupSession *soup_session,
char **out_content_type,
GCancellable *cancellable,
GError **error);
-gboolean flatpak_download_http_uri (SoupSession *soup_session,
+gboolean flatpak_download_http_uri (FlatpakHttpSession *http_session,
const char *uri,
FlatpakHTTPFlags flags,
GOutputStream *out,
@@ -64,7 +80,7 @@ gboolean flatpak_download_http_uri (SoupSession *soup_session,
gpointer user_data,
GCancellable *cancellable,
GError **error);
-gboolean flatpak_cache_http_uri (SoupSession *soup_session,
+gboolean flatpak_cache_http_uri (FlatpakHttpSession *http_session,
const char *uri,
FlatpakHTTPFlags flags,
int dest_dfd,
diff --git a/common/flatpak-utils-http.c b/common/flatpak-utils-http.c
index 6f2421ba..e180345a 100644
--- a/common/flatpak-utils-http.c
+++ b/common/flatpak-utils-http.c
@@ -1,4 +1,4 @@
-/*
+/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
* Copyright © 2018 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
@@ -18,21 +18,64 @@
* Alexander Larsson <alexl@redhat.com>
*/
+#include "config.h"
+
+#include <gio/gio.h>
+#include <glib-unix.h>
#include "flatpak-utils-http-private.h"
+#include "flatpak-uri-private.h"
#include "flatpak-oci-registry-private.h"
#include <gio/gunixoutputstream.h>
-#include <libsoup/soup.h>
#include "libglnx.h"
#include <sys/types.h>
#include <sys/xattr.h>
+#if defined(HAVE_CURL)
+
+#include <curl/curl.h>
+
+/* These macros came from 7.43.0, but we want to check
+ * for versions a bit earlier than that (to work on CentOS 7),
+ * so define them here if we're using an older version.
+ */
+#ifndef CURL_VERSION_BITS
+#define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|z)
+#endif
+#ifndef CURL_AT_LEAST_VERSION
+#define CURL_AT_LEAST_VERSION(x,y,z) (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS(x, y, z))
+#endif
+
+#elif defined(HAVE_SOUP)
+
+#include <libsoup/soup.h>
+
+#if !defined(SOUP_AUTOCLEANUPS_H) && !defined(__SOUP_AUTOCLEANUPS_H__)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (SoupSession, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (SoupMessage, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (SoupRequest, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (SoupRequestHTTP, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (SoupURI, soup_uri_free)
+#endif
+
+#else
+
+# error "No HTTP backend enabled"
+
+#endif
+
+
+#define FLATPAK_HTTP_TIMEOUT_SECS 60
+
/* copied from libostree */
#define DEFAULT_N_NETWORK_RETRIES 5
G_DEFINE_QUARK (flatpak_http_error, flatpak_http_error)
+/* Information about the cache status of a file.
+ Encoded in an xattr on the cached file, or a file on the side if xattrs don't work.
+*/
typedef struct
{
char *uri;
@@ -46,290 +89,516 @@ typedef struct
GMainContext *context;
gboolean done;
GError *error;
- gboolean store_compressed;
+
+ /* Input args */
+
+ FlatpakHTTPFlags flags;
+ const char *auth;
+ const char *token;
+ FlatpakLoadUriProgress progress;
+ GCancellable *cancellable;
+ gpointer user_data;
+ CacheHttpData *cache_data;
+
+ /* Output from the request, set even on http server errors */
+
+ guint64 downloaded_bytes;
+ int status;
+ char *hdr_content_type;
+ char *hdr_www_authenticate;
+ char *hdr_etag;
+ char *hdr_last_modified;
+ char *hdr_cache_control;
+ char *hdr_expires;
+ char *hdr_content_encoding;
+
+ /* Data destination */
GOutputStream *out; /*or */
GString *content; /* or */
GLnxTmpfile *out_tmpfile;
int out_tmpfile_parent_dfd;
- guint64 downloaded_bytes;
+ /* Used during operation */
+
char buffer[16 * 1024];
- FlatpakLoadUriProgress progress;
- GCancellable *cancellable;
- gpointer user_data;
guint64 last_progress_time;
- CacheHttpData *cache_data;
- char **content_type_out;
+ gboolean store_compressed;
+
} LoadUriData;
-#define CACHE_HTTP_XATTR "user.flatpak.http"
-#define CACHE_HTTP_SUFFIX ".flatpak.http"
-#define CACHE_HTTP_TYPE "(sstt)"
+static void
+clear_load_uri_data_headers (LoadUriData *data)
+{
+ g_clear_pointer (&data->hdr_content_type, g_free);
+ g_clear_pointer (&data->hdr_www_authenticate, g_free);
+ g_clear_pointer (&data->hdr_etag, g_free);
+ g_clear_pointer (&data->hdr_last_modified, g_free);
+ g_clear_pointer (&data->hdr_last_modified, g_free);
+ g_clear_pointer (&data->hdr_cache_control, g_free);
+ g_clear_pointer (&data->hdr_expires, g_free);
+ g_clear_pointer (&data->hdr_content_encoding, g_free);
+}
+/* Reset between requests retries */
static void
-clear_cache_http_data (CacheHttpData *data,
- gboolean clear_uri)
+reset_load_uri_data (LoadUriData *data)
{
- if (clear_uri)
- g_clear_pointer (&data->uri, g_free);
- g_clear_pointer (&data->etag, g_free);
- data->last_modified = 0;
- data->expires = 0;
+ g_clear_error (&data->error);
+ data->status = 0;
+ data->downloaded_bytes = 0;
+ if (data->content)
+ g_string_set_size (data->content, 0);
+
+ clear_load_uri_data_headers (data);
+
+ if (data->out_tmpfile)
+ {
+ glnx_tmpfile_clear (data->out_tmpfile);
+ g_clear_pointer (&data->out, g_object_unref);
+ }
+
+ /* Reset the progress */
+ if (data->progress)
+ data->progress (0, data->user_data);
}
+/* Free allocated data at end of full repeated download */
static void
-free_cache_http_data (CacheHttpData *data)
+clear_load_uri_data (LoadUriData *data)
{
- clear_cache_http_data (data, TRUE);
- g_free (data);
+ if (data->content)
+ {
+ g_string_free (data->content, TRUE);
+ data->content = NULL;
+ }
+
+ g_clear_error (&data->error);
+
+ clear_load_uri_data_headers (data);
}
-G_DEFINE_AUTOPTR_CLEANUP_FUNC (CacheHttpData, free_cache_http_data)
+G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(LoadUriData, clear_load_uri_data)
-static GBytes *
-serialize_cache_http_data (CacheHttpData * data)
+static gboolean
+check_http_status (guint status_code,
+ GError **error)
{
- g_autoptr(GVariant) cache_variant = NULL;
+ GQuark domain;
+ int code;
- cache_variant = g_variant_ref_sink (g_variant_new (CACHE_HTTP_TYPE,
- data->uri,
- data->etag ? data->etag : "",
- data->last_modified,
- data->expires));
- if (G_BYTE_ORDER != G_BIG_ENDIAN)
+ if (status_code >= 200 && status_code < 300)
+ return TRUE;
+
+ switch (status_code)
{
- g_autoptr(GVariant) tmp_variant = cache_variant;
- cache_variant = g_variant_byteswap (tmp_variant);
+ case 304: /* Not Modified */
+ domain = FLATPAK_HTTP_ERROR;
+ code = FLATPAK_HTTP_ERROR_NOT_CHANGED;
+ break;
+
+ case 401: /* Unauthorized */
+ domain = FLATPAK_HTTP_ERROR;
+ code = FLATPAK_HTTP_ERROR_UNAUTHORIZED;
+ break;
+
+ case 403: /* Forbidden */
+ case 404: /* Not found */
+ case 410: /* Gone */
+ domain = G_IO_ERROR;
+ code = G_IO_ERROR_NOT_FOUND;
+ break;
+
+ case 408: /* Request Timeout */
+ domain = G_IO_ERROR;
+ code = G_IO_ERROR_TIMED_OUT;
+ break;
+
+ case 500: /* Internal Server Error */
+ /* The server did return something, but it was useless to us, so that’s basically equivalent to not returning */
+ domain = G_IO_ERROR;
+ code = G_IO_ERROR_HOST_UNREACHABLE;
+ break;
+
+ default:
+ domain = G_IO_ERROR;
+ code = G_IO_ERROR_FAILED;
}
- return g_variant_get_data_as_bytes (cache_variant);
+ g_set_error (error, domain, code,
+ "Server returned status %u",
+ status_code);
+ return FALSE;
}
+#if defined(HAVE_CURL)
+
+/************************************************************************
+ * Curl implementation *
+ ************************************************************************/
+
+typedef struct curl_slist auto_curl_slist;
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (auto_curl_slist, curl_slist_free_all)
+
+struct FlatpakHttpSession {
+ CURL *curl;
+ GMutex lock;
+};
+
static void
-deserialize_cache_http_data (CacheHttpData *data,
- GBytes *bytes)
+check_header(char **value_out,
+ const char *header,
+ char *buffer,
+ size_t realsize)
{
- g_autoptr(GVariant) cache_variant = NULL;
+ size_t hlen = strlen (header);
- cache_variant = g_variant_ref_sink (g_variant_new_from_bytes (G_VARIANT_TYPE (CACHE_HTTP_TYPE),
- bytes,
- FALSE));
- if (G_BYTE_ORDER != G_BIG_ENDIAN)
+ if (realsize < hlen + 1)
+ return;
+
+ if (!g_ascii_strncasecmp(buffer, header, hlen) == 0 ||
+ buffer[hlen] != ':')
+ return;
+
+ buffer += hlen + 1;
+ realsize -= hlen + 1;
+
+ while (realsize > 0 && g_ascii_isspace (*buffer))
{
- g_autoptr(GVariant) tmp_variant = cache_variant;
- cache_variant = g_variant_byteswap (tmp_variant);
+ buffer++;
+ realsize--;
}
- g_variant_get (cache_variant,
- CACHE_HTTP_TYPE,
- &data->uri,
- &data->etag,
- &data->last_modified,
- &data->expires);
+ while (realsize > 0 && g_ascii_isspace (buffer[realsize-1]))
+ realsize--;
+
+ g_free (*value_out); /* Use the last header */
+ *value_out = g_strndup (buffer, realsize);
}
-static CacheHttpData *
-load_cache_http_data (int dfd,
- char *name,
- gboolean *no_xattr,
- GCancellable *cancellable,
- GError **error)
+static size_t
+_header_cb (char *buffer,
+ size_t size,
+ size_t nitems,
+ void *userdata)
{
- g_autoptr(CacheHttpData) data = NULL;
- g_autoptr(GBytes) cache_bytes = glnx_lgetxattrat (dfd, name,
- CACHE_HTTP_XATTR,
- error);
- if (cache_bytes == NULL)
- {
- if (errno == ENOTSUP)
- {
- g_autofree char *cache_file = NULL;
- glnx_autofd int fd = -1;
+ size_t realsize = size * nitems;
+ LoadUriData *data = (LoadUriData *)userdata;
- g_clear_error (error);
- *no_xattr = TRUE;
+ check_header(&data->hdr_content_type, "content-type", buffer, realsize);
+ check_header(&data->hdr_www_authenticate, "WWW-Authenticate", buffer, realsize);
- cache_file = g_strconcat (name, CACHE_HTTP_SUFFIX, NULL);
+ check_header(&data->hdr_etag, "ETag", buffer, realsize);
+ check_header(&data->hdr_last_modified, "Last-Modified", buffer, realsize);
+ check_header(&data->hdr_cache_control, "Cache-Control", buffer, realsize);
+ check_header(&data->hdr_expires, "Expires", buffer, realsize);
+ check_header(&data->hdr_content_encoding, "Content-Encoding", buffer, realsize);
- if (!glnx_openat_rdonly (dfd, cache_file, FALSE,
- &fd, error))
- return FALSE;
+ return realsize;
+}
- cache_bytes = glnx_fd_readall_bytes (fd, cancellable, error);
- if (!cache_bytes)
- return NULL;
+static size_t
+_write_cb (void *content_data,
+ size_t size,
+ size_t nmemb,
+ void *userp)
+{
+ size_t realsize = size * nmemb;
+ LoadUriData *data = (LoadUriData *)userp;
+ gsize n_written = 0;
+
+ /* If first write to tmpfile, initiate if needed */
+ if (data->content == NULL && data->out == NULL &&
+ data->out_tmpfile != NULL)
+ {
+ g_autoptr(GOutputStream) out = NULL;
+ g_autoptr(GError) tmp_error = NULL;
+
+ if (!glnx_open_tmpfile_linkable_at (data->out_tmpfile_parent_dfd, ".",
+ O_WRONLY, data->out_tmpfile,
+ &tmp_error))
+ {
+ g_warning ("Failed to open http tmpfile: %s\n", tmp_error->message);
+ return 0; /* This short read will make curl report an error */
}
- else if (errno == ENOENT || errno == ENODATA)
+
+ out = g_unix_output_stream_new (data->out_tmpfile->fd, FALSE);
+ if (data->store_compressed &&
+ g_strcmp0 (data->hdr_content_encoding, "gzip") != 0)
{
- g_clear_error (error);
- return g_new0 (CacheHttpData, 1);
+ g_autoptr(GZlibCompressor) compressor = g_zlib_compressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP, -1);
+ data->out = g_converter_output_stream_new (out, G_CONVERTER (compressor));
}
else
{
- return NULL;
+ data->out = g_steal_pointer (&out);
}
}
+ if (data->content)
+ {
+ g_string_append_len (data->content, content_data, realsize);
+ n_written = realsize;
+ }
+ else if (data->out)
+ {
+ /* We ignore the error here, but reporting a short read will make curl report the error */
+ g_output_stream_write_all (data->out, content_data, realsize,
+ &n_written, NULL, NULL);
+ }
- data = g_new0 (CacheHttpData, 1);
- deserialize_cache_http_data (data, cache_bytes);
- return g_steal_pointer (&data);
+ data->downloaded_bytes += realsize;
+
+ if (g_get_monotonic_time () - data->last_progress_time > 1 * G_USEC_PER_SEC)
+ {
+ if (data->progress)
+ data->progress (data->downloaded_bytes, data->user_data);
+ data->last_progress_time = g_get_monotonic_time ();
+ }
+
+ return realsize;
}
-static void
-set_cache_http_data_from_headers (CacheHttpData *data,
- SoupMessage *msg)
+FlatpakHttpSession *
+flatpak_create_http_session (const char *user_agent)
{
- const char *etag = soup_message_headers_get_one (msg->response_headers, "ETag");
- const char *last_modified = soup_message_headers_get_one (msg->response_headers, "Last-Modified");
- const char *cache_control = soup_message_headers_get_list (msg->response_headers, "Cache-Control");
- const char *expires = soup_message_headers_get_list (msg->response_headers, "Expires");
- gboolean expires_computed = FALSE;
+ FlatpakHttpSession *session = g_new0 (FlatpakHttpSession, 1);
+ CURLcode rc;
+ CURL *curl;
- /* The original HTTP 1/1 specification only required sending the ETag header in a 304
- * response, and implied that a cache might need to save the old Cache-Control
- * values. The updated RFC 7232 from 2014 requires sending Cache-Control, ETags, and
- * Expire if they would have been sent in the original 200 response, and recommends
- * sending Last-Modified for requests without an etag. Since sending these headers was
- * apparently normal previously, for simplicity we assume the RFC 7232 behavior and start
- * from scratch for a 304 response.
+ session->curl = curl = curl_easy_init();
+ g_assert (session->curl != NULL);
+
+ g_mutex_init (&session->lock);
+
+ curl_easy_setopt (curl, CURLOPT_USERAGENT, user_agent);
+ rc = curl_easy_setopt (curl, CURLOPT_PROTOCOLS, (long)(CURLPROTO_HTTP | CURLPROTO_HTTPS));
+ g_assert_cmpint (rc, ==, CURLM_OK);
+
+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
+
+ /* Note: curl automatically respects the http_proxy env var */
+
+ if (g_getenv ("OSTREE_DEBUG_HTTP"))
+ curl_easy_setopt (curl, CURLOPT_VERBOSE, 1L);
+
+ /* Picked the current version in F25 as of 20170127, since
+ * there are numerous HTTP/2 fixes since the original version in
+ * libcurl 7.43.0.
*/
- clear_cache_http_data (data, FALSE);
+#if CURL_AT_LEAST_VERSION(7, 51, 0)
+ if ((curl_version_info (CURLVERSION_NOW))->features & CURL_VERSION_HTTP2) {
+ rc = curl_easy_setopt (curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
+ g_assert_cmpint (rc, ==, CURLM_OK);
+ }
+#endif
+ /* https://github.com/curl/curl/blob/curl-7_53_0/docs/examples/http2-download.c */
+#if (CURLPIPE_MULTIPLEX > 0)
+ /* wait for pipe connection to confirm */
+ rc = curl_easy_setopt (curl, CURLOPT_PIPEWAIT, 1L);
+ g_assert_cmpint (rc, ==, CURLM_OK);
+#endif
- if (etag && *etag)
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, _write_cb);
+ curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, _header_cb);
+
+ curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, (long)FLATPAK_HTTP_TIMEOUT_SECS);
+
+ return session;
+}
+
+void
+flatpak_http_session_free (FlatpakHttpSession* session)
+{
+ g_mutex_lock (&session->lock);
+ curl_easy_cleanup (session->curl);
+ g_mutex_unlock (&session->lock);
+ g_mutex_clear (&session->lock);
+ g_free (session);
+}
+
+static void
+set_error_from_curl (GError **error,
+ const char *uri,
+ CURLcode res)
+{
+ GQuark domain = G_IO_ERROR;
+ int code;
+
+ switch (res)
{
- data->etag = g_strdup (etag);
+ case CURLE_COULDNT_CONNECT:
+ case CURLE_COULDNT_RESOLVE_HOST:
+ case CURLE_COULDNT_RESOLVE_PROXY:
+ code = G_IO_ERROR_HOST_NOT_FOUND;
+ break;
+ case CURLE_OPERATION_TIMEDOUT:
+ code = G_IO_ERROR_TIMED_OUT;
+ break;
+ default:
+ code = G_IO_ERROR_FAILED;
}
- else if (last_modified && *last_modified)
+
+ g_set_error (error, domain, code,
+ "While fetching %s: [%u] %s", uri, res,
+ curl_easy_strerror (res));
+}
+
+static gboolean
+flatpak_download_http_uri_once (FlatpakHttpSession *session,
+ LoadUriData *data,
+ const char *uri,
+ GError **error)
+{
+ CURLcode res;
+ g_autofree char *auth_header = NULL;
+ g_autofree char *cache_header = NULL;
+ g_autoptr(auto_curl_slist) header_list = NULL;
+ g_autoptr(GMutexLocker) curl_lock = g_mutex_locker_new (&session->lock);
+ long response;
+ CURL *curl = session->curl;
+
+ g_info ("Loading %s using curl", uri);
+
+ curl_easy_setopt (curl, CURLOPT_URL, uri);
+ curl_easy_setopt (curl, CURLOPT_WRITEDATA, (void *)data);
+ curl_easy_setopt (curl, CURLOPT_HEADERDATA, (void *)data);
+
+ if (data->flags & FLATPAK_HTTP_FLAGS_HEAD)
+ curl_easy_setopt (curl, CURLOPT_NOBODY, 1L);
+ else
+ curl_easy_setopt (curl, CURLOPT_HTTPGET, 1L);
+
+ if (data->flags & FLATPAK_HTTP_FLAGS_ACCEPT_OCI)
+ header_list = curl_slist_append (header_list,
+ "Accept: " FLATPAK_OCI_MEDIA_TYPE_IMAGE_MANIFEST ", " FLATPAK_DOCKER_MEDIA_TYPE_IMAGE_MANIFEST2 ", " FLATPAK_OCI_MEDIA_TYPE_IMAGE_INDEX);
+
+ if (data->auth)
+ auth_header = g_strdup_printf ("Authorization: Basic %s", data->auth);
+ else if (data->token)
+ auth_header = g_strdup_printf ("Authorization: Bearer %s", data->token);
+ if (auth_header)
+ header_list = curl_slist_append (header_list, auth_header);
+
+ if (data->cache_data)
{
- SoupDate *date = soup_date_new_from_string (last_modified);
- if (date)
+ CacheHttpData *cache_data = data->cache_data;
+
+ if (cache_data->etag && cache_data->etag[0])
+ cache_header = g_strdup_printf ("If-None-Match: %s", cache_data->etag);
+ else if (cache_data->last_modified != 0)
{
- data->last_modified = soup_date_to_time_t (date);
- soup_date_free (date);
+ g_autoptr(GDateTime) date = g_date_time_new_from_unix_utc (cache_data->last_modified);
+ g_autofree char *date_str = flatpak_format_http_date (date);
+ cache_header = g_strdup_printf ("If-Modified-Since: %s", date_str);
}
+ if (cache_header)
+ header_list = curl_slist_append (header_list, cache_header);
}
- if (cache_control && *cache_control)
+ curl_easy_setopt (curl, CURLOPT_HTTPHEADER, header_list);
+
+ if (data->flags & FLATPAK_HTTP_FLAGS_STORE_COMPRESSED)
{
- g_autoptr(GHashTable) params = soup_header_parse_param_list (cache_control);
- GHashTableIter iter;
- gpointer key, value;
+ curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "gzip");
+ curl_easy_setopt(curl, CURLOPT_HTTP_CONTENT_DECODING, 0L);
+ data->store_compressed = TRUE;
+ }
+ else
+ {
+ /* enable all supported built-in compressions */
+ curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");
+ curl_easy_setopt(curl, CURLOPT_HTTP_CONTENT_DECODING, 1L);
+ data->store_compressed = FALSE;
+ }
- g_hash_table_iter_init (&iter, params);
- while (g_hash_table_iter_next (&iter, &key, &value))
- {
- if (g_strcmp0 (key, "max-age") == 0)
- {
- char *end;
+ res = curl_easy_perform (session->curl);
- char *max_age = value;
- int max_age_sec = g_ascii_strtoll (max_age, &end, 10);
- if (*max_age != '\0' && *end == '\0')
- {
- GTimeVal now;
- g_get_current_time (&now);
- data->expires = now.tv_sec + max_age_sec;
- expires_computed = TRUE;
- }
- }
- else if (g_strcmp0 (key, "no-cache") == 0)
- {
- data->expires = 0;
- expires_computed = TRUE;
- }
- }
- }
+ curl_easy_setopt (session->curl, CURLOPT_HTTPHEADER, NULL); /* Don't point to freed list */
- if (!expires_computed && expires && *expires)
+ if (res != CURLE_OK)
{
- SoupDate *date = soup_date_new_from_string (expires);
- if (date)
- {
- data->expires = soup_date_to_time_t (date);
- soup_date_free (date);
- expires_computed = TRUE;
- }
+ set_error_from_curl (error, uri, res);
+
+ /* Make sure we clear the tmpfile stream we possible created during the request */
+ if (data->out_tmpfile && data->out)
+ g_clear_pointer (&data->out, g_object_unref);
+
+ return FALSE;
}
- if (!expires_computed)
+ if (data->out_tmpfile && data->out)
{
- /* If nothing implies an expires time, use 30 minutes. Browsers use
- * 0.1 * (Date - Last-Modified), but it's clearly appropriate here, and
- * better if server's send a value.
- */
- GTimeVal now;
- g_get_current_time (&now);
- data->expires = now.tv_sec + 1800;
+ /* Flush the writes */
+ if (!g_output_stream_close (data->out, data->cancellable, error))
+ return FALSE;
+
+ g_clear_pointer (&data->out, g_object_unref);
}
-}
-static gboolean
-save_cache_http_data_xattr (int fd,
- GBytes *bytes,
- GError **error)
-{
- if (TEMP_FAILURE_RETRY (fsetxattr (fd, (char *) CACHE_HTTP_XATTR,
- g_bytes_get_data (bytes, NULL),
- g_bytes_get_size (bytes),
- 0)) < 0)
- return glnx_throw_errno_prefix (error, "fsetxattr");
+ if (data->progress)
+ data->progress (data->downloaded_bytes, data->user_data);
- return TRUE;
-}
+ curl_easy_getinfo (session->curl, CURLINFO_RESPONSE_CODE, &response);
-static gboolean
-save_cache_http_data_fallback (int fd,
- GBytes *bytes,
- GError **error)
-{
- if (glnx_loop_write (fd,
- g_bytes_get_data (bytes, NULL),
- g_bytes_get_size (bytes)) < 0)
- return glnx_throw_errno_prefix (error, "write");
+ data->status = response;
+
+ if ((data->flags & FLATPAK_HTTP_FLAGS_NOCHECK_STATUS) == 0 &&
+ !check_http_status (data->status, error))
+ return FALSE;
+
+ g_info ("Received %" G_GUINT64_FORMAT " bytes", data->downloaded_bytes);
+
+ /* This is not really needed, but the auto-pointer confuses some compilers in the CI */
+ g_clear_pointer (&curl_lock, g_mutex_locker_free);
return TRUE;
}
+#endif /* HAVE_CURL */
+
+#if defined(HAVE_SOUP)
+
+/************************************************************************
+ * Soup implementation *
+ ***********************************************************************/
+
static gboolean
-save_cache_http_data_to_file (int dfd,
- char *name,
- GBytes *bytes,
- gboolean no_xattr,
- GCancellable *cancellable,
- GError **error)
+check_soup_transfer_error (SoupMessage *msg, GError **error)
{
- glnx_autofd int fd = -1;
- g_autofree char *fallback_name = NULL;
+ GQuark domain = G_IO_ERROR;
+ int code;
- if (!no_xattr)
+ if (!SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code))
+ return TRUE;
+
+ switch (msg->status_code)
{
- if (!glnx_openat_rdonly (dfd, name, FALSE,
- &fd, error))
- return FALSE;
+ case SOUP_STATUS_CANCELLED:
+ code = G_IO_ERROR_CANCELLED;
+ break;
- if (save_cache_http_data_xattr (fd, bytes, error))
- return TRUE;
+ case SOUP_STATUS_CANT_RESOLVE:
+ case SOUP_STATUS_CANT_CONNECT:
+ code = G_IO_ERROR_HOST_NOT_FOUND;
+ break;
- if (errno == ENOTSUP)
- g_clear_error (error);
- else
- return FALSE;
- }
+ case SOUP_STATUS_IO_ERROR:
+ code = G_IO_ERROR_CONNECTION_CLOSED;
+ break;
- fallback_name = g_strconcat (name, CACHE_HTTP_SUFFIX, NULL);
- if (!glnx_file_replace_contents_at (dfd, fallback_name,
- g_bytes_get_data (bytes, NULL),
- g_bytes_get_size (bytes),
- 0,
- cancellable,
- error))
- return FALSE;
+ default:
+ code = G_IO_ERROR_FAILED;
+ }
- return TRUE;
+ g_set_error (error, domain, code,
+ "Error connecting to server: %s",
+ soup_status_get_phrase (msg->status_code));
+ return FALSE;
}
+/* The soup input stream was closed */
static void
stream_closed (GObject *source, GAsyncResult *res, gpointer user_data)
{
@@ -355,6 +624,7 @@ stream_closed (GObject *source, GAsyncResult *res, gpointer user_data)
g_main_context_wakeup (data->context);
}
+/* Got some data from the soup input stream */
static void
load_uri_read_cb (GObject *source, GAsyncResult *res, gpointer user_data)
{
@@ -392,6 +662,7 @@ load_uri_read_cb (GObject *source, GAsyncResult *res, gpointer user_data)
}
else
{
+ g_assert (data->content != NULL);
data->downloaded_bytes += nread;
g_string_append_len (data->content, data->buffer, nread);
}
@@ -408,6 +679,7 @@ load_uri_read_cb (GObject *source, GAsyncResult *res, gpointer user_data)
load_uri_read_cb, data);
}
+/* The http header part of the request is ready */
static void
load_uri_callback (GObject *source_object,
GAsyncResult *res,
@@ -420,81 +692,37 @@ load_uri_callback (GObject *source_object,
in = soup_request_send_finish (SOUP_REQUEST (request), res, &data->error);
if (in == NULL)
{
- /* data->error has been set */
g_main_context_wakeup (data->context);
return;
}
g_autoptr(SoupMessage) msg = soup_request_http_get_message ((SoupRequestHTTP *) request);
- if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
+
+ if (!check_soup_transfer_error (msg, &data->error))
{
- int code;
- GQuark domain = G_IO_ERROR;
+ g_main_context_wakeup (data->context);
+ return;
+ }
- switch (msg->status_code)
- {
- case 304:
- if (data->cache_data)
- set_cache_http_data_from_headers (data->cache_data, msg);
-
- domain = FLATPAK_HTTP_ERROR;
- code = FLATPAK_HTTP_ERROR_NOT_CHANGED;
- break;
-
- case 401:
- domain = FLATPAK_HTTP_ERROR;
- code = FLATPAK_HTTP_ERROR_UNAUTHORIZED;
- break;
-
- case 403:
- case 404:
- case 410:
- code = G_IO_ERROR_NOT_FOUND;
- break;
-
- case 408:
- code = G_IO_ERROR_TIMED_OUT;
- break;
-
- case SOUP_STATUS_CANCELLED:
- code = G_IO_ERROR_CANCELLED;
- break;
-
- case SOUP_STATUS_CANT_RESOLVE:
- case SOUP_STATUS_CANT_CONNECT:
- code = G_IO_ERROR_HOST_NOT_FOUND;
- break;
-
- case SOUP_STATUS_INTERNAL_SERVER_ERROR:
- /* The server did return something, but it was useless to us, so that’s basically equivalent to not returning */
- code = G_IO_ERROR_HOST_UNREACHABLE;
- break;
-
- case SOUP_STATUS_IO_ERROR:
-#if !GLIB_CHECK_VERSION(2, 44, 0)
- code = G_IO_ERROR_BROKEN_PIPE;
-#else
- code = G_IO_ERROR_CONNECTION_CLOSED;
-#endif
- break;
+ /* We correctly made a connection, although it may be a http failure like 404.
+ The status and headers are valid on return, even of a http failure though. */
- default:
- code = G_IO_ERROR_FAILED;
- }
+ data->status = msg->status_code;
+ data->hdr_content_type = g_strdup (soup_message_headers_get_content_type (msg->response_headers, NULL));
+ data->hdr_www_authenticate = g_strdup (soup_message_headers_get_one (msg->response_headers, "WWW-Authenticate"));
+ data->hdr_etag = g_strdup (soup_message_headers_get_one (msg->response_headers, "ETag"));
+ data->hdr_last_modified = g_strdup (soup_message_headers_get_one (msg->response_headers, "Last-Modified"));
+ data->hdr_cache_control = g_strdup (soup_message_headers_get_list (msg->response_headers, "Cache-Control"));
+ data->hdr_expires = g_strdup (soup_message_headers_get_list (msg->response_headers, "Expires"));
- data->error = g_error_new (domain, code,
- "Server returned status %u: %s",
- msg->status_code,
- soup_status_get_phrase (msg->status_code));
+ if ((data->flags & FLATPAK_HTTP_FLAGS_NOCHECK_STATUS) == 0 &&
+ !check_http_status (data->status, &data->error))
+ {
g_main_context_wakeup (data->context);
return;
}
- if (data->cache_data)
- set_cache_http_data_from_headers (data->cache_data, msg);
-
- if (data->content_type_out)
- *data->content_type_out = g_strdup (soup_message_headers_get_content_type (msg->response_headers, NULL));
+ /* All is good, write the body to the destination */
if (data->out_tmpfile)
{
@@ -528,7 +756,7 @@ load_uri_callback (GObject *source_object,
load_uri_read_cb, data);
}
-SoupSession *
+static SoupSession *
flatpak_create_soup_session (const char *user_agent)
{
SoupSession *soup_session;
@@ -537,8 +765,8 @@ flatpak_create_soup_session (const char *user_agent)
soup_session = soup_session_new_with_options (SOUP_SESSION_USER_AGENT, user_agent,
SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE,
SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
- SOUP_SESSION_TIMEOUT, 60,
- SOUP_SESSION_IDLE_TIMEOUT, 60,
+ SOUP_SESSION_TIMEOUT, FLATPAK_HTTP_TIMEOUT_SECS,
+ SOUP_SESSION_IDLE_TIMEOUT, FLATPAK_HTTP_TIMEOUT_SECS,
NULL);
http_proxy = g_getenv ("http_proxy");
if (http_proxy)
@@ -556,6 +784,102 @@ flatpak_create_soup_session (const char *user_agent)
return soup_session;
}
+FlatpakHttpSession *
+flatpak_create_http_session (const char *user_agent)
+{
+ return (FlatpakHttpSession *)flatpak_create_soup_session (user_agent);
+}
+
+void
+flatpak_http_session_free (FlatpakHttpSession* http_session)
+{
+ SoupSession *soup_session = (SoupSession *)http_session;
+
+ g_object_unref (soup_session);
+}
+
+static gboolean
+flatpak_download_http_uri_once (FlatpakHttpSession *http_session,
+ LoadUriData *data,
+ const char *uri,
+ GError **error)
+{
+ SoupSession *soup_session = (SoupSession *)http_session;
+ g_autoptr(SoupRequestHTTP) request = NULL;
+ SoupMessage *m;
+
+ g_info ("Loading %s using libsoup", uri);
+
+ request = soup_session_request_http (soup_session,
+ (data->flags & FLATPAK_HTTP_FLAGS_HEAD) != 0 ? "HEAD" : "GET",
+ uri, error);
+ if (request == NULL)
+ return FALSE;
+
+ m = soup_request_http_get_message (request);
+
+ if (data->flags & FLATPAK_HTTP_FLAGS_ACCEPT_OCI)
+ soup_message_headers_replace (m->request_headers, "Accept",
+ FLATPAK_OCI_MEDIA_TYPE_IMAGE_MANIFEST ", " FLATPAK_DOCKER_MEDIA_TYPE_IMAGE_MANIFEST2 ", " FLATPAK_OCI_MEDIA_TYPE_IMAGE_INDEX);
+
+ if (data->auth)
+ {
+ g_autofree char *basic_auth = g_strdup_printf ("Basic %s", data->auth);
+ soup_message_headers_replace (m->request_headers, "Authorization", basic_auth);
+ }
+
+ if (data->token)
+ {
+ g_autofree char *bearer_token = g_strdup_printf ("Bearer %s", data->token);
+ soup_message_headers_replace (m->request_headers, "Authorization", bearer_token);
+ }
+
+ if (data->cache_data)
+ {
+ CacheHttpData *cache_data = data->cache_data;
+
+ if (cache_data->etag && cache_data->etag[0])
+ soup_message_headers_replace (m->request_headers, "If-None-Match", cache_data->etag);
+ else if (cache_data->last_modified != 0)
+ {
+ g_autoptr(GDateTime) date = g_date_time_new_from_unix_utc (cache_data->last_modified);
+ g_autofree char *date_str = flatpak_format_http_date (date);
+ soup_message_headers_replace (m->request_headers, "If-Modified-Since", date_str);
+ }
+ }
+
+ if (data->flags & FLATPAK_HTTP_FLAGS_STORE_COMPRESSED)
+ {
+ soup_session_remove_feature_by_type (soup_session, SOUP_TYPE_CONTENT_DECODER);
+ soup_message_headers_replace (m->request_headers, "Accept-Encoding", "gzip");
+ data->store_compressed = TRUE;
+ }
+ else if (!soup_session_has_feature (soup_session, SOUP_TYPE_CONTENT_DECODER))
+ {
+ soup_session_add_feature_by_type (soup_session, SOUP_TYPE_CONTENT_DECODER);
+ data->store_compressed = FALSE;
+ }
+
+ soup_request_send_async (SOUP_REQUEST (request),
+ data->cancellable,
+ load_uri_callback, data);
+
+ while (data->error == NULL && !data->done)
+ g_main_context_iteration (data->context, TRUE);
+
+ if (data->error)
+ {
+ g_propagate_error (error, g_steal_pointer (&data->error));
+ return FALSE;
+ }
+
+ g_info ("Received %" G_GUINT64_FORMAT " bytes", data->downloaded_bytes);
+
+ return TRUE;
+}
+
+#endif /* HAVE_SOUP */
+
/* Check whether a particular operation should be retried. This is entirely
* based on how it failed (if at all) last time, and whether the operation has
* some retries left. The retry count is set when the operation is first
@@ -576,91 +900,103 @@ flatpak_http_should_retry_request (const GError *error,
g_error_matches (error, G_IO_ERROR, G_IO_ERROR_HOST_NOT_FOUND) ||
g_error_matches (error, G_IO_ERROR, G_IO_ERROR_HOST_UNREACHABLE) ||
g_error_matches (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT) ||
-#if !GLIB_CHECK_VERSION(2, 44, 0)
- g_error_matches (error, G_IO_ERROR, G_IO_ERROR_BROKEN_PIPE) ||
-#else
g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED) ||
-#endif
g_error_matches (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND) ||
g_error_matches (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_TEMPORARY_FAILURE))
{
- g_debug ("Should retry request (remaining: %u retries), due to transient error: %s",
- n_retries_remaining, error->message);
+ g_info ("Should retry request (remaining: %u retries), due to transient error: %s",
+ n_retries_remaining, error->message);
return TRUE;
}
return FALSE;
}
-static GBytes *
-flatpak_load_http_uri_once (SoupSession *soup_session,
- const char *uri,
- FlatpakHTTPFlags flags,
- const char *token,
- FlatpakLoadUriProgress progress,
- gpointer user_data,
- char **out_content_type,
- GCancellable *cancellable,
- GError **error)
+GBytes *
+flatpak_load_uri_full (FlatpakHttpSession *http_session,
+ const char *uri,
+ FlatpakHTTPFlags flags,
+ const char *auth,
+ const char *token,
+ FlatpakLoadUriProgress progress,
+ gpointer user_data,
+ int *out_status,
+ char **out_content_type,
+ char **out_www_authenticate,
+ GCancellable *cancellable,
+ GError **error)
{
- GBytes *bytes = NULL;
- g_autoptr(GMainContext) context = NULL;
- g_autoptr(SoupRequestHTTP) request = NULL;
- g_autoptr(GString) content = g_string_new ("");
- LoadUriData data = { NULL };
- SoupMessage *m;
+ g_auto(LoadUriData) data = { NULL };
+ g_autoptr(GError) local_error = NULL;
+ guint n_retries_remaining = DEFAULT_N_NETWORK_RETRIES;
+ g_autoptr(GMainContextPopDefault) main_context = NULL;
+ gboolean success = FALSE;
- g_debug ("Loading %s using libsoup", uri);
+ /* Ensure we handle file: uris the same independent of backend */
+ if (g_ascii_strncasecmp (uri, "file:", 5) == 0)
+ {
+ g_autoptr(GFile) file = g_file_new_for_uri (uri);
+ gchar *contents;
+ gsize len;
- context = g_main_context_ref_thread_default ();
+ if (!g_file_load_contents (file, cancellable, &contents, &len, NULL, error))
+ return NULL;
+
+ return g_bytes_new_take (g_steal_pointer (&contents), len);
+ }
+
+ main_context = flatpak_main_context_new_default ();
- data.context = context;
- data.content = content;
+ data.context = main_context;
data.progress = progress;
- data.cancellable = cancellable;
data.user_data = user_data;
data.last_progress_time = g_get_monotonic_time ();
- data.content_type_out = out_content_type;
+ data.cancellable = cancellable;
+ data.flags = flags;
+ data.auth = auth;
+ data.token = token;
- request = soup_session_request_http (soup_session, "GET",
- uri, error);
- if (request == NULL)
- return NULL;
+ data.content = g_string_new ("");
- m = soup_request_http_get_message (request);
+ do
+ {
+ if (n_retries_remaining < DEFAULT_N_NETWORK_RETRIES)
+ {
+ g_clear_error (&local_error);
+ reset_load_uri_data (&data);
+ }
- if (flags & FLATPAK_HTTP_FLAGS_ACCEPT_OCI)
- soup_message_headers_replace (m->request_headers, "Accept",
- FLATPAK_OCI_MEDIA_TYPE_IMAGE_MANIFEST ", " FLATPAK_DOCKER_MEDIA_TYPE_IMAGE_MANIFEST2 ", " FLATPAK_OCI_MEDIA_TYPE_IMAGE_INDEX);
+ success = flatpak_download_http_uri_once (http_session, &data, uri, &local_error);
+ if (success)
+ break;
- if (token)
- {
- g_autofree char *bearer_token = g_strdup_printf ("Bearer %s", token);
- soup_message_headers_replace (m->request_headers, "Authorization", bearer_token);
+ g_assert (local_error != NULL);
}
+ while (flatpak_http_should_retry_request (local_error, n_retries_remaining--));
- soup_request_send_async (SOUP_REQUEST (request),
- cancellable,
- load_uri_callback, &data);
+ if (success)
+ {
+ if (out_content_type)
+ *out_content_type = g_steal_pointer (&data.hdr_content_type);
- while (data.error == NULL && !data.done)
- g_main_context_iteration (data.context, TRUE);
+ if (out_www_authenticate)
+ *out_www_authenticate = g_steal_pointer (&data.hdr_www_authenticate);
- if (data.error)
- {
- g_propagate_error (error, data.error);
- return NULL;
- }
+ if (out_status)
+ *out_status = data.status;
- bytes = g_string_free_to_bytes (g_steal_pointer (&content));
- g_debug ("Received %" G_GUINT64_FORMAT " bytes", data.downloaded_bytes);
+ return g_string_free_to_bytes (g_steal_pointer (&data.content));
+ }
- return bytes;
+ g_assert (local_error != NULL);
+ g_propagate_error (error, g_steal_pointer (&local_error));
+ return NULL;
}
+
GBytes *
-flatpak_load_uri (SoupSession *soup_session,
+flatpak_load_uri (FlatpakHttpSession *http_session,
const char *uri,
FlatpakHTTPFlags flags,
const char *token,
@@ -670,165 +1006,251 @@ flatpak_load_uri (SoupSession *soup_session,
GCancellable *cancellable,
GError **error)
{
+ return flatpak_load_uri_full (http_session, uri, flags, NULL, token,
+ progress, user_data, NULL, out_content_type, NULL,
+ cancellable, error);
+}
+
+gboolean
+flatpak_download_http_uri (FlatpakHttpSession *http_session,
+ const char *uri,
+ FlatpakHTTPFlags flags,
+ GOutputStream *out,
+ const char *token,
+ FlatpakLoadUriProgress progress,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_auto(LoadUriData) data = { NULL };
g_autoptr(GError) local_error = NULL;
guint n_retries_remaining = DEFAULT_N_NETWORK_RETRIES;
g_autoptr(GMainContextPopDefault) main_context = NULL;
+ gboolean success = FALSE;
main_context = flatpak_main_context_new_default ();
- /* Ensure we handle file: uris always */
- if (g_ascii_strncasecmp (uri, "file:", 5) == 0)
- {
- g_autoptr(GFile) file = g_file_new_for_uri (uri);
- gchar *contents;
- gsize len;
-
- if (!g_file_load_contents (file, cancellable, &contents, &len, NULL, error))
- return NULL;
+ data.context = main_context;
+ data.progress = progress;
+ data.user_data = user_data;
+ data.last_progress_time = g_get_monotonic_time ();
+ data.cancellable = cancellable;
+ data.flags = flags;
+ data.token = token;
- return g_bytes_new_take (g_steal_pointer (&contents), len);
- }
+ data.out = out;
do
{
- g_autoptr(GBytes) bytes = NULL;
-
if (n_retries_remaining < DEFAULT_N_NETWORK_RETRIES)
{
g_clear_error (&local_error);
-
- if (progress)
- progress (0, user_data); /* Reset the progress */
+ reset_load_uri_data (&data);
}
- bytes = flatpak_load_http_uri_once (soup_session, uri, flags,
- token, progress, user_data, out_content_type,
- cancellable, &local_error);
+ success = flatpak_download_http_uri_once (http_session, &data, uri, &local_error);
+
+ if (success)
+ break;
+
+ g_assert (local_error != NULL);
- if (local_error == NULL)
- return g_steal_pointer (&bytes);
+ /* If the output stream has already been written to we can't retry.
+ * TODO: use a range request to resume the download */
+ if (data.downloaded_bytes > 0)
+ break;
}
while (flatpak_http_should_retry_request (local_error, n_retries_remaining--));
+ if (success)
+ return TRUE;
+
g_assert (local_error != NULL);
g_propagate_error (error, g_steal_pointer (&local_error));
- return NULL;
+ return FALSE;
}
-static gboolean
-flatpak_download_http_uri_once (SoupSession *soup_session,
- const char *uri,
- FlatpakHTTPFlags flags,
- GOutputStream *out,
- const char *token,
- FlatpakLoadUriProgress progress,
- gpointer user_data,
- guint64 *out_bytes_written,
- GCancellable *cancellable,
- GError **error)
-{
- g_autoptr(SoupRequestHTTP) request = NULL;
- g_autoptr(GMainContext) context = NULL;
- LoadUriData data = { NULL };
- SoupMessage *m;
+/************************************************************************
+ * Cached http support *
+ ***********************************************************************/
- g_debug ("Loading %s using libsoup", uri);
+#define CACHE_HTTP_XATTR "user.flatpak.http"
+#define CACHE_HTTP_SUFFIX ".flatpak.http"
+#define CACHE_HTTP_TYPE "(sstt)"
- context = g_main_context_ref_thread_default ();
+static void
+clear_cache_http_data (CacheHttpData *data,
+ gboolean clear_uri)
+{
+ if (clear_uri)
+ g_clear_pointer (&data->uri, g_free);
+ g_clear_pointer (&data->etag, g_free);
+ data->last_modified = 0;
+ data->expires = 0;
+}
- data.context = context;
- data.out = out;
- data.progress = progress;
- data.cancellable = cancellable;
- data.user_data = user_data;
- data.last_progress_time = g_get_monotonic_time ();
+static void
+free_cache_http_data (CacheHttpData *data)
+{
+ clear_cache_http_data (data, TRUE);
+ g_free (data);
+}
- request = soup_session_request_http (soup_session, "GET",
- uri, error);
- if (request == NULL)
- return FALSE;
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (CacheHttpData, free_cache_http_data)
- m = soup_request_http_get_message (request);
- if (flags & FLATPAK_HTTP_FLAGS_ACCEPT_OCI)
- soup_message_headers_replace (m->request_headers, "Accept",
- FLATPAK_OCI_MEDIA_TYPE_IMAGE_MANIFEST ", " FLATPAK_DOCKER_MEDIA_TYPE_IMAGE_MANIFEST2);
+static GBytes *
+serialize_cache_http_data (CacheHttpData * data)
+{
+ g_autoptr(GVariant) cache_variant = NULL;
- if (token)
+ cache_variant = g_variant_ref_sink (g_variant_new (CACHE_HTTP_TYPE,
+ data->uri,
+ data->etag ? data->etag : "",
+ data->last_modified,
+ data->expires));
+ if (G_BYTE_ORDER != G_BIG_ENDIAN)
{
- g_autofree char *bearer_token = g_strdup_printf ("Bearer %s", token);
- soup_message_headers_replace (m->request_headers, "Authorization", bearer_token);
+ g_autoptr(GVariant) tmp_variant = cache_variant;
+ cache_variant = g_variant_byteswap (tmp_variant);
}
- soup_request_send_async (SOUP_REQUEST (request),
- cancellable,
- load_uri_callback, &data);
-
- while (data.error == NULL && !data.done)
- g_main_context_iteration (data.context, TRUE);
+ return g_variant_get_data_as_bytes (cache_variant);
+}
- if (out_bytes_written)
- *out_bytes_written = data.downloaded_bytes;
+static void
+deserialize_cache_http_data (CacheHttpData *data,
+ GBytes *bytes)
+{
+ g_autoptr(GVariant) cache_variant = NULL;
- if (data.error)
+ cache_variant = g_variant_ref_sink (g_variant_new_from_bytes (G_VARIANT_TYPE (CACHE_HTTP_TYPE),
+ bytes,
+ FALSE));
+ if (G_BYTE_ORDER != G_BIG_ENDIAN)
{
- g_propagate_error (error, data.error);
- return FALSE;
+ g_autoptr(GVariant) tmp_variant = cache_variant;
+ cache_variant = g_variant_byteswap (tmp_variant);
}
- g_debug ("Received %" G_GUINT64_FORMAT " bytes", data.downloaded_bytes);
-
- return TRUE;
+ g_variant_get (cache_variant,
+ CACHE_HTTP_TYPE,
+ &data->uri,
+ &data->etag,
+ &data->last_modified,
+ &data->expires);
}
-gboolean
-flatpak_download_http_uri (SoupSession *soup_session,
- const char *uri,
- FlatpakHTTPFlags flags,
- GOutputStream *out,
- const char *token,
- FlatpakLoadUriProgress progress,
- gpointer user_data,
- GCancellable *cancellable,
- GError **error)
+static CacheHttpData *
+load_cache_http_data (int dfd,
+ char *name,
+ gboolean *no_xattr,
+ GCancellable *cancellable,
+ GError **error)
{
- g_autoptr(GError) local_error = NULL;
- guint n_retries_remaining = DEFAULT_N_NETWORK_RETRIES;
- g_autoptr(GMainContextPopDefault) main_context = NULL;
+ g_autoptr(CacheHttpData) data = NULL;
+ g_autoptr(GBytes) cache_bytes = glnx_lgetxattrat (dfd, name,
+ CACHE_HTTP_XATTR,
+ error);
+ if (cache_bytes == NULL)
+ {
+ if (errno == ENOTSUP)
+ {
+ g_autofree char *cache_file = NULL;
+ glnx_autofd int fd = -1;
- main_context = flatpak_main_context_new_default ();
+ g_clear_error (error);
+ *no_xattr = TRUE;
- do
- {
- guint64 bytes_written = 0;
+ cache_file = g_strconcat (name, CACHE_HTTP_SUFFIX, NULL);
- if (n_retries_remaining < DEFAULT_N_NETWORK_RETRIES)
- {
- g_clear_error (&local_error);
+ if (!glnx_openat_rdonly (dfd, cache_file, FALSE,
+ &fd, error))
+ return FALSE;
- if (progress)
- progress (0, user_data); /* Reset the progress */
+ cache_bytes = glnx_fd_readall_bytes (fd, cancellable, error);
+ if (!cache_bytes)
+ return NULL;
}
-
- if (flatpak_download_http_uri_once (soup_session, uri, flags,
- out, token,
- progress, user_data,
- &bytes_written,
- cancellable, &local_error))
+ else if (errno == ENOENT || errno == ENODATA)
{
- g_assert (local_error == NULL);
- return TRUE;
+ g_clear_error (error);
+ return g_new0 (CacheHttpData, 1);
}
+ else
+ {
+ return NULL;
+ }
+ }
- /* If the output stream has already been written to we can't retry.
- * TODO: use a range request to resume the download */
- if (bytes_written > 0)
- break;
+
+ data = g_new0 (CacheHttpData, 1);
+ deserialize_cache_http_data (data, cache_bytes);
+ return g_steal_pointer (&data);
+}
+
+static gboolean
+save_cache_http_data_xattr (int fd,
+ GBytes *bytes,
+ GError **error)
+{
+ if (TEMP_FAILURE_RETRY (fsetxattr (fd, (char *) CACHE_HTTP_XATTR,
+ g_bytes_get_data (bytes, NULL),
+ g_bytes_get_size (bytes),
+ 0)) < 0)
+ return glnx_throw_errno_prefix (error, "fsetxattr");
+
+ return TRUE;
+}
+
+static gboolean
+save_cache_http_data_fallback (int fd,
+ GBytes *bytes,
+ GError **error)
+{
+ if (glnx_loop_write (fd,
+ g_bytes_get_data (bytes, NULL),
+ g_bytes_get_size (bytes)) < 0)
+ return glnx_throw_errno_prefix (error, "write");
+
+ return TRUE;
+}
+
+static gboolean
+save_cache_http_data_to_file (int dfd,
+ char *name,
+ GBytes *bytes,
+ gboolean no_xattr,
+ GCancellable *cancellable,
+ GError **error)
+{
+ glnx_autofd int fd = -1;
+ g_autofree char *fallback_name = NULL;
+
+ if (!no_xattr)
+ {
+ if (!glnx_openat_rdonly (dfd, name, FALSE,
+ &fd, error))
+ return FALSE;
+
+ if (save_cache_http_data_xattr (fd, bytes, error))
+ return TRUE;
+
+ if (errno == ENOTSUP)
+ g_clear_error (error);
+ else
+ return FALSE;
}
- while (flatpak_http_should_retry_request (local_error, n_retries_remaining--));
- g_assert (local_error != NULL);
- g_propagate_error (error, g_steal_pointer (&local_error));
- return FALSE;
+ fallback_name = g_strconcat (name, CACHE_HTTP_SUFFIX, NULL);
+ if (!glnx_file_replace_contents_at (dfd, fallback_name,
+ g_bytes_get_data (bytes, NULL),
+ g_bytes_get_size (bytes),
+ 0,
+ cancellable,
+ error))
+ return FALSE;
+
+ return TRUE;
}
static gboolean
@@ -863,34 +1285,119 @@ sync_and_rename_tmpfile (GLnxTmpfile *tmpfile,
return TRUE;
}
-static gboolean
-flatpak_cache_http_uri_once (SoupSession *soup_session,
- const char *uri,
- FlatpakHTTPFlags flags,
- int dest_dfd,
- const char *dest_subpath,
- FlatpakLoadUriProgress progress,
- gpointer user_data,
- GCancellable *cancellable,
- GError **error)
+static void
+set_cache_http_data_from_headers (CacheHttpData *cache_data,
+ LoadUriData *data)
{
- g_autoptr(SoupRequestHTTP) request = NULL;
- g_autoptr(GMainContext) context = NULL;
+ const char *etag = data->hdr_etag;
+ const char *last_modified = data->hdr_last_modified;
+ const char *cache_control = data->hdr_cache_control;
+ const char *expires = data->hdr_expires;
+ gboolean expires_computed = FALSE;
+
+ /* The original HTTP 1/1 specification only required sending the ETag header in a 304
+ * response, and implied that a cache might need to save the old Cache-Control
+ * values. The updated RFC 7232 from 2014 requires sending Cache-Control, ETags, and
+ * Expire if they would have been sent in the original 200 response, and recommends
+ * sending Last-Modified for requests without an etag. Since sending these headers was
+ * apparently normal previously, for simplicity we assume the RFC 7232 behavior and start
+ * from scratch for a 304 response.
+ */
+ clear_cache_http_data (cache_data, FALSE);
+
+ if (etag && *etag)
+ {
+ cache_data->etag = g_strdup (etag);
+ }
+ else if (last_modified && *last_modified)
+ {
+ g_autoptr(GDateTime) date = flatpak_parse_http_time (last_modified);
+ if (date)
+ cache_data->last_modified = g_date_time_to_unix (date);
+ }
+
+ if (cache_control && *cache_control)
+ {
+ g_autoptr(GHashTable) params = flatpak_parse_http_header_param_list (cache_control);
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init (&iter, params);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ if (g_strcmp0 (key, "max-age") == 0)
+ {
+ char *end;
+
+ char *max_age = value;
+ int max_age_sec = g_ascii_strtoll (max_age, &end, 10);
+ if (*max_age != '\0' && *end == '\0')
+ {
+ GTimeVal now;
+ g_get_current_time (&now);
+ cache_data->expires = now.tv_sec + max_age_sec;
+ expires_computed = TRUE;
+ }
+ }
+ else if (g_strcmp0 (key, "no-cache") == 0)
+ {
+ cache_data->expires = 0;
+ expires_computed = TRUE;
+ }
+ }
+ }
+
+ if (!expires_computed && expires && *expires)
+ {
+ g_autoptr(GDateTime) date = flatpak_parse_http_time (expires);
+ if (date)
+ {
+ cache_data->expires = g_date_time_to_unix (date);
+ expires_computed = TRUE;
+ }
+ }
+
+ if (!expires_computed)
+ {
+ /* If nothing implies an expires time, use 30 minutes. Browsers use
+ * 0.1 * (Date - Last-Modified), but it's clearly appropriate here, and
+ * better if server's send a value.
+ */
+ GTimeVal now;
+ g_get_current_time (&now);
+ cache_data->expires = now.tv_sec + 1800;
+ }
+}
+
+gboolean
+flatpak_cache_http_uri (FlatpakHttpSession *http_session,
+ const char *uri,
+ FlatpakHTTPFlags flags,
+ int dest_dfd,
+ const char *dest_subpath,
+ FlatpakLoadUriProgress progress,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_auto(LoadUriData) data = { NULL };
+ g_autoptr(GError) local_error = NULL;
+ guint n_retries_remaining = DEFAULT_N_NETWORK_RETRIES;
g_autoptr(CacheHttpData) cache_data = NULL;
+ g_autoptr(GMainContextPopDefault) main_context = NULL;
g_autofree char *parent_path = g_path_get_dirname (dest_subpath);
g_autofree char *name = g_path_get_basename (dest_subpath);
- glnx_autofd int dfd = -1;
- gboolean no_xattr = FALSE;
- LoadUriData data = { NULL };
g_auto(GLnxTmpfile) out_tmpfile = { 0 };
g_auto(GLnxTmpfile) cache_tmpfile = { 0 };
g_autoptr(GBytes) cache_bytes = NULL;
- SoupMessage *m;
+ gboolean no_xattr = FALSE;
+ glnx_autofd int cache_dfd = -1;
+ gboolean success;
- if (!glnx_opendirat (dest_dfd, parent_path, TRUE, &dfd, error))
+ if (!glnx_opendirat (dest_dfd, parent_path, TRUE, &cache_dfd, error))
return FALSE;
- cache_data = load_cache_http_data (dfd, name, &no_xattr,
+ cache_data = load_cache_http_data (cache_dfd, name, &no_xattr,
cancellable, error);
if (!cache_data)
return FALSE;
@@ -905,10 +1412,9 @@ flatpak_cache_http_uri_once (SoupSession *soup_session,
g_get_current_time (&now);
if (cache_data->expires > now.tv_sec)
{
- if (error)
- *error = g_error_new (FLATPAK_HTTP_ERROR,
- FLATPAK_HTTP_ERROR_NOT_CHANGED,
- "Reusing cached value");
+ g_set_error (error, FLATPAK_HTTP_ERROR,
+ FLATPAK_HTTP_ERROR_NOT_CHANGED,
+ "Reusing cached value");
return FALSE;
}
}
@@ -916,83 +1422,66 @@ flatpak_cache_http_uri_once (SoupSession *soup_session,
if (cache_data->uri == NULL)
cache_data->uri = g_strdup (uri);
- /* Must revalidate */
-
- g_debug ("Loading %s using libsoup", uri);
+ /* Missing from cache, or expired so must revalidate via etag/last-modified headers */
- context = g_main_context_ref_thread_default ();
+ main_context = flatpak_main_context_new_default ();
- data.context = context;
- data.cache_data = cache_data;
- data.out_tmpfile = &out_tmpfile;
- data.out_tmpfile_parent_dfd = dfd;
+ data.context = main_context;
data.progress = progress;
- data.cancellable = cancellable;
data.user_data = user_data;
data.last_progress_time = g_get_monotonic_time ();
+ data.cancellable = cancellable;
+ data.flags = flags;
- request = soup_session_request_http (soup_session, "GET",
- uri, error);
- if (request == NULL)
- return FALSE;
+ data.cache_data = cache_data;
- m = soup_request_http_get_message (request);
+ data.out_tmpfile = &out_tmpfile;
+ data.out_tmpfile_parent_dfd = cache_dfd;
- if (cache_data->etag && cache_data->etag[0])
- soup_message_headers_replace (m->request_headers, "If-None-Match", cache_data->etag);
- else if (cache_data->last_modified != 0)
+ do
{
- SoupDate *date = soup_date_new_from_time_t (cache_data->last_modified);
- g_autofree char *date_str = soup_date_to_string (date, SOUP_DATE_HTTP);
- soup_message_headers_replace (m->request_headers, "If-Modified-Since", date_str);
- soup_date_free (date);
- }
+ if (n_retries_remaining < DEFAULT_N_NETWORK_RETRIES)
+ {
+ g_clear_error (&local_error);
+ reset_load_uri_data (&data);
+ }
- if (flags & FLATPAK_HTTP_FLAGS_ACCEPT_OCI)
- soup_message_headers_replace (m->request_headers, "Accept",
- FLATPAK_OCI_MEDIA_TYPE_IMAGE_MANIFEST ", " FLATPAK_DOCKER_MEDIA_TYPE_IMAGE_MANIFEST2);
+ success = flatpak_download_http_uri_once (http_session, &data, uri, &local_error);
- if (flags & FLATPAK_HTTP_FLAGS_STORE_COMPRESSED)
- {
- soup_session_remove_feature_by_type (soup_session, SOUP_TYPE_CONTENT_DECODER);
- soup_message_headers_replace (m->request_headers, "Accept-Encoding",
- "gzip");
- data.store_compressed = TRUE;
- }
- else if (!soup_session_has_feature (soup_session, SOUP_TYPE_CONTENT_DECODER))
- soup_session_add_feature_by_type (soup_session, SOUP_TYPE_CONTENT_DECODER);
+ if (success)
+ break;
- soup_request_send_async (SOUP_REQUEST (request),
- cancellable,
- load_uri_callback, &data);
+ g_assert (local_error != NULL);
+ }
+ while (flatpak_http_should_retry_request (local_error, n_retries_remaining--));
- while (data.error == NULL && !data.done)
- g_main_context_iteration (data.context, TRUE);
+ /* Update the cache data on success or cache-valid */
+ if (success || g_error_matches (local_error, FLATPAK_HTTP_ERROR, FLATPAK_HTTP_ERROR_NOT_CHANGED))
+ {
+ set_cache_http_data_from_headers (cache_data, &data);
+ cache_bytes = serialize_cache_http_data (cache_data);
+ }
- if (data.error)
+ if (local_error)
{
- if (data.error->domain == FLATPAK_HTTP_ERROR &&
- data.error->code == FLATPAK_HTTP_ERROR_NOT_CHANGED)
+ if (cache_bytes)
{
GError *tmp_error = NULL;
- cache_bytes = serialize_cache_http_data (cache_data);
-
- if (!save_cache_http_data_to_file (dfd, name, cache_bytes, no_xattr,
+ if (!save_cache_http_data_to_file (cache_dfd, name, cache_bytes, no_xattr,
cancellable, &tmp_error))
{
- g_clear_error (&data.error);
+ g_clear_error (&local_error);
g_propagate_error (error, tmp_error);
return FALSE;
}
}
- g_propagate_error (error, data.error);
+ g_propagate_error (error, g_steal_pointer (&local_error));
return FALSE;
}
- cache_bytes = serialize_cache_http_data (cache_data);
if (!no_xattr)
{
if (!save_cache_http_data_xattr (out_tmpfile.fd, cache_bytes, error))
@@ -1007,7 +1496,7 @@ flatpak_cache_http_uri_once (SoupSession *soup_session,
if (no_xattr)
{
- if (!glnx_open_tmpfile_linkable_at (dfd, ".", O_WRONLY, &cache_tmpfile, error))
+ if (!glnx_open_tmpfile_linkable_at (cache_dfd, ".", O_WRONLY, &cache_tmpfile, error))
return FALSE;
if (!save_cache_http_data_fallback (cache_tmpfile.fd, cache_bytes, error))
@@ -1025,50 +1514,5 @@ flatpak_cache_http_uri_once (SoupSession *soup_session,
return FALSE;
}
- g_debug ("Received %" G_GUINT64_FORMAT " bytes", data.downloaded_bytes);
-
return TRUE;
}
-
-gboolean
-flatpak_cache_http_uri (SoupSession *soup_session,
- const char *uri,
- FlatpakHTTPFlags flags,
- int dest_dfd,
- const char *dest_subpath,
- FlatpakLoadUriProgress progress,
- gpointer user_data,
- GCancellable *cancellable,
- GError **error)
-{
- g_autoptr(GError) local_error = NULL;
- guint n_retries_remaining = DEFAULT_N_NETWORK_RETRIES;
- g_autoptr(GMainContextPopDefault) main_context = NULL;
-
- main_context = flatpak_main_context_new_default ();
-
- do
- {
- if (n_retries_remaining < DEFAULT_N_NETWORK_RETRIES)
- {
- g_clear_error (&local_error);
-
- if (progress)
- progress (0, user_data); /* Reset the progress */
- }
-
- if (flatpak_cache_http_uri_once (soup_session, uri, flags,
- dest_dfd, dest_subpath,
- progress, user_data,
- cancellable, &local_error))
- {
- g_assert (local_error == NULL);
- return TRUE;
- }
- }
- while (flatpak_http_should_retry_request (local_error, n_retries_remaining--));
-
- g_assert (local_error != NULL);
- g_propagate_error (error, g_steal_pointer (&local_error));
- return FALSE;
-}
diff --git a/common/flatpak-utils-private.h b/common/flatpak-utils-private.h
index 7ddaa03b..ceecb5c6 100644
--- a/common/flatpak-utils-private.h
+++ b/common/flatpak-utils-private.h
@@ -102,9 +102,6 @@ gboolean flatpak_fail_error (GError **error,
const char *fmt,
...) G_GNUC_PRINTF (3, 4);
-void flatpak_debug2 (const char *format,
- ...) G_GNUC_PRINTF (1, 2);
-
gint flatpak_strcmp0_ptr (gconstpointer a,
gconstpointer b);
@@ -132,6 +129,7 @@ gboolean flatpak_extension_matches_reason (const char *extension_id,
gboolean default_value);
const char * flatpak_get_bwrap (void);
+gboolean flatpak_bwrap_is_unprivileged (void);
char **flatpak_strv_sort_by_length (const char * const *strv);
char **flatpak_strv_merge (char **strv1,
@@ -359,6 +357,13 @@ g_hash_table_steal_extended (GHashTable *hash_table,
}
#endif
+#if !GLIB_CHECK_VERSION (2, 62, 0)
+void g_ptr_array_extend (GPtrArray *array_to_extend,
+ GPtrArray *array,
+ GCopyFunc func,
+ gpointer user_data);
+#endif
+
#if !GLIB_CHECK_VERSION (2, 68, 0)
guint g_string_replace (GString *string,
const gchar *find,
@@ -407,6 +412,10 @@ gboolean flatpak_switch_symlink_and_remove (const char *symlink_path,
const char *target,
GError **error);
+char *flatpak_keyfile_get_string_non_empty (GKeyFile *keyfile,
+ const char *group,
+ const char *key);
+
GKeyFile * flatpak_parse_repofile (const char *remote_name,
gboolean from_ref,
GKeyFile *keyfile,
@@ -738,14 +747,6 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (FlatpakRepoTransaction, flatpak_repo_transaction_
#define AUTOLOCK(name) G_GNUC_UNUSED __attribute__((cleanup (flatpak_auto_unlock_helper))) GMutex * G_PASTE (auto_unlock, __LINE__) = flatpak_auto_lock_helper (&G_LOCK_NAME (name))
-#if !defined(SOUP_AUTOCLEANUPS_H) && !defined(__SOUP_AUTOCLEANUPS_H__)
-G_DEFINE_AUTOPTR_CLEANUP_FUNC (SoupSession, g_object_unref)
-G_DEFINE_AUTOPTR_CLEANUP_FUNC (SoupMessage, g_object_unref)
-G_DEFINE_AUTOPTR_CLEANUP_FUNC (SoupRequest, g_object_unref)
-G_DEFINE_AUTOPTR_CLEANUP_FUNC (SoupRequestHTTP, g_object_unref)
-G_DEFINE_AUTOPTR_CLEANUP_FUNC (SoupURI, soup_uri_free)
-#endif
-
#if !JSON_CHECK_VERSION (1, 1, 2)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (JsonArray, json_array_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (JsonBuilder, g_object_unref)
diff --git a/common/flatpak-utils.c b/common/flatpak-utils.c
index ba77ba74..e348dcbf 100644
--- a/common/flatpak-utils.c
+++ b/common/flatpak-utils.c
@@ -1,4 +1,4 @@
-/*
+/* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s:
* Copyright © 1995-1998 Free Software Foundation, Inc.
* Copyright © 2014-2019 Red Hat, Inc
*
@@ -79,6 +79,8 @@ static const GDBusErrorEntry flatpak_error_entries[] = {
{FLATPAK_ERROR_NOT_CACHED, "org.freedesktop.Flatpak.Error.NotCached"}, /* Since: 1.3.3 */
{FLATPAK_ERROR_REF_NOT_FOUND, "org.freedesktop.Flatpak.Error.RefNotFound"}, /* Since: 1.4.0 */
{FLATPAK_ERROR_PERMISSION_DENIED, "org.freedesktop.Flatpak.Error.PermissionDenied"}, /* Since: 1.5.1 */
+ {FLATPAK_ERROR_AUTHENTICATION_FAILED, "org.freedesktop.Flatpak.Error.AuthenticationFailed"}, /* Since: 1.7.3 */
+ {FLATPAK_ERROR_NOT_AUTHORIZED, "org.freedesktop.Flatpak.Error.NotAuthorized"}, /* Since: 1.7.3 */
};
typedef struct archive FlatpakAutoArchiveRead;
@@ -118,18 +120,6 @@ flatpak_fail_error (GError **error, FlatpakError code, const char *fmt, ...)
return FALSE;
}
-void
-flatpak_debug2 (const char *format, ...)
-{
- va_list var_args;
-
- va_start (var_args, format);
- g_logv (G_LOG_DOMAIN "2",
- G_LOG_LEVEL_DEBUG,
- format, var_args);
- va_end (var_args);
-}
-
gboolean
flatpak_write_update_checksum (GOutputStream *out,
gconstpointer data,
@@ -626,7 +616,7 @@ load_kernel_module_list (void)
if (!g_file_get_contents ("/proc/modules", &modules_data, NULL, &error))
{
- g_debug ("Failed to read /proc/modules: %s", error->message);
+ g_info ("Failed to read /proc/modules: %s", error->message);
return modules;
}
@@ -750,6 +740,19 @@ flatpak_get_bwrap (void)
}
gboolean
+flatpak_bwrap_is_unprivileged (void)
+{
+ const char *path = g_find_program_in_path (flatpak_get_bwrap ());
+ struct stat st;
+
+ /* Various features are supported only if bwrap exists and is not setuid */
+ return
+ path != NULL &&
+ stat (path, &st) == 0 &&
+ (st.st_mode & S_ISUID) == 0;
+}
+
+gboolean
flatpak_get_allowed_exports (const char *source_path,
const char *app_id,
FlatpakContext *context,
@@ -2262,6 +2265,20 @@ flatpak_summary_lookup_ref (GVariant *summary_v,
return TRUE;
}
+char *
+flatpak_keyfile_get_string_non_empty (GKeyFile *keyfile,
+ const char *group,
+ const char *key)
+{
+ g_autofree char *value = NULL;
+
+ value = g_key_file_get_string (keyfile, group, key, NULL);
+ if (value != NULL && *value == '\0')
+ g_clear_pointer (&value, g_free);
+
+ return g_steal_pointer (&value);
+}
+
GKeyFile *
flatpak_parse_repofile (const char *remote_name,
gboolean from_ref,
@@ -2368,15 +2385,23 @@ flatpak_parse_repofile (const char *remote_name,
g_key_file_set_boolean (config, group, "gpg-verify", FALSE);
}
- collection_id = g_key_file_get_string (keyfile, source_group,
- FLATPAK_REPO_DEPLOY_COLLECTION_ID_KEY, NULL);
- if (collection_id != NULL && *collection_id == '\0')
- g_clear_pointer (&collection_id, g_free);
+ /* We have a hierarchy of keys for setting the collection ID, which all have
+ * the same effect. The only difference is which versions of Flatpak support
+ * them, and therefore what P2P implementation is enabled by them:
+ * DeploySideloadCollectionID: supported by Flatpak >= 1.12.8 (1.7.1
+ * introduced sideload support but this key was added late)
+ * DeployCollectionID: supported by Flatpak >= 1.0.6 (but fully supported in
+ * >= 1.2.0)
+ * CollectionID: supported by Flatpak >= 0.9.8
+ */
+ collection_id = flatpak_keyfile_get_string_non_empty (keyfile, source_group,
+ FLATPAK_REPO_DEPLOY_SIDELOAD_COLLECTION_ID_KEY);
if (collection_id == NULL)
- collection_id = g_key_file_get_string (keyfile, source_group,
- FLATPAK_REPO_COLLECTION_ID_KEY, NULL);
- if (collection_id != NULL && *collection_id == '\0')
- g_clear_pointer (&collection_id, g_free);
+ collection_id = flatpak_keyfile_get_string_non_empty (keyfile, source_group,
+ FLATPAK_REPO_DEPLOY_COLLECTION_ID_KEY);
+ if (collection_id == NULL)
+ collection_id = flatpak_keyfile_get_string_non_empty (keyfile, source_group,
+ FLATPAK_REPO_COLLECTION_ID_KEY);
if (collection_id != NULL)
{
if (gpg_key == NULL)
@@ -3175,7 +3200,7 @@ flatpak_repo_save_digested_summary (OstreeRepo *repo,
if (fstatat (repo_dfd, path, &stbuf, 0) == 0 &&
stbuf.st_size != 0)
{
- g_debug ("Reusing digested summary at %s for %s", path, name);
+ g_info ("Reusing digested summary at %s for %s", path, name);
return g_steal_pointer (&digest);
}
@@ -3191,7 +3216,7 @@ flatpak_repo_save_digested_summary (OstreeRepo *repo,
cancellable, error))
return NULL;
- g_debug ("Wrote digested summary at %s for %s", path, name);
+ g_info ("Wrote digested summary at %s for %s", path, name);
return g_steal_pointer (&digest);
}
@@ -3220,7 +3245,7 @@ flatpak_repo_save_digested_summary_delta (OstreeRepo *repo,
if (fstatat (repo_dfd, path, &stbuf, 0) == 0 &&
stbuf.st_size == g_bytes_get_size (delta))
{
- g_debug ("Reusing digested summary-diff for %s", filename);
+ g_info ("Reusing digested summary-diff for %s", filename);
return TRUE;
}
@@ -3231,7 +3256,7 @@ flatpak_repo_save_digested_summary_delta (OstreeRepo *repo,
cancellable, error))
return FALSE;
- g_debug ("Wrote digested summary delta at %s", path);
+ g_info ("Wrote digested summary delta at %s", path);
return TRUE;
}
@@ -3294,7 +3319,7 @@ populate_commit_data_cache (OstreeRepo *repo,
if (cache_version < FLATPAK_XA_CACHE_VERSION)
{
/* Need to re-index to get all data */
- g_debug ("Old summary cache version %d, not using cache", cache_version);
+ g_info ("Old summary cache version %d, not using cache", cache_version);
return NULL;
}
@@ -3316,7 +3341,7 @@ populate_commit_data_cache (OstreeRepo *repo,
checksum_bytes = var_subsummary_peek_checksum (subsummary, &checksum_bytes_len);
if (G_UNLIKELY (checksum_bytes_len != OSTREE_SHA256_DIGEST_LEN))
{
- g_debug ("Invalid checksum for digested summary, not using cache");
+ g_info ("Invalid checksum for digested summary, not using cache");
return NULL;
}
digest = ostree_checksum_from_bytes (checksum_bytes);
@@ -3330,7 +3355,7 @@ populate_commit_data_cache (OstreeRepo *repo,
summary_v = flatpak_repo_load_digested_summary (repo, digest, NULL);
if (summary_v == NULL)
{
- g_debug ("Failed to load digested summary %s, not using cache", digest);
+ g_info ("Failed to load digested summary %s, not using cache", digest);
return NULL;
}
@@ -3362,7 +3387,7 @@ populate_commit_data_cache (OstreeRepo *repo,
if (!var_metadata_lookup (commit_metadata, "xa.data", NULL, &xa_data_v) ||
!var_variant_is_type (xa_data_v, G_VARIANT_TYPE ("(tts)")))
{
- g_debug ("Missing xa.data for ref %s, not using cache", ref);
+ g_info ("Missing xa.data for ref %s, not using cache", ref);
return NULL;
}
@@ -3638,7 +3663,7 @@ _ostree_repo_static_delta_superblock_digest (OstreeRepo *repo,
g_checksum_get_digest (checksum, digest, &len);
return g_variant_new_from_data (G_VARIANT_TYPE ("ay"),
- g_memdup (digest, len), len,
+ g_memdup2 (digest, len), len,
FALSE, g_free, FALSE);
}
@@ -4267,7 +4292,7 @@ add_summary_metadata (OstreeRepo *repo,
g_variant_builder_add (metadata_builder, "{sv}", "xa.deploy-collection-id",
g_variant_new_string (collection_id));
else if (deploy_collection_id)
- g_debug ("Ignoring deploy-collection-id=true because no collection ID is set.");
+ g_info ("Ignoring deploy-collection-id=true because no collection ID is set.");
if (authenticator_name)
g_variant_builder_add (metadata_builder, "{sv}", "xa.authenticator-name",
@@ -4729,7 +4754,7 @@ flatpak_repo_gc_digested_summaries (OstreeRepo *repo,
/* Keep all the referenced summaries */
if (g_hash_table_contains (digested_summary_cache, sha256))
{
- g_debug ("Keeping referenced summary %s", dent->d_name);
+ g_info ("Keeping referenced summary %s", dent->d_name);
continue;
}
/* Remove rest */
@@ -4745,7 +4770,7 @@ flatpak_repo_gc_digested_summaries (OstreeRepo *repo,
/* Only keep deltas going to a generated summary */
if (g_hash_table_contains (digested_summaries, to_sha256))
{
- g_debug ("Keeping delta to generated summary %s", dent->d_name);
+ g_info ("Keeping delta to generated summary %s", dent->d_name);
continue;
}
/* Remove rest */
@@ -4769,7 +4794,7 @@ flatpak_repo_gc_digested_summaries (OstreeRepo *repo,
if (remove)
{
- g_debug ("Removing old digested summary file %s", dent->d_name);
+ g_info ("Removing old digested summary file %s", dent->d_name);
if (unlinkat (iter.fd, dent->d_name, 0) != 0)
{
glnx_set_error_from_errno (error);
@@ -4777,7 +4802,7 @@ flatpak_repo_gc_digested_summaries (OstreeRepo *repo,
}
}
else
- g_debug ("Keeping unexpected summary file %s", dent->d_name);
+ g_info ("Keeping unexpected summary file %s", dent->d_name);
}
return TRUE;
@@ -5292,7 +5317,7 @@ copy_icon (const char *id,
if (!ostree_repo_file_ensure_resolved (OSTREE_REPO_FILE(icon_file), NULL))
{
- g_debug ("No icon at size %s for %s", size, id);
+ g_info ("No icon at size %s for %s", size, id);
return TRUE;
}
@@ -5595,9 +5620,9 @@ _flatpak_repo_generate_appstream (OstreeRepo *repo,
const char *collection_id;
if (subset != NULL && *subset != 0)
- g_debug ("Generating appstream for %s, subset %s", arch, subset);
+ g_info ("Generating appstream for %s, subset %s", arch, subset);
else
- g_debug ("Generating appstream for %s", arch);
+ g_info ("Generating appstream for %s", arch);
collection_id = ostree_repo_get_collection_id (repo);
@@ -5657,7 +5682,7 @@ _flatpak_repo_generate_appstream (OstreeRepo *repo,
if (var_metadata_lookup (commit_metadata, OSTREE_COMMIT_META_KEY_ENDOFLIFE, NULL, NULL) ||
var_metadata_lookup (commit_metadata, OSTREE_COMMIT_META_KEY_ENDOFLIFE_REBASE, NULL, NULL))
{
- g_debug (_("%s is end-of-life, ignoring for appstream"), flatpak_decomposed_get_ref (ref));
+ g_info (_("%s is end-of-life, ignoring for appstream"), flatpak_decomposed_get_ref (ref));
continue;
}
@@ -5749,7 +5774,7 @@ _flatpak_repo_generate_appstream (OstreeRepo *repo,
if (g_file_equal (root, parent_root))
{
skip_commit = TRUE;
- g_debug ("Not updating %s, no change", branch);
+ g_info ("Not updating %s, no change", branch);
}
}
@@ -5800,7 +5825,7 @@ _flatpak_repo_generate_appstream (OstreeRepo *repo,
}
}
- g_debug ("Creating appstream branch %s", branch);
+ g_info ("Creating appstream branch %s", branch);
if (collection_id != NULL)
{
const OstreeCollectionRef collection_ref = { (char *) collection_id, branch };
@@ -6755,7 +6780,8 @@ flatpak_pull_from_bundle (OstreeRepo *repo,
if (metadata == NULL)
return FALSE;
- metadata_size = strlen (metadata_contents);
+ if (metadata_contents != NULL)
+ metadata_size = strlen (metadata_contents);
if (!ostree_repo_get_remote_option (repo, remote, "collection-id", NULL,
&remote_collection_id, NULL))
@@ -6973,7 +6999,7 @@ flatpak_mirror_image_from_oci (FlatpakOciRegistry *dst_registry,
if (delta_layer)
{
- g_debug ("Using OCI delta %s for layer %s", delta_layer->digest, layer->digest);
+ g_info ("Using OCI delta %s for layer %s", delta_layer->digest, layer->digest);
g_autofree char *delta_digest = NULL;
glnx_autofd int delta_fd = flatpak_oci_registry_download_blob (registry, oci_repository, FALSE,
delta_layer->digest, (const char **)delta_layer->urls,
@@ -7162,7 +7188,7 @@ flatpak_pull_from_oci (OstreeRepo *repo,
if (delta_layer)
{
- g_debug ("Using OCI delta %s for layer %s", delta_layer->digest, layer->digest);
+ g_info ("Using OCI delta %s for layer %s", delta_layer->digest, layer->digest);
expected_digest = image_config->rootfs.diff_ids[i]; /* The delta recreates the uncompressed tar so use that digest */
}
else
@@ -8224,7 +8250,7 @@ flatpak_log_dir_access (FlatpakDir *dir)
if (dir_path != NULL)
dir_path_str = g_file_get_path (dir_path);
dir_name = flatpak_dir_get_name (dir);
- g_debug ("Opening %s flatpak installation at path %s", dir_name, dir_path_str);
+ g_info ("Opening %s flatpak installation at path %s", dir_name, dir_path_str);
}
}
@@ -9214,6 +9240,23 @@ running_under_sudo (void)
return FALSE;
}
+#if !GLIB_CHECK_VERSION (2, 62, 0)
+void
+g_ptr_array_extend (GPtrArray *array_to_extend,
+ GPtrArray *array,
+ GCopyFunc func,
+ gpointer user_data)
+{
+ for (gsize i = 0; i < array->len; i++)
+ {
+ if (func)
+ g_ptr_array_add (array_to_extend, func (g_ptr_array_index (array, i), user_data));
+ else
+ g_ptr_array_add (array_to_extend, g_ptr_array_index (array, i));
+ }
+}
+#endif
+
#if !GLIB_CHECK_VERSION (2, 68, 0)
/* All this code is backported directly from glib */
guint
diff --git a/common/meson.build b/common/meson.build
new file mode 100644
index 00000000..b87f22ac
--- /dev/null
+++ b/common/meson.build
@@ -0,0 +1,263 @@
+# Copyright 2022 Collabora Ltd.
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+public_headers = [
+ 'flatpak-bundle-ref.h',
+ 'flatpak-error.h',
+ 'flatpak-installation.h',
+ 'flatpak-installed-ref.h',
+ 'flatpak-instance.h',
+ 'flatpak-portal-error.h',
+ 'flatpak-ref.h',
+ 'flatpak-related-ref.h',
+ 'flatpak-remote-ref.h',
+ 'flatpak-remote.h',
+ 'flatpak-transaction.h',
+ 'flatpak.h',
+]
+
+install_headers(
+ public_headers,
+ subdir : 'flatpak',
+)
+
+flatpak_version_macros = configure_file(
+ input : 'flatpak-version-macros.h.in',
+ output : 'flatpak-version-macros.h',
+ configuration : {
+ 'FLATPAK_MAJOR_VERSION' : flatpak_major_version,
+ 'FLATPAK_MINOR_VERSION' : flatpak_minor_version,
+ 'FLATPAK_MICRO_VERSION' : flatpak_micro_version,
+ },
+ install_dir : get_option('includedir') / 'flatpak',
+)
+
+# TODO: After the Autotools build system is removed, we can probably
+# switch this to gnome.mkenums_simple, but it's easier to keep them
+# consistent if we use the same templates
+enums = gnome.mkenums(
+ 'flatpak-enum-types',
+ c_template : 'flatpak-enum-types.c.template',
+ h_template : 'flatpak-enum-types.h.template',
+ install_dir : get_option('includedir') / 'flatpak',
+ install_header : true,
+ sources : public_headers,
+)
+
+flatpak_gdbus = gnome.gdbus_codegen(
+ 'flatpak-dbus-generated',
+ sources : [
+ '../data/org.freedesktop.Flatpak.xml',
+ '../data/org.freedesktop.Flatpak.Authenticator.xml',
+ ],
+ interface_prefix : 'org.freedesktop.Flatpak.',
+ namespace : 'Flatpak',
+)
+
+flatpak_document_gdbus = gnome.gdbus_codegen(
+ 'flatpak-document-dbus-generated',
+ sources: [
+ '../data/org.freedesktop.portal.Documents.xml',
+ ],
+ interface_prefix : 'org.freedesktop.portal.',
+ namespace : 'XdpDbus',
+)
+
+systemd_gdbus = gnome.gdbus_codegen(
+ 'flatpak-systemd-dbus-generated',
+ sources: [
+ '../data/org.freedesktop.systemd1.xml',
+ ],
+ interface_prefix : 'org.freedesktop.systemd1.',
+ namespace : 'Systemd',
+)
+
+variant_schema_compiler_command = [
+ global_source_root / 'subprojects' / 'variant-schema-compiler' / 'variant-schema-compiler',
+]
+
+if get_option('internal_checks')
+ variant_schema_compiler_command += ['--internal-validation']
+endif
+
+variant_schema_compiler_command += [
+ '--outfile', '@OUTPUT0@',
+ '--outfile-header', '@OUTPUT1@',
+ '--prefix', 'var',
+ '@INPUT@',
+]
+
+flatpak_variant = custom_target(
+ 'flatpak-variant-private.h',
+ input : [
+ '../data/flatpak-variants.gv',
+ ],
+ output : [
+ 'flatpak-variant-impl-private.h',
+ 'flatpak-variant-private.h',
+ ],
+ build_by_default : true,
+ command : variant_schema_compiler_command,
+)
+
+built_headers = [
+ enums[1],
+ flatpak_version_macros,
+ flatpak_gdbus[1],
+ flatpak_document_gdbus[1],
+ systemd_gdbus[1],
+ flatpak_variant[1],
+]
+
+libflatpak_common_base = static_library(
+ 'flatpak-common-base',
+ dependencies : base_deps + [libglnx_dep],
+ gnu_symbol_visibility : 'hidden',
+ include_directories : [common_include_directories],
+ install : false,
+ sources : [
+ 'flatpak-utils-base.c',
+ 'flatpak-utils-base-private.h',
+ ] + flatpak_gdbus + flatpak_document_gdbus,
+)
+libflatpak_common_base_dep = declare_dependency(
+ dependencies : base_deps + [libglnx_dep],
+ include_directories : [common_include_directories],
+ link_with : [
+ libflatpak_common_base,
+ ],
+ sources : built_headers,
+)
+
+sources = [
+ 'flatpak-appdata.c',
+ 'flatpak-auth.c',
+ 'flatpak-bundle-ref.c',
+ 'flatpak-bwrap.c',
+ 'flatpak-chain-input-stream.c',
+ 'flatpak-context.c',
+ 'flatpak-dir.c',
+ 'flatpak-error.c',
+ 'flatpak-exports.c',
+ 'flatpak-installation.c',
+ 'flatpak-installed-ref.c',
+ 'flatpak-instance.c',
+ 'flatpak-json-oci.c',
+ 'flatpak-json.c',
+ 'flatpak-oci-registry.c',
+ 'flatpak-portal-error.c',
+ 'flatpak-progress.c',
+ 'flatpak-prune.c',
+ 'flatpak-ref-utils.c',
+ 'flatpak-ref.c',
+ 'flatpak-related-ref.c',
+ 'flatpak-remote-ref.c',
+ 'flatpak-remote.c',
+ 'flatpak-run.c',
+ 'flatpak-transaction.c',
+ 'flatpak-utils-http.c',
+ 'flatpak-utils.c',
+ 'flatpak-uri.c',
+ 'flatpak-zstd-decompressor.c',
+]
+
+if malcontent_dep.found()
+ sources += ['flatpak-parental-controls.c']
+endif
+
+libflatpak_common = static_library(
+ 'flatpak-common',
+ dependencies : [
+ base_deps,
+ dconf_dep,
+ gpgme_dep,
+ json_glib_dep,
+ libarchive_dep,
+ libcurl_dep,
+ libflatpak_common_base_dep,
+ libglnx_dep,
+ libostree_dep,
+ libseccomp_dep,
+ libsoup_dep,
+ libsystemd_dep,
+ libxml_dep,
+ libzstd_dep,
+ malcontent_dep,
+ polkit_agent_dep,
+ xau_dep,
+ ],
+ gnu_symbol_visibility : 'hidden',
+ include_directories : [common_include_directories],
+ install : false,
+ sources : enums + public_headers + sources + systemd_gdbus + [
+ flatpak_variant[0],
+ flatpak_variant[1],
+ ],
+)
+libflatpak_common_dep = declare_dependency(
+ dependencies : [
+ base_deps,
+ libflatpak_common_base_dep,
+ libglnx_dep,
+ ],
+ include_directories : [common_include_directories],
+ link_with : [
+ libflatpak_common,
+ ],
+ sources : built_headers,
+)
+
+libflatpak = library(
+ 'flatpak',
+ 'flatpak.c',
+ gnu_symbol_visibility : 'hidden',
+ include_directories : [common_include_directories],
+ install : true,
+ link_args : ['-export-dynamic'],
+ link_whole : [
+ libflatpak_common_base,
+ libflatpak_common,
+ ],
+ soversion : '0',
+ version : '0.@0@.0'.format(flatpak_binary_age),
+)
+libflatpak_dep = declare_dependency(
+ dependencies : base_deps,
+ include_directories : [common_include_directories],
+ link_with : [
+ libflatpak,
+ ],
+ sources : built_headers,
+)
+
+test_libflatpak = executable(
+ 'test-libflatpak',
+ 'test-lib.c',
+ dependencies : base_deps + [libglnx_dep, libflatpak_dep],
+ install : false,
+)
+
+if gir_dep.found()
+ gnome.generate_gir(
+ libflatpak,
+ export_packages : 'flatpak',
+ extra_args : [
+ '-DFLATPAK_EXTERN=__attribute__((visibility("default"))) extern',
+ '-DFLATPAK_COMPILATION=1',
+ '--warn-all',
+ ],
+ header : 'flatpak.h',
+ identifier_prefix : 'Flatpak',
+ includes : ['GObject-2.0', 'Gio-2.0'],
+ install : true,
+ namespace : 'Flatpak',
+ nsversion : '1.0',
+ sources : [
+ enums,
+ flatpak_version_macros,
+ public_headers,
+ sources,
+ ],
+ symbol_prefix : 'flatpak',
+ )
+endif