mirror of
https://github.com/flatpak/flatpak.git
synced 2026-05-11 17:36:44 -04:00
dir: Rewrite dynamic launcher entries on deploy
Fixes https://github.com/flatpak/flatpak/issues/4703
This commit is contained in:
committed by
Phaedrus Leeds
parent
c3ca2c7010
commit
ecafded3b1
@@ -8909,6 +8909,195 @@ flatpak_dir_deploy_update (FlatpakDir *self,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
rewrite_one_dynamic_launcher (const char *portal_desktop_dir,
|
||||
const char *portal_icon_dir,
|
||||
const char *desktop_name,
|
||||
const char *old_app_id,
|
||||
const char *new_app_id)
|
||||
{
|
||||
g_autoptr(GKeyFile) old_key_file = NULL;
|
||||
g_autoptr(GKeyFile) new_key_file = NULL;
|
||||
g_autoptr(GFile) link_file = NULL;
|
||||
g_autoptr(GFile) new_link_file = NULL;
|
||||
g_autoptr(GString) data_string = NULL;
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
g_autofree char *old_data = NULL;
|
||||
g_autofree char *desktop_path = NULL;
|
||||
g_autofree char *new_desktop_path = NULL;
|
||||
g_autofree char *icon_path = NULL;
|
||||
g_autofree char *relative_path = NULL;
|
||||
g_autofree char *new_desktop = NULL;
|
||||
const gchar *desktop_suffix;
|
||||
|
||||
g_assert (g_str_has_suffix (desktop_name, ".desktop"));
|
||||
g_assert (g_str_has_prefix (desktop_name, old_app_id));
|
||||
|
||||
desktop_path = g_build_filename (portal_desktop_dir,
|
||||
desktop_name, NULL);
|
||||
old_key_file = g_key_file_new ();
|
||||
if (!g_key_file_load_from_file (old_key_file, desktop_path,
|
||||
G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS,
|
||||
&local_error))
|
||||
{
|
||||
g_warning ("Error encountered loading key file %s: %s", desktop_path, local_error->message);
|
||||
return;
|
||||
}
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Fix paths in desktop file with a find-and-replace. The portal handled
|
||||
* quoting the app ID in the Exec line for us.
|
||||
*/
|
||||
old_data = g_key_file_to_data (old_key_file, NULL, NULL);
|
||||
data_string = g_string_new ((const char *)old_data);
|
||||
g_string_replace (data_string, old_app_id, new_app_id, 0);
|
||||
new_key_file = g_key_file_new ();
|
||||
if (!g_key_file_load_from_data (new_key_file, data_string->str, -1,
|
||||
G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS,
|
||||
&local_error))
|
||||
{
|
||||
g_warning ("Cannot load desktop file %s after rewrite: %s", desktop_path, local_error->message);
|
||||
g_warning ("Key file contents:\n%s\n", (const char *)data_string->str);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Write it out at the new path */
|
||||
desktop_suffix = desktop_name + strlen (old_app_id);
|
||||
new_desktop = g_strconcat (new_app_id, desktop_suffix, NULL);
|
||||
new_desktop_path = g_build_filename (portal_desktop_dir, new_desktop, NULL);
|
||||
if (!g_key_file_save_to_file (new_key_file, new_desktop_path, &local_error))
|
||||
{
|
||||
g_warning ("Couldn't rewrite desktop file from %s to %s: %s",
|
||||
desktop_path, new_desktop_path, local_error->message);
|
||||
return;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
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))
|
||||
{
|
||||
g_warning ("Unable to rename desktop file link %s -> %s: %s",
|
||||
desktop_name, new_desktop, local_error->message);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Delete the old desktop file */
|
||||
unlink (desktop_path);
|
||||
|
||||
/* And rename the icon */
|
||||
icon_path = g_key_file_get_string (old_key_file, G_KEY_FILE_DESKTOP_GROUP, "Icon", NULL);
|
||||
if (g_str_has_prefix (icon_path, portal_icon_dir))
|
||||
{
|
||||
g_autoptr(GFile) icon_file = NULL;
|
||||
g_autofree char *icon_basename = NULL;
|
||||
g_autofree char *new_icon = NULL;
|
||||
gchar *icon_suffix;
|
||||
|
||||
icon_file = g_file_new_for_path (icon_path);
|
||||
icon_basename = g_path_get_basename (icon_path);
|
||||
if (g_str_has_prefix (icon_basename, old_app_id))
|
||||
{
|
||||
icon_suffix = icon_basename + strlen (old_app_id);
|
||||
new_icon = g_strconcat (new_app_id, icon_suffix, NULL);
|
||||
if (!g_file_set_display_name (icon_file, new_icon, NULL, &local_error))
|
||||
{
|
||||
g_warning ("Unable to rename icon file %s -> %s: %s", icon_basename, new_icon,
|
||||
local_error->message);
|
||||
g_clear_error (&local_error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
rewrite_dynamic_launchers (FlatpakDecomposed *ref,
|
||||
const char * const * previous_ids)
|
||||
|
||||
{
|
||||
g_autoptr(GFile) portal_desktop_dir = NULL;
|
||||
g_autofree char *portal_icon_path = NULL;
|
||||
g_autofree char *app_id = NULL;
|
||||
g_autoptr(GFileEnumerator) dir_enum = NULL;
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
|
||||
if (!flatpak_decomposed_is_app (ref))
|
||||
return;
|
||||
|
||||
app_id = flatpak_decomposed_dup_id (ref);
|
||||
|
||||
/* Rename any dynamic launchers written by xdg-desktop-portal. The
|
||||
* portal has its own code for renaming launchers on session start but we
|
||||
* need to do it here as well so the launchers are correct in both cases:
|
||||
* (1) the app rename transaction is being executed by the same user that
|
||||
* has the launchers, or (2) the app is installed system-wide and another
|
||||
* user has launchers.
|
||||
*/
|
||||
if (previous_ids != NULL)
|
||||
{
|
||||
portal_desktop_dir = g_file_new_build_filename (g_get_user_data_dir (),
|
||||
"xdg-desktop-portal",
|
||||
"applications", NULL);
|
||||
portal_icon_path = g_build_filename (g_get_user_data_dir (),
|
||||
"xdg-desktop-portal", "icons", NULL);
|
||||
dir_enum = g_file_enumerate_children (portal_desktop_dir,
|
||||
G_FILE_ATTRIBUTE_STANDARD_NAME,
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
||||
NULL, &local_error);
|
||||
}
|
||||
if (dir_enum == NULL)
|
||||
{
|
||||
if (local_error && !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
|
||||
{
|
||||
g_warning ("Failed to enumerate portal desktop dir %s: %s",
|
||||
flatpak_file_get_path_cached (portal_desktop_dir),
|
||||
local_error->message);
|
||||
}
|
||||
g_clear_error (&local_error);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_autoptr(GFileInfo) child_info = NULL;
|
||||
g_auto(GStrv) previous_ids_sorted = NULL;
|
||||
|
||||
/* Sort by decreasing length so we get the longest prefix below */
|
||||
previous_ids_sorted = flatpak_strv_sort_by_length (previous_ids);
|
||||
|
||||
while ((child_info = g_file_enumerator_next_file (dir_enum, NULL, &local_error)) != NULL)
|
||||
{
|
||||
const char *desktop_name;
|
||||
int i;
|
||||
|
||||
desktop_name = g_file_info_get_name (child_info);
|
||||
if (!g_str_has_suffix (desktop_name, ".desktop"))
|
||||
continue;
|
||||
|
||||
for (i = 0; previous_ids_sorted[i] != NULL; i++)
|
||||
{
|
||||
if (g_str_has_prefix (desktop_name, previous_ids_sorted[i]))
|
||||
{
|
||||
rewrite_one_dynamic_launcher (flatpak_file_get_path_cached (portal_desktop_dir),
|
||||
portal_icon_path, desktop_name,
|
||||
previous_ids_sorted[i], app_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (local_error)
|
||||
{
|
||||
g_warning ("Failed to enumerate portal desktop dir %s: %s",
|
||||
flatpak_file_get_path_cached (portal_desktop_dir),
|
||||
local_error->message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static FlatpakOciRegistry *
|
||||
flatpak_dir_create_system_child_oci_registry (FlatpakDir *self,
|
||||
GLnxLockFile *file_lock,
|
||||
@@ -9447,6 +9636,13 @@ flatpak_dir_install (FlatpakDir *self,
|
||||
if (child_repo_path && !is_revokefs_pull)
|
||||
(void) glnx_shutil_rm_rf_at (AT_FDCWD, child_repo_path, NULL, NULL);
|
||||
|
||||
/* In case the app is being renamed, rewrite any launchers made by
|
||||
* xdg-desktop-portal. This has to be done as the user so can't be in the
|
||||
* system helper.
|
||||
*/
|
||||
if (opt_previous_ids)
|
||||
rewrite_dynamic_launchers (ref, opt_previous_ids);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -9464,6 +9660,12 @@ flatpak_dir_install (FlatpakDir *self,
|
||||
opt_previous_ids, reinstall, pin_on_deploy,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
/* In case the app is being renamed, rewrite any launchers made by
|
||||
* xdg-desktop-portal.
|
||||
*/
|
||||
if (opt_previous_ids)
|
||||
rewrite_dynamic_launchers (ref, opt_previous_ids);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
@@ -10124,6 +10326,13 @@ flatpak_dir_update (FlatpakDir *self,
|
||||
if (child_repo_path && !is_revokefs_pull)
|
||||
(void) glnx_shutil_rm_rf_at (AT_FDCWD, child_repo_path, NULL, NULL);
|
||||
|
||||
/* In case the app is being renamed, rewrite any launchers made by
|
||||
* xdg-desktop-portal. This has to be done as the user so can't be in the
|
||||
* system helper.
|
||||
*/
|
||||
if (opt_previous_ids)
|
||||
rewrite_dynamic_launchers (ref, opt_previous_ids);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -10153,6 +10362,12 @@ flatpak_dir_update (FlatpakDir *self,
|
||||
subpaths, opt_previous_ids,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
/* In case the app is being renamed, rewrite any launchers made by
|
||||
* xdg-desktop-portal.
|
||||
*/
|
||||
if (opt_previous_ids)
|
||||
rewrite_dynamic_launchers (ref, opt_previous_ids);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
@@ -133,6 +133,7 @@ gboolean flatpak_extension_matches_reason (const char *extension_id,
|
||||
|
||||
const char * flatpak_get_bwrap (void);
|
||||
|
||||
char **flatpak_strv_sort_by_length (const char * const *strv);
|
||||
char **flatpak_strv_merge (char **strv1,
|
||||
char **strv2);
|
||||
char **flatpak_subpaths_merge (char **subpaths1,
|
||||
@@ -358,6 +359,13 @@ g_hash_table_steal_extended (GHashTable *hash_table,
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !GLIB_CHECK_VERSION (2, 68, 0)
|
||||
guint g_string_replace (GString *string,
|
||||
const gchar *find,
|
||||
const gchar *replace,
|
||||
guint limit);
|
||||
#endif
|
||||
|
||||
gboolean flatpak_g_ptr_array_contains_string (GPtrArray *array,
|
||||
const char *str);
|
||||
|
||||
|
||||
@@ -7720,6 +7720,35 @@ flatpak_format_choices (const char **choices,
|
||||
g_print ("\n");
|
||||
}
|
||||
|
||||
static gint
|
||||
string_length_compare_func (gconstpointer a,
|
||||
gconstpointer b)
|
||||
{
|
||||
return strlen (*(char * const *) a) - strlen (*(char * const *) b);
|
||||
}
|
||||
|
||||
/* Sort a string array by decreasing length */
|
||||
char **
|
||||
flatpak_strv_sort_by_length (const char * const *strv)
|
||||
{
|
||||
GPtrArray *array;
|
||||
int i;
|
||||
|
||||
if (strv == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Combine both */
|
||||
array = g_ptr_array_new ();
|
||||
|
||||
for (i = 0; strv[i] != NULL; i++)
|
||||
g_ptr_array_add (array, g_strdup (strv[i]));
|
||||
|
||||
g_ptr_array_sort (array, string_length_compare_func);
|
||||
|
||||
g_ptr_array_add (array, NULL);
|
||||
return (char **) g_ptr_array_free (array, FALSE);
|
||||
}
|
||||
|
||||
char **
|
||||
flatpak_strv_merge (char **strv1,
|
||||
char **strv2)
|
||||
@@ -9164,3 +9193,48 @@ running_under_sudo (void)
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#if !GLIB_CHECK_VERSION (2, 68, 0)
|
||||
/* All this code is backported directly from glib */
|
||||
guint
|
||||
g_string_replace (GString *string,
|
||||
const gchar *find,
|
||||
const gchar *replace,
|
||||
guint limit)
|
||||
{
|
||||
gsize f_len, r_len, pos;
|
||||
gchar *cur, *next;
|
||||
guint n = 0;
|
||||
|
||||
g_return_val_if_fail (string != NULL, 0);
|
||||
g_return_val_if_fail (find != NULL, 0);
|
||||
g_return_val_if_fail (replace != NULL, 0);
|
||||
|
||||
f_len = strlen (find);
|
||||
r_len = strlen (replace);
|
||||
cur = string->str;
|
||||
|
||||
while ((next = strstr (cur, find)) != NULL)
|
||||
{
|
||||
pos = next - string->str;
|
||||
g_string_erase (string, pos, f_len);
|
||||
g_string_insert (string, pos, replace);
|
||||
cur = string->str + pos + r_len;
|
||||
n++;
|
||||
/* Only match the empty string once at any given position, to
|
||||
* avoid infinite loops */
|
||||
if (f_len == 0)
|
||||
{
|
||||
if (cur[0] == '\0')
|
||||
break;
|
||||
else
|
||||
cur++;
|
||||
}
|
||||
if (n == limit)
|
||||
break;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
#endif /* GLIB_CHECK_VERSION (2, 68, 0) */
|
||||
|
||||
Reference in New Issue
Block a user