static void as_app_validate_image (AsImage *im, AsAppValidateHelper *helper) { const gchar *url; gboolean ret; /* blank */ url = as_image_get_url (im); if (url == NULL || (guint) strlen (url) == 0) { ai_app_validate_add (helper, AS_PROBLEM_KIND_VALUE_MISSING, "<screenshot> has no content"); return; } /* check for duplicates */ ret = as_app_validate_image_url_already_exists (helper, url); if (ret) { ai_app_validate_add (helper, AS_PROBLEM_KIND_DUPLICATE_DATA, "<screenshot> has duplicated data"); return; } /* validate the URL */ ret = ai_app_validate_image_check (im, helper); if (ret) g_ptr_array_add (helper->screenshot_urls, g_strdup (url)); }
static gboolean ai_app_validate_image_check (AsImage *im, AsAppValidateHelper *helper) { AsImageAlphaFlags alpha_flags; const gchar *url; gboolean require_correct_aspect_ratio = FALSE; gdouble desired_aspect = 1.777777778; gdouble screenshot_aspect; guint status_code; guint screenshot_height; guint screenshot_width; guint ss_size_height_max = 900; guint ss_size_height_min = 351; guint ss_size_width_max = 1600; guint ss_size_width_min = 624; g_autoptr(GdkPixbuf) pixbuf = NULL; g_autoptr(GInputStream) stream = NULL; g_autoptr(SoupMessage) msg = NULL; g_autoptr(SoupURI) base_uri = NULL; /* make the requirements more strict */ if ((helper->flags & AS_APP_VALIDATE_FLAG_STRICT) > 0) { require_correct_aspect_ratio = TRUE; } /* relax the requirements a bit */ if ((helper->flags & AS_APP_VALIDATE_FLAG_RELAX) > 0) { ss_size_height_max = 1800; ss_size_height_min = 150; ss_size_width_max = 3200; ss_size_width_min = 300; } /* have we got network access */ if ((helper->flags & AS_APP_VALIDATE_FLAG_NO_NETWORK) > 0) return TRUE; /* GET file */ url = as_image_get_url (im); g_debug ("checking %s", url); base_uri = soup_uri_new (url); if (!SOUP_URI_VALID_FOR_HTTP (base_uri)) { ai_app_validate_add (helper, AS_PROBLEM_KIND_URL_NOT_FOUND, "<screenshot> url not valid [%s]", url); return FALSE; } msg = soup_message_new_from_uri (SOUP_METHOD_GET, base_uri); if (msg == NULL) { g_warning ("Failed to setup message"); return FALSE; } /* send sync */ status_code = soup_session_send_message (helper->session, msg); if (SOUP_STATUS_IS_TRANSPORT_ERROR(status_code)) { ai_app_validate_add (helper, AS_PROBLEM_KIND_URL_NOT_FOUND, "<screenshot> failed to connect: %s [%s]", soup_status_get_phrase(status_code), url); return FALSE; } else if (status_code != SOUP_STATUS_OK) { ai_app_validate_add (helper, AS_PROBLEM_KIND_URL_NOT_FOUND, "<screenshot> failed to download (HTTP %d: %s) [%s]", status_code, soup_status_get_phrase(status_code), url); return FALSE; } /* check if it's a zero sized file */ if (msg->response_body->length == 0) { ai_app_validate_add (helper, AS_PROBLEM_KIND_FILE_INVALID, "<screenshot> url is a zero length file [%s]", url); return FALSE; } /* create a buffer with the data */ stream = g_memory_input_stream_new_from_data (msg->response_body->data, (gssize) msg->response_body->length, NULL); if (stream == NULL) { ai_app_validate_add (helper, AS_PROBLEM_KIND_URL_NOT_FOUND, "<screenshot> failed to load data [%s]", url); return FALSE; } /* load the image */ pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, NULL); if (pixbuf == NULL) { ai_app_validate_add (helper, AS_PROBLEM_KIND_FILE_INVALID, "<screenshot> failed to load [%s]", url); return FALSE; } /* check width matches */ screenshot_width = (guint) gdk_pixbuf_get_width (pixbuf); screenshot_height = (guint) gdk_pixbuf_get_height (pixbuf); if (as_image_get_width (im) != 0 && as_image_get_width (im) != screenshot_width) { ai_app_validate_add (helper, AS_PROBLEM_KIND_ATTRIBUTE_INVALID, "<screenshot> width did not match specified [%s]", url); } /* check height matches */ if (as_image_get_height (im) != 0 && as_image_get_height (im) != screenshot_height) { ai_app_validate_add (helper, AS_PROBLEM_KIND_ATTRIBUTE_INVALID, "<screenshot> height did not match specified [%s]", url); } /* check size is reasonable */ if (screenshot_width < ss_size_width_min) { ai_app_validate_add (helper, AS_PROBLEM_KIND_ATTRIBUTE_INVALID, "<screenshot> width too small [%s] minimum is %upx", url, ss_size_width_min); } if (screenshot_height < ss_size_height_min) { ai_app_validate_add (helper, AS_PROBLEM_KIND_ATTRIBUTE_INVALID, "<screenshot> height too small [%s] minimum is %upx", url, ss_size_height_min); } if (screenshot_width > ss_size_width_max) { ai_app_validate_add (helper, AS_PROBLEM_KIND_ATTRIBUTE_INVALID, "<screenshot> width too large [%s] maximum is %upx", url, ss_size_width_max); } if (screenshot_height > ss_size_height_max) { ai_app_validate_add (helper, AS_PROBLEM_KIND_ATTRIBUTE_INVALID, "<screenshot> height too large [%s] maximum is %upx", url, ss_size_height_max); } /* check padding */ as_image_set_pixbuf (im, pixbuf); alpha_flags = as_image_get_alpha_flags (im); if ((alpha_flags & AS_IMAGE_ALPHA_FLAG_TOP) > 0|| (alpha_flags & AS_IMAGE_ALPHA_FLAG_BOTTOM) > 0) { ai_app_validate_add (helper, AS_PROBLEM_KIND_STYLE_INCORRECT, "<image> has vertical padding [%s]", url); } if ((alpha_flags & AS_IMAGE_ALPHA_FLAG_LEFT) > 0|| (alpha_flags & AS_IMAGE_ALPHA_FLAG_RIGHT) > 0) { ai_app_validate_add (helper, AS_PROBLEM_KIND_STYLE_INCORRECT, "<image> has horizontal padding [%s]", url); } /* check aspect ratio */ if (require_correct_aspect_ratio) { screenshot_aspect = (gdouble) screenshot_width / (gdouble) screenshot_height; if (ABS (screenshot_aspect - 1.777777777) > 0.1) { g_debug ("got aspect %.2f, wanted %.2f", screenshot_aspect, desired_aspect); ai_app_validate_add (helper, AS_PROBLEM_KIND_ASPECT_RATIO_INCORRECT, "<screenshot> aspect ratio not 16:9 [%s]", url); } } return TRUE; }
/** * ai_app_validate_image_check: */ static gboolean ai_app_validate_image_check (AsImage *im, AsAppValidateHelper *helper) { const gchar *url; gboolean require_correct_aspect_ratio = FALSE; gdouble desired_aspect = 1.777777778; gdouble screenshot_aspect; gint status_code; guint screenshot_height; guint screenshot_width; guint ss_size_height_max = 900; guint ss_size_height_min = 351; guint ss_size_width_max = 1600; guint ss_size_width_min = 624; _cleanup_object_unref_ GdkPixbuf *pixbuf = NULL; _cleanup_object_unref_ GInputStream *stream = NULL; _cleanup_object_unref_ SoupMessage *msg = NULL; _cleanup_uri_unref_ SoupURI *base_uri = NULL; /* make the requirements more strict */ if ((helper->flags & AS_APP_VALIDATE_FLAG_STRICT) > 0) { require_correct_aspect_ratio = TRUE; } /* relax the requirements a bit */ if ((helper->flags & AS_APP_VALIDATE_FLAG_RELAX) > 0) { ss_size_height_max = 1800; ss_size_height_min = 150; ss_size_width_max = 3200; ss_size_width_min = 300; } /* have we got network access */ if ((helper->flags & AS_APP_VALIDATE_FLAG_NO_NETWORK) > 0) return TRUE; /* GET file */ url = as_image_get_url (im); g_debug ("checking %s", url); base_uri = soup_uri_new (url); if (base_uri == NULL) { ai_app_validate_add (helper->probs, AS_PROBLEM_KIND_URL_NOT_FOUND, "<screenshot> url not valid"); return FALSE; } msg = soup_message_new_from_uri (SOUP_METHOD_GET, base_uri); if (msg == NULL) { g_warning ("Failed to setup message"); return FALSE; } /* send sync */ status_code = soup_session_send_message (helper->session, msg); if (status_code != SOUP_STATUS_OK) { ai_app_validate_add (helper->probs, AS_PROBLEM_KIND_URL_NOT_FOUND, "<screenshot> url not found"); return FALSE; } /* check if it's a zero sized file */ if (msg->response_body->length == 0) { ai_app_validate_add (helper->probs, AS_PROBLEM_KIND_FILE_INVALID, "<screenshot> url is a zero length file"); return FALSE; } /* create a buffer with the data */ stream = g_memory_input_stream_new_from_data (msg->response_body->data, msg->response_body->length, NULL); if (stream == NULL) { ai_app_validate_add (helper->probs, AS_PROBLEM_KIND_URL_NOT_FOUND, "<screenshot> failed to load data"); return FALSE; } /* load the image */ pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, NULL); if (pixbuf == NULL) { ai_app_validate_add (helper->probs, AS_PROBLEM_KIND_FILE_INVALID, "<screenshot> failed to load image"); return FALSE; } /* check width matches */ screenshot_width = gdk_pixbuf_get_width (pixbuf); screenshot_height = gdk_pixbuf_get_height (pixbuf); if (as_image_get_width (im) != 0 && as_image_get_width (im) != screenshot_width) { ai_app_validate_add (helper->probs, AS_PROBLEM_KIND_ATTRIBUTE_INVALID, "<screenshot> width did not match specified"); } /* check height matches */ if (as_image_get_height (im) != 0 && as_image_get_height (im) != screenshot_height) { ai_app_validate_add (helper->probs, AS_PROBLEM_KIND_ATTRIBUTE_INVALID, "<screenshot> height did not match specified"); } /* check size is reasonable */ if (screenshot_width < ss_size_width_min) { ai_app_validate_add (helper->probs, AS_PROBLEM_KIND_ATTRIBUTE_INVALID, "<screenshot> width was too small"); } if (screenshot_height < ss_size_height_min) { ai_app_validate_add (helper->probs, AS_PROBLEM_KIND_ATTRIBUTE_INVALID, "<screenshot> height was too small"); } if (screenshot_width > ss_size_width_max) { ai_app_validate_add (helper->probs, AS_PROBLEM_KIND_ATTRIBUTE_INVALID, "<screenshot> width was too large"); } if (screenshot_height > ss_size_height_max) { ai_app_validate_add (helper->probs, AS_PROBLEM_KIND_ATTRIBUTE_INVALID, "<screenshot> height was too large"); } /* check aspect ratio */ if (require_correct_aspect_ratio) { screenshot_aspect = (gdouble) screenshot_width / (gdouble) screenshot_height; if (ABS (screenshot_aspect - 1.777777777) > 0.1) { g_debug ("got aspect %.2f, wanted %.2f", screenshot_aspect, desired_aspect); ai_app_validate_add (helper->probs, AS_PROBLEM_KIND_ASPECT_RATIO_INCORRECT, "<screenshot> aspect ratio was not 16:9"); } } return TRUE; }
/** * gs_screenshot_image_load_async: **/ void gs_screenshot_image_load_async (GsScreenshotImage *ssimg, GCancellable *cancellable) { AsImage *im = NULL; GsScreenshotImagePrivate *priv; SoupURI *base_uri = NULL; const gchar *url; gint rc; _cleanup_free_ gchar *basename = NULL; _cleanup_free_ gchar *cachedir2 = NULL; _cleanup_free_ gchar *cachedir = NULL; _cleanup_free_ gchar *sizedir = NULL; g_return_if_fail (GS_IS_SCREENSHOT_IMAGE (ssimg)); priv = gs_screenshot_image_get_instance_private (ssimg); g_return_if_fail (AS_IS_SCREENSHOT (priv->screenshot)); g_return_if_fail (priv->width != 0); g_return_if_fail (priv->height != 0); /* load an image according to the scale factor */ priv->scale = gtk_widget_get_scale_factor (GTK_WIDGET (ssimg)); im = as_screenshot_get_image (priv->screenshot, priv->width * priv->scale, priv->height * priv->scale); /* if we've failed to load a HiDPI image, fallback to LoDPI */ if (im == NULL && priv->scale > 1) { priv->scale = 1; im = as_screenshot_get_image (priv->screenshot, priv->width, priv->height); } if (im == NULL) { /* TRANSLATORS: this is when we request a screenshot size that * the generator did not create or the parser did not add */ gs_screenshot_image_set_error (ssimg, _("Screenshot size not found")); return; } url = as_image_get_url (im); basename = g_path_get_basename (url); if (priv->width == G_MAXUINT || priv->height == G_MAXUINT) { sizedir = g_strdup ("unknown"); } else { sizedir = g_strdup_printf ("%ux%u", priv->width * priv->scale, priv->height * priv->scale); } cachedir = g_build_filename (priv->cachedir, "gnome-software", "screenshots", sizedir, NULL); rc = g_mkdir_with_parents (cachedir, 0700); if (rc != 0) { /* TRANSLATORS: this is when we try create the cache directory * but we were out of space or permission was denied */ gs_screenshot_image_set_error (ssimg, _("Could not create cache")); return; } /* does local file already exist */ priv->filename = g_build_filename (cachedir, basename, NULL); if (g_file_test (priv->filename, G_FILE_TEST_EXISTS)) { as_screenshot_show_image (ssimg); return; } /* can we load a blurred smaller version of this straight away */ if (priv->width > AS_IMAGE_THUMBNAIL_WIDTH && priv->height > AS_IMAGE_THUMBNAIL_HEIGHT) { cachedir2 = g_build_filename (priv->cachedir, "gnome-software", "screenshots", "112x63", basename, NULL); if (g_file_test (cachedir2, G_FILE_TEST_EXISTS)) gs_screenshot_image_show_blurred (ssimg, cachedir2); } /* download file */ g_debug ("downloading %s to %s", url, priv->filename); base_uri = soup_uri_new (url); if (base_uri == NULL || !SOUP_URI_VALID_FOR_HTTP (base_uri)) { /* TRANSLATORS: this is when we try to download a screenshot * that was not a valid URL */ gs_screenshot_image_set_error (ssimg, _("Screenshot not valid")); soup_uri_free (base_uri); return; } /* cancel any previous messages */ if (priv->message != NULL) { soup_session_cancel_message (priv->session, priv->message, SOUP_STATUS_CANCELLED); g_clear_object (&priv->message); } priv->message = soup_message_new_from_uri (SOUP_METHOD_GET, base_uri); if (priv->message == NULL) { /* TRANSLATORS: this is when networking is not available */ gs_screenshot_image_set_error (ssimg, _("Screenshot not available")); soup_uri_free (base_uri); return; } /* send async */ soup_session_queue_message (priv->session, g_object_ref (priv->message) /* transfer full */, gs_screenshot_image_complete_cb, g_object_ref (ssimg)); soup_uri_free (base_uri); }
void gs_screenshot_image_load_async (GsScreenshotImage *ssimg, GCancellable *cancellable) { AsImage *im = NULL; const gchar *url; g_autofree gchar *basename = NULL; g_autofree gchar *cache_kind = NULL; g_autofree gchar *cachefn_thumb = NULL; g_autofree gchar *sizedir = NULL; g_autoptr(SoupURI) base_uri = NULL; g_return_if_fail (GS_IS_SCREENSHOT_IMAGE (ssimg)); g_return_if_fail (AS_IS_SCREENSHOT (ssimg->screenshot)); g_return_if_fail (ssimg->width != 0); g_return_if_fail (ssimg->height != 0); /* load an image according to the scale factor */ ssimg->scale = (guint) gtk_widget_get_scale_factor (GTK_WIDGET (ssimg)); im = as_screenshot_get_image (ssimg->screenshot, ssimg->width * ssimg->scale, ssimg->height * ssimg->scale); /* if we've failed to load a HiDPI image, fallback to LoDPI */ if (im == NULL && ssimg->scale > 1) { ssimg->scale = 1; im = as_screenshot_get_image (ssimg->screenshot, ssimg->width, ssimg->height); } if (im == NULL) { /* TRANSLATORS: this is when we request a screenshot size that * the generator did not create or the parser did not add */ gs_screenshot_image_set_error (ssimg, _("Screenshot size not found")); return; } /* check if the URL points to a local file */ url = as_image_get_url (im); if (g_str_has_prefix (url, "file://")) { g_free (ssimg->filename); ssimg->filename = g_strdup (url + 7); if (g_file_test (ssimg->filename, G_FILE_TEST_EXISTS)) { as_screenshot_show_image (ssimg); return; } } basename = gs_screenshot_get_cachefn_for_url (url); if (ssimg->width == G_MAXUINT || ssimg->height == G_MAXUINT) { sizedir = g_strdup ("unknown"); } else { sizedir = g_strdup_printf ("%ux%u", ssimg->width * ssimg->scale, ssimg->height * ssimg->scale); } cache_kind = g_build_filename ("screenshots", sizedir, NULL); g_free (ssimg->filename); ssimg->filename = gs_utils_get_cache_filename (cache_kind, basename, GS_UTILS_CACHE_FLAG_NONE, NULL); if (ssimg->filename == NULL) { /* TRANSLATORS: this is when we try create the cache directory * but we were out of space or permission was denied */ gs_screenshot_image_set_error (ssimg, _("Could not create cache")); return; } /* does local file already exist and has recently been downloaded */ if (g_file_test (ssimg->filename, G_FILE_TEST_EXISTS)) { guint64 age_max; g_autoptr(GFile) file = NULL; /* show the image we have in cache while we're checking for the * new screenshot (which probably won't have changed) */ as_screenshot_show_image (ssimg); /* verify the cache age against the maximum allowed */ age_max = g_settings_get_uint (ssimg->settings, "screenshot-cache-age-maximum"); file = g_file_new_for_path (ssimg->filename); /* image new enough, not re-requesting from server */ if (age_max > 0 && gs_utils_get_file_age (file) < age_max) return; } /* if we're not showing a full-size image, we try loading a blurred * smaller version of it straight away */ if (!ssimg->showing_image && ssimg->width > AS_IMAGE_THUMBNAIL_WIDTH && ssimg->height > AS_IMAGE_THUMBNAIL_HEIGHT) { const gchar *url_thumb; g_autofree gchar *basename_thumb = NULL; g_autofree gchar *cache_kind_thumb = NULL; im = as_screenshot_get_image (ssimg->screenshot, AS_IMAGE_THUMBNAIL_WIDTH * ssimg->scale, AS_IMAGE_THUMBNAIL_HEIGHT * ssimg->scale); url_thumb = as_image_get_url (im); basename_thumb = gs_screenshot_get_cachefn_for_url (url_thumb); cache_kind_thumb = g_build_filename ("screenshots", "112x63", NULL); cachefn_thumb = gs_utils_get_cache_filename (cache_kind_thumb, basename_thumb, GS_UTILS_CACHE_FLAG_NONE, NULL); if (cachefn_thumb == NULL) return; if (g_file_test (cachefn_thumb, G_FILE_TEST_EXISTS)) gs_screenshot_image_show_blurred (ssimg, cachefn_thumb); } /* re-request the cache filename, which might be different as it needs * to be writable this time */ g_free (ssimg->filename); ssimg->filename = gs_utils_get_cache_filename (cache_kind, basename, GS_UTILS_CACHE_FLAG_WRITEABLE, NULL); /* download file */ g_debug ("downloading %s to %s", url, ssimg->filename); base_uri = soup_uri_new (url); if (base_uri == NULL || !SOUP_URI_VALID_FOR_HTTP (base_uri)) { /* TRANSLATORS: this is when we try to download a screenshot * that was not a valid URL */ gs_screenshot_image_set_error (ssimg, _("Screenshot not valid")); return; } /* cancel any previous messages */ if (ssimg->message != NULL) { soup_session_cancel_message (ssimg->session, ssimg->message, SOUP_STATUS_CANCELLED); g_clear_object (&ssimg->message); } ssimg->message = soup_message_new_from_uri (SOUP_METHOD_GET, base_uri); if (ssimg->message == NULL) { /* TRANSLATORS: this is when networking is not available */ gs_screenshot_image_set_error (ssimg, _("Screenshot not available")); return; } /* not all servers support If-Modified-Since, but worst case we just * re-download the entire file again every 30 days */ if (g_file_test (ssimg->filename, G_FILE_TEST_EXISTS)) { g_autoptr(GFile) file = g_file_new_for_path (ssimg->filename); gs_screenshot_soup_msg_set_modified_request (ssimg->message, file); } /* send async */ soup_session_queue_message (ssimg->session, g_object_ref (ssimg->message) /* transfer full */, gs_screenshot_image_complete_cb, g_object_ref (ssimg)); }