/** * asb_context_write_xml: **/ static gboolean asb_context_write_xml (AsbContext *ctx, GError **error) { AsApp *app; AsbContextPrivate *priv = GET_PRIVATE (ctx); GList *l; g_autofree gchar *filename = NULL; g_autoptr(AsStore) store = NULL; g_autoptr(GFile) file = NULL; /* convert any vetod applications into dummy components */ for (l = priv->apps; l != NULL; l = l->next) { app = AS_APP (l->data); if (!ASB_IS_APP (app)) continue; if (as_app_get_vetos(app)->len == 0) continue; asb_context_add_app_ignore (ctx, asb_app_get_package (ASB_APP (app))); } /* add any non-vetoed applications */ store = as_store_new (); for (l = priv->apps; l != NULL; l = l->next) { app = AS_APP (l->data); if (as_app_get_vetos(app)->len > 0) continue; as_store_add_app (store, app); as_store_remove_app (priv->store_failed, app); /* remove from the ignore list if the application was useful */ if (ASB_IS_APP (app)) { AsbPackage *pkg = asb_app_get_package (ASB_APP (app)); g_autofree gchar *name_arch = NULL; name_arch = g_strdup_printf ("%s.%s", asb_package_get_name (pkg), asb_package_get_arch (pkg)); as_store_remove_app_by_id (priv->store_ignore, name_arch); } } filename = g_strdup_printf ("%s/%s.xml.gz", priv->output_dir, priv->basename); file = g_file_new_for_path (filename); g_print ("Writing %s...\n", filename); as_store_set_origin (store, priv->origin); as_store_set_api_version (store, priv->api_version); if (priv->flags & ASB_CONTEXT_FLAG_ADD_CACHE_ID) { g_autofree gchar *builder_id = asb_utils_get_builder_id (); as_store_set_builder_id (store, builder_id); } return as_store_to_file (store, file, AS_NODE_TO_XML_FLAG_ADD_HEADER | AS_NODE_TO_XML_FLAG_FORMAT_INDENT | AS_NODE_TO_XML_FLAG_FORMAT_MULTILINE, NULL, error); }
/** * 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); } }
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_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_merge_prepare_deps: */ static void asb_plugin_merge_prepare_deps (GList *list) { AsApp *app; AsbPackage *pkg; GList *l; for (l = list; l != NULL; l = l->next) { app = AS_APP (l->data); if (as_app_get_id_kind (app) != AS_ID_KIND_DESKTOP) continue; if (!ASB_IS_APP (app)) continue; if (as_app_get_vetos(app)->len > 0) continue; pkg = asb_app_get_package (ASB_APP (app)); asb_plugin_absorb_parent_for_pkgname (list, app, asb_package_get_name (pkg)); } }
/** * asb_plugin_merge_prepare_deps: */ static void asb_plugin_merge_prepare_deps (GList *list) { AsApp *app; AsbPackage *pkg; GList *l; gchar **deps; guint i; for (l = list; l != NULL; l = l->next) { app = AS_APP (l->data); if (as_app_get_id_kind (app) != AS_ID_KIND_DESKTOP) continue; pkg = asb_app_get_package (ASB_APP (app)); deps = asb_package_get_deps (pkg); for (i = 0; deps[i] != NULL; i++) asb_plugin_absorb_parent_for_pkgname (list, app, deps[i]); } }
/** * 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_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_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_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; }
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; }
/** * _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; }