From bb6e1d535fca3ed3c8ff6e94e00342dc476b80a3 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Fri, 2 Dec 2016 16:30:15 +0100 Subject: [PATCH] install: Support RuntimeRepo= key in flatpakref files This allows an app to point to the repo where the runtime it uses comes from, and the user will be asked to add that if its not already configured. --- app/flatpak-builtins-add-remote.c | 7 --- app/flatpak-builtins-install.c | 93 ++++++++++++++++++++++++++++++ common/flatpak-dir.c | 95 +++++++++++++++++++++++++++++++ common/flatpak-dir.h | 17 ++++++ 4 files changed, 205 insertions(+), 7 deletions(-) diff --git a/app/flatpak-builtins-add-remote.c b/app/flatpak-builtins-add-remote.c index f1b6f7b9..8a7afaf1 100644 --- a/app/flatpak-builtins-add-remote.c +++ b/app/flatpak-builtins-add-remote.c @@ -36,13 +36,6 @@ #include "flatpak-utils.h" #include "flatpak-chain-input-stream.h" -#define FLATPAK_REPO_GROUP "Flatpak Repo" -#define FLATPAK_REPO_URL_KEY "Url" -#define FLATPAK_REPO_TITLE_KEY "Title" -#define FLATPAK_REPO_DEFAULT_BRANCH_KEY "DefaultBranch" -#define FLATPAK_REPO_GPGKEY_KEY "GPGKey" -#define FLATPAK_REPO_NODEPS_KEY "NoDeps" - static gboolean opt_no_gpg_verify; static gboolean opt_do_gpg_verify; static gboolean opt_do_enumerate; diff --git a/app/flatpak-builtins-install.c b/app/flatpak-builtins-install.c index 744a9f0e..81426dca 100644 --- a/app/flatpak-builtins-install.c +++ b/app/flatpak-builtins-install.c @@ -144,6 +144,96 @@ install_bundle (FlatpakDir *dir, return TRUE; } +static gboolean +handle_runtime_repo_deps (FlatpakDir *dir, GBytes *data, GError **error) +{ + g_autoptr(GKeyFile) keyfile = g_key_file_new (); + g_autoptr(GKeyFile) dep_keyfile = g_key_file_new (); + g_autofree char *dep_url = NULL; + g_autoptr(GBytes) dep_data = NULL; + g_autofree char *runtime_url = NULL; + g_autofree char *old_remote = NULL; + g_autofree char *new_remote = NULL; + g_autofree char *basename = NULL; + g_autoptr(SoupURI) uri = NULL; + g_auto(GStrv) remotes = NULL; + g_autoptr(GKeyFile) config = NULL; + g_autoptr(GBytes) gpg_key = NULL; + g_autofree char *group = NULL; + char *t; + int i; + + if (!g_key_file_load_from_data (keyfile, g_bytes_get_data (data, NULL), g_bytes_get_size (data), + 0, error)) + return FALSE; + + dep_url = g_key_file_get_string (keyfile, FLATPAK_REF_GROUP, + FLATPAK_REF_RUNTIME_REPO_KEY, NULL); + if (dep_url == NULL) + return TRUE; + + dep_data = download_uri (dep_url, error); + if (dep_data == NULL) + { + g_prefix_error (error, "Can't load dependent file %s", dep_url); + return FALSE; + } + + uri = soup_uri_new (dep_url); + basename = g_path_get_basename (soup_uri_get_path (uri)); + /* Strip suffix */ + t = strchr (basename, '.'); + if (t != NULL) + *t = 0; + + /* Find a free remote name */ + remotes = flatpak_dir_list_remotes (dir, NULL, NULL); + i = 0; + do + { + g_clear_pointer (&new_remote, g_free); + + if (i == 0) + new_remote = g_strdup (basename); + else + new_remote = g_strdup_printf ("%s-%d", basename, i); + i++; + } + while (remotes != NULL && g_strv_contains ((const char * const*)remotes, new_remote)); + + config = flatpak_dir_parse_repofile (dir, new_remote, dep_data, &gpg_key, NULL, error); + if (config == NULL) + { + g_prefix_error (error, "Can't parse dependent file %s", dep_url); + return FALSE; + } + + /* See if it already exists */ + group = g_strdup_printf ("remote \"%s\"", new_remote); + runtime_url = g_key_file_get_string (config, group, "url", NULL); + g_assert (runtime_url != NULL); + + old_remote = flatpak_dir_find_remote_by_uri (dir, runtime_url); + if (old_remote == NULL && flatpak_dir_is_user (dir)) + { + g_autoptr(FlatpakDir) system_dir = flatpak_dir_get_system (); + old_remote = flatpak_dir_find_remote_by_uri (system_dir, runtime_url); + } + + if (old_remote != NULL) + return TRUE; + + if (flatpak_yes_no_prompt (_("This application depends on runtimes from:\n %s\nConfigure this as new remote '%s'"), + runtime_url, new_remote)) + { + if (!flatpak_dir_modify_remote (dir, new_remote, config, gpg_key, NULL, error)) + return FALSE; + if (!flatpak_dir_recreate_repo (dir, NULL, error)) + return FALSE; + } + + return TRUE; +} static gboolean install_from (FlatpakDir *dir, @@ -194,6 +284,9 @@ install_from (FlatpakDir *dir, file_data = g_bytes_new_take (g_steal_pointer (&data), data_len); } + if (!handle_runtime_repo_deps (dir, file_data, error)) + return FALSE; + if (!flatpak_dir_create_remote_for_ref_file (dir, file_data, &remote, &ref, error)) return FALSE; diff --git a/common/flatpak-dir.c b/common/flatpak-dir.c index c09f2e5e..9a843134 100644 --- a/common/flatpak-dir.c +++ b/common/flatpak-dir.c @@ -897,6 +897,16 @@ flatpak_dir_ensure_path (FlatpakDir *self, return flatpak_mkdir_p (self->basedir, cancellable, error); } +/* Warning: This is not threadsafe, don't use in libflatpak */ +gboolean +flatpak_dir_recreate_repo (FlatpakDir *self, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(OstreeRepo) old_repo = g_steal_pointer (&self->repo); + return flatpak_dir_ensure_repo (self, cancellable, error); +} + gboolean flatpak_dir_ensure_repo (FlatpakDir *self, GCancellable *cancellable, @@ -5643,6 +5653,91 @@ flatpak_dir_create_origin_remote (FlatpakDir *self, return g_steal_pointer (&remote); } +GKeyFile * +flatpak_dir_parse_repofile (FlatpakDir *self, + const char *remote_name, + GBytes *data, + GBytes **gpg_data_out, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(GKeyFile) keyfile = g_key_file_new (); + g_autofree char *remote = NULL; + g_autoptr(GError) local_error = NULL; + g_autoptr(GBytes) gpg_data = NULL; + g_autofree char *uri = NULL; + g_autofree char *title = NULL; + g_autofree char *gpg_key = NULL; + g_autofree char *default_branch = NULL; + gboolean nodeps; + GKeyFile *config = g_key_file_new (); + g_autofree char *group = g_strdup_printf ("remote \"%s\"", remote_name); + + if (!g_key_file_load_from_data (keyfile, + g_bytes_get_data (data, NULL), + g_bytes_get_size (data), + 0, &local_error)) + { + flatpak_fail (error, "Invalid .flatpakref: %s\n", local_error->message); + return NULL; + } + + if (!g_key_file_has_group (keyfile, FLATPAK_REPO_GROUP)) + { + flatpak_fail (error, "Invalid .flatpakref\n"); + return NULL; + } + + uri = g_key_file_get_string (keyfile, FLATPAK_REPO_GROUP, + FLATPAK_REPO_URL_KEY, NULL); + if (uri == NULL) + { + flatpak_fail (error, "Invalid .flatpakref\n"); + return NULL; + } + + g_key_file_set_string (config, group, "url", uri); + + title = g_key_file_get_locale_string (keyfile, FLATPAK_REPO_GROUP, + FLATPAK_REPO_TITLE_KEY, NULL, NULL); + if (title != NULL) + g_key_file_set_string (config, group, "xa.title", title); + + default_branch = g_key_file_get_locale_string (keyfile, FLATPAK_REPO_GROUP, + FLATPAK_REPO_DEFAULT_BRANCH_KEY, NULL, NULL); + if (default_branch != NULL) + g_key_file_set_string (config, group, "xa.default-branch", default_branch); + + nodeps = g_key_file_get_boolean (keyfile, FLATPAK_REPO_GROUP, + FLATPAK_REPO_NODEPS_KEY, NULL); + if (nodeps) + g_key_file_set_boolean (config, group, "xa.nodeps", TRUE); + + gpg_key = g_key_file_get_string (keyfile, FLATPAK_REPO_GROUP, + FLATPAK_REPO_GPGKEY_KEY, NULL); + if (gpg_key != NULL) + { + guchar *decoded; + gsize decoded_len; + + gpg_key = g_strstrip (gpg_key); + decoded = g_base64_decode (gpg_key, &decoded_len); + if (decoded_len < 10) /* Check some minimal size so we don't get crap */ + { + flatpak_fail (error, "Invalid gpg key\n"); + return NULL; + } + + gpg_data = g_bytes_new_take (decoded, decoded_len); + g_key_file_set_boolean (config, group, "gpg-verify", TRUE); + g_key_file_set_boolean (config, group, "gpg-verify-summary", TRUE); + } + + *gpg_data_out = g_steal_pointer (&gpg_data); + + return g_steal_pointer (&config); +} + static gboolean parse_ref_file (GBytes *data, char **name_out, diff --git a/common/flatpak-dir.h b/common/flatpak-dir.h index e663de31..745f4ea4 100644 --- a/common/flatpak-dir.h +++ b/common/flatpak-dir.h @@ -39,12 +39,20 @@ GType flatpak_deploy_get_type (void); #define FLATPAK_REF_GROUP "Flatpak Ref" #define FLATPAK_REF_URL_KEY "Url" +#define FLATPAK_REF_RUNTIME_REPO_KEY "RuntimeRepo" #define FLATPAK_REF_TITLE_KEY "Title" #define FLATPAK_REF_GPGKEY_KEY "GPGKey" #define FLATPAK_REF_IS_RUNTIME_KEY "IsRuntime" #define FLATPAK_REF_NAME_KEY "Name" #define FLATPAK_REF_BRANCH_KEY "Branch" +#define FLATPAK_REPO_GROUP "Flatpak Repo" +#define FLATPAK_REPO_URL_KEY "Url" +#define FLATPAK_REPO_TITLE_KEY "Title" +#define FLATPAK_REPO_DEFAULT_BRANCH_KEY "DefaultBranch" +#define FLATPAK_REPO_GPGKEY_KEY "GPGKey" +#define FLATPAK_REPO_NODEPS_KEY "NoDeps" + typedef struct { char *ref; @@ -223,6 +231,9 @@ gboolean flatpak_dir_ensure_path (FlatpakDir *self, gboolean flatpak_dir_use_child_repo (FlatpakDir *self); gboolean flatpak_dir_ensure_system_child_repo (FlatpakDir *self, GError **error); +gboolean flatpak_dir_recreate_repo (FlatpakDir *self, + GCancellable *cancellable, + GError **error); gboolean flatpak_dir_ensure_repo (FlatpakDir *self, GCancellable *cancellable, GError **error); @@ -423,6 +434,12 @@ gboolean flatpak_dir_create_remote_for_ref_file (FlatpakDir *self, char **remote_name_out, char **ref_out, GError **error); +GKeyFile * flatpak_dir_parse_repofile (FlatpakDir *self, + const char *remote_name, + GBytes *data, + GBytes **gpg_data_out, + GCancellable *cancellable, + GError **error); char *flatpak_dir_find_remote_by_uri (FlatpakDir *self, const char *uri);