/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2011 Willem van Engen * Copyright © 2012 – 2017 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, see . */ #include "config.h" #include #include #include #include "goaprovider.h" #include "goaprovider-priv.h" #include "goaflickrprovider.h" #include "goaobjectskeletonutils.h" #include "goasouplogger.h" struct _GoaFlickrProvider { GoaOAuthProvider parent_instance; }; G_DEFINE_TYPE_WITH_CODE (GoaFlickrProvider, goa_flickr_provider, GOA_TYPE_OAUTH_PROVIDER, goa_provider_ensure_extension_points_registered (); g_io_extension_point_implement (GOA_PROVIDER_EXTENSION_POINT_NAME, g_define_type_id, GOA_FLICKR_NAME, 0)); /* ---------------------------------------------------------------------------------------------------- */ static const gchar * get_provider_type (GoaProvider *provider) { return GOA_FLICKR_NAME; } static gchar * get_provider_name (GoaProvider *provider, GoaObject *object) { return g_strdup (_("Flickr")); } static GoaProviderGroup get_provider_group (GoaProvider *provider) { return GOA_PROVIDER_GROUP_BRANDED; } static GoaProviderFeatures get_provider_features (GoaProvider *provider) { return GOA_PROVIDER_FEATURE_BRANDED | GOA_PROVIDER_FEATURE_PHOTOS; } static const gchar * get_consumer_key (GoaOAuthProvider *oauth_provider) { return GOA_FLICKR_CONSUMER_KEY; } static const gchar * get_consumer_secret (GoaOAuthProvider *oauth_provider) { return GOA_FLICKR_CONSUMER_SECRET; } static const gchar * get_request_uri (GoaOAuthProvider *oauth_provider) { return "https://www.flickr.com/services/oauth/request_token"; } static const gchar * get_authorization_uri (GoaOAuthProvider *oauth_provider) { return "https://www.flickr.com/services/oauth/authorize"; } static const gchar * get_token_uri (GoaOAuthProvider *oauth_provider) { return "https://www.flickr.com/services/oauth/access_token"; } static const gchar * get_callback_uri (GoaOAuthProvider *oauth_provider) { /* Should match the URI specified in the Flickr App * Garden in order to detect when the user denied access via * the OAuth1 web page. */ return "https://www.gnome.org/"; } /* ---------------------------------------------------------------------------------------------------- */ static gchar * get_identity_sync (GoaOAuthProvider *oauth_provider, const gchar *access_token, const gchar *access_token_secret, gchar **out_presentation_identity, GCancellable *cancellable, GError **error) { GError *identity_error = NULL; RestProxy *proxy = NULL; RestProxyCall *call = NULL; JsonParser *parser = NULL; JsonObject *json_object; SoupLogger *logger = NULL; gchar *ret = NULL; gchar *id = NULL; gchar *presentation_identity = NULL; /* TODO: cancellable */ proxy = oauth_proxy_new_with_token (goa_oauth_provider_get_consumer_key (oauth_provider), goa_oauth_provider_get_consumer_secret (oauth_provider), access_token, access_token_secret, "https://api.flickr.com/services/rest", FALSE); logger = goa_soup_logger_new (SOUP_LOGGER_LOG_BODY, -1); rest_proxy_add_soup_feature (proxy, SOUP_SESSION_FEATURE (logger)); call = rest_proxy_new_call (proxy); rest_proxy_call_add_param (call, "method", "flickr.test.login"); rest_proxy_call_add_param (call, "format", "json"); rest_proxy_call_add_param (call, "nojsoncallback", "1"); rest_proxy_call_set_method (call, "GET"); if (!rest_proxy_call_sync (call, error)) goto out; if (rest_proxy_call_get_status_code (call) != 200) { g_set_error (error, GOA_ERROR, GOA_ERROR_FAILED, _("Expected status 200 when requesting your identity, instead got status %d (%s)"), rest_proxy_call_get_status_code (call), rest_proxy_call_get_status_message (call)); goto out; } parser = json_parser_new (); if (!json_parser_load_from_data (parser, rest_proxy_call_get_payload (call), rest_proxy_call_get_payload_length (call), &identity_error)) { g_warning ("json_parser_load_from_data() failed: %s (%s, %d)", identity_error->message, g_quark_to_string (identity_error->domain), identity_error->code); g_set_error (error, GOA_ERROR, GOA_ERROR_FAILED, _("Could not parse response")); goto out; } json_object = json_node_get_object (json_parser_get_root (parser)); if (!json_object_has_member (json_object, "user")) { g_warning ("Did not find user in JSON data"); g_set_error (error, GOA_ERROR, GOA_ERROR_FAILED, _("Could not parse response")); goto out; } json_object = json_object_get_object_member (json_object, "user"); if (!json_object_has_member (json_object, "id")) { g_warning ("Did not find user.id in JSON data"); g_set_error (error, GOA_ERROR, GOA_ERROR_FAILED, _("Could not parse response")); goto out; } if (!json_object_has_member (json_object, "username")) { g_warning ("Did not find user.username in JSON data"); g_set_error (error, GOA_ERROR, GOA_ERROR_FAILED, _("Could not parse response")); goto out; } id = g_strdup (json_object_get_string_member (json_object, "id")); json_object = json_object_get_object_member (json_object, "username"); if (!json_object_has_member (json_object, "_content")) { g_warning ("Did not find user.username._content in JSON data"); g_set_error (error, GOA_ERROR, GOA_ERROR_FAILED, _("Could not parse response")); goto out; } presentation_identity = g_strdup (json_object_get_string_member (json_object, "_content")); ret = id; id = NULL; if (out_presentation_identity != NULL) { *out_presentation_identity = presentation_identity; presentation_identity = NULL; } out: g_clear_object (&parser); g_clear_error (&identity_error); g_clear_object (&call); g_clear_object (&proxy); g_clear_object (&logger); g_free (id); g_free (presentation_identity); return ret; } /* ---------------------------------------------------------------------------------------------------- */ static gboolean is_identity_node (GoaOAuthProvider *oauth_provider, WebKitDOMHTMLInputElement *element) { /* Flickr does not provide a way to query the string used by the * user to log in via the web interface. The user id and username * returned by flickr.test.login [1] are not what we are looking * for. * * [1] http://www.flickr.com/services/api/flickr.test.login.html */ return FALSE; } /* ---------------------------------------------------------------------------------------------------- */ static gchar * parse_request_token_error (GoaOAuthProvider *oauth_provider, RestProxyCall *call) { const gchar *payload; gchar *msg = NULL; guint status; payload = rest_proxy_call_get_payload (call); status = rest_proxy_call_get_status_code (call); if (status == 401 && g_strcmp0 (payload, "oauth_problem=timestamp_refused") == 0) msg = g_strdup (_("Your system time is invalid. Check your date and time settings.")); return msg; } /* ---------------------------------------------------------------------------------------------------- */ static gboolean build_object (GoaProvider *provider, GoaObjectSkeleton *object, GKeyFile *key_file, const gchar *group, GDBusConnection *connection, gboolean just_added, GError **error) { GoaAccount *account = NULL; gboolean photos_enabled; gboolean ret = FALSE; /* Chain up */ if (!GOA_PROVIDER_CLASS (goa_flickr_provider_parent_class)->build_object (provider, object, key_file, group, connection, just_added, error)) goto out; account = goa_object_get_account (GOA_OBJECT (object)); /* Photos */ photos_enabled = g_key_file_get_boolean (key_file, group, "PhotosEnabled", NULL); goa_object_skeleton_attach_photos (object, photos_enabled); if (just_added) { goa_account_set_photos_disabled (account, !photos_enabled); g_signal_connect (account, "notify::photos-disabled", G_CALLBACK (goa_util_account_notify_property_cb), (gpointer) "PhotosEnabled"); } ret = TRUE; out: g_clear_object (&account); return ret; } /* ---------------------------------------------------------------------------------------------------- */ static void add_account_key_values (GoaOAuthProvider *oauth_provider, GVariantBuilder *builder) { g_variant_builder_add (builder, "{ss}", "PhotosEnabled", "true"); } /* ---------------------------------------------------------------------------------------------------- */ static void goa_flickr_provider_init (GoaFlickrProvider *self) { } static void goa_flickr_provider_class_init (GoaFlickrProviderClass *klass) { GoaProviderClass *provider_class; GoaOAuthProviderClass *oauth_class; provider_class = GOA_PROVIDER_CLASS (klass); provider_class->get_provider_type = get_provider_type; provider_class->get_provider_name = get_provider_name; provider_class->get_provider_group = get_provider_group; provider_class->get_provider_features = get_provider_features; provider_class->build_object = build_object; oauth_class = GOA_OAUTH_PROVIDER_CLASS (klass); oauth_class->get_identity_sync = get_identity_sync; oauth_class->is_identity_node = is_identity_node; oauth_class->get_consumer_key = get_consumer_key; oauth_class->get_consumer_secret = get_consumer_secret; oauth_class->get_request_uri = get_request_uri; oauth_class->get_authorization_uri = get_authorization_uri; oauth_class->get_token_uri = get_token_uri; oauth_class->get_callback_uri = get_callback_uri; oauth_class->parse_request_token_error = parse_request_token_error; oauth_class->add_account_key_values = add_account_key_values; }