/** * gs_plugin_refresh: */ gboolean gs_plugin_refresh (GsPlugin *plugin, guint cache_age, GsPluginRefreshFlags flags, GCancellable *cancellable, GError **error) { GsPluginData *priv = gs_plugin_get_data (plugin); guint i; g_autoptr(GPtrArray) xrefs = NULL; /* update AppStream metadata */ if (flags & GS_PLUGIN_REFRESH_FLAGS_METADATA) { if (!gs_plugin_refresh_appstream (plugin, cache_age, cancellable, error)) return FALSE; } /* no longer interesting */ if ((flags & GS_PLUGIN_REFRESH_FLAGS_PAYLOAD) == 0) return TRUE; /* get all the updates available from all remotes */ xrefs = xdg_app_installation_list_installed_refs_for_update (priv->installation, cancellable, error); if (xrefs == NULL) return FALSE; for (i = 0; i < xrefs->len; i++) { XdgAppInstalledRef *xref = g_ptr_array_index (xrefs, i); g_autoptr(GsApp) app = NULL; g_autoptr(XdgAppInstalledRef) xref2 = NULL; /* try to create a GsApp so we can do progress reporting */ app = gs_plugin_xdg_app_create_installed (plugin, xref, NULL); /* fetch but do not deploy */ g_debug ("pulling update for %s", xdg_app_ref_get_name (XDG_APP_REF (xref))); xref2 = xdg_app_installation_update (priv->installation, XDG_APP_UPDATE_FLAGS_NO_DEPLOY, xdg_app_ref_get_kind (XDG_APP_REF (xref)), xdg_app_ref_get_name (XDG_APP_REF (xref)), xdg_app_ref_get_arch (XDG_APP_REF (xref)), xdg_app_ref_get_branch (XDG_APP_REF (xref)), gs_plugin_xdg_app_progress_cb, app, cancellable, error); if (xref2 == NULL) return FALSE; } return TRUE; }
/** * gs_plugin_add_updates: */ gboolean gs_plugin_add_updates (GsPlugin *plugin, GList **list, GCancellable *cancellable, GError **error) { GsPluginData *priv = gs_plugin_get_data (plugin); guint i; g_autoptr(GPtrArray) xrefs = NULL; /* get all the installed apps (no network I/O) */ xrefs = xdg_app_installation_list_installed_refs (priv->installation, cancellable, error); if (xrefs == NULL) return FALSE; for (i = 0; i < xrefs->len; i++) { XdgAppInstalledRef *xref = g_ptr_array_index (xrefs, i); const gchar *commit; const gchar *latest_commit; g_autoptr(GsApp) app = NULL; g_autoptr(GError) error_local = NULL; /* check the application has already been downloaded */ commit = xdg_app_ref_get_commit (XDG_APP_REF (xref)); latest_commit = xdg_app_installed_ref_get_latest_commit (xref); if (g_strcmp0 (commit, latest_commit) == 0) { g_debug ("no downloaded update for %s", xdg_app_ref_get_name (XDG_APP_REF (xref))); continue; } /* we have an update to show */ g_debug ("%s has a downloaded update %s->%s", xdg_app_ref_get_name (XDG_APP_REF (xref)), commit, latest_commit); app = gs_plugin_xdg_app_create_installed (plugin, xref, &error_local); if (app == NULL) { g_warning ("failed to add xdg-app: %s", error_local->message); continue; } if (gs_app_get_state (app) == AS_APP_STATE_INSTALLED) gs_app_set_state (app, AS_APP_STATE_UNKNOWN); gs_app_set_state (app, AS_APP_STATE_UPDATABLE_LIVE); gs_app_list_add (list, app); } return TRUE; }
XdgApplication::XdgApplication(XdgAppInstalledRef *app_ref, SoftwareBackend *backend) : Application(backend) { m_state = Application::Installed; m_id = xdg_app_ref_get_name(XDG_APP_REF(app_ref)); m_branch = xdg_app_ref_get_branch(XDG_APP_REF(app_ref)); m_origin = xdg_app_installed_ref_get_origin(app_ref); m_name = m_id; m_arch = xdg_app_ref_get_arch(XDG_APP_REF(app_ref)); m_currentCommit = xdg_app_ref_get_commit(XDG_APP_REF(app_ref)); m_latestCommit = xdg_app_installed_ref_get_latest_commit(app_ref); if (m_branch.isEmpty()) m_branch = "master"; QString desktopId; switch (xdg_app_ref_get_kind(XDG_APP_REF(app_ref))) { case XDG_APP_REF_KIND_APP: m_type = Application::App; desktopId = m_name + ".desktop"; break; case XDG_APP_REF_KIND_RUNTIME: m_type = Application::Runtime; m_icon = QIcon::fromTheme("package-x-generic"); m_summary = "Framework for applications"; desktopId = m_name + ".runtime"; break; default: // TODO: Handle errors here! break; } QString deployDir = xdg_app_installed_ref_get_deploy_dir(app_ref); QString desktopPath = deployDir + "/files/share/applications"; QString appdataPath = deployDir + "/files/share/appdata"; Appstream::Store store; store.load(desktopPath); store.load(appdataPath); Appstream::Component component = store.componentById(desktopId); if (!component.isNull()) refineFromAppstream(component); }
/** * gs_plugin_refine_item_commit: */ static gboolean gs_plugin_refine_item_commit (GsPlugin *plugin, GsApp *app, GCancellable *cancellable, GError **error) { GsPluginData *priv = gs_plugin_get_data (plugin); g_autoptr(AsProfileTask) ptask = NULL; g_autoptr(XdgAppRemoteRef) xref_remote = NULL; if (gs_app_get_xdgapp_commit (app) != NULL) return TRUE; if (gs_app_get_origin (app) == NULL) { g_debug ("no origin got commit, so refining origin first"); if (!gs_plugin_refine_item_origin (plugin, app, cancellable, error)) return FALSE; } ptask = as_profile_start_literal (gs_plugin_get_profile (plugin), "xdg-app::fetch-remote-ref"); xref_remote = xdg_app_installation_fetch_remote_ref_sync (priv->installation, gs_app_get_origin (app), gs_app_get_xdgapp_kind (app), gs_app_get_xdgapp_name (app), gs_app_get_xdgapp_arch (app), gs_app_get_xdgapp_branch (app), cancellable, error); if (xref_remote == NULL) return FALSE; gs_app_set_xdgapp_commit (app, xdg_app_ref_get_commit (XDG_APP_REF (xref_remote))); return TRUE; }
/** * gs_plugin_xdg_app_set_metadata_installed: */ static void gs_plugin_xdg_app_set_metadata_installed (GsApp *app, XdgAppInstalledRef *xref) { guint64 mtime; guint64 size_installed; g_autofree gchar *metadata_fn = NULL; g_autoptr(GFile) file = NULL; g_autoptr(GFileInfo) info = NULL; /* for all types */ gs_plugin_xdg_app_set_metadata (app, XDG_APP_REF (xref)); /* get the last time the app was updated */ metadata_fn = g_build_filename (xdg_app_installed_ref_get_deploy_dir (xref), "..", "active", NULL); file = g_file_new_for_path (metadata_fn); info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, NULL); if (info != NULL) { mtime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED); gs_app_set_install_date (app, mtime); } /* this is faster than resolving */ gs_app_set_origin (app, xdg_app_installed_ref_get_origin (xref)); /* this is faster than xdg_app_installation_fetch_remote_size_sync() */ size_installed = xdg_app_installed_ref_get_installed_size (xref); if (size_installed != 0) gs_app_set_size_installed (app, size_installed); }
/** * gs_plugin_refine_item_state: */ static gboolean gs_plugin_refine_item_state (GsPlugin *plugin, GsApp *app, GCancellable *cancellable, GError **error) { GsPluginData *priv = gs_plugin_get_data (plugin); guint i; g_autoptr(GPtrArray) xrefs = NULL; g_autoptr(AsProfileTask) ptask = NULL; /* already found */ if (gs_app_get_state (app) != AS_APP_STATE_UNKNOWN) return TRUE; /* need broken out metadata */ if (!gs_plugin_refine_item_metadata (plugin, app, cancellable, error)) return FALSE; /* get apps and runtimes */ ptask = as_profile_start_literal (gs_plugin_get_profile (plugin), "xdg-app::refine-action"); xrefs = xdg_app_installation_list_installed_refs (priv->installation, cancellable, error); if (xrefs == NULL) return FALSE; for (i = 0; i < xrefs->len; i++) { XdgAppInstalledRef *xref = g_ptr_array_index (xrefs, i); /* check xref is app */ if (!gs_plugin_xdg_app_is_xref (app, XDG_APP_REF(xref))) continue; /* mark as installed */ g_debug ("marking %s as installed with xdg-app", gs_app_get_id (app)); gs_plugin_xdg_app_set_metadata_installed (app, xref); if (gs_app_get_state (app) == AS_APP_STATE_UNKNOWN) gs_app_set_state (app, AS_APP_STATE_INSTALLED); } /* anything not installed just check the remote is still present */ if (gs_app_get_state (app) == AS_APP_STATE_UNKNOWN && gs_app_get_origin (app) != NULL) { g_autoptr(XdgAppRemote) xremote = NULL; xremote = xdg_app_installation_get_remote_by_name (priv->installation, gs_app_get_origin (app), cancellable, NULL); if (xremote != NULL) { g_debug ("marking %s as available with xdg-app", gs_app_get_id (app)); gs_app_set_state (app, AS_APP_STATE_AVAILABLE); } } /* success */ return TRUE; }
/** * gs_plugin_xdg_app_create_installed: */ static GsApp * gs_plugin_xdg_app_create_installed (GsPlugin *plugin, XdgAppInstalledRef *xref, GError **error) { g_autofree gchar *id = NULL; g_autoptr(AsIcon) icon = NULL; g_autoptr(GsApp) app = NULL; g_return_val_if_fail (xref != NULL, NULL); /* * Only show the current application in GNOME Software * * You can have multiple versions/branches of a particular app-id * installed but only one of them is "current" where this means: * 1) the default to launch unless you specify a version * 2) The one that gets its exported files exported */ if (!xdg_app_installed_ref_get_is_current (xref) && xdg_app_ref_get_kind (XDG_APP_REF(xref)) == XDG_APP_REF_KIND_APP) { g_set_error (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_NOT_SUPPORTED, "%s not current, ignoring", xdg_app_ref_get_name (XDG_APP_REF (xref))); return NULL; } /* create new object */ id = gs_plugin_xdg_app_build_id (XDG_APP_REF (xref)); app = gs_app_new (id); gs_plugin_xdg_app_set_metadata_installed (app, xref); switch (xdg_app_ref_get_kind (XDG_APP_REF(xref))) { case XDG_APP_REF_KIND_APP: gs_app_set_kind (app, AS_APP_KIND_DESKTOP); break; case XDG_APP_REF_KIND_RUNTIME: gs_app_set_xdgapp_kind (app, XDG_APP_REF_KIND_RUNTIME); gs_app_set_kind (app, AS_APP_KIND_RUNTIME); gs_app_set_name (app, GS_APP_QUALITY_NORMAL, xdg_app_ref_get_name (XDG_APP_REF (xref))); gs_app_set_summary (app, GS_APP_QUALITY_NORMAL, "Framework for applications"); gs_app_set_version (app, xdg_app_ref_get_branch (XDG_APP_REF (xref))); icon = as_icon_new (); as_icon_set_kind (icon, AS_ICON_KIND_STOCK); as_icon_set_name (icon, "system-run-symbolic"); gs_app_set_icon (app, icon); break; default: g_set_error_literal (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_NOT_SUPPORTED, "XdgAppRefKind not known"); return NULL; } return g_object_ref (app); }
/** * gs_plugin_add_installed: */ gboolean gs_plugin_add_installed (GsPlugin *plugin, GList **list, GCancellable *cancellable, GError **error) { GsPluginData *priv = gs_plugin_get_data (plugin); g_autoptr(GError) error_md = NULL; g_autoptr(GPtrArray) xrefs = NULL; guint i; /* if we've never ever run before, get the AppStream data */ if (!gs_plugin_refresh_appstream (plugin, G_MAXUINT, cancellable, &error_md)) { g_warning ("failed to get initial available data: %s", error_md->message); } /* get apps and runtimes */ xrefs = xdg_app_installation_list_installed_refs (priv->installation, cancellable, error); if (xrefs == NULL) return FALSE; for (i = 0; i < xrefs->len; i++) { XdgAppInstalledRef *xref = g_ptr_array_index (xrefs, i); g_autoptr(GError) error_local = NULL; g_autoptr(GsApp) app = NULL; /* only apps */ if (xdg_app_ref_get_kind (XDG_APP_REF (xref)) != XDG_APP_REF_KIND_APP) continue; app = gs_plugin_xdg_app_create_installed (plugin, xref, &error_local); if (app == NULL) { g_warning ("failed to add xdg-app: %s", error_local->message); continue; } gs_app_set_state (app, AS_APP_STATE_INSTALLED); gs_app_list_add (list, app); } return TRUE; }
/** * gs_plugin_file_to_app: */ gboolean gs_plugin_file_to_app (GsPlugin *plugin, GList **list, GFile *file, GCancellable *cancellable, GError **error) { g_autofree gchar *content_type = NULL; g_autofree gchar *id_prefixed = NULL; g_autoptr(GBytes) appstream_gz = NULL; g_autoptr(GBytes) icon_data = NULL; g_autoptr(GBytes) metadata = NULL; g_autoptr(GsApp) app = NULL; g_autoptr(XdgAppBundleRef) xref_bundle = NULL; const gchar *mimetypes[] = { "application/vnd.xdgapp", NULL }; /* does this match any of the mimetypes we support */ content_type = gs_utils_get_content_type (file, cancellable, error); if (content_type == NULL) return FALSE; if (!g_strv_contains (mimetypes, content_type)) return TRUE; /* load bundle */ xref_bundle = xdg_app_bundle_ref_new (file, error); if (xref_bundle == NULL) { g_prefix_error (error, "error loading bundle: "); return FALSE; } /* create a virtual ID */ id_prefixed = gs_plugin_xdg_app_build_id (XDG_APP_REF (xref_bundle)); /* load metadata */ app = gs_app_new (id_prefixed); gs_app_set_kind (app, AS_APP_KIND_DESKTOP); gs_app_set_state (app, AS_APP_STATE_AVAILABLE_LOCAL); gs_app_set_size_installed (app, xdg_app_bundle_ref_get_installed_size (xref_bundle)); gs_plugin_xdg_app_set_metadata (app, XDG_APP_REF (xref_bundle)); metadata = xdg_app_bundle_ref_get_metadata (xref_bundle); if (!gs_plugin_xdg_app_set_app_metadata (app, g_bytes_get_data (metadata, NULL), g_bytes_get_size (metadata), error)) return FALSE; /* load AppStream */ appstream_gz = xdg_app_bundle_ref_get_appstream (xref_bundle); if (appstream_gz != NULL) { g_autoptr(GZlibDecompressor) decompressor = NULL; g_autoptr(GInputStream) stream_gz = NULL; g_autoptr(GInputStream) stream_data = NULL; g_autoptr(GBytes) appstream = NULL; g_autoptr(AsStore) store = NULL; g_autofree gchar *id = NULL; AsApp *item; /* decompress data */ decompressor = g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP); stream_gz = g_memory_input_stream_new_from_bytes (appstream_gz); if (stream_gz == NULL) return FALSE; stream_data = g_converter_input_stream_new (stream_gz, G_CONVERTER (decompressor)); appstream = g_input_stream_read_bytes (stream_data, 0x100000, /* 1Mb */ cancellable, error); if (appstream == NULL) return FALSE; store = as_store_new (); if (!as_store_from_bytes (store, appstream, cancellable, error)) return FALSE; /* find app */ id = g_strdup_printf ("%s.desktop", gs_app_get_xdgapp_name (app)); item = as_store_get_app_by_id (store, id); if (item == NULL) { g_set_error (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_FAILED, "application %s not found", id); return FALSE; } /* copy details from AppStream to app */ if (!gs_appstream_refine_app (plugin, app, item, error)) return FALSE; } /* load icon */ icon_data = xdg_app_bundle_ref_get_icon (xref_bundle, 64 * gs_plugin_get_scale (plugin)); if (icon_data == NULL) icon_data = xdg_app_bundle_ref_get_icon (xref_bundle, 64); if (icon_data != NULL) { g_autoptr(GInputStream) stream_icon = NULL; g_autoptr(GdkPixbuf) pixbuf = NULL; stream_icon = g_memory_input_stream_new_from_bytes (icon_data); pixbuf = gdk_pixbuf_new_from_stream (stream_icon, cancellable, error); if (pixbuf == NULL) return FALSE; gs_app_set_pixbuf (app, pixbuf); } else { g_autoptr(AsIcon) icon = NULL; icon = as_icon_new (); as_icon_set_kind (icon, AS_ICON_KIND_STOCK); as_icon_set_name (icon, "application-x-executable"); gs_app_set_icon (app, icon); } /* not quite true: this just means we can update this specific app */ if (xdg_app_bundle_ref_get_origin (xref_bundle)) gs_app_add_quirk (app, AS_APP_QUIRK_HAS_SOURCE); g_debug ("created local app: %s", gs_app_to_string (app)); gs_app_list_add (list, app); return TRUE; }