/** * as_merge_components: * * Merge selected data from two components. */ static void as_merge_components (AsComponent *dest_cpt, AsComponent *src_cpt) { guint i; AsMergeKind merge_kind; merge_kind = as_component_get_merge_kind (src_cpt); g_return_if_fail (merge_kind != AS_MERGE_KIND_NONE); /* FIXME: We need to merge more attributes */ /* merge stuff in append mode */ if (merge_kind == AS_MERGE_KIND_APPEND) { GPtrArray *suggestions; GPtrArray *cats; /* merge categories */ cats = as_component_get_categories (src_cpt); if (cats->len > 0) { g_autoptr(GHashTable) cat_table = NULL; GPtrArray *dest_categories; cat_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); for (i = 0; i < cats->len; i++) { const gchar *cat = (const gchar*) g_ptr_array_index (cats, i); g_hash_table_add (cat_table, g_strdup (cat)); } dest_categories = as_component_get_categories (dest_cpt); if (dest_categories->len > 0) { for (i = 0; i < dest_categories->len; i++) { const gchar *cat = (const gchar*) g_ptr_array_index (dest_categories, i); g_hash_table_add (cat_table, g_strdup (cat)); } } g_ptr_array_set_size (dest_categories, 0); as_hash_table_string_keys_to_array (cat_table, dest_categories); } /* merge suggestions */ suggestions = as_component_get_suggested (src_cpt); if (suggestions != NULL) { for (i = 0; i < suggestions->len; i++) { as_component_add_suggested (dest_cpt, AS_SUGGESTED (g_ptr_array_index (suggestions, i))); } } } /* merge stuff in replace mode */ if (merge_kind == AS_MERGE_KIND_REPLACE) { gchar **pkgnames; /* merge names */ if (as_component_get_name (src_cpt) != NULL) as_component_set_name (dest_cpt, as_component_get_name (src_cpt), as_component_get_active_locale (src_cpt)); /* merge package names */ pkgnames = as_component_get_pkgnames (src_cpt); if ((pkgnames != NULL) && (pkgnames[0] != '\0')) as_component_set_pkgnames (dest_cpt, as_component_get_pkgnames (src_cpt)); /* merge bundles */ if (as_component_has_bundle (src_cpt)) as_component_set_bundles_array (dest_cpt, as_component_get_bundles (src_cpt)); } g_debug ("Merged data for '%s'", as_component_get_data_id (dest_cpt)); }
/** * as_validator_analyze_component_metainfo_relation_cb: * * Helper function for GHashTable foreach iteration. */ static void as_validator_analyze_component_metainfo_relation_cb (const gchar *fname, AsComponent *cpt, struct MInfoCheckData *data) { gchar *tmp; /* if we have no component-id, we can't check anything */ if (as_component_get_id (cpt) == NULL) return; as_validator_set_current_cpt (data->validator, cpt); as_validator_set_current_fname (data->validator, fname); /* check if the fname and the component-id match */ tmp = g_strndup (as_component_get_id (cpt), g_strrstr (as_component_get_id (cpt), ".") - as_component_get_id (cpt)); if (!as_matches_metainfo (fname, tmp)) { /* the name-without-type didn't match - check for the full id in the component name */ if (!as_matches_metainfo (fname, as_component_get_id (cpt))) { as_validator_add_issue (data->validator, AS_ISSUE_IMPORTANCE_WARNING, AS_ISSUE_KIND_WRONG_NAME, "The metainfo filename does not match the component ID."); } } g_free (tmp); /* check if the referenced .desktop file exists */ if (as_component_get_kind (cpt) == AS_COMPONENT_KIND_DESKTOP_APP) { if (g_hash_table_contains (data->desktop_fnames, as_component_get_id (cpt))) { g_autofree gchar *desktop_fname_full = NULL; g_autoptr(GKeyFile) dfile = NULL; GError *tmp_error = NULL; desktop_fname_full = g_build_filename (data->apps_dir, as_component_get_id (cpt), NULL); dfile = g_key_file_new (); g_key_file_load_from_file (dfile, desktop_fname_full, G_KEY_FILE_NONE, &tmp_error); if (tmp_error != NULL) { as_validator_add_issue (data->validator, AS_ISSUE_IMPORTANCE_WARNING, AS_ISSUE_KIND_READ_ERROR, "Unable to read associated .desktop file: %s", tmp_error->message); g_error_free (tmp_error); tmp_error = NULL; } else { /* we successfully opened the .desktop file, now perform some checks */ /* name */ if ((g_strcmp0 (as_component_get_name (cpt), "") == 0) && (!g_key_file_has_key (dfile, "Desktop Entry", "Name", NULL))) { /* we don't have a summary, and there is also none in the .desktop file - this is bad. */ as_validator_add_issue (data->validator, AS_ISSUE_IMPORTANCE_ERROR, AS_ISSUE_KIND_VALUE_MISSING, "The component is missing a name (none found in its metainfo or .desktop file)"); } /* summary */ if ((g_strcmp0 (as_component_get_summary (cpt), "") == 0) && (!g_key_file_has_key (dfile, "Desktop Entry", "Comment", NULL))) { /* we don't have a summary, and there is also none in the .desktop file - this is bad. */ as_validator_add_issue (data->validator, AS_ISSUE_IMPORTANCE_ERROR, AS_ISSUE_KIND_VALUE_MISSING, "The component is missing a summary (none found in its metainfo or .desktop file)"); } } } else { as_validator_add_issue (data->validator, AS_ISSUE_IMPORTANCE_ERROR, AS_ISSUE_KIND_FILE_MISSING, "Component metadata refers to a non-existing .desktop file."); } } as_validator_clear_current_cpt (data->validator); as_validator_clear_current_fname (data->validator); }
/** * as_validator_analyze_component_metainfo_relation_cb: * * Helper function for GHashTable foreach iteration. */ static void as_validator_analyze_component_metainfo_relation_cb (const gchar *fname, AsComponent *cpt, struct MInfoCheckData *data) { g_autofree gchar *cid_base = NULL; /* if we have no component-id, we can't check anything */ if (as_component_get_id (cpt) == NULL) return; as_validator_set_current_cpt (data->validator, cpt); as_validator_set_current_fname (data->validator, fname); /* check if the fname and the component-id match */ if (g_str_has_suffix (as_component_get_id (cpt), ".desktop")) { cid_base = g_strndup (as_component_get_id (cpt), g_strrstr (as_component_get_id (cpt), ".") - as_component_get_id (cpt)); } else { cid_base = g_strdup (as_component_get_id (cpt)); } if (!as_matches_metainfo (fname, cid_base)) { /* the name-without-type didn't match - check for the full id in the component name */ if (!as_matches_metainfo (fname, as_component_get_id (cpt))) { as_validator_add_issue (data->validator, NULL, AS_ISSUE_IMPORTANCE_WARNING, AS_ISSUE_KIND_WRONG_NAME, "The metainfo filename does not match the component ID."); } } /* check if the referenced .desktop file exists */ if (as_component_get_kind (cpt) == AS_COMPONENT_KIND_DESKTOP_APP) { if (g_hash_table_contains (data->desktop_fnames, as_component_get_desktop_id (cpt))) { g_autofree gchar *desktop_fname_full = NULL; g_autoptr(GKeyFile) dfile = NULL; GError *tmp_error = NULL; desktop_fname_full = g_build_filename (data->apps_dir, as_component_get_desktop_id (cpt), NULL); dfile = g_key_file_new (); g_key_file_load_from_file (dfile, desktop_fname_full, G_KEY_FILE_NONE, &tmp_error); if (tmp_error != NULL) { as_validator_add_issue (data->validator, NULL, AS_ISSUE_IMPORTANCE_WARNING, AS_ISSUE_KIND_READ_ERROR, "Unable to read associated .desktop file: %s", tmp_error->message); g_error_free (tmp_error); tmp_error = NULL; } else { /* we successfully opened the .desktop file, now perform some checks */ /* name */ if (as_str_empty (as_component_get_name (cpt)) && (!g_key_file_has_key (dfile, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, NULL))) { /* we don't have a summary, and there is also none in the .desktop file - this is bad. */ as_validator_add_issue (data->validator, NULL, AS_ISSUE_IMPORTANCE_ERROR, AS_ISSUE_KIND_VALUE_MISSING, "The component is missing a name (none found in its metainfo or .desktop file)"); } /* summary */ if (as_str_empty (as_component_get_summary (cpt)) && (!g_key_file_has_key (dfile, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_COMMENT, NULL))) { /* we don't have a summary, and there is also none in the .desktop file - this is bad. */ as_validator_add_issue (data->validator, NULL, AS_ISSUE_IMPORTANCE_ERROR, AS_ISSUE_KIND_VALUE_MISSING, "The component is missing a summary (none found in its metainfo or .desktop file)"); } /* categories */ if (g_key_file_has_key (dfile, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_CATEGORIES, NULL)) { g_autofree gchar *cats_str = NULL; g_auto(GStrv) cats = NULL; guint i; cats_str = g_key_file_get_string (dfile, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_CATEGORIES, NULL); cats = g_strsplit (cats_str, ";", -1); for (i = 0; cats[i] != NULL; i++) { if (as_str_empty (cats[i])) continue; if (!as_utils_is_category_name (cats[i])) { as_validator_add_issue (data->validator, NULL, AS_ISSUE_IMPORTANCE_ERROR, AS_ISSUE_KIND_VALUE_WRONG, "The category '%s' defined in the .desktop file does not exist.", cats[i]); } } } } } else { as_validator_add_issue (data->validator, NULL, AS_ISSUE_IMPORTANCE_ERROR, AS_ISSUE_KIND_FILE_MISSING, "Component metadata refers to a non-existing .desktop file."); } } as_validator_clear_current_cpt (data->validator); as_validator_clear_current_fname (data->validator); }
/** * as_data_pool_add_component: * @dpool: An instance of #AsDataPool * @cpt: The #AsComponent to add to the pool. * @error: A #GError or %NULL * * Register a new component in the AppStream metadata pool. * * Returns: %TRUE if the new component was successfully added to the pool. */ gboolean as_data_pool_add_component (AsDataPool *dpool, AsComponent *cpt, GError **error) { const gchar *cpt_id; AsComponent *existing_cpt; int priority; AsDataPoolPrivate *priv = GET_PRIVATE (dpool); g_return_val_if_fail (cpt != NULL, FALSE); cpt_id = as_component_get_id (cpt); existing_cpt = g_hash_table_lookup (priv->cpt_table, cpt_id); /* add additional data to the component, e.g. external screenshots. Also refines * the component's icon paths */ as_component_complete (cpt, priv->screenshot_service_url, priv->icon_paths); if (existing_cpt == NULL) { g_hash_table_insert (priv->cpt_table, g_strdup (cpt_id), g_object_ref (cpt)); return TRUE; } /* if we are here, we have duplicates */ priority = as_component_get_priority (existing_cpt); if (priority < as_component_get_priority (cpt)) { g_hash_table_replace (priv->cpt_table, g_strdup (cpt_id), g_object_ref (cpt)); g_debug ("Replaced '%s' with data of higher priority.", cpt_id); } else { gboolean ecpt_has_name; gboolean ncpt_has_name; if ((!as_component_has_bundle (existing_cpt)) && (as_component_has_bundle (cpt))) { GHashTable *bundles; /* propagate bundle information to existing component */ bundles = as_component_get_bundles_table (cpt); as_component_set_bundles_table (existing_cpt, bundles); return TRUE; } ecpt_has_name = as_component_get_name (existing_cpt) != NULL; ncpt_has_name = as_component_get_name (cpt) != NULL; if ((ecpt_has_name) && (!ncpt_has_name)) { /* existing component is updated with data from the new one */ as_data_pool_merge_components (dpool, cpt, existing_cpt); g_debug ("Merged stub data for component %s (<-, based on name)", cpt_id); return TRUE; } if ((!ecpt_has_name) && (ncpt_has_name)) { /* new component is updated with data from the existing component, * then it replaces the prior metadata */ as_data_pool_merge_components (dpool, existing_cpt, cpt); g_hash_table_replace (priv->cpt_table, g_strdup (cpt_id), g_object_ref (cpt)); g_debug ("Merged stub data for component %s (->, based on name)", cpt_id); return TRUE; } if (as_component_get_architecture (cpt) != NULL) { if (as_arch_compatible (as_component_get_architecture (cpt), priv->current_arch)) { const gchar *earch; /* this component is compatible with our current architecture */ earch = as_component_get_architecture (existing_cpt); if (earch != NULL) { if (as_arch_compatible (earch, priv->current_arch)) { g_hash_table_replace (priv->cpt_table, g_strdup (cpt_id), g_object_ref (cpt)); g_debug ("Preferred component for native architecture for %s (was %s)", cpt_id, earch); return TRUE; } else { g_debug ("Ignored additional entry for '%s' on architecture %s.", cpt_id, earch); return FALSE; } } } } if (priority == as_component_get_priority (cpt)) { g_set_error (error, AS_DATA_POOL_ERROR, AS_DATA_POOL_ERROR_COLLISION, "Detected colliding ids: %s was already added with the same priority.", cpt_id); return FALSE; } else { g_set_error (error, AS_DATA_POOL_ERROR, AS_DATA_POOL_ERROR_COLLISION, "Detected colliding ids: %s was already added with a higher priority.", cpt_id); return FALSE; } } return TRUE; }