[gnome-control-center/wip/um-avatar-picker: 2/4] user-accounts: Add methods to fetch avatars from online accounts



commit f41a1dad0707ee5d47d5d3e668844a1465780ede
Author: Jasper St. Pierre <jstpierre mecheye net>
Date:   Mon Oct 21 14:22:58 2013 -0400

    user-accounts: Add methods to fetch avatars from online accounts
    
    Along with a simple test utility that fetches my gravatar and the
    local online accounts.
    
    Based-heavily-on-a-patch-by: Bogdan Ciobanu <bgdn ciobanu gmail com>

 configure.ac                               |    6 +-
 panels/user-accounts/Makefile.am           |   15 +-
 panels/user-accounts/online-avatars.c      |  369 ++++++++++++++++++++++++++++
 panels/user-accounts/online-avatars.h      |   45 ++++
 panels/user-accounts/test-online-avatars.c |   63 +++++
 5 files changed, 496 insertions(+), 2 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index b0b492d..0673828 100644
--- a/configure.ac
+++ b/configure.ac
@@ -105,6 +105,7 @@ ACCOUNTSSERVICE_REQUIRED_VERSION=0.6.30
 COLORD_REQUIRED_VERSION=0.1.34
 COLORD_GTK_REQUIRED_VERSION=0.1.24
 PWQUALITY_REQUIRED_VERSION=1.2.2
+REST_REQUIRED_VERSION=0.7.90
 
 COMMON_MODULES="gtk+-3.0 >= $GTK_REQUIRED_VERSION
  glib-2.0 >= $GLIB_REQUIRED_VERSION
@@ -164,7 +165,10 @@ PKG_CHECK_MODULES(USER_ACCOUNTS_PANEL, $COMMON_MODULES
                   gnome-desktop-3.0
                   gdk-pixbuf-2.0 >= $GDKPIXBUF_REQUIRED_VERSION
                   pwquality >= $PWQUALITY_REQUIRED_VERSION
-                  accountsservice >= $ACCOUNTSSERVICE_REQUIRED_VERSION)
+                  json-glib-1.0
+                  accountsservice >= $ACCOUNTSSERVICE_REQUIRED_VERSION
+                  goa-1.0 goa-backend-1.0 >= GOA_REQUIRED_VERSION
+                  rest-0.7 >= REST_REQUIRED_VERSION)
 PKG_CHECK_MODULES(SHARING_PANEL, $COMMON_MODULES)
 PKG_CHECK_MODULES(REMOTE_LOGIN_HELPER, glib-2.0 >= $GLIB_REQUIRED_VERSION gio-2.0)
 
diff --git a/panels/user-accounts/Makefile.am b/panels/user-accounts/Makefile.am
index 54e2462..4bb02ee 100644
--- a/panels/user-accounts/Makefile.am
+++ b/panels/user-accounts/Makefile.am
@@ -55,6 +55,8 @@ libuser_accounts_la_SOURCES =         \
        um-realm-manager.h              \
        um-history-dialog.h             \
        um-history-dialog.c             \
+       online-avatars.c                \
+       online-avatars.h                \
        $(BUILT_SOURCES)
 
 libuser_accounts_la_LIBADD =           \
@@ -82,7 +84,7 @@ um-resources.c: user-accounts.gresource.xml $(resource_files)
 um-resources.h: user-accounts.gresource.xml $(resource_files)
        $(AM_V_GEN) glib-compile-resources --target=$@ --sourcedir=$(srcdir) --generate-header --c-name um $<
 
-noinst_PROGRAMS = frob-account-dialog
+noinst_PROGRAMS = frob-account-dialog test-online-avatars
 
 frob_account_dialog_SOURCES = \
        frob-account-dialog.c \
@@ -102,6 +104,17 @@ frob_account_dialog_LDADD = \
 frob_account_dialog_CFLAGS = \
        $(AM_CFLAGS)
 
+test_online_avatars_SOURCES = \
+       test-online-avatars.c \
+       online-avatars.c \
+       online-avatars.h
+
+test_online_avatars_LDADD = \
+       $(libuser_accounts_la_LIBADD)
+
+test_online_avatars_CFLAGS = \
+       $(AM_CPPFLAGS)
+
 polkitdir = $(datadir)/polkit-1/actions
 polkit_in_files = org.gnome.controlcenter.user-accounts.policy.in
 
diff --git a/panels/user-accounts/online-avatars.c b/panels/user-accounts/online-avatars.c
new file mode 100644
index 0000000..016e0b3
--- /dev/null
+++ b/panels/user-accounts/online-avatars.c
@@ -0,0 +1,369 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * Copyright 2013 Red Hat, Inc,
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written by:
+ *   Jasper St. Pierre <jstpierre mecheye net>
+ *   Bogdan Ciobanu <bgdn ciobanu gmail com>
+ */
+
+#include "online-avatars.h"
+
+#include <string.h>
+#include <libsoup/soup.h>
+#include <json-glib/json-glib.h>
+
+#define FLICKR_API_KEY "9a942002593e262349dd084439b69640"
+#define FLICKR_API_SECRET "967724da5ff77246"
+
+#define IMAGE_SIZE 96
+
+static void
+got_image_cb (SoupSession *session,
+              SoupMessage *msg,
+              gpointer     user_data)
+{
+    GBytes *bytes = NULL;
+    GTask *task = user_data;
+
+    if (msg->status_code == 200) {
+        SoupBuffer *buffer = soup_message_body_flatten (msg->response_body);
+        bytes = soup_buffer_get_as_bytes (buffer);
+        g_task_return_pointer (task, bytes, (GDestroyNotify) g_bytes_unref);
+    } else {
+        /* XXX -- return a real error? */
+        g_task_return_pointer (task, NULL, NULL);
+    }
+
+    soup_session_abort (session);
+    g_object_unref (session);
+    g_object_unref (task);
+}
+
+static void
+got_facebook_cb (SoupSession *session,
+                 SoupMessage *msg,
+                 gpointer     user_data)
+{
+    GTask *task = user_data;
+    SoupMessage *newmsg;
+    SoupBuffer *buffer;
+    JsonParser *parser;
+    JsonNode *root;
+    JsonReader *reader;
+    gboolean is_silhouette;
+    GError *error = NULL;
+    const char *url;
+
+    buffer = soup_message_body_flatten (msg->response_body);
+    parser = json_parser_new ();
+
+    if (!json_parser_load_from_data (parser, buffer->data, buffer->length, &error)) {
+        g_print ("Error loading json: %s\n", error->message);
+        goto out;
+    }
+
+    root = json_parser_get_root (parser);
+    reader = json_reader_new (root);
+
+    json_reader_read_member (reader, "data");
+
+    json_reader_read_member (reader, "is_silhouette");
+    is_silhouette = json_reader_get_boolean_value (reader);
+    /* The user doesn't have an avatar set on Facebook; use the image default */
+    if (is_silhouette) {
+        g_task_return_pointer (task, NULL, NULL);
+        goto out;
+    }
+    json_reader_end_member (reader); /* is_silhouette */
+
+    json_reader_read_member (reader, "url");
+    url = json_reader_get_string_value (reader);
+    json_reader_end_member (reader); /* url */
+
+    json_reader_end_member (reader); /* data */
+
+    newmsg = soup_message_new ("GET", url);
+    soup_session_queue_message (session, newmsg, got_image_cb, task);
+
+ out:
+    soup_buffer_free (buffer);
+    g_clear_object (&reader);
+    g_clear_object (&parser);
+}
+
+static void
+get_facebook_image (GoaAccount *account,
+                    GTask      *task)
+{
+    SoupSession *session;
+    SoupMessage *msg;
+
+    gchar *url = g_strdup_printf ("http://graph.facebook.com/%s/picture?redirect=false&width=%d&height=%d";,
+                                  goa_account_get_identity (account), IMAGE_SIZE, IMAGE_SIZE);
+
+    session = soup_session_new ();
+    msg = soup_message_new ("GET", url);
+    soup_message_headers_append (msg->request_headers, "Content-Type", "application/json");
+
+    soup_session_queue_message (session, msg, got_facebook_cb, task);
+
+    g_free (url);
+}
+
+static void
+got_google_user_info_cb (SoupSession *session,
+                         SoupMessage *msg,
+                         gpointer     user_data)
+{
+    GTask *task = user_data;
+    SoupMessage *newmsg;
+    SoupBuffer *buffer;
+    JsonParser *parser;
+    JsonNode *root;
+    JsonReader *reader;
+    GError *error = NULL;
+    const char *user_id;
+    char *url;
+
+    buffer = soup_message_body_flatten (msg->response_body);
+    parser = json_parser_new ();
+
+    if (!json_parser_load_from_data (parser, buffer->data, buffer->length, &error)) {
+        g_print ("Error loading JSON response: %s\n", error->message);
+        goto out;
+    }
+
+    root = json_parser_get_root (parser);
+    reader = json_reader_new (root);
+
+    json_reader_read_member (reader, "id");
+    user_id = json_reader_get_string_value (reader);
+    json_reader_end_member (reader); /* id */
+
+    g_object_unref (reader);
+
+    url = g_strdup_printf ("https://profiles.google.com/s2/photos/profile/%s";, user_id);
+
+    newmsg = soup_message_new ("GET", url);
+    soup_session_queue_message (session, newmsg, got_image_cb, task);
+
+ out:
+    soup_buffer_free (buffer);
+    g_object_unref (parser);
+}
+
+static void
+got_google_access_token_cb (GObject      *source,
+                            GAsyncResult *res,
+                            gpointer      user_data)
+{
+    GoaOAuth2Based *oauth2 = (GoaOAuth2Based*) source;
+    SoupSession *session;
+    SoupMessage *msg;
+    GError *error = NULL;
+    gint expires;
+    gchar *access_token;
+    gchar *url;
+
+    if (!goa_oauth2_based_call_get_access_token_finish (oauth2, &access_token, &expires, res, &error)) {
+        g_print("Error in getting the access token: %s\n", error->message);
+        return;
+    }
+
+    url = g_strdup_printf ("https://www.googleapis.com/oauth2/v1/userinfo?access_token=%s";, access_token);
+
+    session = soup_session_new ();
+    msg = soup_message_new ("GET", url);
+    soup_message_headers_append (msg->request_headers, "Content-Type", "application/json");
+
+    soup_session_queue_message (session, msg, got_google_user_info_cb, user_data);
+
+    g_object_unref (oauth2);
+    g_free (url);
+}
+
+static void
+ensure_google_credentials_ready_cb (GObject      *source,
+                                    GAsyncResult *res,
+                                    gpointer      user_data)
+{
+    GoaAccount *account = GOA_ACCOUNT (source);
+    GTask *task = user_data;
+    GoaObject *goa_object;
+    GoaOAuth2Based *oauth;
+    GError *error = NULL;
+    gint expires;
+
+    if (!goa_account_call_ensure_credentials_finish (account, &expires, res, &error)) {
+        g_warning ("Failed to get credentials for Google: %s\n", error->message);
+        return;
+    }
+
+    goa_object = GOA_OBJECT (g_dbus_interface_get_object (G_DBUS_INTERFACE (account)));
+    oauth = goa_object_get_oauth2_based (goa_object);
+    goa_oauth2_based_call_get_access_token (oauth, g_task_get_cancellable (task),
+                                            got_google_access_token_cb, user_data);
+}
+
+static void
+get_google_image (GoaAccount   *account,
+                  GTask        *task)
+{
+    goa_account_call_ensure_credentials (account, g_task_get_cancellable (task),
+                                         ensure_google_credentials_ready_cb, task);
+}
+
+static void
+got_flickr_details_cb (SoupSession *session,
+                       SoupMessage *msg,
+                       gpointer     user_data)
+{
+    GTask *task = user_data;
+    GoaAccount *account;
+    SoupMessage *newmsg;
+    SoupBuffer *buffer;
+    JsonParser *parser;
+    JsonNode *root;
+    JsonReader *reader;
+    GError *error = NULL;
+    int icon_farm;
+    char *json = NULL;
+    char *url = NULL;
+    const char *icon_server;
+
+    buffer = soup_message_body_flatten (msg->response_body);
+
+    /* Eliminates jsonFlickrApi() from the response so it can be parsed correctly */
+    json = g_strndup (buffer->data + 14, buffer->length - 15);
+
+    parser = json_parser_new ();
+    json_parser_load_from_data (parser, json, -1, &error);
+    if (error) {
+        g_print ("Error parsing json: %s\n", error->message);
+        goto out;
+    }
+
+    root = json_parser_get_root (parser);
+    reader = json_reader_new (root);
+    json_reader_read_member (reader, "person");
+
+    json_reader_read_member (reader, "iconserver");
+    icon_server = json_reader_get_string_value (reader);
+    json_reader_end_member (reader); /* iconserver */
+
+    json_reader_read_member (reader, "iconfarm");
+    icon_farm = json_reader_get_int_value (reader);
+    json_reader_end_member (reader); /* iconfarm */
+
+    json_reader_end_member (reader); /* person */
+
+    account = GOA_ACCOUNT (g_task_get_source_object (task));
+
+    url = g_strdup_printf ("http://farm%d.staticflickr.com/%s/buddyicons/%s.jpg";,
+                           icon_farm, icon_server, goa_account_get_identity (account));
+    newmsg = soup_message_new ("GET", url);
+    soup_session_queue_message (session, newmsg, got_image_cb, user_data);
+
+ out:
+    soup_buffer_free (buffer);
+    g_clear_object (&reader);
+    g_clear_object (&parser);
+    g_free (json);
+    g_free (url);
+}
+
+static void
+get_flickr_image (GoaAccount *account,
+                  GTask      *task)
+{
+    SoupSession *session;
+    SoupMessage *msg;
+    gchar *url;
+
+    url = g_strdup_printf 
("https://api.flickr.com/services/rest/?method=flickr.people.getInfo&user_id=%s&api_key=%s&format=json";,
+                           goa_account_get_identity (account), FLICKR_API_KEY);
+
+    session = soup_session_new ();
+    msg = soup_message_new ("GET", url);
+    g_free (url);
+
+    soup_session_queue_message (session, msg, got_flickr_details_cb, task);
+}
+
+void
+get_avatar_from_online_account (GoaAccount          *account,
+                                GCancellable        *cancellable,
+                                GAsyncReadyCallback  callback,
+                                gpointer             user_data)
+{
+    const char *provider_type = goa_account_get_provider_type (account);
+    GTask *task = g_task_new (account, cancellable, callback, user_data);
+
+    if (strcmp (provider_type, "facebook") == 0)
+        get_facebook_image (account, task);
+    else if (strcmp (provider_type, "google") == 0)
+        get_google_image (account, task);
+    else if (strcmp (provider_type, "flickr") == 0)
+        get_flickr_image (account, task);
+    else
+        g_task_return_pointer (task, NULL, NULL);
+}
+
+GBytes *
+get_avatar_from_online_account_finish (GoaAccount    *account,
+                                       GAsyncResult  *result,
+                                       GError       **error)
+{
+    g_return_val_if_fail (g_task_is_valid (result, account), NULL);
+    return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+void
+get_gravatar_from_email (const char          *email,
+                         GCancellable        *cancellable,
+                         GAsyncReadyCallback  callback,
+                         gpointer             user_data)
+{
+    SoupSession *session;
+    SoupMessage *msg;
+    gchar *copy, *hash, *url;
+    GTask *task = g_task_new (NULL, cancellable, callback, user_data);
+
+    copy = g_strdup (email);
+    copy = g_strstrip (copy);
+
+    hash = g_compute_checksum_for_string (G_CHECKSUM_MD5, copy, -1);
+
+    url = g_strdup_printf ("https://secure.gravatar.com/avatar/%s.png?s=%d&d=404";,
+                           hash, IMAGE_SIZE);
+
+    session = soup_session_new ();
+    msg = soup_message_new ("GET", url);
+    soup_session_queue_message (session, msg, got_image_cb, task);
+
+    g_free (url);
+    g_free (hash);
+    g_free (copy);
+}
+
+GBytes *
+get_gravatar_from_email_finish (GAsyncResult  *result,
+                                GError       **error)
+{
+    return g_task_propagate_pointer (G_TASK (result), error);
+}
diff --git a/panels/user-accounts/online-avatars.h b/panels/user-accounts/online-avatars.h
new file mode 100644
index 0000000..094456a
--- /dev/null
+++ b/panels/user-accounts/online-avatars.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright 2013 Red Hat, Inc,
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written by:
+ *   Jasper St. Pierre <jstpierre mecheye net>
+ *   Bogdan Ciobanu <bgdn ciobanu gmail com>
+ */
+
+#define GOA_API_IS_SUBJECT_TO_CHANGE
+#include <goa/goa.h>
+
+#ifndef __ONLINE_AVATARS__
+#define __ONLINE_AVATARS__
+
+void     get_avatar_from_online_account              (GoaAccount          *account,
+                                                      GCancellable        *cancellable,
+                                                      GAsyncReadyCallback  callback,
+                                                      gpointer             user_data);
+GBytes * get_avatar_from_online_account_finish       (GoaAccount    *account,
+                                                      GAsyncResult  *result,
+                                                      GError       **error);
+
+void     get_gravatar_from_email                     (const char          *email,
+                                                      GCancellable        *cancellable,
+                                                      GAsyncReadyCallback  callback,
+                                                      gpointer             user_data);
+GBytes * get_gravatar_from_email_finish              (GAsyncResult  *result,
+                                                      GError       **error);
+
+#endif /* __ONLINE_AVATARS__ */
diff --git a/panels/user-accounts/test-online-avatars.c b/panels/user-accounts/test-online-avatars.c
new file mode 100644
index 0000000..d8e858a
--- /dev/null
+++ b/panels/user-accounts/test-online-avatars.c
@@ -0,0 +1,63 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * Copyright 2013 Red Hat, Inc,
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written by:
+ *   Jasper St. Pierre <jstpierre mecheye net>
+ *   Bogdan Ciobanu <bgdn ciobanu gmail com>
+ */
+
+#include "online-avatars.h"
+
+#include <gio/gio.h>
+
+static GMainLoop *loop;
+
+static void
+got_gravatar (GObject      *source_object,
+              GAsyncResult *result,
+              gpointer      user_data)
+{
+    GError *error = NULL;
+    GBytes *bytes = get_gravatar_from_email_finish (result, &error);
+
+    if (error) {
+        g_warning ("Failed to fetch gravatar: %s\n", error->message);
+        g_error_free (error);
+    } else {
+        g_file_set_contents ("out.png",
+                             g_bytes_get_data (bytes, NULL),
+                             g_bytes_get_size (bytes),
+                             NULL);
+    }
+
+    g_main_loop_quit (loop);
+}
+
+int
+main (int argc, char *argv[])
+{
+    get_gravatar_from_email ("jstpierre mecheye net", NULL,
+                             got_gravatar, NULL);
+
+    loop = g_main_loop_new (NULL, FALSE);
+
+    g_main_loop_run (loop);
+    g_main_loop_unref (loop);
+
+    return 0;
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]