/** * asb_plugin_merge: */ void asb_plugin_merge (AsbPlugin *plugin, GList **list) { AsApp *app; AsApp *found; GList *l; GList *list_new = NULL; const gchar *tmp; _cleanup_hashtable_unref_ GHashTable *hash; /* 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_full (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) { asb_plugin_add_app (&list_new, app); continue; } /* find the parent app */ found = g_hash_table_lookup (hash, tmp); if (found == NULL) { g_error ("Cannot find referenced '%s' from '%s'", tmp, as_app_get_id_full (app)); continue; } /* partially absorb */ g_debug ("partially absorbing %s into %s", as_app_get_id_full (app), as_app_get_id_full (found)); as_app_subsume_full (found, app, AS_APP_SUBSUME_FLAG_PARTIAL); } /* success */ g_list_free_full (*list, (GDestroyNotify) g_object_unref); *list = list_new; }
/** * asb_plugin_merge: */ void asb_plugin_merge (AsbPlugin *plugin, GList **list) { AsApp *app; AsApp *found; GList *l; GList *list_new = NULL; _cleanup_hashtable_unref_ GHashTable *hash; /* 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_full (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)) { asb_plugin_add_app (&list_new, l->data); continue; } app = AS_APP (l->data); if (as_app_get_id_kind (app) != AS_ID_KIND_ADDON) { asb_plugin_add_app (&list_new, l->data); continue; } found = g_hash_table_lookup (hash, as_app_get_id_full (app)); if (found == NULL) { asb_plugin_add_app (&list_new, l->data); continue; } if (g_strcmp0 (as_app_get_pkgname_default (app), as_app_get_pkgname_default (found)) == 0) { g_debug ("absorbing addon %s shipped in main package %s", as_app_get_id_full (app), as_app_get_pkgname_default (app)); as_app_subsume_full (found, app, AS_APP_SUBSUME_FLAG_PARTIAL); continue; } asb_plugin_add_app (&list_new, app); } /* success */ g_list_free_full (*list, (GDestroyNotify) g_object_unref); *list = list_new; }
/** * asb_plugin_process_filename: */ static gboolean asb_plugin_process_filename (AsbPlugin *plugin, AsbPackage *pkg, const gchar *filename, GList **apps, GError **error) { _cleanup_object_unref_ AsbApp *app = NULL; app = asb_app_new (pkg, NULL); if (!as_app_parse_file (AS_APP (app), filename, AS_APP_PARSE_FLAG_APPEND_DATA, error)) return FALSE; if (as_app_get_id_kind (AS_APP (app)) != AS_ID_KIND_ADDON) { g_set_error (error, ASB_PLUGIN_ERROR, ASB_PLUGIN_ERROR_FAILED, "%s is not an addon", as_app_get_id_full (AS_APP (app))); return FALSE; } asb_app_set_requires_appdata (app, FALSE); asb_plugin_add_app (apps, AS_APP (app)); return TRUE; }
/** * asb_plugin_absorb_parent_for_pkgname: */ static void asb_plugin_absorb_parent_for_pkgname (GList *list, AsApp *parent, const gchar *pkgname) { AsApp *app; GList *l; for (l = list; l != NULL; l = l->next) { app = AS_APP (l->data); if (as_app_get_id_kind (app) != AS_ID_KIND_ADDON) continue; if (g_strcmp0 (as_app_get_pkgname_default (app), pkgname) != 0) continue; g_debug ("Adding X-Merge-With-Parent on %s as %s depends on %s", as_app_get_id_full (app), as_app_get_pkgname_default (parent), as_app_get_pkgname_default (app)); as_app_add_metadata (app, "X-Merge-With-Parent", as_app_get_id_full (parent), -1); } }
/** * 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; }