[libsocialweb/libsocialweb-0.25] lastfm: login to last.fm to validate credentials (BMC#8986)



commit fb42bf7173a418713211b31c35fb979b65f33381
Author: Raul Gutiereez Segales <raul gutierrez segales collabora co uk>
Date:   Fri May 20 19:19:11 2011 +0100

    lastfm: login to last.fm to validate credentials (BMC#8986)

 services/lastfm/lastfm.c |  224 +++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 210 insertions(+), 14 deletions(-)
---
diff --git a/services/lastfm/lastfm.c b/services/lastfm/lastfm.c
index 0b780bc..4547568 100644
--- a/services/lastfm/lastfm.c
+++ b/services/lastfm/lastfm.c
@@ -1,6 +1,7 @@
 /*
  * libsocialweb - social data store
  * Copyright (C) 2009 Intel Corporation.
+ * Copyright (C) 2011 Collabora Ltd.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU Lesser General Public License,
@@ -27,6 +28,7 @@
 #include <libsocialweb/sw-service.h>
 #include <libsocialweb/sw-item.h>
 #include <libsocialweb/sw-utils.h>
+#include <libsocialweb/sw-online.h>
 #include <libsocialweb/sw-web.h>
 #include <libsocialweb/sw-call-list.h>
 #include <libsocialweb/sw-debug.h>
@@ -62,6 +64,12 @@ G_DEFINE_TYPE_WITH_CODE (SwServiceLastfm, sw_service_lastfm, SW_TYPE_SERVICE,
 struct _SwServiceLastfmPrivate {
   RestProxy *proxy;
   char *username;
+  char *password;
+  char *session_key;
+  char *api_key;
+  char *api_secret;
+  gboolean checked_with_server;
+  gboolean inited;
 };
 
 static const char ** get_dynamic_caps (SwService *service);
@@ -97,6 +105,155 @@ lastfm_submit_track (SwLastfmIface *self,
   sw_lastfm_iface_return_from_submit_track (context);
 }
 
+/* FIXME: This is mostly copied from services/twitter/twitter.c
+ * so we should move it to some common location and make
+ * it accessible */
+static RestXmlNode *
+node_from_call (RestProxyCall *call)
+{
+  static RestXmlParser *parser = NULL;
+  RestXmlNode *root;
+
+  if (parser == NULL)
+    parser = rest_xml_parser_new ();
+
+  if (!SOUP_STATUS_IS_SUCCESSFUL (rest_proxy_call_get_status_code (call))) {
+    g_message ("Error from LastFM: %s (%d)",
+               rest_proxy_call_get_status_message (call),
+               rest_proxy_call_get_status_code (call));
+    return NULL;
+  }
+
+  root = rest_xml_parser_parse_from_data (parser,
+                                          rest_proxy_call_get_payload (call),
+                                          rest_proxy_call_get_payload_length (call));
+
+  if (root == NULL) {
+    g_message ("Error from LastFM: %s",
+               rest_proxy_call_get_payload (call));
+    return NULL;
+  }
+
+  return root;
+}
+
+static void
+_mobile_session_cb (RestProxyCall *call,
+		    const GError  *error,
+		    GObject       *weak_object,
+		    gpointer       userdata)
+{
+  const gchar *status;
+  SwService *service = SW_SERVICE (weak_object);
+  SwServiceLastfm *lastfm = SW_SERVICE_LASTFM (service);
+  SwServiceLastfmPrivate *priv = lastfm->priv;
+  RestXmlNode *node;
+
+  priv->checked_with_server = TRUE;
+
+  if (error) {
+    g_message ("Error: %s", error->message);
+    g_object_unref (call);
+    sw_service_emit_capabilities_changed (service, get_dynamic_caps (service));
+    return;
+  }
+
+  node = node_from_call (call);
+  if (!node)
+    return;
+
+  status = g_hash_table_lookup (node->attrs, "status");
+
+  if (g_strcmp0 (status, "ok") == 0) {
+    RestXmlNode *session_key = rest_xml_node_find (node, "key");
+
+    if (session_key) {
+      g_free (priv->session_key);
+      priv->session_key = g_strdup (session_key->content);
+    }
+  }
+
+  rest_xml_node_unref (node);
+  g_object_unref (call);
+
+  sw_service_emit_capabilities_changed (service, get_dynamic_caps (service));
+}
+
+static char *
+build_call_sig (GHashTable *params, const char *secret)
+{
+  char *str = NULL;
+  char *sig, *temp;
+  GList *keys = g_hash_table_get_keys (params);
+  keys = g_list_sort (keys, (GCompareFunc)g_strcmp0);
+
+  while (keys) {
+    const char *value = (const char *) g_hash_table_lookup (params, keys->data);
+
+    if (str == NULL) {
+      str = g_strconcat((char *)keys->data, value, NULL);
+    } else {
+      temp = str;
+      str = g_strconcat(str, (char *)keys->data, value, NULL);
+      g_free (temp);
+    }
+
+    keys = keys->next;
+  }
+
+  temp = str;
+  str = g_strconcat(str, secret, NULL);
+  g_free (temp);
+
+  sig = g_compute_checksum_for_string (G_CHECKSUM_MD5, str, -1);
+
+  g_free (str);
+  g_list_free (keys);
+
+  return sig;
+}
+
+static void
+verify_user (SwService *service)
+{
+  SwServiceLastfm *lastfm = SW_SERVICE_LASTFM (service);
+  SwServiceLastfmPrivate *priv = lastfm->priv;
+  char *hash_pw, *user_pass, *auth_token;
+  RestProxyCall *call;
+  RestParams *params;
+  GHashTable *params_t;
+  char *api_sig;
+
+  hash_pw = g_compute_checksum_for_string (G_CHECKSUM_MD5,
+					   priv->password, -1);
+  user_pass = g_strconcat(priv->username, hash_pw, NULL);
+  auth_token = g_compute_checksum_for_string (G_CHECKSUM_MD5,
+					      user_pass, -1);
+
+  call = rest_proxy_new_call (priv->proxy);
+  rest_proxy_call_add_params (call,
+			      "api_key", priv->api_key,
+			      "username", priv->username,
+			      "authToken", auth_token,
+			      "method", "auth.getMobileSession",
+			      NULL);
+  params = rest_proxy_call_get_params (call);
+  params_t = rest_params_as_string_hash_table (params);
+
+  api_sig = build_call_sig (params_t, priv->api_secret);
+  rest_proxy_call_add_params (call,
+			      "api_sig", api_sig,
+			      NULL);
+
+  rest_proxy_call_async (call, _mobile_session_cb, (GObject*)lastfm, NULL, NULL);
+
+  g_hash_table_unref (params_t);
+  g_free (api_sig);
+  g_free (hash_pw);
+  g_free (user_pass);
+  g_free (auth_token);
+}
+
 /*
  * Callback from the keyring lookup in refresh_credentials.
  */
@@ -109,15 +266,24 @@ found_password_cb (GnomeKeyringResult  result,
   SwServiceLastfm *lastfm = SW_SERVICE_LASTFM (service);
   SwServiceLastfmPrivate *priv = lastfm->priv;
 
+  g_free (priv->username);
+  g_free (priv->password);
+  g_free (priv->session_key);
+  priv->session_key = NULL;
+  priv->username = NULL;
+  priv->password = NULL;
+  priv->checked_with_server = FALSE;
+
   if (result == GNOME_KEYRING_RESULT_OK && list != NULL) {
     GnomeKeyringNetworkPasswordData *data = list->data;
 
-    g_free (priv->username);
     priv->username = g_strdup (data->user);
-  } else {
-    g_free (priv->username);
-    priv->username = NULL;
+    priv->password = g_strdup (data->password);
 
+    if (sw_is_online ()) {
+      verify_user (service);
+    }
+  } else {
     if (result != GNOME_KEYRING_RESULT_NO_MATCH) {
       g_warning (G_STRLOC ": Error getting password: %s", gnome_keyring_result_to_message (result));
     }
@@ -156,22 +322,40 @@ get_dynamic_caps (SwService *service)
 {
   SwServiceLastfmPrivate *priv = GET_PRIVATE (service);
 
+  static const char *no_caps[] = { NULL };
+
   static const char *configured_caps[] = {
     IS_CONFIGURED,
-    CREDENTIALS_VALID,
     NULL
   };
 
-  static const char *unconfigured_caps[] = {
+  static const char *invalid_caps[] = {
+    IS_CONFIGURED,
+    CREDENTIALS_INVALID,
     NULL
   };
 
-  if (priv->username)
-  {
+  static const char *full_caps[] = {
+    IS_CONFIGURED,
+    CREDENTIALS_VALID,
+    NULL
+  };
+
+  if (priv->username == NULL) {
+    return no_caps;
+  } else if (priv->username && priv->checked_with_server == FALSE) {
     return configured_caps;
-  } else {
-    return unconfigured_caps;
+  } else if (priv->checked_with_server == TRUE) {
+    if (priv->session_key != NULL) {
+      return full_caps;
+    } else {
+      return invalid_caps;
+    }
   }
+
+  /* Just in case we fell through */
+  g_warning ("Unhandled credential state");
+  return no_caps;
 }
 
 static const char **
@@ -214,6 +398,10 @@ sw_service_lastfm_finalize (GObject *object)
   SwServiceLastfmPrivate *priv = ((SwServiceLastfm*)object)->priv;
 
   g_free (priv->username);
+  g_free (priv->password);
+  g_free (priv->session_key);
+  g_free (priv->api_key);
+  g_free (priv->api_secret);
 
   G_OBJECT_CLASS (sw_service_lastfm_parent_class)->finalize (object);
 }
@@ -225,8 +413,14 @@ sw_service_lastfm_initable (GInitable     *initable,
 {
   SwServiceLastfm *lastfm = SW_SERVICE_LASTFM (initable);
   SwServiceLastfmPrivate *priv = lastfm->priv;
+  const char *key = NULL, *secret = NULL;
+
+  if (priv->inited)
+    return TRUE;
 
-  if (sw_keystore_get_key ("lastfm") == NULL) {
+  sw_keystore_get_key_secret ("lastfm", &key, &secret);
+
+  if (key == NULL || secret == NULL) {
     g_set_error_literal (error,
                          SW_SERVICE_ERROR,
                          SW_SERVICE_ERROR_NO_KEYS,
@@ -234,13 +428,14 @@ sw_service_lastfm_initable (GInitable     *initable,
     return FALSE;
   }
 
-  if (priv->proxy)
-    return TRUE;
-
   priv->proxy = rest_proxy_new ("http://ws.audioscrobbler.com/2.0/";, FALSE);
+  priv->api_key = g_strdup (key);
+  priv->api_secret = g_strdup (secret);
 
   refresh_credentials (lastfm);
 
+  priv->inited = TRUE;
+
   return TRUE;
 }
 
@@ -397,6 +592,7 @@ static void
 sw_service_lastfm_init (SwServiceLastfm *self)
 {
   self->priv = GET_PRIVATE (self);
+  self->priv->inited = FALSE;
 }
 
 



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