/** * asb_plugin_process_app: */ gboolean asb_plugin_process_app (AsbPlugin *plugin, AsbPackage *pkg, AsbApp *app, const gchar *tmpdir, GError **error) { gchar **filelist; guint i; filelist = asb_package_get_filelist (pkg); for (i = 0; filelist[i] != NULL; i++) { GError *error_local = NULL; g_autofree gchar *filename = NULL; if (!asb_plugin_match_glob ("/usr/bin/*", filelist[i])) continue; if (as_app_has_kudo_kind (AS_APP (app), AS_KUDO_KIND_APP_MENU)) break; filename = g_build_filename (tmpdir, filelist[i], NULL); if (!asb_plugin_gresource_app (app, filename, &error_local)) { asb_package_log (pkg, ASB_PACKAGE_LOG_LEVEL_WARNING, "Failed to get resources from %s: %s", filename, error_local->message); g_clear_error (&error_local); } } return TRUE; }
/** * asb_plugin_process_app: */ gboolean asb_plugin_process_app (AsbPlugin *plugin, AsbPackage *pkg, AsbApp *app, const gchar *tmpdir, GError **error) { gchar **filelist; guint i; /* look for a krunner provider */ filelist = asb_package_get_filelist (pkg); for (i = 0; filelist[i] != NULL; i++) { g_autoptr(GError) error_local = NULL; g_autofree gchar *filename = NULL; if (!asb_plugin_match_glob ("/metadata", filelist[i])) continue; filename = g_build_filename (tmpdir, filelist[i], NULL); if (!asb_plugin_process_filename (filename, app, &error_local)) { asb_package_log (pkg, ASB_PACKAGE_LOG_LEVEL_INFO, "Failed to read ostree metadata file %s: %s", filelist[i], error_local->message); g_clear_error (&error_local); } } return TRUE; }
static gboolean asb_plugin_gresource_app (AsbApp *app, const gchar *filename, GError **error) { gboolean ret; g_autofree gchar *data_err = NULL; g_autofree gchar *data_out = NULL; const gchar *argv[] = { "/usr/bin/gresource", "list", filename, NULL }; ret = g_spawn_sync (NULL, (gchar **) argv, NULL, #if GLIB_CHECK_VERSION(2,40,0) G_SPAWN_CLOEXEC_PIPES, #else G_SPAWN_DEFAULT, #endif NULL, NULL, &data_out, &data_err, NULL, error); if (!ret) return FALSE; if (g_strstr_len (data_out, -1, "gtk/menus.ui") != NULL) { asb_package_log (asb_app_get_package (app), ASB_PACKAGE_LOG_LEVEL_DEBUG, "Auto-adding kudo AppMenu for %s", as_app_get_id (AS_APP (app))); as_app_add_kudo_kind (AS_APP (app), AS_KUDO_KIND_APP_MENU); } return TRUE; }
/** * asb_plugin_process_app: */ gboolean asb_plugin_process_app (AsbPlugin *plugin, AsbPackage *pkg, AsbApp *app, const gchar *tmpdir, GError **error) { gchar **filelist; guint i; /* already set */ if (as_app_has_kudo_kind (AS_APP (app), AS_KUDO_KIND_NOTIFICATIONS)) return TRUE; /* look for a shell search provider */ filelist = asb_package_get_filelist (pkg); for (i = 0; filelist[i] != NULL; i++) { if (!asb_plugin_match_glob ("/usr/share/kde4/apps/*/*.notifyrc", filelist[i])) continue; asb_package_log (pkg, ASB_PACKAGE_LOG_LEVEL_DEBUG, "Auto-adding kudo Notifications for %s", as_app_get_id (AS_APP (app))); as_app_add_kudo_kind (AS_APP (app), AS_KUDO_KIND_NOTIFICATIONS); break; } return TRUE; }
/** * asb_context_write_app_xml: **/ static void asb_context_write_app_xml (AsbContext *ctx) { AsbApp *app; AsbContextPrivate *priv = GET_PRIVATE (ctx); GList *l; /* log the XML in the log file */ for (l = priv->apps; l != NULL; l = l->next) { g_autoptr(GString) xml = NULL; g_autoptr(AsStore) store = NULL; /* we have an open log file? */ if (!ASB_IS_APP (l->data)) continue; /* just log raw XML */ app = ASB_APP (l->data); store = as_store_new (); as_store_set_api_version (store, 1.0f); as_store_add_app (store, AS_APP (app)); xml = as_store_to_xml (store, AS_NODE_TO_XML_FLAG_FORMAT_INDENT | AS_NODE_TO_XML_FLAG_FORMAT_MULTILINE); asb_package_log (asb_app_get_package (app), ASB_PACKAGE_LOG_LEVEL_NONE, "%s", xml->str); } }
/** * asb_plugin_process_app: */ gboolean asb_plugin_process_app (AsbPlugin *plugin, AsbPackage *pkg, AsbApp *app, const gchar *tmpdir, GError **error) { gchar **filelist; guint i; filelist = asb_package_get_filelist (pkg); for (i = 0; filelist[i] != NULL; i++) { GError *error_local = NULL; _cleanup_free_ gchar *filename = NULL; if (!g_str_has_prefix (filelist[i], "/usr/bin/")) continue; if (as_app_get_metadata_item (AS_APP (app), "X-Kudo-UsesAppMenu") != NULL) break; filename = g_build_filename (tmpdir, filelist[i], NULL); if (!asb_plugin_nm_app (app, filename, &error_local)) { asb_package_log (pkg, ASB_PACKAGE_LOG_LEVEL_WARNING, "Failed to run nm on %s: %s", filename, error_local->message); g_clear_error (&error_local); } } return TRUE; }
/** * asb_plugin_process_app: */ gboolean asb_plugin_process_app (AsbPlugin *plugin, AsbPackage *pkg, AsbApp *app, const gchar *tmpdir, GError **error) { guint i; guint j; for (i = 0; data[i].path != NULL; i++) { g_auto(GStrv) split = NULL; if (!asb_utils_is_file_in_tmpdir (tmpdir, data[i].path)) continue; split = g_strsplit (data[i].text, "|", -1); for (j = 0; split[j] != NULL; j++) { as_app_add_keyword (AS_APP (app), NULL, split[j]); asb_package_log (pkg, ASB_PACKAGE_LOG_LEVEL_DEBUG, "Auto-adding keyword %s for %s", split[j], as_app_get_id (AS_APP (app))); } } return TRUE; }
/** * asb_task_explode_extra_package: **/ static gboolean asb_task_explode_extra_package (AsbTask *task, const gchar *pkg_name, gboolean require_same_srpm, GError **error) { AsbTaskPrivate *priv = GET_PRIVATE (task); AsbPackage *pkg_extra; GPtrArray *deps; guint i; const gchar *dep; /* if not found, that's fine */ pkg_extra = asb_context_find_by_pkgname (priv->ctx, pkg_name); if (pkg_extra == NULL) return TRUE; /* check it's from the same source package */ if (!asb_package_ensure (pkg_extra, ASB_PACKAGE_ENSURE_SOURCE, error)) return FALSE; if (require_same_srpm && (g_strcmp0 (asb_package_get_source (pkg_extra), asb_package_get_source (priv->pkg)) != 0)) return TRUE; g_debug ("decompressing extra pkg %s", asb_package_get_name (pkg_extra)); asb_package_log (priv->pkg, ASB_PACKAGE_LOG_LEVEL_DEBUG, "Adding extra package %s for %s", asb_package_get_name (pkg_extra), asb_package_get_name (priv->pkg)); if (!asb_package_ensure (pkg_extra, ASB_PACKAGE_ENSURE_FILES | ASB_PACKAGE_ENSURE_DEPS, error)) return FALSE; if (!asb_package_explode (pkg_extra, priv->tmpdir, asb_context_get_file_globs (priv->ctx), error)) return FALSE; /* copy all the extra package requires into the main package too */ deps = asb_package_get_deps (pkg_extra); for (i = 0; i < deps->len; i++) { dep = g_ptr_array_index (deps, i); asb_package_add_dep (priv->pkg, dep); } /* free resources */ if (!asb_package_close (pkg_extra, error)) return FALSE; asb_package_clear (pkg_extra, ASB_PACKAGE_ENSURE_DEPS | ASB_PACKAGE_ENSURE_FILES); return TRUE; }
static gboolean asb_plugin_desktop_refine (AsbPlugin *plugin, AsbPackage *pkg, const gchar *filename, AsbApp *app, const gchar *tmpdir, GError **error) { AsIcon *icon; AsAppParseFlags parse_flags = AS_APP_PARSE_FLAG_USE_HEURISTICS | AS_APP_PARSE_FLAG_ALLOW_VETO; gboolean ret; g_autoptr(AsApp) desktop_app = NULL; g_autoptr(GdkPixbuf) pixbuf = NULL; /* use GenericName fallback */ if (asb_context_get_flag (plugin->ctx, ASB_CONTEXT_FLAG_USE_FALLBACKS)) parse_flags |= AS_APP_PARSE_FLAG_USE_FALLBACKS; /* create app */ desktop_app = as_app_new (); if (!as_app_parse_file (desktop_app, filename, parse_flags, error)) return FALSE; /* copy all metadata */ as_app_subsume_full (AS_APP (app), desktop_app, AS_APP_SUBSUME_FLAG_NO_OVERWRITE); /* is the icon a stock-icon-name? */ icon = as_app_get_icon_default (AS_APP (app)); if (icon != NULL) { g_autofree gchar *key = NULL; key = g_strdup (as_icon_get_name (icon)); if (as_icon_get_kind (icon) == AS_ICON_KIND_STOCK) { asb_package_log (pkg, ASB_PACKAGE_LOG_LEVEL_DEBUG, "using stock icon %s", key); } else { g_autoptr(GError) error_local = NULL; g_ptr_array_set_size (as_app_get_icons (AS_APP (app)), 0); ret = asb_plugin_desktop_add_icons (plugin, app, tmpdir, key, &error_local); if (!ret) { as_app_add_veto (AS_APP (app), "%s", error_local->message); } } } return TRUE; }
/** * asb_plugin_loader_process_app: * @plugin_loader: A #AsbPluginLoader * @pkg: The #AsbPackage * @app: The #AsbApp to refine * @tmpdir: A temporary location to use * @error: A #GError or %NULL * * Processes an application object, refining any available data. * * Returns: %TRUE for success, %FALSE otherwise * * Since: 0.2.1 **/ gboolean asb_plugin_loader_process_app (AsbPluginLoader *plugin_loader, AsbPackage *pkg, AsbApp *app, const gchar *tmpdir, GError **error) { AsbPluginLoaderPrivate *priv = GET_PRIVATE (plugin_loader); AsbPlugin *plugin; AsbPluginProcessAppFunc plugin_func = NULL; GError *error_local = NULL; gboolean ret; guint i; /* run each plugin */ for (i = 0; i < priv->plugins->len; i++) { plugin = g_ptr_array_index (priv->plugins, i); ret = g_module_symbol (plugin->module, "asb_plugin_process_app", (gpointer *) &plugin_func); if (!ret) continue; asb_package_log (pkg, ASB_PACKAGE_LOG_LEVEL_DEBUG, "Running asb_plugin_process_app() from %s", plugin->name); if (!plugin_func (plugin, pkg, app, tmpdir, &error_local)) { asb_package_log (pkg, ASB_PACKAGE_LOG_LEVEL_WARNING, "Ignoring: %s", error_local->message); g_clear_error (&error_local); } } return TRUE; }
/** * asb_plugin_process_gir: */ static gboolean asb_plugin_process_gir (AsbApp *app, const gchar *tmpdir, const gchar *filename, GError **error) { GNode *l; GNode *node = NULL; const gchar *name; const gchar *version; gboolean ret = TRUE; g_autofree gchar *filename_full = NULL; g_autoptr(GFile) file = NULL; /* load file */ filename_full = g_build_filename (tmpdir, filename, NULL); file = g_file_new_for_path (filename_full); node = as_node_from_file (file, AS_NODE_FROM_XML_FLAG_NONE, NULL, error); if (node == NULL) { ret = FALSE; goto out; } /* look for includes */ l = as_node_find (node, "repository"); if (l == NULL) goto out; for (l = l->children; l != NULL; l = l->next) { if (g_strcmp0 (as_node_get_name (l), "include") != 0) continue; name = as_node_get_attribute (l, "name"); version = as_node_get_attribute (l, "version"); if (g_strcmp0 (name, "Gtk") == 0 && g_strcmp0 (version, "3.0") == 0) { asb_package_log (asb_app_get_package (app), ASB_PACKAGE_LOG_LEVEL_DEBUG, "Auto-adding kudo ModernToolkit for %s", as_app_get_id (AS_APP (app))); as_app_add_kudo_kind (AS_APP (app), AS_KUDO_KIND_MODERN_TOOLKIT); } } out: if (node != NULL) as_node_unref (node); return ret; }
/** * asb_plugin_composite_app: */ static void asb_plugin_composite_app (AsApp *app, AsApp *donor) { _cleanup_error_free_ GError *error = NULL; /* composite the app */ if (!_as_app_composite (app, donor, &error)) { if (ASB_IS_APP (app)) { asb_package_log (asb_app_get_package (ASB_APP (app)), ASB_PACKAGE_LOG_LEVEL_INFO, "%s", error->message); } else { g_warning ("%s", error->message); } return; } }
/** * asb_plugin_process: */ GList * asb_plugin_process (AsbPlugin *plugin, AsbPackage *pkg, const gchar *tmpdir, GError **error) { gboolean ret; GError *error_local = NULL; GList *apps = NULL; guint i; gchar **filelist; filelist = asb_package_get_filelist (pkg); for (i = 0; filelist[i] != NULL; i++) { if (!_asb_plugin_check_filename (filelist[i])) continue; ret = asb_plugin_process_filename (plugin, pkg, filelist[i], &apps, tmpdir, &error_local); if (!ret) { asb_package_log (pkg, ASB_PACKAGE_LOG_LEVEL_INFO, "Failed to process %s: %s", filelist[i], error_local->message); g_clear_error (&error_local); } } /* no desktop files we care about */ if (apps == NULL) { g_set_error (error, ASB_PLUGIN_ERROR, ASB_PLUGIN_ERROR_FAILED, "nothing interesting in %s", asb_package_get_basename (pkg)); return NULL; } return apps; }
/** * asb_plugin_hardcoded_add_screenshots: */ static gboolean asb_plugin_hardcoded_add_screenshots (AsbApp *app, const gchar *location, GError **error) { const gchar *tmp; gboolean ret = TRUE; GList *l; GList *list = NULL; _cleanup_dir_close_ GDir *dir = NULL; /* scan for files */ dir = g_dir_open (location, 0, error); if (dir == NULL) { ret = FALSE; goto out; } while ((tmp = g_dir_read_name (dir)) != NULL) { if (!g_str_has_suffix (tmp, ".png")) continue; list = g_list_prepend (list, g_build_filename (location, tmp, NULL)); } list = g_list_sort (list, asb_plugin_hardcoded_sort_screenshots_cb); for (l = list; l != NULL; l = l->next) { tmp = l->data; asb_package_log (asb_app_get_package (app), ASB_PACKAGE_LOG_LEVEL_DEBUG, "Adding extra screenshot: %s", tmp); ret = asb_app_add_screenshot_source (app, tmp, error); if (!ret) goto out; } as_app_add_metadata (AS_APP (app), "DistroScreenshots", NULL, -1); out: g_list_free_full (list, g_free); return ret; }
/** * asb_plugin_process_app: */ gboolean asb_plugin_process_app (AsbPlugin *plugin, AsbPackage *pkg, AsbApp *app, const gchar *tmpdir, GError **error) { gchar **filelist; guint i; /* already set */ if (as_app_has_kudo_kind (AS_APP (app), AS_KUDO_KIND_SEARCH_PROVIDER)) return TRUE; /* look for a krunner provider */ filelist = asb_package_get_filelist (pkg); for (i = 0; filelist[i] != NULL; i++) { g_autoptr(GError) error_local = NULL; g_autofree gchar *filename = NULL; if (!asb_plugin_match_glob ("/usr/share/kde4/services/*.desktop", filelist[i])) continue; filename = g_build_filename (tmpdir, filelist[i], NULL); if (!asb_plugin_process_filename (filename, app, tmpdir, &error_local)) { asb_package_log (pkg, ASB_PACKAGE_LOG_LEVEL_INFO, "Failed to read KDE service file %s: %s", filelist[i], error_local->message); g_clear_error (&error_local); } } return TRUE; }
/** * asb_plugin_process_filename: */ static gboolean asb_plugin_process_filename (const gchar *filename, AsbApp *app, const gchar *tmpdir, GError **error) { g_autofree gchar *types = NULL; g_autoptr(GKeyFile) kf = NULL; kf = g_key_file_new (); if (!g_key_file_load_from_file (kf, filename, G_KEY_FILE_NONE, error)) return FALSE; types = g_key_file_get_string (kf, G_KEY_FILE_DESKTOP_GROUP, "X-KDE-ServiceTypes", NULL); if (types == NULL) return TRUE; if (g_strcmp0 (types, "Plasma/Runner") != 0) return TRUE; asb_package_log (asb_app_get_package (app), ASB_PACKAGE_LOG_LEVEL_DEBUG, "Auto-adding kudo SearchProvider for %s", as_app_get_id (AS_APP (app))); as_app_add_kudo_kind (AS_APP (app), AS_KUDO_KIND_SEARCH_PROVIDER); return TRUE; }
/** * asb_app_load_icon: */ static GdkPixbuf * asb_app_load_icon (AsbApp *app, const gchar *filename, const gchar *logfn, guint icon_size, guint min_icon_size, GError **error) { GdkPixbuf *pixbuf = NULL; guint pixbuf_height; guint pixbuf_width; guint tmp_height; guint tmp_width; _cleanup_object_unref_ GdkPixbuf *pixbuf_src = NULL; _cleanup_object_unref_ GdkPixbuf *pixbuf_tmp = NULL; /* open file in native size */ if (g_str_has_suffix (filename, ".svg")) { pixbuf_src = gdk_pixbuf_new_from_file_at_scale (filename, icon_size, icon_size, TRUE, error); } else { pixbuf_src = gdk_pixbuf_new_from_file (filename, error); } if (pixbuf_src == NULL) return NULL; /* check size */ if (gdk_pixbuf_get_width (pixbuf_src) < (gint) min_icon_size && gdk_pixbuf_get_height (pixbuf_src) < (gint) min_icon_size) { g_set_error (error, ASB_PLUGIN_ERROR, ASB_PLUGIN_ERROR_FAILED, "icon %s was too small %ix%i", logfn, gdk_pixbuf_get_width (pixbuf_src), gdk_pixbuf_get_height (pixbuf_src)); return NULL; } /* does the icon not have an alpha channel */ if (!gdk_pixbuf_get_has_alpha (pixbuf_src)) { asb_package_log (asb_app_get_package (app), ASB_PACKAGE_LOG_LEVEL_INFO, "icon %s does not have an alpha channel", logfn); } /* don't do anything to an icon with the perfect size */ pixbuf_width = gdk_pixbuf_get_width (pixbuf_src); pixbuf_height = gdk_pixbuf_get_height (pixbuf_src); if (pixbuf_width == icon_size && pixbuf_height == icon_size) return g_object_ref (pixbuf_src); /* never scale up, just pad */ if (pixbuf_width < icon_size && pixbuf_height < icon_size) { _cleanup_free_ gchar *size_str = NULL; size_str = g_strdup_printf ("%ix%i", pixbuf_width, pixbuf_height); asb_package_log (asb_app_get_package (app), ASB_PACKAGE_LOG_LEVEL_INFO, "icon %s padded to %ix%i as size %s", logfn, icon_size, icon_size, size_str); pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, icon_size, icon_size); gdk_pixbuf_fill (pixbuf, 0x00000000); gdk_pixbuf_copy_area (pixbuf_src, 0, 0, /* of src */ pixbuf_width, pixbuf_height, pixbuf, (icon_size - pixbuf_width) / 2, (icon_size - pixbuf_height) / 2); return pixbuf; } /* is the aspect ratio perfectly square */ if (pixbuf_width == pixbuf_height) { pixbuf = gdk_pixbuf_scale_simple (pixbuf_src, icon_size, icon_size, GDK_INTERP_HYPER); as_pixbuf_sharpen (pixbuf, 1, -0.5); return pixbuf; } /* create new square pixbuf with alpha padding */ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, icon_size, icon_size); gdk_pixbuf_fill (pixbuf, 0x00000000); if (pixbuf_width > pixbuf_height) { tmp_width = icon_size; tmp_height = icon_size * pixbuf_height / pixbuf_width; } else { tmp_width = icon_size * pixbuf_width / pixbuf_height; tmp_height = icon_size; } pixbuf_tmp = gdk_pixbuf_scale_simple (pixbuf_src, tmp_width, tmp_height, GDK_INTERP_HYPER); as_pixbuf_sharpen (pixbuf_tmp, 1, -0.5); gdk_pixbuf_copy_area (pixbuf_tmp, 0, 0, /* of src */ tmp_width, tmp_height, pixbuf, (icon_size - tmp_width) / 2, (icon_size - tmp_height) / 2); return pixbuf; }
/** * asb_task_process: * @task: A #AsbTask * @error_not_used: A #GError or %NULL * * Processes the task. * * Returns: %TRUE for success, %FALSE otherwise * * Since: 0.1.0 **/ gboolean asb_task_process (AsbTask *task, GError **error_not_used) { AsRelease *release; AsbApp *app; AsbPlugin *plugin = NULL; AsbTaskPrivate *priv = GET_PRIVATE (task); GList *apps = NULL; GList *l; GPtrArray *array; gboolean ret; gchar *cache_id; guint i; guint nr_added = 0; g_autoptr(GError) error = NULL; g_autofree gchar *basename = NULL; /* reset the profile timer */ asb_package_log_start (priv->pkg); /* ensure nevra read */ if (!asb_package_ensure (priv->pkg, ASB_PACKAGE_ENSURE_NEVRA, error_not_used)) return FALSE; g_debug ("starting: %s", asb_package_get_name (priv->pkg)); /* treat archive as a special case */ if (g_str_has_suffix (priv->filename, ".cab")) { AsApp *app_tmp; GPtrArray *apps_tmp; g_autoptr(AsStore) store = as_store_new (); g_autoptr(GFile) file = g_file_new_for_path (priv->filename); if (!as_store_from_file (store, file, NULL, NULL, &error)) { asb_package_log (priv->pkg, ASB_PACKAGE_LOG_LEVEL_WARNING, "Failed to parse %s: %s", asb_package_get_filename (priv->pkg), error->message); return TRUE; } apps_tmp = as_store_get_apps (store); for (i = 0; i < apps_tmp->len; i++) { g_autoptr(AsbApp) app2 = NULL; app_tmp = AS_APP (g_ptr_array_index (apps_tmp, i)); app2 = asb_app_new (priv->pkg, as_app_get_id (app_tmp)); as_app_subsume (AS_APP (app2), app_tmp); asb_context_add_app (priv->ctx, app2); /* set cache-id in case we want to use the metadata directly */ if (asb_context_get_flag (priv->ctx, ASB_CONTEXT_FLAG_ADD_CACHE_ID)) { cache_id = asb_utils_get_cache_id_for_filename (priv->filename); as_app_add_metadata (AS_APP (app2), "X-CacheID", cache_id); g_free (cache_id); } nr_added++; } g_debug ("added %i apps from archive", apps_tmp->len); goto skip; } /* ensure file list read */ if (!asb_package_ensure (priv->pkg, ASB_PACKAGE_ENSURE_FILES, error_not_used)) return FALSE; /* did we get a file match on any plugin */ basename = g_path_get_basename (priv->filename); asb_package_log (priv->pkg, ASB_PACKAGE_LOG_LEVEL_DEBUG, "Getting filename match for %s", basename); asb_task_add_suitable_plugins (task); if (priv->plugins_to_run->len == 0) { asb_context_add_app_ignore (priv->ctx, priv->pkg); goto out; } /* delete old tree if it exists */ ret = asb_utils_ensure_exists_and_empty (priv->tmpdir, &error); if (!ret) { asb_package_log (priv->pkg, ASB_PACKAGE_LOG_LEVEL_WARNING, "Failed to clear: %s", error->message); goto out; } /* explode tree */ g_debug ("decompressing files: %s", asb_package_get_name (priv->pkg)); asb_package_log (priv->pkg, ASB_PACKAGE_LOG_LEVEL_DEBUG, "Exploding tree for %s", asb_package_get_name (priv->pkg)); ret = asb_package_explode (priv->pkg, priv->tmpdir, asb_context_get_file_globs (priv->ctx), &error); if (!ret) { asb_package_log (priv->pkg, ASB_PACKAGE_LOG_LEVEL_WARNING, "Failed to explode: %s", error->message); g_clear_error (&error); goto skip; } /* add extra packages */ if (!asb_package_ensure (priv->pkg, ASB_PACKAGE_ENSURE_DEPS | ASB_PACKAGE_ENSURE_SOURCE, error_not_used)) return FALSE; ret = asb_task_explode_extra_packages (task, &error); if (!ret) { asb_package_log (priv->pkg, ASB_PACKAGE_LOG_LEVEL_WARNING, "Failed to explode extra file: %s", error->message); goto skip; } /* run plugins */ g_debug ("examining: %s", asb_package_get_name (priv->pkg)); for (i = 0; i < priv->plugins_to_run->len; i++) { GList *apps_tmp = NULL; plugin = g_ptr_array_index (priv->plugins_to_run, i); asb_package_log (priv->pkg, ASB_PACKAGE_LOG_LEVEL_DEBUG, "Processing %s with %s", basename, plugin->name); apps_tmp = asb_plugin_process (plugin, priv->pkg, priv->tmpdir, &error); if (apps_tmp == NULL) { asb_package_log (priv->pkg, ASB_PACKAGE_LOG_LEVEL_WARNING, "Failed to run process '%s': %s", plugin->name, error->message); g_clear_error (&error); } for (l = apps_tmp; l != NULL; l = l->next) { app = ASB_APP (l->data); asb_plugin_add_app (&apps, AS_APP (app)); } g_list_free_full (apps_tmp, g_object_unref); } if (apps == NULL) goto skip; /* print */ g_debug ("processing: %s", asb_package_get_name (priv->pkg)); for (l = apps; l != NULL; l = l->next) { app = l->data; /* never set */ if (as_app_get_id (AS_APP (app)) == NULL) { asb_package_log (priv->pkg, ASB_PACKAGE_LOG_LEVEL_INFO, "app id not set for %s", asb_package_get_name (priv->pkg)); continue; } /* copy data from pkg into app */ if (!asb_package_ensure (priv->pkg, ASB_PACKAGE_ENSURE_LICENSE | ASB_PACKAGE_ENSURE_RELEASES | ASB_PACKAGE_ENSURE_VCS | ASB_PACKAGE_ENSURE_URL, error_not_used)) return FALSE; if (asb_package_get_url (priv->pkg) != NULL && as_app_get_url_item (AS_APP (app), AS_URL_KIND_HOMEPAGE) == NULL) { as_app_add_url (AS_APP (app), AS_URL_KIND_HOMEPAGE, asb_package_get_url (priv->pkg)); } if (asb_package_get_license (priv->pkg) != NULL && as_app_get_project_license (AS_APP (app)) == NULL) { as_app_set_project_license (AS_APP (app), asb_package_get_license (priv->pkg)); } /* add the source name so we can suggest these together */ if (g_strcmp0 (asb_package_get_source_pkgname (priv->pkg), asb_package_get_name (priv->pkg)) != 0) { as_app_set_source_pkgname (AS_APP (app), asb_package_get_source_pkgname (priv->pkg)); } /* set all the releases on the app */ array = asb_package_get_releases (priv->pkg); for (i = 0; i < array->len; i++) { release = g_ptr_array_index (array, i); as_app_add_release (AS_APP (app), release); } /* run each refine plugin on each app */ ret = asb_plugin_loader_process_app (asb_context_get_plugin_loader (priv->ctx), priv->pkg, app, priv->tmpdir, &error); if (!ret) { asb_package_log (priv->pkg, ASB_PACKAGE_LOG_LEVEL_WARNING, "Failed to run process on %s: %s", as_app_get_id (AS_APP (app)), error->message); g_clear_error (&error); goto skip; } /* set cache-id in case we want to use the metadata directly */ if (asb_context_get_flag (priv->ctx, ASB_CONTEXT_FLAG_ADD_CACHE_ID)) { cache_id = asb_utils_get_cache_id_for_filename (priv->filename); as_app_add_metadata (AS_APP (app), "X-CacheID", cache_id); g_free (cache_id); } /* set the VCS information into the metadata */ if (asb_package_get_vcs (priv->pkg) != NULL) { as_app_add_metadata (AS_APP (app), "VersionControlSystem", asb_package_get_vcs (priv->pkg)); } /* save any screenshots early */ if (array->len == 0) { if (!asb_app_save_resources (ASB_APP (app), ASB_APP_SAVE_FLAG_SCREENSHOTS, error_not_used)) return FALSE; } /* all okay */ asb_context_add_app (priv->ctx, app); nr_added++; } skip: /* add a dummy element to the AppStream metadata so that we don't keep * parsing this every time */ if (asb_context_get_flag (priv->ctx, ASB_CONTEXT_FLAG_ADD_CACHE_ID) && nr_added == 0) asb_context_add_app_ignore (priv->ctx, priv->pkg); /* delete tree */ g_debug ("deleting temp files: %s", asb_package_get_name (priv->pkg)); if (!asb_utils_rmtree (priv->tmpdir, &error)) { asb_package_log (priv->pkg, ASB_PACKAGE_LOG_LEVEL_WARNING, "Failed to delete tree: %s", error->message); goto out; } /* write log */ g_debug ("writing log: %s", asb_package_get_name (priv->pkg)); if (!asb_package_log_flush (priv->pkg, &error)) { asb_package_log (priv->pkg, ASB_PACKAGE_LOG_LEVEL_WARNING, "Failed to write package log: %s", error->message); goto out; } out: /* clear loaded resources */ asb_package_close (priv->pkg, NULL); asb_package_clear (priv->pkg, ASB_PACKAGE_ENSURE_DEPS | ASB_PACKAGE_ENSURE_FILES); g_list_free_full (apps, (GDestroyNotify) g_object_unref); return TRUE; }
/** * asb_plugin_loader_merge: * @plugin_loader: A #AsbPluginLoader * @apps: (element-type AsbApp): a list of applications that need merging * * Merge the list of applications using the plugins. * * Since: 0.2.5 **/ void asb_plugin_loader_merge (AsbPluginLoader *plugin_loader, GList *apps) { AsbApp *app; AsbApp *found; AsbPluginLoaderPrivate *priv = GET_PRIVATE (plugin_loader); AsbPluginMergeFunc plugin_func = NULL; AsbPlugin *plugin; GList *l; const gchar *key; const gchar *tmp; gboolean ret; guint i; g_autoptr(GHashTable) hash = NULL; /* run each plugin */ for (i = 0; i < priv->plugins->len; i++) { plugin = g_ptr_array_index (priv->plugins, i); ret = g_module_symbol (plugin->module, "asb_plugin_merge", (gpointer *) &plugin_func); if (!ret) continue; plugin_func (plugin, apps); } /* FIXME: move to font plugin */ for (l = apps; l != NULL; l = l->next) { if (!ASB_IS_APP (l->data)) continue; app = ASB_APP (l->data); as_app_remove_metadata (AS_APP (app), "FontFamily"); as_app_remove_metadata (AS_APP (app), "FontFullName"); as_app_remove_metadata (AS_APP (app), "FontIconText"); as_app_remove_metadata (AS_APP (app), "FontParent"); as_app_remove_metadata (AS_APP (app), "FontSampleText"); as_app_remove_metadata (AS_APP (app), "FontSubFamily"); as_app_remove_metadata (AS_APP (app), "FontClassifier"); } /* deduplicate */ hash = g_hash_table_new (g_str_hash, g_str_equal); for (l = apps; l != NULL; l = l->next) { if (!ASB_IS_APP (l->data)) continue; app = ASB_APP (l->data); if (as_app_get_vetos(AS_APP(app))->len > 0) continue; key = as_app_get_id (AS_APP (app)); found = g_hash_table_lookup (hash, key); if (found == NULL) { g_hash_table_insert (hash, (gpointer) key, (gpointer) app); continue; } if (as_app_get_kind (AS_APP (app)) == AS_APP_KIND_FIRMWARE) { as_app_subsume_full (AS_APP (found), AS_APP (app), AS_APP_SUBSUME_FLAG_MERGE); } tmp = asb_package_get_nevr (asb_app_get_package (found)); as_app_add_veto (AS_APP (app), "duplicate of %s", tmp); asb_package_log (asb_app_get_package (app), ASB_PACKAGE_LOG_LEVEL_WARNING, "duplicate %s not included as added from %s", key, tmp); } }
/** * asb_context_process: * @ctx: A #AsbContext * @error: A #GError or %NULL * * Processes all the packages that have been added to the context. * * Returns: %TRUE for success, %FALSE otherwise * * Since: 0.1.0 **/ gboolean asb_context_process (AsbContext *ctx, GError **error) { AsbContextPrivate *priv = GET_PRIVATE (ctx); AsbPackage *pkg; AsbTask *task; GThreadPool *pool; gboolean ret; guint i; g_autoptr(GPtrArray) tasks = NULL; /* only process the newest packages */ asb_context_disable_multiarch_pkgs (ctx); asb_context_disable_older_pkgs (ctx); /* create thread pool */ pool = g_thread_pool_new (asb_task_process_func, ctx, priv->max_threads, TRUE, error); if (pool == NULL) return FALSE; /* add each package */ g_print ("Processing packages...\n"); tasks = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); for (i = 0; i < priv->packages->len; i++) { pkg = g_ptr_array_index (priv->packages, i); if (!asb_package_get_enabled (pkg)) { asb_package_log (pkg, ASB_PACKAGE_LOG_LEVEL_DEBUG, "%s is not enabled", asb_package_get_nevr (pkg)); asb_context_add_app_ignore (ctx, pkg); asb_package_log_flush (pkg, NULL); continue; } /* set locations of external resources */ asb_package_set_config (pkg, "LogDir", priv->log_dir); asb_package_set_config (pkg, "TempDir", priv->temp_dir); asb_package_set_config (pkg, "IconsDir", priv->icons_dir); asb_package_set_config (pkg, "OutputDir", priv->output_dir); /* create task */ task = asb_task_new (ctx); asb_task_set_package (task, pkg); g_ptr_array_add (tasks, task); /* run the task */ if (priv->max_threads == 1) { if (!asb_task_process (task, error)) return FALSE; } else { if (!g_thread_pool_push (pool, task, error)) return FALSE; } } /* wait for them to finish */ g_thread_pool_free (pool, FALSE, TRUE); /* merge */ g_print ("Merging applications...\n"); asb_plugin_loader_merge (priv->plugin_loader, priv->apps); /* print any warnings */ if ((priv->flags & ASB_CONTEXT_FLAG_IGNORE_MISSING_INFO) == 0) { if (!asb_context_detect_missing_data (ctx, error)) return FALSE; } if ((priv->flags & ASB_CONTEXT_FLAG_IGNORE_MISSING_PARENTS) == 0) { if (!asb_context_detect_missing_parents (ctx, error)) return FALSE; } if (!asb_context_detect_pkgname_dups (ctx, error)) return FALSE; if (!asb_context_convert_icons (ctx, error)) return FALSE; if (!asb_context_save_resources (ctx, error)) return FALSE; /* write the application XML to the log file */ asb_context_write_app_xml (ctx); /* write XML file */ ret = asb_context_write_xml (ctx, error); if (!ret) return FALSE; /* write XML file */ ret = asb_context_write_xml_fail (ctx, error); if (!ret) return FALSE; /* write XML file */ ret = asb_context_write_xml_ignore (ctx, error); if (!ret) return FALSE; /* write icons archive */ ret = asb_context_write_icons (ctx, priv->temp_dir, error); if (!ret) return FALSE; /* write screenshots archive */ ret = asb_context_write_screenshots (ctx, priv->temp_dir, error); if (!ret) return FALSE; /* ensure all packages are flushed */ for (i = 0; i < priv->packages->len; i++) { pkg = g_ptr_array_index (priv->packages, i); if (!asb_package_log_flush (pkg, error)) return FALSE; } return TRUE; }
/** * _as_app_composite: */ static gboolean _as_app_composite (AsApp *app, AsApp *donor, GError **error) { AsApp *tmp; gint rc; _cleanup_free_ gchar *id = NULL; /* check this makes sense */ if (as_app_get_id_kind (app) != as_app_get_id_kind (donor)) { g_set_error (error, AS_APP_ERROR, AS_APP_ERROR_INVALID_TYPE, "Cannot composite %s:%s of different id kind", as_app_get_id (app), as_app_get_id (donor)); return FALSE; } /* the ID, name with the shortest length wins */ rc = strlen (as_app_get_id (app)) - strlen (as_app_get_id (donor)); if (rc == 0) { rc = strlen (as_app_get_name (app, "C")) - strlen (as_app_get_name (donor, "C")); } if (rc > 0) { tmp = app; app = donor; donor = tmp; } /* set the new composite string */ id = as_utils_get_string_overlap (as_app_get_id (app), as_app_get_id (donor)); if (id == NULL || !_as_app_is_id_valid (id)) { g_set_error (error, AS_APP_ERROR, AS_APP_ERROR_INVALID_TYPE, "Cannot composite %s:%s as no ID overlap", as_app_get_id (app), as_app_get_id (donor)); return FALSE; } /* log */ if (ASB_IS_APP (app) && g_strcmp0 (as_app_get_id (app), id) != 0) { asb_package_log (asb_app_get_package (ASB_APP (app)), ASB_PACKAGE_LOG_LEVEL_INFO, "Renamed %s into %s so it could be " "composited with %s", as_app_get_id (app), id, as_app_get_id (donor)); } if (ASB_IS_APP (donor)) { asb_package_log (asb_app_get_package (ASB_APP (donor)), ASB_PACKAGE_LOG_LEVEL_INFO, "Composited %s into %s", as_app_get_id (donor), id); } /* set the new ID (on both apps?) */ as_app_set_id (app, id, -1); /* add some easily merged properties */ as_app_subsume_full (app, donor, AS_APP_SUBSUME_FLAG_PARTIAL); as_app_add_veto (donor, "absorbed into %s", as_app_get_id (app)); return TRUE; }
static gboolean asb_plugin_process_filename (AsbPlugin *plugin, AsbPackage *pkg, const gchar *filename, GList **apps, GError **error) { AsProblemKind problem_kind; AsProblem *problem; GPtrArray *icons; const gchar *tmp; guint i; g_autoptr(AsbApp) app = NULL; g_autoptr(GPtrArray) problems = NULL; app = asb_app_new (NULL, NULL); if (!as_app_parse_file (AS_APP (app), filename, AS_APP_PARSE_FLAG_USE_HEURISTICS, error)) return FALSE; if (as_app_get_kind (AS_APP (app)) == AS_APP_KIND_UNKNOWN) { g_set_error (error, ASB_PLUGIN_ERROR, ASB_PLUGIN_ERROR_FAILED, "%s has no recognised type", as_app_get_id (AS_APP (app))); return FALSE; } /* validate */ problems = as_app_validate (AS_APP (app), AS_APP_VALIDATE_FLAG_NO_NETWORK | AS_APP_VALIDATE_FLAG_RELAX, error); if (problems == NULL) return FALSE; asb_app_set_package (app, pkg); for (i = 0; i < problems->len; i++) { problem = g_ptr_array_index (problems, i); problem_kind = as_problem_get_kind (problem); asb_package_log (asb_app_get_package (app), ASB_PACKAGE_LOG_LEVEL_WARNING, "AppData problem: %s : %s", as_problem_kind_to_string (problem_kind), as_problem_get_message (problem)); } /* nuke things that do not belong */ icons = as_app_get_icons (AS_APP (app)); if (icons->len > 0) g_ptr_array_set_size (icons, 0); /* fix up the project license */ tmp = as_app_get_project_license (AS_APP (app)); if (tmp != NULL && !as_utils_is_spdx_license (tmp)) { g_autofree gchar *license_spdx = NULL; license_spdx = as_utils_license_to_spdx (tmp); if (as_utils_is_spdx_license (license_spdx)) { asb_package_log (asb_app_get_package (app), ASB_PACKAGE_LOG_LEVEL_WARNING, "project license fixup: %s -> %s", tmp, license_spdx); as_app_set_project_license (AS_APP (app), license_spdx); } else { asb_package_log (asb_app_get_package (app), ASB_PACKAGE_LOG_LEVEL_WARNING, "project license is invalid: %s", tmp); as_app_set_project_license (AS_APP (app), NULL); } } /* check license */ tmp = as_app_get_metadata_license (AS_APP (app)); if (tmp == NULL) { g_set_error (error, ASB_PLUGIN_ERROR, ASB_PLUGIN_ERROR_FAILED, "AppData %s has no licence", filename); return FALSE; } if (!as_utils_is_spdx_license (tmp)) { g_set_error (error, ASB_PLUGIN_ERROR, ASB_PLUGIN_ERROR_FAILED, "AppData %s license '%s' invalid", filename, tmp); return FALSE; } /* log updateinfo */ tmp = as_app_get_update_contact (AS_APP (app)); if (tmp != NULL) { asb_package_log (asb_app_get_package (app), ASB_PACKAGE_LOG_LEVEL_INFO, "Upstream contact <%s>", tmp); } /* add icon for firmware */ if (as_app_get_kind (AS_APP (app)) == AS_APP_KIND_FIRMWARE) { 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"); as_app_add_icon (AS_APP (app), icon); } /* fix up input methods */ if (as_app_get_kind (AS_APP (app)) == AS_APP_KIND_INPUT_METHOD) { g_autoptr(AsIcon) icon = NULL; icon = as_icon_new (); as_icon_set_kind (icon, AS_ICON_KIND_STOCK); as_icon_set_name (icon, "system-run-symbolic"); as_app_add_icon (AS_APP (app), icon); as_app_add_category (AS_APP (app), "Addons"); as_app_add_category (AS_APP (app), "InputSources"); } /* fix up codecs */ if (as_app_get_kind (AS_APP (app)) == AS_APP_KIND_CODEC) { 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"); as_app_add_icon (AS_APP (app), icon); as_app_add_category (AS_APP (app), "Addons"); as_app_add_category (AS_APP (app), "Codecs"); } /* success */ asb_app_set_hidpi_enabled (app, asb_context_get_flag (plugin->ctx, ASB_CONTEXT_FLAG_HIDPI_ICONS)); asb_plugin_add_app (apps, AS_APP (app)); return TRUE; }
/** * asb_plugin_process_filename: */ static gboolean asb_plugin_process_filename (AsbPlugin *plugin, AsbPackage *pkg, const gchar *filename, GList **apps, const gchar *tmpdir, GError **error) { AsIcon *icon; AsAppParseFlags parse_flags = AS_APP_PARSE_FLAG_USE_HEURISTICS; gboolean ret; _cleanup_free_ gchar *app_id = NULL; _cleanup_free_ gchar *full_filename = NULL; _cleanup_object_unref_ AsbApp *app = NULL; _cleanup_object_unref_ GdkPixbuf *pixbuf = NULL; /* use GenericName fallback */ if (asb_context_get_flag (plugin->ctx, ASB_CONTEXT_FLAG_USE_FALLBACKS)) parse_flags |= AS_APP_PARSE_FLAG_USE_FALLBACKS; /* create app */ app_id = g_path_get_basename (filename); app = asb_app_new (pkg, app_id); asb_app_set_hidpi_enabled (app, asb_context_get_flag (plugin->ctx, ASB_CONTEXT_FLAG_HIDPI_ICONS)); full_filename = g_build_filename (tmpdir, filename, NULL); if (!as_app_parse_file (AS_APP (app), full_filename, parse_flags, error)) return FALSE; /* NoDisplay apps are never included */ if (as_app_get_metadata_item (AS_APP (app), "NoDisplay") != NULL) asb_app_add_requires_appdata (app, "NoDisplay=true"); /* Settings or DesktopSettings requires AppData */ if (!asb_context_get_flag (plugin->ctx, ASB_CONTEXT_FLAG_IGNORE_SETTINGS)) { if (as_app_has_category (AS_APP (app), "Settings")) asb_app_add_requires_appdata (app, "Category=Settings"); if (as_app_has_category (AS_APP (app), "DesktopSettings")) asb_app_add_requires_appdata (app, "Category=DesktopSettings"); } /* is the icon a stock-icon-name? */ icon = as_app_get_icon_default (AS_APP (app)); if (icon != NULL) { _cleanup_free_ gchar *key = NULL; key = g_strdup (as_icon_get_name (icon)); if (as_icon_get_kind (icon) == AS_ICON_KIND_STOCK) { asb_package_log (pkg, ASB_PACKAGE_LOG_LEVEL_DEBUG, "using stock icon %s", key); } else { _cleanup_error_free_ GError *error_local = NULL; g_ptr_array_set_size (as_app_get_icons (AS_APP (app)), 0); ret = asb_plugin_desktop_add_icons (plugin, app, tmpdir, key, &error_local); if (!ret) { as_app_add_veto (AS_APP (app), "%s", error_local->message); } } } /* add */ asb_plugin_add_app (apps, AS_APP (app)); return TRUE; }