/** * 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; }
/** * cra_plugin_process_app: */ gboolean cra_plugin_process_app (CraPlugin *plugin, CraPackage *pkg, CraApp *app, const gchar *tmpdir, GError **error) { const gchar *tmp; AsRelease *release; gchar **deps; gchar **filelist; GPtrArray *releases; guint i; guint secs; guint days; /* add extra categories */ tmp = as_app_get_id (AS_APP (app)); if (g_strcmp0 (tmp, "0install") == 0) as_app_add_category (AS_APP (app), "System", -1); if (g_strcmp0 (tmp, "alacarte") == 0) as_app_add_category (AS_APP (app), "System", -1); if (g_strcmp0 (tmp, "deja-dup") == 0) as_app_add_category (AS_APP (app), "Utility", -1); if (g_strcmp0 (tmp, "gddccontrol") == 0) as_app_add_category (AS_APP (app), "System", -1); if (g_strcmp0 (tmp, "nautilus") == 0) as_app_add_category (AS_APP (app), "System", -1); if (g_strcmp0 (tmp, "pessulus") == 0) as_app_add_category (AS_APP (app), "System", -1); if (g_strcmp0 (tmp, "pmdefaults") == 0) as_app_add_category (AS_APP (app), "System", -1); if (g_strcmp0 (tmp, "fwfstab") == 0) as_app_add_category (AS_APP (app), "System", -1); /* add extra project groups */ if (g_strcmp0 (tmp, "nemo") == 0) as_app_set_project_group (AS_APP (app), "Cinnamon", -1); if (g_strcmp0 (tmp, "xfdashboard") == 0) as_app_set_project_group (AS_APP (app), "XFCE", -1); /* use the URL to guess the project group */ tmp = cra_package_get_url (pkg); if (as_app_get_project_group (AS_APP (app)) == NULL && tmp != NULL) { tmp = cra_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 = cra_package_get_filelist (pkg); for (i = 0; filelist[i] != NULL; i++) { if (g_str_has_prefix (filelist[i], "/usr/share/help/")) { as_app_add_metadata (AS_APP (app), "X-Kudo-InstallsUserDocs", "", -1); 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_metadata (AS_APP (app), "X-Kudo-SearchProvider", "", -1); break; } } /* look for a modern toolkit */ deps = cra_package_get_deps (pkg); for (i = 0; deps != NULL && deps[i] != NULL; i++) { if (g_strcmp0 (deps[i], "libgtk-3.so.0") == 0) { as_app_add_metadata (AS_APP (app), "X-Kudo-GTK3", "", -1); break; } if (g_strcmp0 (deps[i], "libQt5Core.so.5") == 0) { as_app_add_metadata (AS_APP (app), "X-Kudo-QT5", "", -1); 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) { cra_app_add_veto (app, "Uses obsolete GTK1 toolkit"); break; } if (g_strcmp0 (deps[i], "libqt-mt.so.3") == 0) { cra_app_add_veto (app, "Uses obsolete QT3 toolkit"); break; } if (g_strcmp0 (deps[i], "liblcms.so.1") == 0) { cra_app_add_veto (app, "Uses obsolete LCMS library"); break; } if (g_strcmp0 (deps[i], "libelektra.so.4") == 0) { cra_app_add_veto (app, "Uses obsolete Elektra library"); break; } if (g_strcmp0 (deps[i], "libXt.so.6") == 0) { cra_app_add_requires_appdata (app, "Uses obsolete X11 toolkit"); break; } if (g_strcmp0 (deps[i], "wine-core") == 0) { cra_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)); 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 (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); /* this is just too old for us to care about */ if (days > 365 * 10) { cra_app_add_veto (app, "Dead upstream for %i years", secs / (60 * 60 * 24 * 365)); } /* we need AppData if the app needs saving */ else if (days > 365 * 5) { cra_app_add_requires_appdata (app, "Dead upstream for > %i years", 5); } } /* do any extra screenshots exist */ tmp = cra_package_get_config (pkg, "ScreenshotsExtra"); if (tmp != NULL) { _cleanup_free_ gchar *dirname = NULL; dirname = g_build_filename (tmp, as_app_get_id (AS_APP (app)), NULL); if (g_file_test (dirname, G_FILE_TEST_EXISTS)) { if (!cra_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")) cra_app_add_requires_appdata (app, "ConsoleOnly"); /* no categories means we require AppData */ if (as_app_get_categories(AS_APP(app))->len == 0) cra_app_add_requires_appdata (app, "no Categories"); return TRUE; }
/** * gs_appstream_refine_app: */ gboolean gs_appstream_refine_app (GsPlugin *plugin, GsApp *app, AsApp *item, GError **error) { AsRelease *rel; GHashTable *urls; GPtrArray *pkgnames; GPtrArray *kudos; const gchar *tmp; guint i; /* set the kind to be more precise */ if (gs_app_get_kind (app) == AS_APP_KIND_UNKNOWN || gs_app_get_kind (app) == AS_APP_KIND_GENERIC) { gs_app_set_kind (app, as_app_get_kind (item)); } /* is installed already */ if (gs_app_get_state (app) == AS_APP_STATE_UNKNOWN) { switch (as_app_get_source_kind (item)) { case AS_APP_SOURCE_KIND_APPDATA: case AS_APP_SOURCE_KIND_DESKTOP: gs_app_set_kind (app, AS_APP_KIND_DESKTOP); gs_app_set_state (app, AS_APP_STATE_INSTALLED); break; case AS_APP_SOURCE_KIND_METAINFO: gs_app_set_state (app, AS_APP_STATE_INSTALLED); break; case AS_APP_SOURCE_KIND_APPSTREAM: gs_app_set_state (app, as_app_get_state (item)); break; default: break; } } /* set management plugin automatically */ gs_refine_item_management_plugin (app, item); /* set id */ if (as_app_get_id (item) != NULL && gs_app_get_id (app) == NULL) gs_app_set_id (app, as_app_get_id (item)); /* set name */ tmp = as_app_get_name (item, NULL); if (tmp != NULL) { if (g_str_has_prefix (tmp, "(Nightly) ")) { tmp += 10; if (gs_app_get_metadata_item (app, "X-XdgApp-Tags") == NULL) gs_app_set_metadata (app, "X-XdgApp-Tags", "nightly"); } gs_app_set_name (app, GS_APP_QUALITY_HIGHEST, tmp); } /* set summary */ tmp = as_app_get_comment (item, NULL); if (tmp != NULL) { gs_app_set_summary (app, GS_APP_QUALITY_HIGHEST, tmp); } /* add urls */ urls = as_app_get_urls (item); if (g_hash_table_size (urls) > 0 && gs_app_get_url (app, AS_URL_KIND_HOMEPAGE) == NULL) { GList *l; g_autoptr(GList) keys = NULL; keys = g_hash_table_get_keys (urls); for (l = keys; l != NULL; l = l->next) { gs_app_set_url (app, as_url_kind_from_string (l->data), g_hash_table_lookup (urls, l->data)); } } /* set licence */ if (as_app_get_project_license (item) != NULL && gs_app_get_license (app) == NULL) gs_app_set_license (app, GS_APP_QUALITY_HIGHEST, as_app_get_project_license (item)); /* set keywords */ if (as_app_get_keywords (item, NULL) != NULL && gs_app_get_keywords (app) == NULL) { gs_app_set_keywords (app, as_app_get_keywords (item, NULL)); gs_app_add_kudo (app, GS_APP_KUDO_HAS_KEYWORDS); } /* set origin */ if (as_app_get_origin (item) != NULL && gs_app_get_origin (app) == NULL) { gs_app_set_origin (app, as_app_get_origin (item)); } /* set description */ tmp = as_app_get_description (item, NULL); if (tmp != NULL) { g_autofree gchar *from_xml = NULL; from_xml = as_markup_convert_simple (tmp, error); if (from_xml == NULL) { g_prefix_error (error, "trying to parse '%s': ", tmp); return FALSE; } gs_app_set_description (app, GS_APP_QUALITY_HIGHEST, from_xml); } /* set icon */ if (as_app_get_icon_default (item) != NULL && gs_app_get_pixbuf (app) == NULL) gs_refine_item_pixbuf (plugin, app, item); /* set categories */ if (as_app_get_categories (item) != NULL && gs_app_get_categories (app)->len == 0) gs_app_set_categories (app, as_app_get_categories (item)); /* set project group */ if (as_app_get_project_group (item) != NULL && gs_app_get_project_group (app) == NULL) gs_app_set_project_group (app, as_app_get_project_group (item)); /* this is a core application for the desktop and cannot be removed */ if (_as_app_has_compulsory_for_desktop (item, "GNOME") && gs_app_get_kind (app) == AS_APP_KIND_DESKTOP) gs_app_add_quirk (app, AS_APP_QUIRK_COMPULSORY); /* set id kind */ if (gs_app_get_kind (app) == AS_APP_KIND_UNKNOWN) gs_app_set_kind (app, as_app_get_kind (item)); /* copy all the metadata */ gs_appstream_copy_metadata (app, item); /* set package names */ pkgnames = as_app_get_pkgnames (item); if (pkgnames->len > 0 && gs_app_get_sources(app)->len == 0) gs_app_set_sources (app, pkgnames); /* set addons */ gs_appstream_refine_add_addons (plugin, app, item); /* set screenshots */ gs_appstream_refine_add_screenshots (app, item); /* are the screenshots perfect */ if (gs_appstream_are_screenshots_perfect (item)) gs_app_add_kudo (app, GS_APP_KUDO_PERFECT_SCREENSHOTS); /* was this application released recently */ if (gs_appstream_is_recent_release (item)) gs_app_add_kudo (app, GS_APP_KUDO_RECENT_RELEASE); /* add kudos */ if (as_app_get_language (item, plugin->locale) > 50) gs_app_add_kudo (app, GS_APP_KUDO_MY_LANGUAGE); /* add new-style kudos */ kudos = as_app_get_kudos (item); for (i = 0; i < kudos->len; i++) { tmp = g_ptr_array_index (kudos, i); switch (as_kudo_kind_from_string (tmp)) { case AS_KUDO_KIND_SEARCH_PROVIDER: gs_app_add_kudo (app, GS_APP_KUDO_SEARCH_PROVIDER); break; case AS_KUDO_KIND_USER_DOCS: gs_app_add_kudo (app, GS_APP_KUDO_INSTALLS_USER_DOCS); break; case AS_KUDO_KIND_APP_MENU: gs_app_add_kudo (app, GS_APP_KUDO_USES_APP_MENU); break; case AS_KUDO_KIND_MODERN_TOOLKIT: gs_app_add_kudo (app, GS_APP_KUDO_MODERN_TOOLKIT); break; case AS_KUDO_KIND_NOTIFICATIONS: gs_app_add_kudo (app, GS_APP_KUDO_USES_NOTIFICATIONS); break; case AS_KUDO_KIND_HIGH_CONTRAST: gs_app_add_kudo (app, GS_APP_KUDO_HIGH_CONTRAST); break; case AS_KUDO_KIND_HI_DPI_ICON: gs_app_add_kudo (app, GS_APP_KUDO_HI_DPI_ICON); break; default: g_debug ("no idea how to handle kudo '%s'", tmp); break; } } /* is there any update information */ rel = as_app_get_release_default (item); if (rel != NULL) { tmp = as_release_get_description (rel, NULL); if (tmp != NULL) { g_autofree gchar *desc = NULL; desc = as_markup_convert (tmp, AS_MARKUP_CONVERT_FORMAT_MARKDOWN, error); if (desc == NULL) return FALSE; gs_app_set_update_details (app, desc); } gs_app_set_update_urgency (app, as_release_get_urgency (rel)); gs_app_set_update_version (app, as_release_get_version (rel)); } return TRUE; }
/** * as_app_validate: * @app: a #AsApp instance. * @flags: the #AsAppValidateFlags to use, e.g. %AS_APP_VALIDATE_FLAG_NONE * @error: A #GError or %NULL. * * Validates data in the instance for style and consistency. * * Returns: (transfer container) (element-type AsProblem): A list of problems, or %NULL * * Since: 0.1.4 **/ GPtrArray * as_app_validate (AsApp *app, guint32 flags, GError **error) { AsAppProblems problems; AsFormat *format; GError *error_local = NULL; GHashTable *urls; GList *l; const gchar *description; const gchar *key; const gchar *license; const gchar *name; const gchar *summary; const gchar *tmp; const gchar *update_contact; gboolean deprecated_failure = FALSE; gboolean require_appstream_spec_only = FALSE; gboolean require_contactdetails = FALSE; gboolean require_copyright = FALSE; gboolean require_description = FALSE; gboolean require_project_license = FALSE; gboolean require_sentence_case = FALSE; gboolean require_translations = FALSE; gboolean require_url = TRUE; gboolean require_content_license = TRUE; gboolean require_name = TRUE; gboolean require_translation = FALSE; gboolean require_content_rating = FALSE; gboolean require_name_shorter_than_summary = FALSE; gboolean validate_license = TRUE; gboolean ret; guint length_name_max = 60; guint length_name_min = 3; guint length_summary_max = 200; guint length_summary_min = 8; guint number_para_max = 10; guint number_para_min = 1; guint str_len; g_autoptr(GList) keys = NULL; g_autoptr(AsAppValidateHelper) helper = g_new0 (AsAppValidateHelper, 1); /* has to be set */ format = as_app_get_format_default (app); if (format == NULL) { g_set_error_literal (error, AS_APP_ERROR, AS_APP_ERROR_FAILED, "cannot validate without at least one format"); return NULL; } /* only for desktop and console apps */ if (as_app_get_kind (app) == AS_APP_KIND_DESKTOP || as_app_get_kind (app) == AS_APP_KIND_CONSOLE) { require_content_rating = TRUE; require_description = TRUE; } /* relax the requirements a bit */ if ((flags & AS_APP_VALIDATE_FLAG_RELAX) > 0) { length_name_max = 100; length_summary_max = 200; require_content_license = FALSE; validate_license = FALSE; require_url = FALSE; number_para_max = 20; number_para_min = 1; require_sentence_case = FALSE; require_content_rating = FALSE; switch (as_format_get_kind (format)) { case AS_FORMAT_KIND_METAINFO: case AS_FORMAT_KIND_APPDATA: require_name = FALSE; break; default: break; } } /* make the requirements more strict */ if ((flags & AS_APP_VALIDATE_FLAG_STRICT) > 0) { deprecated_failure = TRUE; require_copyright = TRUE; require_translations = TRUE; require_project_license = TRUE; require_content_license = TRUE; require_appstream_spec_only = TRUE; require_sentence_case = TRUE; require_name_shorter_than_summary = TRUE; require_contactdetails = TRUE; require_translation = TRUE; number_para_min = 2; number_para_max = 4; } /* addons don't need such a long description */ switch (as_format_get_kind (format)) { case AS_FORMAT_KIND_METAINFO: case AS_FORMAT_KIND_APPDATA: number_para_min = 1; break; default: break; } /* set up networking */ helper->app = app; helper->probs = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); helper->screenshot_urls = g_ptr_array_new_with_free_func (g_free); helper->flags = flags; if (!as_app_validate_setup_networking (helper, error)) return NULL; /* invalid component type */ if (as_app_get_kind (app) == AS_APP_KIND_UNKNOWN) { ai_app_validate_add (helper, AS_PROBLEM_KIND_ATTRIBUTE_INVALID, "<component> has invalid type attribute"); } as_app_validate_check_id (helper, as_app_get_id (app)); /* metadata_license */ license = as_app_get_metadata_license (app); if (license != NULL) { if (require_content_license && !as_app_validate_is_content_license (license)) { ai_app_validate_add (helper, AS_PROBLEM_KIND_TAG_INVALID, "<metadata_license> is not valid [%s]", license); } else if (validate_license) { if (!as_app_validate_license (license, &error_local)) { g_prefix_error (&error_local, "<metadata_license> is not valid [%s]", license); ai_app_validate_add (helper, AS_PROBLEM_KIND_TAG_INVALID, "%s", error_local->message); g_clear_error (&error_local); } } } if (license == NULL) { switch (as_format_get_kind (format)) { case AS_FORMAT_KIND_APPDATA: case AS_FORMAT_KIND_METAINFO: ai_app_validate_add (helper, AS_PROBLEM_KIND_TAG_MISSING, "<metadata_license> is not present"); break; default: break; } } /* project_license */ license = as_app_get_project_license (app); if (license != NULL && validate_license) { if (!as_app_validate_license (license, &error_local)) { g_prefix_error (&error_local, "<project_license> is not valid [%s]", license); ai_app_validate_add (helper, AS_PROBLEM_KIND_TAG_INVALID, "%s", error_local->message); g_clear_error (&error_local); } } if (require_project_license && license == NULL) { switch (as_format_get_kind (format)) { case AS_FORMAT_KIND_APPDATA: case AS_FORMAT_KIND_METAINFO: ai_app_validate_add (helper, AS_PROBLEM_KIND_TAG_MISSING, "<project_license> is not present"); break; default: break; } } /* categories */ if (as_format_get_kind (format) == AS_FORMAT_KIND_APPSTREAM && as_app_get_kind (app) == AS_APP_KIND_DESKTOP) { GPtrArray *categories = as_app_get_categories (app); guint nr_toplevel_cats = 0; const gchar *cats[] = { "AudioVideo", "Development", "Education", "Game", "Graphics", "Network", "Office", "Science", "Settings", "System", "Utility", NULL }; for (guint i = 0; i < categories->len; i++) { const gchar *cat = g_ptr_array_index (categories, i); for (guint j = 0; cats[j] != NULL; j++) { if (g_strcmp0 (cats[j], cat) == 0) nr_toplevel_cats++; } } if (nr_toplevel_cats == 0) { ai_app_validate_add (helper, AS_PROBLEM_KIND_TAG_MISSING, "<category> must include main categories " "from the desktop entry spec"); } else if (nr_toplevel_cats > 3) { ai_app_validate_add (helper, AS_PROBLEM_KIND_TAG_MISSING, "too many main <category> types: %u", nr_toplevel_cats); } } /* translation */ if (require_translation && as_format_get_kind (format) == AS_FORMAT_KIND_APPDATA && as_app_get_translations (app)->len == 0) { ai_app_validate_add (helper, AS_PROBLEM_KIND_TAG_MISSING, "<translation> not specified"); } /* pkgname */ if (as_app_get_pkgname_default (app) != NULL && as_format_get_kind (format) == AS_FORMAT_KIND_METAINFO) { ai_app_validate_add (helper, AS_PROBLEM_KIND_TAG_INVALID, "<pkgname> not allowed in metainfo"); } /* appdata */ if (as_format_get_kind (format) == AS_FORMAT_KIND_APPDATA && as_app_get_kind (app) == AS_APP_KIND_DESKTOP) { AsIcon *icon = as_app_get_icon_default (app); if (icon != NULL && as_icon_get_kind (icon) != AS_ICON_KIND_REMOTE) { ai_app_validate_add (helper, AS_PROBLEM_KIND_TAG_INVALID, "<icon> not allowed in desktop appdata"); } } /* extends */ if (as_app_get_extends(app)->len == 0 && as_app_get_kind (app) == AS_APP_KIND_ADDON && as_format_get_kind (format) == AS_FORMAT_KIND_METAINFO) { ai_app_validate_add (helper, AS_PROBLEM_KIND_TAG_MISSING, "<extends> is not present"); } /* update_contact */ update_contact = as_app_get_update_contact (app); if (g_strcmp0 (update_contact, "someone_who_cares@upstream_project.org") == 0) { ai_app_validate_add (helper, AS_PROBLEM_KIND_TAG_INVALID, "<update_contact> is still set to a dummy value"); } if (update_contact != NULL && strlen (update_contact) < 6) { ai_app_validate_add (helper, AS_PROBLEM_KIND_STYLE_INCORRECT, "<update_contact> is too short [%s]", update_contact); } if (require_contactdetails && update_contact == NULL) { switch (as_format_get_kind (format)) { case AS_FORMAT_KIND_APPDATA: case AS_FORMAT_KIND_METAINFO: ai_app_validate_add (helper, AS_PROBLEM_KIND_TAG_MISSING, "<update_contact> is not present"); break; default: break; } } /* only found for files */ problems = as_app_get_problems (app); if (as_format_get_kind (format) == AS_FORMAT_KIND_APPDATA || as_format_get_kind (format) == AS_FORMAT_KIND_METAINFO) { if ((problems & AS_APP_PROBLEM_NO_XML_HEADER) > 0) { ai_app_validate_add (helper, AS_PROBLEM_KIND_MARKUP_INVALID, "<?xml> header not found"); } if (require_copyright && (problems & AS_APP_PROBLEM_NO_COPYRIGHT_INFO) > 0) { ai_app_validate_add (helper, AS_PROBLEM_KIND_VALUE_MISSING, "<!-- Copyright [year] [name] --> is not present"); } if (deprecated_failure && (problems & AS_APP_PROBLEM_UPDATECONTACT_FALLBACK) > 0) { ai_app_validate_add (helper, AS_PROBLEM_KIND_TAG_INVALID, "<updatecontact> should be <update_contact>"); } } /* check invalid values */ if ((problems & AS_APP_PROBLEM_INVALID_PROJECT_GROUP) > 0) { ai_app_validate_add (helper, AS_PROBLEM_KIND_TAG_INVALID, "<project_group> is not valid"); } /* only allow XML in the specification */ if (require_appstream_spec_only && (problems & AS_APP_PROBLEM_INVALID_XML_TAG) > 0) { ai_app_validate_add (helper, AS_PROBLEM_KIND_TAG_INVALID, "XML data contains unknown tag"); } /* only allow XML in the specification */ if (problems & AS_APP_PROBLEM_EXPECTED_CHILDREN) { ai_app_validate_add (helper, AS_PROBLEM_KIND_TAG_INVALID, "Expected children for tag"); } /* only allow XML in the specification */ if (problems & AS_APP_PROBLEM_INVALID_KEYWORDS) { ai_app_validate_add (helper, AS_PROBLEM_KIND_TAG_INVALID, "<keyword> invalid contents"); } /* releases all have to have unique versions */ if (problems & AS_APP_PROBLEM_DUPLICATE_RELEASE) { ai_app_validate_add (helper, AS_PROBLEM_KIND_TAG_INVALID, "<release> version was duplicated"); } if (problems & AS_APP_PROBLEM_DUPLICATE_SCREENSHOT) { ai_app_validate_add (helper, AS_PROBLEM_KIND_TAG_INVALID, "<screenshot> content was duplicated"); } if (problems & AS_APP_PROBLEM_DUPLICATE_CONTENT_RATING) { ai_app_validate_add (helper, AS_PROBLEM_KIND_TAG_INVALID, "<content_rating> was duplicated"); } /* check for things that have to exist */ if (as_app_get_id (app) == NULL) { ai_app_validate_add (helper, AS_PROBLEM_KIND_TAG_MISSING, "<id> is not present"); } /* games require a content rating */ if (require_content_rating) { GPtrArray *ratings = as_app_get_content_ratings (app); if (ratings->len == 0) { ai_app_validate_add (helper, AS_PROBLEM_KIND_TAG_MISSING, "<content_rating> required " "[use https://odrs.gnome.org/oars]"); } } /* url */ urls = as_app_get_urls (app); keys = g_hash_table_get_keys (urls); for (l = keys; l != NULL; l = l->next) { key = l->data; if (g_strcmp0 (key, "unknown") == 0) { ai_app_validate_add (helper, AS_PROBLEM_KIND_TAG_INVALID, "<url> type invalid [%s]", key); } tmp = g_hash_table_lookup (urls, key); if (tmp == NULL || tmp[0] == '\0') continue; if (!g_str_has_prefix (tmp, "http://") && !g_str_has_prefix (tmp, "https://")) { ai_app_validate_add (helper, AS_PROBLEM_KIND_TAG_INVALID, "<url> does not start with 'http://' [%s]", tmp); } } /* screenshots */ as_app_validate_screenshots (app, helper); /* icons */ as_app_validate_icons (app, helper); /* releases */ if (!as_app_validate_releases (app, helper, error)) return NULL; /* kudos */ if (!as_app_validate_kudos (app, helper, error)) return NULL; /* name */ name = as_app_get_name (app, "C"); if (name != NULL) { str_len = (guint) strlen (name); if (str_len < length_name_min) { ai_app_validate_add (helper, AS_PROBLEM_KIND_STYLE_INCORRECT, "<name> is too short [%s] minimum is %u chars", name, length_name_min); } if (str_len > length_name_max) { ai_app_validate_add (helper, AS_PROBLEM_KIND_STYLE_INCORRECT, "<name> is too long [%s] maximum is %u chars", name, length_name_max); } if (ai_app_validate_fullstop_ending (name)) { ai_app_validate_add (helper, AS_PROBLEM_KIND_STYLE_INCORRECT, "<name> cannot end in '.' [%s]", name); } if (as_app_validate_has_hyperlink (name)) { ai_app_validate_add (helper, AS_PROBLEM_KIND_STYLE_INCORRECT, "<name> cannot contain a hyperlink [%s]", name); } if (require_sentence_case && !as_app_validate_has_first_word_capital (helper, name)) { ai_app_validate_add (helper, AS_PROBLEM_KIND_STYLE_INCORRECT, "<name> requires sentence case [%s]", name); } } else if (require_name) { ai_app_validate_add (helper, AS_PROBLEM_KIND_TAG_MISSING, "<name> is not present"); } /* comment */ summary = as_app_get_comment (app, "C"); if (summary != NULL) { str_len = (guint) strlen (summary); if (str_len < length_summary_min) { ai_app_validate_add (helper, AS_PROBLEM_KIND_STYLE_INCORRECT, "<summary> is too short [%s] minimum is %u chars", summary, length_summary_min); } if (str_len > length_summary_max) { ai_app_validate_add (helper, AS_PROBLEM_KIND_STYLE_INCORRECT, "<summary> is too long [%s] maximum is %u chars", summary, length_summary_max); } if (require_sentence_case && ai_app_validate_fullstop_ending (summary)) { ai_app_validate_add (helper, AS_PROBLEM_KIND_STYLE_INCORRECT, "<summary> cannot end in '.' [%s]", summary); } if (as_app_validate_has_hyperlink (summary)) { ai_app_validate_add (helper, AS_PROBLEM_KIND_STYLE_INCORRECT, "<summary> cannot contain a hyperlink [%s]", summary); } if (require_sentence_case && !as_app_validate_has_first_word_capital (helper, summary)) { ai_app_validate_add (helper, AS_PROBLEM_KIND_STYLE_INCORRECT, "<summary> requires sentence case [%s]", summary); } } else if (require_name) { ai_app_validate_add (helper, AS_PROBLEM_KIND_TAG_MISSING, "<summary> is not present"); } if (require_name_shorter_than_summary && summary != NULL && name != NULL && strlen (summary) < strlen (name)) { ai_app_validate_add (helper, AS_PROBLEM_KIND_STYLE_INCORRECT, "<summary> is shorter than <name>"); } description = as_app_get_description (app, "C"); if (description == NULL) { if (require_description) { ai_app_validate_add (helper, AS_PROBLEM_KIND_TAG_MISSING, "<description> required"); } } else { ret = as_app_validate_description (description, helper, number_para_min, number_para_max, FALSE, &error_local); if (!ret) { ai_app_validate_add (helper, AS_PROBLEM_KIND_MARKUP_INVALID, "%s", error_local->message); g_error_free (error_local); } } if (require_translations) { if (name != NULL && as_app_get_name_size (app) == 1 && (problems & AS_APP_PROBLEM_INTLTOOL_NAME) == 0) { ai_app_validate_add (helper, AS_PROBLEM_KIND_TRANSLATIONS_REQUIRED, "<name> has no translations"); } if (summary != NULL && as_app_get_comment_size (app) == 1 && (problems & AS_APP_PROBLEM_INTLTOOL_SUMMARY) == 0) { ai_app_validate_add (helper, AS_PROBLEM_KIND_TRANSLATIONS_REQUIRED, "<summary> has no translations"); } if (description != NULL && as_app_get_description_size (app) == 1 && (problems & AS_APP_PROBLEM_INTLTOOL_DESCRIPTION) == 0) { ai_app_validate_add (helper, AS_PROBLEM_KIND_TRANSLATIONS_REQUIRED, "<description> has no translations"); } } /* developer_name */ name = as_app_get_developer_name (app, NULL); if (name != NULL) { str_len = (guint) strlen (name); if (str_len < length_name_min) { ai_app_validate_add (helper, AS_PROBLEM_KIND_STYLE_INCORRECT, "<developer_name> is too short [%s] minimum is %u chars", name, length_name_min); } if (str_len > length_name_max) { ai_app_validate_add (helper, AS_PROBLEM_KIND_STYLE_INCORRECT, "<developer_name> is too long [%s] maximum is %u chars", name, length_name_max); } if (as_app_validate_has_hyperlink (name)) { ai_app_validate_add (helper, AS_PROBLEM_KIND_STYLE_INCORRECT, "<developer_name> cannot contain a hyperlink [%s]", name); } if (as_app_validate_has_email (name)) { ai_app_validate_add (helper, AS_PROBLEM_KIND_STYLE_INCORRECT, "<developer_name> cannot contain an email address [%s]", name); } } /* using deprecated names */ if (deprecated_failure && (problems & AS_APP_PROBLEM_DEPRECATED_LICENCE) > 0) { ai_app_validate_add (helper, AS_PROBLEM_KIND_ATTRIBUTE_INVALID, "<licence> is deprecated, use " "<metadata_license> instead"); } if ((problems & AS_APP_PROBLEM_MULTIPLE_ENTRIES) > 0) { ai_app_validate_add (helper, AS_PROBLEM_KIND_MARKUP_INVALID, "<application> used more than once"); } /* require homepage */ if (require_url && as_app_get_url_item (app, AS_URL_KIND_HOMEPAGE) == NULL) { switch (as_format_get_kind (format)) { case AS_FORMAT_KIND_APPDATA: case AS_FORMAT_KIND_METAINFO: ai_app_validate_add (helper, AS_PROBLEM_KIND_TAG_MISSING, "<url> is not present"); break; default: break; } } return helper->probs; }
static void gs_editor_refresh_details (GsEditor *self) { AsAppKind app_kind = AS_APP_KIND_UNKNOWN; GtkWidget *widget; const gchar *css = NULL; g_autoptr(GError) error = NULL; g_autoptr(GsApp) app = NULL; /* ignore changed events */ self->is_in_refresh = TRUE; /* create a GsApp for the AsApp */ if (self->selected_item != NULL) { app = gs_editor_convert_app (self, self->selected_item); g_debug ("refreshing details for %s", gs_app_get_id (app)); } /* get kind */ if (self->selected_item != NULL) app_kind = as_app_get_kind (self->selected_item); /* feature tiles */ if (app_kind != AS_APP_KIND_OS_UPGRADE) { if (self->selected_item != NULL) { gs_app_tile_set_app (GS_APP_TILE (self->featured_tile1), app); gtk_widget_set_sensitive (self->featured_tile1, TRUE); } else { gtk_widget_set_sensitive (self->featured_tile1, FALSE); } gtk_widget_set_visible (self->featured_tile1, TRUE); } else { gtk_widget_set_visible (self->featured_tile1, FALSE); } /* upgrade banner */ if (app_kind == AS_APP_KIND_OS_UPGRADE) { if (self->selected_item != NULL) { gs_upgrade_banner_set_app (GS_UPGRADE_BANNER (self->upgrade_banner), app); gtk_widget_set_sensitive (self->upgrade_banner, TRUE); } else { gtk_widget_set_sensitive (self->upgrade_banner, FALSE); } gtk_widget_set_visible (self->upgrade_banner, TRUE); } else { gtk_widget_set_visible (self->upgrade_banner, FALSE); } /* name */ widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "box_name")); if (self->selected_item != NULL) { const gchar *tmp; gtk_widget_set_visible (widget, app_kind == AS_APP_KIND_OS_UPGRADE); widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "entry_name")); tmp = as_app_get_name (self->selected_item, NULL); if (tmp != NULL) gtk_entry_set_text (GTK_ENTRY (widget), tmp); } else { gtk_widget_set_visible (widget, FALSE); } /* summary */ widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "box_summary")); if (self->selected_item != NULL) { const gchar *tmp; gtk_widget_set_visible (widget, app_kind == AS_APP_KIND_OS_UPGRADE); widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "entry_summary")); tmp = as_app_get_comment (self->selected_item, NULL); if (tmp != NULL) gtk_entry_set_text (GTK_ENTRY (widget), tmp); } else { gtk_widget_set_visible (widget, FALSE); } /* kudos */ widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "box_kudos")); if (self->selected_item != NULL) { gtk_widget_set_visible (widget, app_kind != AS_APP_KIND_OS_UPGRADE); } else { gtk_widget_set_visible (widget, TRUE); } /* category featured */ widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "checkbutton_category_featured")); if (self->selected_item != NULL) { gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), as_app_has_category (self->selected_item, "Featured")); gtk_widget_set_sensitive (widget, TRUE); } else { gtk_widget_set_sensitive (widget, FALSE); } /* kudo popular */ widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "checkbutton_editors_pick")); if (self->selected_item != NULL) { gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), as_app_has_kudo (self->selected_item, "GnomeSoftware::popular")); gtk_widget_set_sensitive (widget, TRUE); } else { gtk_widget_set_sensitive (widget, FALSE); } /* featured */ widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "textview_css")); if (self->selected_item != NULL) { GtkTextBuffer *buffer; GtkTextIter iter_end; GtkTextIter iter_start; g_autofree gchar *css_existing = NULL; if (app_kind == AS_APP_KIND_OS_UPGRADE) { css = as_app_get_metadata_item (self->selected_item, "GnomeSoftware::UpgradeBanner-css"); } else { css = as_app_get_metadata_item (self->selected_item, "GnomeSoftware::FeatureTile-css"); } if (css == NULL) css = ""; buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget)); gtk_text_buffer_get_bounds (buffer, &iter_start, &iter_end); css_existing = gtk_text_buffer_get_text (buffer, &iter_start, &iter_end, FALSE); if (g_strcmp0 (css_existing, css) != 0) gtk_text_buffer_set_text (buffer, css, -1); gtk_widget_set_sensitive (widget, TRUE); } else { gtk_widget_set_sensitive (widget, FALSE); } /* desktop ID */ widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "entry_desktop_id")); if (self->selected_item != NULL) { const gchar *id = as_app_get_id (self->selected_item); if (id == NULL) id = ""; gtk_entry_set_text (GTK_ENTRY (widget), id); gtk_widget_set_sensitive (widget, TRUE); } else { gtk_entry_set_text (GTK_ENTRY (widget), ""); gtk_widget_set_sensitive (widget, FALSE); } /* validate CSS */ if (css == NULL) { widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "label_infobar_css")); gtk_label_set_label (GTK_LABEL (widget), ""); widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "infobar_css")); gtk_info_bar_set_message_type (GTK_INFO_BAR (widget), GTK_MESSAGE_OTHER); } else if (!gs_design_validate_css (self, css, &error)) { g_autofree gchar *msg = g_strdup (error->message); g_strdelimit (msg, "\n\r<>", '\0'); widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "label_infobar_css")); gtk_label_set_label (GTK_LABEL (widget), msg); widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "infobar_css")); gtk_info_bar_set_message_type (GTK_INFO_BAR (widget), GTK_MESSAGE_WARNING); } else { widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "label_infobar_css")); gtk_label_set_label (GTK_LABEL (widget), _("CSS validated OK!")); widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "infobar_css")); gtk_info_bar_set_message_type (GTK_INFO_BAR (widget), GTK_MESSAGE_OTHER); } /* do not ignore changed events */ self->is_in_refresh = FALSE; }
static GsApp * gs_editor_convert_app (GsEditor *self, AsApp *item) { AsApp *item_global; AsAppState item_state; GsApp *app; const gchar *keys[] = { "GnomeSoftware::AppTile-css", "GnomeSoftware::FeatureTile-css", "GnomeSoftware::UpgradeBanner-css", NULL }; /* copy name, summary and description */ app = gs_app_new (as_app_get_id (item)); item_global = as_store_get_app_by_id (self->store_global, as_app_get_id (item)); if (item_global == NULL) { const gchar *tmp; g_autoptr(AsIcon) ic = NULL; g_debug ("no app found for %s, using fallback", as_app_get_id (item)); /* copy from AsApp, falling back to something sane */ tmp = as_app_get_name (item, NULL); if (tmp == NULL) tmp = "Application"; gs_app_set_name (app, GS_APP_QUALITY_NORMAL, tmp); tmp = as_app_get_comment (item, NULL); if (tmp == NULL) tmp = "Description"; gs_app_set_summary (app, GS_APP_QUALITY_NORMAL, tmp); tmp = as_app_get_description (item, NULL); if (tmp == NULL) tmp = "A multiline description"; gs_app_set_description (app, GS_APP_QUALITY_NORMAL, tmp); ic = as_icon_new (); as_icon_set_kind (ic, AS_ICON_KIND_STOCK); as_icon_set_name (ic, "application-x-executable"); gs_app_add_icon (app, ic); item_state = as_app_get_state (item); } else { GPtrArray *icons; g_debug ("found global app for %s", as_app_get_id (item)); gs_app_set_name (app, GS_APP_QUALITY_NORMAL, as_app_get_name (item_global, NULL)); gs_app_set_summary (app, GS_APP_QUALITY_NORMAL, as_app_get_comment (item_global, NULL)); gs_app_set_description (app, GS_APP_QUALITY_NORMAL, as_app_get_description (item_global, NULL)); icons = as_app_get_icons (item_global); for (guint i = 0; i < icons->len; i++) { AsIcon *icon = g_ptr_array_index (icons, i); gs_app_add_icon (app, icon); } item_state = as_app_get_state (item_global); } /* copy state */ if (item_state == AS_APP_STATE_UNKNOWN) item_state = AS_APP_STATE_AVAILABLE; gs_app_set_state (app, item_state); /* copy version */ gs_app_set_version (app, "3.28"); /* load pixbuf */ gs_editor_refine_app_pixbuf (app); /* copy metadata */ for (guint i = 0; keys[i] != NULL; i++) { g_autoptr(GError) error = NULL; const gchar *markup = as_app_get_metadata_item (item, keys[i]); if (markup != NULL) { g_autofree gchar *css_new = NULL; css_new = gs_editor_css_download_resources (self, markup, &error); if (css_new == NULL) { g_warning ("%s", error->message); gs_app_set_metadata (app, keys[i], markup); } else { gs_app_set_metadata (app, keys[i], css_new); } } else { gs_app_set_metadata (app, keys[i], NULL); } } return app; }
/** * 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; GPtrArray *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); if (g_strcmp0 (tmp, "wallpapoz.desktop") == 0) as_app_add_category (AS_APP (app), "System", -1); if (g_strcmp0 (tmp, "superkaramba.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 (asb_plugin_match_glob ("/usr/share/help/*", filelist[i])) { 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 (asb_plugin_match_glob ("/usr/share/gnome-shell/search-providers/*", filelist[i])) { 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 (asb_plugin_match_glob ("/usr/share/icons/HighContrast/*", filelist[i])) { as_app_add_kudo_kind (AS_APP (app), AS_KUDO_KIND_HIGH_CONTRAST); break; } if (asb_plugin_match_glob ("/usr/share/icons/hicolor/symbolic/apps/*.svg", filelist[i])) { 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; i < deps->len; i++) { tmp = g_ptr_array_index (deps, i); if (g_strcmp0 (tmp, "libgtk-3.so.0") == 0 || g_strcmp0 (tmp, "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; i < deps->len; i++) { tmp = g_ptr_array_index (deps, i); if (g_strcmp0 (tmp, "libgtk-1.2.so.0") == 0) { as_app_add_veto (AS_APP (app), "Uses obsolete GTK1 toolkit"); break; } if (g_strcmp0 (tmp, "libglib-1.2.so.0") == 0) { as_app_add_veto (AS_APP (app), "Uses obsolete GLib library"); break; } if (g_strcmp0 (tmp, "libqt-mt.so.3") == 0) { as_app_add_veto (AS_APP (app), "Uses obsolete QT3 toolkit"); break; } if (g_strcmp0 (tmp, "liblcms.so.1") == 0) { as_app_add_veto (AS_APP (app), "Uses obsolete LCMS library"); break; } if (g_strcmp0 (tmp, "libelektra.so.4") == 0) { as_app_add_veto (AS_APP (app), "Uses obsolete Elektra library"); break; } if (g_strcmp0 (tmp, "libXt.so.6") == 0) { asb_app_add_requires_appdata (app, "Uses obsolete X11 toolkit"); break; } if (g_strcmp0 (tmp, "Xvfb") == 0) { asb_app_add_requires_appdata (app, "Uses obsolete Xvfb"); break; } if (g_strcmp0 (tmp, "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); } } /* 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; }
/** * as_app_validate: * @app: a #AsApp instance. * @flags: the #AsAppValidateFlags to use, e.g. %AS_APP_VALIDATE_FLAG_NONE * @error: A #GError or %NULL. * * Validates data in the instance for style and consitency. * * Returns: (transfer container) (element-type AsProblem): A list of problems, or %NULL * * Since: 0.1.4 **/ GPtrArray * as_app_validate (AsApp *app, AsAppValidateFlags flags, GError **error) { AsAppProblems problems; AsAppValidateHelper helper; GError *error_local = NULL; GHashTable *urls; GList *l; GPtrArray *probs = NULL; const gchar *description; const gchar *id_full; const gchar *key; const gchar *license; const gchar *name; const gchar *summary; const gchar *tmp; const gchar *update_contact; gboolean deprectated_failure = FALSE; gboolean require_contactdetails = TRUE; gboolean require_copyright = FALSE; gboolean require_project_license = FALSE; gboolean require_translations = FALSE; gboolean require_url = TRUE; gboolean ret; guint length_name_max = 30; guint length_name_min = 3; guint length_summary_max = 100; guint length_summary_min = 8; guint number_para_max = 4; guint number_para_min = 2; guint str_len; _cleanup_list_free_ GList *keys = NULL; /* relax the requirements a bit */ if ((flags & AS_APP_VALIDATE_FLAG_RELAX) > 0) { length_name_max = 100; length_summary_max = 200; require_contactdetails = FALSE; require_url = FALSE; number_para_max = 10; number_para_min = 1; } /* make the requirements more strict */ if ((flags & AS_APP_VALIDATE_FLAG_STRICT) > 0) { deprectated_failure = TRUE; require_copyright = TRUE; require_translations = TRUE; require_project_license = TRUE; } /* set up networking */ helper.probs = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); helper.screenshot_urls = g_ptr_array_new_with_free_func (g_free); helper.flags = flags; helper.previous_para_was_short = FALSE; helper.para_chars_before_list = 0; helper.number_paragraphs = 0; ret = as_app_validate_setup_networking (&helper, error); if (!ret) goto out; /* success, enough */ probs = helper.probs; /* id */ ret = FALSE; id_full = as_app_get_id_full (app); switch (as_app_get_id_kind (app)) { case AS_ID_KIND_DESKTOP: if (g_str_has_suffix (id_full, ".desktop")) ret = TRUE; break; case AS_ID_KIND_FONT: if (g_str_has_suffix (id_full, ".ttf")) ret = TRUE; else if (g_str_has_suffix (id_full, ".otf")) ret = TRUE; break; case AS_ID_KIND_INPUT_METHOD: if (g_str_has_suffix (id_full, ".xml")) ret = TRUE; else if (g_str_has_suffix (id_full, ".db")) ret = TRUE; break; case AS_ID_KIND_CODEC: if (g_str_has_prefix (id_full, "gstreamer")) ret = TRUE; break; case AS_ID_KIND_UNKNOWN: ai_app_validate_add (probs, AS_PROBLEM_KIND_ATTRIBUTE_INVALID, "<id> has invalid type attribute"); break; case AS_ID_KIND_ADDON: /* anything goes */ ret = TRUE; default: break; } if (!ret) { ai_app_validate_add (probs, AS_PROBLEM_KIND_MARKUP_INVALID, "<id> does not have correct extension for kind"); } /* metadata_license */ license = as_app_get_metadata_license (app); if (license != NULL) { if (g_strcmp0 (license, "CC0-1.0") != 0 && g_strcmp0 (license, "CC-BY-3.0") != 0 && g_strcmp0 (license, "CC-BY-SA-3.0") != 0 && g_strcmp0 (license, "GFDL-1.3") != 0) { ai_app_validate_add (probs, AS_PROBLEM_KIND_TAG_INVALID, "<metadata_license> is not valid"); } } if (license == NULL) { switch (as_app_get_source_kind (app)) { case AS_APP_SOURCE_KIND_APPDATA: case AS_APP_SOURCE_KIND_METAINFO: ai_app_validate_add (probs, AS_PROBLEM_KIND_TAG_MISSING, "<metadata_license> is not present"); break; default: break; } } /* project_license */ license = as_app_get_project_license (app); if (license != NULL) { ret = as_app_validate_license (license, &error_local); if (!ret) { g_prefix_error (&error_local, "<project_license> is not valid: "); ai_app_validate_add (probs, AS_PROBLEM_KIND_TAG_INVALID, error_local->message); g_clear_error (&error_local); } } if (require_project_license && license == NULL) { switch (as_app_get_source_kind (app)) { case AS_APP_SOURCE_KIND_APPDATA: case AS_APP_SOURCE_KIND_METAINFO: ai_app_validate_add (probs, AS_PROBLEM_KIND_TAG_MISSING, "<project_license> is not present"); break; default: break; } } /* updatecontact */ update_contact = as_app_get_update_contact (app); if (g_strcmp0 (update_contact, "someone_who_cares@upstream_project.org") == 0) { ai_app_validate_add (probs, AS_PROBLEM_KIND_TAG_INVALID, "<update_contact> is still set to a dummy value"); } if (update_contact != NULL && strlen (update_contact) < 6) { ai_app_validate_add (probs, AS_PROBLEM_KIND_STYLE_INCORRECT, "<update_contact> is too short"); } if (require_contactdetails && update_contact == NULL) { switch (as_app_get_source_kind (app)) { case AS_APP_SOURCE_KIND_APPDATA: case AS_APP_SOURCE_KIND_METAINFO: ai_app_validate_add (probs, AS_PROBLEM_KIND_TAG_MISSING, "<updatecontact> is not present"); break; default: break; } } /* only found for files */ problems = as_app_get_problems (app); if (as_app_get_source_kind (app) == AS_APP_SOURCE_KIND_APPDATA || as_app_get_source_kind (app) == AS_APP_SOURCE_KIND_METAINFO) { if ((problems & AS_APP_PROBLEM_NO_XML_HEADER) > 0) { ai_app_validate_add (probs, AS_PROBLEM_KIND_MARKUP_INVALID, "<?xml> header not found"); } if (require_copyright && (problems & AS_APP_PROBLEM_NO_COPYRIGHT_INFO) > 0) { ai_app_validate_add (probs, AS_PROBLEM_KIND_VALUE_MISSING, "<!-- Copyright [year] [name] --> is not present"); } } /* check for things that have to exist */ if (as_app_get_id_full (app) == NULL) { ai_app_validate_add (probs, AS_PROBLEM_KIND_TAG_MISSING, "<id> is not present"); } /* url */ urls = as_app_get_urls (app); keys = g_hash_table_get_keys (urls); for (l = keys; l != NULL; l = l->next) { key = l->data; if (g_strcmp0 (key, "unknown") == 0) { ai_app_validate_add (probs, AS_PROBLEM_KIND_TAG_INVALID, "<url> type invalid"); } tmp = g_hash_table_lookup (urls, key); if (!g_str_has_prefix (tmp, "http://") && !g_str_has_prefix (tmp, "https://")) { ai_app_validate_add (probs, AS_PROBLEM_KIND_TAG_INVALID, "<url> does not start with 'http://'"); } } /* screenshots */ as_app_validate_screenshots (app, &helper); /* releases */ ret = as_app_validate_releases (app, &helper, error); if (!ret) goto out; /* name */ name = as_app_get_name (app, "C"); if (name != NULL) { str_len = strlen (name); if (str_len < length_name_min) { ai_app_validate_add (probs, AS_PROBLEM_KIND_STYLE_INCORRECT, "<name> is too short"); } if (str_len > length_name_max) { ai_app_validate_add (probs, AS_PROBLEM_KIND_STYLE_INCORRECT, "<name> is too long"); } if (ai_app_validate_fullstop_ending (name)) { ai_app_validate_add (probs, AS_PROBLEM_KIND_STYLE_INCORRECT, "<name> cannot end in '.'"); } if (as_app_validate_has_hyperlink (name)) { ai_app_validate_add (probs, AS_PROBLEM_KIND_STYLE_INCORRECT, "<name> cannot contain a hyperlink"); } } /* comment */ summary = as_app_get_comment (app, "C"); if (summary != NULL) { str_len = strlen (summary); if (str_len < length_summary_min) { ai_app_validate_add (probs, AS_PROBLEM_KIND_STYLE_INCORRECT, "<summary> is too short"); } if (str_len > length_summary_max) { ai_app_validate_add (probs, AS_PROBLEM_KIND_STYLE_INCORRECT, "<summary> is too long"); } if (ai_app_validate_fullstop_ending (summary)) { ai_app_validate_add (probs, AS_PROBLEM_KIND_STYLE_INCORRECT, "<summary> cannot end in '.'"); } if (as_app_validate_has_hyperlink (summary)) { ai_app_validate_add (probs, AS_PROBLEM_KIND_STYLE_INCORRECT, "<summary> cannot contain a hyperlink"); } } if (summary != NULL && name != NULL && strlen (summary) < strlen (name)) { ai_app_validate_add (probs, AS_PROBLEM_KIND_STYLE_INCORRECT, "<summary> is shorter than <name>"); } description = as_app_get_description (app, "C"); if (description != NULL) { ret = as_app_validate_description (description, &helper, number_para_min, number_para_max, &error_local); if (!ret) { ai_app_validate_add (probs, AS_PROBLEM_KIND_MARKUP_INVALID, error_local->message); g_error_free (error_local); } } if (require_translations) { if (name != NULL && as_app_get_name_size (app) == 1 && (problems & AS_APP_PROBLEM_INTLTOOL_NAME) == 0) { ai_app_validate_add (probs, AS_PROBLEM_KIND_TRANSLATIONS_REQUIRED, "<name> has no translations"); } if (summary != NULL && as_app_get_comment_size (app) == 1 && (problems & AS_APP_PROBLEM_INTLTOOL_SUMMARY) == 0) { ai_app_validate_add (probs, AS_PROBLEM_KIND_TRANSLATIONS_REQUIRED, "<summary> has no translations"); } if (description != NULL && as_app_get_description_size (app) == 1 && (problems & AS_APP_PROBLEM_INTLTOOL_DESCRIPTION) == 0) { ai_app_validate_add (probs, AS_PROBLEM_KIND_TRANSLATIONS_REQUIRED, "<description> has no translations"); } } /* using deprecated names */ if (deprectated_failure && (problems & AS_APP_PROBLEM_DEPRECATED_LICENCE) > 0) { ai_app_validate_add (probs, AS_PROBLEM_KIND_ATTRIBUTE_INVALID, "<licence> is deprecated, use " "<metadata_license> instead"); } if ((problems & AS_APP_PROBLEM_MULTIPLE_ENTRIES) > 0) { ai_app_validate_add (probs, AS_PROBLEM_KIND_MARKUP_INVALID, "<application> used more than once"); } /* require homepage */ if (require_url && as_app_get_url_item (app, AS_URL_KIND_HOMEPAGE) == NULL) { switch (as_app_get_source_kind (app)) { case AS_APP_SOURCE_KIND_APPDATA: case AS_APP_SOURCE_KIND_METAINFO: ai_app_validate_add (probs, AS_PROBLEM_KIND_TAG_MISSING, "<url> is not present"); break; default: break; } } out: g_ptr_array_unref (helper.screenshot_urls); if (helper.session != NULL) g_object_unref (helper.session); return probs; }