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_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; }
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; }