/**
 * 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;
}
Ejemplo n.º 3
0
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;
}