/** * asb_plugin_merge: */ void asb_plugin_merge (AsbPlugin *plugin, GList *list) { AsApp *app; AsApp *found; GList *l; const gchar *tmp; _cleanup_hashtable_unref_ GHashTable *hash = NULL; /* add X-Merge-With-Parent on any metainfo files that are in a package * required by a desktop package */ asb_plugin_merge_prepare_deps (list); /* add all packages to the hash */ hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_object_unref); for (l = list; l != NULL; l = l->next) { app = AS_APP (l->data); g_hash_table_insert (hash, g_strdup (as_app_get_id (app)), g_object_ref (app)); } /* absorb some apps into their parent */ for (l = list; l != NULL; l = l->next) { app = AS_APP (l->data); /* no absorb metadata */ tmp = as_app_get_metadata_item (app, "X-Merge-With-Parent"); if (tmp == NULL) continue; /* find the parent app */ found = g_hash_table_lookup (hash, tmp); if (found == NULL) { as_app_add_veto (app, "No referenced '%s'", tmp); continue; } /* partially absorb */ as_app_add_veto (app, "partially absorbing %s into %s", as_app_get_id (app), as_app_get_id (found)); as_app_subsume_full (found, app, AS_APP_SUBSUME_FLAG_PARTIAL); } }
/** * asb_context_detect_missing_data: **/ static gboolean asb_context_detect_missing_data (AsbContext *ctx, GError **error) { AsApp *app; AsbContextPrivate *priv = GET_PRIVATE (ctx); GList *l; /* look for the thing that an addon extends */ for (l = priv->apps; l != NULL; l = l->next) { app = AS_APP (l->data); if (as_app_get_name (AS_APP (app), "C") == NULL) as_app_add_veto (AS_APP (app), "No <name> in AppData"); if (as_app_get_comment (AS_APP (app), "C") == NULL) as_app_add_veto (AS_APP (app), "No <summary> in AppData"); if (as_app_get_id_kind (AS_APP (app)) != AS_ID_KIND_ADDON) { if (as_app_get_icon_default (AS_APP (app)) == NULL) as_app_add_veto (AS_APP (app), "Has no Icon"); } } 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_context_detect_missing_parents: **/ static gboolean asb_context_detect_missing_parents (AsbContext *ctx, GError **error) { AsApp *app; AsApp *found; AsbContextPrivate *priv = GET_PRIVATE (ctx); GList *l; const gchar *tmp; g_autoptr(GHashTable) hash = NULL; /* add all desktop apps to the hash */ hash = g_hash_table_new (g_str_hash, g_str_equal); for (l = priv->apps; l != NULL; l = l->next) { app = AS_APP (l->data); if (!ASB_IS_APP (app)) continue; if (as_app_get_pkgname_default (app) == NULL) continue; if (as_app_get_id_kind (app) != AS_ID_KIND_DESKTOP) continue; g_hash_table_insert (hash, (gpointer) as_app_get_id (app), app); } /* look for the thing that an addon extends */ for (l = priv->apps; l != NULL; l = l->next) { app = AS_APP (l->data); if (!ASB_IS_APP (app)) continue; if (as_app_get_pkgname_default (app) == NULL) continue; if (as_app_get_id_kind (app) != AS_ID_KIND_ADDON) continue; if (as_app_get_extends(app)->len == 0) continue; tmp = g_ptr_array_index (as_app_get_extends(app), 0); found = g_hash_table_lookup (hash, tmp); if (found != NULL) continue; found = as_store_get_app_by_id (priv->store_old, tmp); if (found != NULL) continue; /* do not add the addon */ as_app_add_veto (app, "%s has no parent of '%s'", as_app_get_id (app), tmp); g_print ("WARNING: %s has no parent of '%s'\n", as_app_get_id (app), tmp); } return TRUE; }
/** * asb_plugin_merge: */ void asb_plugin_merge (AsbPlugin *plugin, GList *list) { AsApp *app; AsApp *found; GList *l; _cleanup_hashtable_unref_ GHashTable *hash = NULL; /* make a hash table of ID->AsApp */ hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_object_unref); 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; g_hash_table_insert (hash, g_strdup (as_app_get_id (app)), g_object_ref (app)); } /* add addons where the pkgname is different from the * main package */ for (l = list; l != NULL; l = l->next) { if (!ASB_IS_APP (l->data)) continue; app = AS_APP (l->data); if (as_app_get_id_kind (app) != AS_ID_KIND_ADDON) continue; found = g_hash_table_lookup (hash, as_app_get_id (app)); if (found == NULL) continue; if (g_strcmp0 (as_app_get_pkgname_default (app), as_app_get_pkgname_default (found)) != 0) continue; as_app_add_veto (app, "absorbing addon %s shipped in " "main package %s", as_app_get_id (app), as_app_get_pkgname_default (app)); as_app_subsume_full (found, app, AS_APP_SUBSUME_FLAG_PARTIAL); } }
/** * as_app_parse_desktop_file: **/ gboolean as_app_parse_desktop_file (AsApp *app, const gchar *desktop_file, AsAppParseFlags flags, GError **error) { GKeyFileFlags kf_flags = G_KEY_FILE_KEEP_TRANSLATIONS; gchar *tmp; guint i; g_autoptr(GError) error_local = NULL; g_autofree gchar *app_id = NULL; g_autoptr(GKeyFile) kf = NULL; g_auto(GStrv) keys = NULL; /* load file */ kf = g_key_file_new (); if (flags & AS_APP_PARSE_FLAG_KEEP_COMMENTS) kf_flags |= G_KEY_FILE_KEEP_COMMENTS; if (!g_key_file_load_from_file (kf, desktop_file, kf_flags, &error_local)) { g_set_error (error, AS_APP_ERROR, AS_APP_ERROR_INVALID_TYPE, "Failed to parse %s: %s", desktop_file, error_local->message); return FALSE; } /* create app */ app_id = g_path_get_basename (desktop_file); as_app_set_id_kind (app, AS_ID_KIND_DESKTOP); /* is blacklisted */ if (as_utils_is_blacklisted_id (app_id)) as_app_add_veto (app, "%s is not an application", app_id); /* Ubuntu helpfully put the package name in the desktop file name */ tmp = g_strstr_len (app_id, -1, ":"); if (tmp != NULL) as_app_set_id (app, tmp + 1); else as_app_set_id (app, app_id); /* look at all the keys */ keys = g_key_file_get_keys (kf, G_KEY_FILE_DESKTOP_GROUP, NULL, error); if (keys == NULL) return FALSE; for (i = 0; keys[i] != NULL; i++) { if (!as_app_parse_file_key (app, kf, keys[i], flags, error)) return FALSE; if ((flags & AS_APP_PARSE_FLAG_USE_HEURISTICS) > 0) { if (!as_app_infer_file_key (app, kf, keys[i], error)) return FALSE; } } /* perform any fallbacks */ if ((flags & AS_APP_PARSE_FLAG_USE_FALLBACKS) > 0 && as_app_get_comment_size (app) == 0) { for (i = 0; keys[i] != NULL; i++) { if (!as_app_parse_file_key_fallback_comment (app, kf, keys[i], error)) return FALSE; } } /* all applications require icons */ if (as_app_get_icons(app)->len == 0) as_app_add_veto (app, "%s has no icon", app_id); return TRUE; }
/** * as_app_parse_file_key: **/ static gboolean as_app_parse_file_key (AsApp *app, GKeyFile *kf, const gchar *key, AsAppParseFlags flags, GError **error) { gchar *dot = NULL; guint i; guint j; g_autofree gchar *locale = NULL; g_autofree gchar *tmp = NULL; g_auto(GStrv) list = NULL; /* NoDisplay */ if (g_strcmp0 (key, G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY) == 0) { tmp = g_key_file_get_string (kf, G_KEY_FILE_DESKTOP_GROUP, key, NULL); if (tmp != NULL && strcasecmp (tmp, "True") == 0) as_app_add_veto (app, "NoDisplay=true"); /* Type */ } else if (g_strcmp0 (key, G_KEY_FILE_DESKTOP_KEY_TYPE) == 0) { tmp = g_key_file_get_string (kf, G_KEY_FILE_DESKTOP_GROUP, key, NULL); if (g_strcmp0 (tmp, G_KEY_FILE_DESKTOP_TYPE_APPLICATION) != 0) { g_set_error_literal (error, AS_APP_ERROR, AS_APP_ERROR_INVALID_TYPE, "not an application"); return FALSE; } /* Icon */ } else if (g_strcmp0 (key, G_KEY_FILE_DESKTOP_KEY_ICON) == 0) { tmp = g_key_file_get_string (kf, G_KEY_FILE_DESKTOP_GROUP, key, NULL); if (tmp != NULL && tmp[0] != '\0') { g_autoptr(AsIcon) icon = NULL; icon = as_icon_new (); as_icon_set_name (icon, tmp); dot = g_strstr_len (tmp, -1, "."); if (dot != NULL) *dot = '\0'; if (as_utils_is_stock_icon_name (tmp)) { as_icon_set_name (icon, tmp); as_icon_set_kind (icon, AS_ICON_KIND_STOCK); } else if ((flags & AS_APP_PARSE_FLAG_USE_FALLBACKS) > 0 && _as_utils_is_stock_icon_name_fallback (tmp)) { as_icon_set_name (icon, tmp); as_icon_set_kind (icon, AS_ICON_KIND_STOCK); } else { as_icon_set_kind (icon, AS_ICON_KIND_LOCAL); } as_app_add_icon (app, icon); } /* Categories */ } else if (g_strcmp0 (key, G_KEY_FILE_DESKTOP_KEY_CATEGORIES) == 0) { list = g_key_file_get_string_list (kf, G_KEY_FILE_DESKTOP_GROUP, key, NULL, NULL); for (i = 0; list[i] != NULL; i++) { /* not a standard category */ if (g_str_has_prefix (list[i], "X-")) continue; /* check the category is valid */ if (!as_utils_is_category_id (list[i])) continue; /* ignore some useless keys */ if (g_strcmp0 (list[i], "GTK") == 0) continue; if (g_strcmp0 (list[i], "Qt") == 0) continue; if (g_strcmp0 (list[i], "KDE") == 0) continue; if (g_strcmp0 (list[i], "GNOME") == 0) continue; as_app_add_category (app, list[i]); } } else if (g_strcmp0 (key, "Keywords") == 0) { list = g_key_file_get_string_list (kf, G_KEY_FILE_DESKTOP_GROUP, key, NULL, NULL); for (i = 0; list[i] != NULL; i++) { g_auto(GStrv) kw_split = NULL; kw_split = g_strsplit (list[i], ",", -1); for (j = 0; kw_split[j] != NULL; j++) { if (kw_split[j][0] == '\0') continue; as_app_add_keyword (app, "C", kw_split[j]); } } } else if (g_str_has_prefix (key, "Keywords")) { locale = as_app_desktop_key_get_locale (key); list = g_key_file_get_locale_string_list (kf, G_KEY_FILE_DESKTOP_GROUP, key, locale, NULL, NULL); for (i = 0; list[i] != NULL; i++) { g_auto(GStrv) kw_split = NULL; kw_split = g_strsplit (list[i], ",", -1); for (j = 0; kw_split[j] != NULL; j++) { if (kw_split[j][0] == '\0') continue; as_app_add_keyword (app, locale, kw_split[j]); } } } else if (g_strcmp0 (key, "MimeType") == 0) { list = g_key_file_get_string_list (kf, G_KEY_FILE_DESKTOP_GROUP, key, NULL, NULL); for (i = 0; list[i] != NULL; i++) as_app_add_mimetype (app, list[i]); } else if (g_strcmp0 (key, "X-AppInstall-Package") == 0) { tmp = g_key_file_get_string (kf, G_KEY_FILE_DESKTOP_GROUP, key, NULL); if (tmp != NULL && tmp[0] != '\0') as_app_add_pkgname (app, tmp); /* OnlyShowIn */ } else if (g_strcmp0 (key, G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN) == 0) { /* if an app has only one entry, it's that desktop */ list = g_key_file_get_string_list (kf, G_KEY_FILE_DESKTOP_GROUP, key, NULL, NULL); if (g_strv_length (list) == 1) as_app_set_project_group (app, list[0]); /* Name */ } else if (g_strcmp0 (key, G_KEY_FILE_DESKTOP_KEY_NAME) == 0 || g_strcmp0 (key, "_Name") == 0) { tmp = g_key_file_get_string (kf, G_KEY_FILE_DESKTOP_GROUP, key, NULL); if (tmp != NULL && tmp[0] != '\0') as_app_set_name (app, "C", tmp); /* Name[] */ } else if (g_str_has_prefix (key, G_KEY_FILE_DESKTOP_KEY_NAME)) { locale = as_app_desktop_key_get_locale (key); tmp = g_key_file_get_locale_string (kf, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, locale, NULL); if (tmp != NULL && tmp[0] != '\0') as_app_set_name (app, locale, tmp); /* Comment */ } else if (g_strcmp0 (key, G_KEY_FILE_DESKTOP_KEY_COMMENT) == 0 || g_strcmp0 (key, "_Comment") == 0) { tmp = g_key_file_get_string (kf, G_KEY_FILE_DESKTOP_GROUP, key, NULL); if (tmp != NULL && tmp[0] != '\0') as_app_set_comment (app, "C", tmp); /* Comment[] */ } else if (g_str_has_prefix (key, G_KEY_FILE_DESKTOP_KEY_COMMENT)) { locale = as_app_desktop_key_get_locale (key); tmp = g_key_file_get_locale_string (kf, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_COMMENT, locale, NULL); if (tmp != NULL && tmp[0] != '\0') as_app_set_comment (app, locale, tmp); /* non-standard */ } else if (g_strcmp0 (key, "X-Ubuntu-Software-Center-Name") == 0) { tmp = g_key_file_get_string (kf, G_KEY_FILE_DESKTOP_GROUP, key, NULL); if (tmp != NULL && tmp[0] != '\0') as_app_set_name (app, "C", tmp); } else if (g_str_has_prefix (key, "X-Ubuntu-Software-Center-Name")) { locale = as_app_desktop_key_get_locale (key); tmp = g_key_file_get_locale_string (kf, G_KEY_FILE_DESKTOP_GROUP, "X-Ubuntu-Software-Center-Name", locale, NULL); if (tmp != NULL && tmp[0] != '\0') as_app_set_name (app, locale, tmp); } 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_plugin_process_app: */ gboolean asb_plugin_process_app (AsbPlugin *plugin, AsbPackage *pkg, AsbApp *app, const gchar *tmpdir, GError **error) { const gchar *tmp; AsRelease *release; gchar **deps; gchar **filelist; GPtrArray *releases; guint i; gint64 secs; guint days; /* add extra categories */ tmp = as_app_get_id (AS_APP (app)); if (g_strcmp0 (tmp, "0install.desktop") == 0) as_app_add_category (AS_APP (app), "System", -1); if (g_strcmp0 (tmp, "alacarte.desktop") == 0) as_app_add_category (AS_APP (app), "System", -1); if (g_strcmp0 (tmp, "deja-dup.desktop") == 0) as_app_add_category (AS_APP (app), "Utility", -1); if (g_strcmp0 (tmp, "gddccontrol.desktop") == 0) as_app_add_category (AS_APP (app), "System", -1); if (g_strcmp0 (tmp, "nautilus.desktop") == 0) as_app_add_category (AS_APP (app), "System", -1); if (g_strcmp0 (tmp, "pessulus.desktop") == 0) as_app_add_category (AS_APP (app), "System", -1); if (g_strcmp0 (tmp, "pmdefaults.desktop") == 0) as_app_add_category (AS_APP (app), "System", -1); if (g_strcmp0 (tmp, "fwfstab.desktop") == 0) as_app_add_category (AS_APP (app), "System", -1); if (g_strcmp0 (tmp, "bmpanel2cfg.desktop") == 0) as_app_add_category (AS_APP (app), "System", -1); /* add extra project groups */ if (g_strcmp0 (tmp, "nemo.desktop") == 0) as_app_set_project_group (AS_APP (app), "Cinnamon", -1); if (g_strcmp0 (tmp, "xfdashboard.desktop") == 0) as_app_set_project_group (AS_APP (app), "XFCE", -1); /* use the URL to guess the project group */ tmp = asb_package_get_url (pkg); if (as_app_get_project_group (AS_APP (app)) == NULL && tmp != NULL) { tmp = asb_glob_value_search (plugin->priv->project_groups, tmp); if (tmp != NULL) as_app_set_project_group (AS_APP (app), tmp, -1); } /* use summary to guess the project group */ tmp = as_app_get_comment (AS_APP (app), NULL); if (tmp != NULL && g_strstr_len (tmp, -1, "for KDE") != NULL) as_app_set_project_group (AS_APP (app), "KDE", -1); /* look for any installed docs */ filelist = asb_package_get_filelist (pkg); for (i = 0; filelist[i] != NULL; i++) { if (g_str_has_prefix (filelist[i], "/usr/share/help/")) { as_app_add_kudo_kind (AS_APP (app), AS_KUDO_KIND_USER_DOCS); break; } } /* look for a shell search provider */ for (i = 0; filelist[i] != NULL; i++) { if (g_str_has_prefix (filelist[i], "/usr/share/gnome-shell/search-providers/")) { as_app_add_kudo_kind (AS_APP (app), AS_KUDO_KIND_SEARCH_PROVIDER); break; } } /* look for a high contrast icon */ for (i = 0; filelist[i] != NULL; i++) { if (g_str_has_prefix (filelist[i], "/usr/share/icons/HighContrast/")) { as_app_add_kudo_kind (AS_APP (app), AS_KUDO_KIND_HIGH_CONTRAST); break; } } /* look for a modern toolkit */ deps = asb_package_get_deps (pkg); for (i = 0; deps != NULL && deps[i] != NULL; i++) { if (g_strcmp0 (deps[i], "libgtk-3.so.0") == 0 || g_strcmp0 (deps[i], "libQt5Core.so.5") == 0) { as_app_add_kudo_kind (AS_APP (app), AS_KUDO_KIND_MODERN_TOOLKIT); break; } } /* look for ancient toolkits */ for (i = 0; deps != NULL && deps[i] != NULL; i++) { if (g_strcmp0 (deps[i], "libgtk-1.2.so.0") == 0) { as_app_add_veto (AS_APP (app), "Uses obsolete GTK1 toolkit"); break; } if (g_strcmp0 (deps[i], "libglib-1.2.so.0") == 0) { as_app_add_veto (AS_APP (app), "Uses obsolete GLib library"); break; } if (g_strcmp0 (deps[i], "libqt-mt.so.3") == 0) { as_app_add_veto (AS_APP (app), "Uses obsolete QT3 toolkit"); break; } if (g_strcmp0 (deps[i], "liblcms.so.1") == 0) { as_app_add_veto (AS_APP (app), "Uses obsolete LCMS library"); break; } if (g_strcmp0 (deps[i], "libelektra.so.4") == 0) { as_app_add_veto (AS_APP (app), "Uses obsolete Elektra library"); break; } if (g_strcmp0 (deps[i], "libXt.so.6") == 0) { asb_app_add_requires_appdata (app, "Uses obsolete X11 toolkit"); break; } if (g_strcmp0 (deps[i], "wine-core") == 0) { asb_app_add_requires_appdata (app, "Uses wine"); break; } } /* has the application been updated in the last year */ releases = as_app_get_releases (AS_APP (app)); if (asb_context_get_api_version (plugin->ctx) < 0.8) { for (i = 0; i < releases->len; i++) { release = g_ptr_array_index (releases, i); secs = (g_get_real_time () / G_USEC_PER_SEC) - as_release_get_timestamp (release); days = secs / (60 * 60 * 24); if (secs > 0 && days < 365) { as_app_add_metadata (AS_APP (app), "X-Kudo-RecentRelease", "", -1); break; } } } /* has there been no upstream version recently */ if (releases->len > 0 && as_app_get_id_kind (AS_APP (app)) == AS_ID_KIND_DESKTOP) { release = g_ptr_array_index (releases, 0); secs = (g_get_real_time () / G_USEC_PER_SEC) - as_release_get_timestamp (release); days = secs / (60 * 60 * 24); /* we need AppData if the app needs saving */ if (secs > 0 && days > 365 * 5) { asb_app_add_requires_appdata (app, "Dead upstream for > %i years", 5); } } /* do any extra screenshots exist */ tmp = asb_package_get_config (pkg, "ScreenshotsExtra"); if (tmp != NULL) { _cleanup_free_ gchar *dirname = NULL; dirname = g_build_filename (tmp, as_app_get_id_filename (AS_APP (app)), NULL); if (g_file_test (dirname, G_FILE_TEST_EXISTS)) { if (!asb_plugin_hardcoded_add_screenshots (app, dirname, error)) return FALSE; } } /* a ConsoleOnly category means we require AppData */ if (as_app_has_category (AS_APP(app), "ConsoleOnly")) asb_app_add_requires_appdata (app, "ConsoleOnly"); /* no categories means we require AppData */ if (as_app_get_id_kind (AS_APP (app)) == AS_ID_KIND_DESKTOP && as_app_get_categories(AS_APP(app))->len == 0) asb_app_add_requires_appdata (app, "no Categories"); 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; }
/** * _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 gs_plugin_steam_update_store_app (GsPlugin *plugin, AsStore *store, GHashTable *app, GError **error) { const gchar *name; GVariant *tmp; guint32 gameid; gchar *app_id; g_autofree gchar *cache_basename = NULL; g_autofree gchar *cache_fn = NULL; g_autofree gchar *gameid_str = NULL; g_autofree gchar *html = NULL; g_autofree gchar *uri = NULL; g_autoptr(AsApp) item = NULL; /* this is the key */ tmp = g_hash_table_lookup (app, "gameid"); if (tmp == NULL) return TRUE; gameid = g_variant_get_uint32 (tmp); /* valve use the name as the application ID, not the gameid */ tmp = g_hash_table_lookup (app, "name"); if (tmp == NULL) return TRUE; name = g_variant_get_string (tmp, NULL); app_id = g_strdup_printf ("%s.desktop", name); /* already exists */ if (as_store_get_app_by_id (store, app_id) != NULL) { g_debug ("already exists %" G_GUINT32_FORMAT ", skipping", gameid); return TRUE; } /* create application with the gameid as the key */ g_debug ("parsing steam %" G_GUINT32_FORMAT, gameid); item = as_app_new (); as_app_set_kind (item, AS_APP_KIND_DESKTOP); as_app_set_project_license (item, "Steam"); as_app_set_id (item, app_id); as_app_set_name (item, NULL, name); as_app_add_category (item, "Game"); as_app_add_kudo_kind (item, AS_KUDO_KIND_MODERN_TOOLKIT); as_app_set_comment (item, NULL, "Available on Steam"); /* this is for the GNOME Software plugin */ gameid_str = g_strdup_printf ("%" G_GUINT32_FORMAT, gameid); as_app_add_metadata (item, "X-Steam-GameID", gameid_str); as_app_add_metadata (item, "GnomeSoftware::Plugin", "steam"); /* ban certains apps based on the name */ if (g_strstr_len (name, -1, "Dedicated Server") != NULL) as_app_add_veto (item, "Dedicated Server"); /* oslist */ tmp = g_hash_table_lookup (app, "oslist"); if (tmp == NULL) { as_app_add_veto (item, "No operating systems listed"); } else if (g_strstr_len (g_variant_get_string (tmp, NULL), -1, "linux") == NULL) { as_app_add_veto (item, "No Linux support"); } /* url: homepage */ tmp = g_hash_table_lookup (app, "homepage"); if (tmp != NULL) as_app_add_url (item, AS_URL_KIND_HOMEPAGE, g_variant_get_string (tmp, NULL)); /* developer name */ tmp = g_hash_table_lookup (app, "developer"); if (tmp != NULL) as_app_set_developer_name (item, NULL, g_variant_get_string (tmp, NULL)); /* type */ tmp = g_hash_table_lookup (app, "type"); if (tmp != NULL) { const gchar *kind = g_variant_get_string (tmp, NULL); if (g_strcmp0 (kind, "DLC") == 0 || g_strcmp0 (kind, "Config") == 0 || g_strcmp0 (kind, "Tool") == 0) as_app_add_veto (item, "type is %s", kind); } /* don't bother saving apps with failures */ if (as_app_get_vetos(item)->len > 0) return TRUE; /* icons */ tmp = g_hash_table_lookup (app, "clienticns"); if (tmp != NULL) { g_autoptr(GError) error_local = NULL; g_autofree gchar *ic_uri = NULL; ic_uri = g_strdup_printf ("https://steamcdn-a.akamaihd.net/steamcommunity/public/images/apps/%" G_GUINT32_FORMAT "/%s.icns", gameid, g_variant_get_string (tmp, NULL)); if (!gs_plugin_steam_download_icon (plugin, item, ic_uri, &error_local)) { g_warning ("Failed to parse clienticns: %s", error_local->message); } } /* try clienticon */ if (as_app_get_icons(item)->len == 0) { tmp = g_hash_table_lookup (app, "clienticon"); if (tmp != NULL) { g_autoptr(GError) error_local = NULL; g_autofree gchar *ic_uri = NULL; ic_uri = g_strdup_printf ("http://cdn.akamai.steamstatic.com/steamcommunity/public/images/apps/%" G_GUINT32_FORMAT "/%s.ico", gameid, g_variant_get_string (tmp, NULL)); if (!gs_plugin_steam_download_icon (plugin, item, ic_uri, &error_local)) { g_warning ("Failed to parse clienticon: %s", error_local->message); } } } /* fall back to a resized logo */ if (as_app_get_icons(item)->len == 0) { tmp = g_hash_table_lookup (app, "logo"); if (tmp != NULL) { AsIcon *icon = NULL; g_autofree gchar *ic_uri = NULL; ic_uri = g_strdup_printf ("http://cdn.akamai.steamstatic.com/steamcommunity/public/images/apps/%" G_GUINT32_FORMAT "/%s.jpg", gameid, g_variant_get_string (tmp, NULL)); icon = as_icon_new (); as_icon_set_kind (icon, AS_ICON_KIND_REMOTE); as_icon_set_url (icon, ic_uri); as_app_add_icon (item, icon); } } /* size */ tmp = g_hash_table_lookup (app, "maxsize"); if (tmp != NULL) { /* string when over 16Gb... :/ */ if (g_strcmp0 (g_variant_get_type_string (tmp), "u") == 0) { g_autofree gchar *val = NULL; val = g_strdup_printf ("%" G_GUINT32_FORMAT, g_variant_get_uint32 (tmp)); as_app_add_metadata (item, "X-Steam-Size", val); } else { as_app_add_metadata (item, "X-Steam-Size", g_variant_get_string (tmp, NULL)); } } /* download page from the store */ cache_basename = g_strdup_printf ("%s.html", gameid_str); cache_fn = gs_utils_get_cache_filename ("steam", cache_basename, GS_UTILS_CACHE_FLAG_WRITEABLE, error); if (cache_fn == NULL) return FALSE; if (!g_file_test (cache_fn, G_FILE_TEST_EXISTS)) { g_autoptr(GsApp) app_dl = gs_app_new (gs_plugin_get_name (plugin)); uri = g_strdup_printf ("http://store.steampowered.com/app/%s/", gameid_str); if (!gs_plugin_download_file (plugin, app_dl, uri, cache_fn, NULL, /* GCancellable */ error)) return FALSE; } /* get screenshots and descriptions */ if (!g_file_get_contents (cache_fn, &html, NULL, error)) { gs_utils_error_convert_gio (error); return FALSE; } if (!gs_plugin_steam_update_screenshots (item, html, error)) return FALSE; if (!gs_plugin_steam_update_description (item, html, error)) return FALSE; /* add */ as_store_add_app (store, item); return TRUE; }
static gboolean as_app_parse_file_key (AsApp *app, GKeyFile *kf, const gchar *key, AsAppParseFlags flags, GError **error) { guint i; guint j; g_autofree gchar *locale = NULL; g_autofree gchar *tmp = NULL; g_auto(GStrv) list = NULL; /* NoDisplay */ if (g_strcmp0 (key, G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY) == 0) { tmp = g_key_file_get_string (kf, G_KEY_FILE_DESKTOP_GROUP, key, NULL); if (tmp != NULL && strcasecmp (tmp, "True") == 0) as_app_add_veto (app, "NoDisplay=true"); /* Type */ } else if (g_strcmp0 (key, G_KEY_FILE_DESKTOP_KEY_TYPE) == 0) { tmp = g_key_file_get_string (kf, G_KEY_FILE_DESKTOP_GROUP, key, NULL); if (g_strcmp0 (tmp, G_KEY_FILE_DESKTOP_TYPE_APPLICATION) != 0) { g_set_error_literal (error, AS_APP_ERROR, AS_APP_ERROR_INVALID_TYPE, "not an application"); return FALSE; } /* Icon */ } else if (g_strcmp0 (key, G_KEY_FILE_DESKTOP_KEY_ICON) == 0) { tmp = g_key_file_get_string (kf, G_KEY_FILE_DESKTOP_GROUP, key, NULL); if (tmp != NULL && tmp[0] != '\0') { g_autoptr(AsIcon) icon = NULL; icon = as_app_desktop_create_icon (app, tmp, flags); as_app_add_icon (app, icon); } /* Categories */ } else if (g_strcmp0 (key, G_KEY_FILE_DESKTOP_KEY_CATEGORIES) == 0) { list = g_key_file_get_string_list (kf, G_KEY_FILE_DESKTOP_GROUP, key, NULL, NULL); for (i = 0; list[i] != NULL; i++) { const gchar *category_blacklist[] = { "X-GNOME-Settings-Panel", "X-Unity-Settings-Panel", NULL }; /* we have to veto these */ for (j = 0; category_blacklist[j] != NULL; j++) { if (g_strcmp0 (list[i], category_blacklist[j]) == 0) { as_app_add_veto (app, "Has category %s", category_blacklist[j]); } } /* check the category is valid */ if (!as_utils_is_category_id (list[i])) continue; /* ignore some useless keys */ if (g_strcmp0 (list[i], "GTK") == 0) continue; if (g_strcmp0 (list[i], "Qt") == 0) continue; if (g_strcmp0 (list[i], "KDE") == 0) continue; if (g_strcmp0 (list[i], "GNOME") == 0) continue; as_app_add_category (app, list[i]); } } else if (g_strcmp0 (key, "Keywords") == 0) { list = g_key_file_get_string_list (kf, G_KEY_FILE_DESKTOP_GROUP, key, NULL, NULL); for (i = 0; list[i] != NULL; i++) { g_auto(GStrv) kw_split = NULL; kw_split = g_strsplit (list[i], ",", -1); for (j = 0; kw_split[j] != NULL; j++) { if (kw_split[j][0] == '\0') continue; as_app_add_keyword (app, "C", kw_split[j]); } } } else if (g_str_has_prefix (key, "Keywords")) { locale = as_app_desktop_key_get_locale (key); if (flags & AS_APP_PARSE_FLAG_ONLY_NATIVE_LANGS && !g_strv_contains (g_get_language_names (), locale)) return TRUE; list = g_key_file_get_locale_string_list (kf, G_KEY_FILE_DESKTOP_GROUP, key, locale, NULL, NULL); for (i = 0; list[i] != NULL; i++) { g_auto(GStrv) kw_split = NULL; kw_split = g_strsplit (list[i], ",", -1); for (j = 0; kw_split[j] != NULL; j++) { if (kw_split[j][0] == '\0') continue; as_app_add_keyword (app, locale, kw_split[j]); } } } else if (g_strcmp0 (key, "MimeType") == 0) { list = g_key_file_get_string_list (kf, G_KEY_FILE_DESKTOP_GROUP, key, NULL, NULL); for (i = 0; list[i] != NULL; i++) as_app_add_mimetype (app, list[i]); } else if (g_strcmp0 (key, "X-AppInstall-Package") == 0) { tmp = g_key_file_get_string (kf, G_KEY_FILE_DESKTOP_GROUP, key, NULL); if (tmp != NULL && tmp[0] != '\0') as_app_add_pkgname (app, tmp); /* OnlyShowIn */ } else if (g_strcmp0 (key, G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN) == 0) { /* if an app has only one entry, it's that desktop */ list = g_key_file_get_string_list (kf, G_KEY_FILE_DESKTOP_GROUP, key, NULL, NULL); if (g_strv_length (list) == 1) as_app_set_project_group (app, list[0]); /* Name */ } else if (g_strcmp0 (key, G_KEY_FILE_DESKTOP_KEY_NAME) == 0 || g_strcmp0 (key, "_Name") == 0) { tmp = g_key_file_get_string (kf, G_KEY_FILE_DESKTOP_GROUP, key, NULL); if (tmp != NULL && tmp[0] != '\0') as_app_set_name (app, "C", tmp); /* Name[] */ } else if (g_str_has_prefix (key, G_KEY_FILE_DESKTOP_KEY_NAME)) { locale = as_app_desktop_key_get_locale (key); if (flags & AS_APP_PARSE_FLAG_ONLY_NATIVE_LANGS && !g_strv_contains (g_get_language_names (), locale)) return TRUE; tmp = g_key_file_get_locale_string (kf, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, locale, NULL); if (tmp != NULL && tmp[0] != '\0') as_app_set_name (app, locale, tmp); /* Comment */ } else if (g_strcmp0 (key, G_KEY_FILE_DESKTOP_KEY_COMMENT) == 0 || g_strcmp0 (key, "_Comment") == 0) { tmp = g_key_file_get_string (kf, G_KEY_FILE_DESKTOP_GROUP, key, NULL); if (tmp != NULL && tmp[0] != '\0') as_app_set_comment (app, "C", tmp); /* Comment[] */ } else if (g_str_has_prefix (key, G_KEY_FILE_DESKTOP_KEY_COMMENT)) { locale = as_app_desktop_key_get_locale (key); if (flags & AS_APP_PARSE_FLAG_ONLY_NATIVE_LANGS && !g_strv_contains (g_get_language_names (), locale)) return TRUE; tmp = g_key_file_get_locale_string (kf, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_COMMENT, locale, NULL); if (tmp != NULL && tmp[0] != '\0') as_app_set_comment (app, locale, tmp); /* non-standard */ } else if (g_strcmp0 (key, "X-Ubuntu-Software-Center-Name") == 0) { tmp = g_key_file_get_string (kf, G_KEY_FILE_DESKTOP_GROUP, key, NULL); if (tmp != NULL && tmp[0] != '\0') as_app_set_name (app, "C", tmp); } else if (g_str_has_prefix (key, "X-Ubuntu-Software-Center-Name")) { locale = as_app_desktop_key_get_locale (key); if (flags & AS_APP_PARSE_FLAG_ONLY_NATIVE_LANGS && !g_strv_contains (g_get_language_names (), locale)) return TRUE; tmp = g_key_file_get_locale_string (kf, G_KEY_FILE_DESKTOP_GROUP, "X-Ubuntu-Software-Center-Name", locale, NULL); if (tmp != NULL && tmp[0] != '\0') as_app_set_name (app, locale, tmp); /* for Ubuntu */ } else if (g_strcmp0 (key, "X-AppStream-Ignore") == 0) { gboolean ret; ret = g_key_file_get_boolean (kf, G_KEY_FILE_DESKTOP_GROUP, key, NULL); if (ret) as_app_add_veto (app, "X-AppStream-Ignore"); } /* add any external attribute as metadata to the application */ if (flags & AS_APP_PARSE_FLAG_ADD_ALL_METADATA) as_app_parse_file_metadata (app, kf, key); return TRUE; }