diff options
author | Ross Burton <ross@linux.intel.com> | 2010-05-14 15:25:48 +0100 |
---|---|---|
committer | Ross Burton <ross@linux.intel.com> | 2010-05-14 15:25:48 +0100 |
commit | 833fd9307977bdcc8b104332915fbbb43c439088 (patch) | |
tree | 31c383f863e03eac7ee7be5e63c0cf1eb1761554 /rest-extras | |
parent | 3b742fd56393619bd819ce427b710a4337919c22 (diff) | |
download | librest-833fd9307977bdcc8b104332915fbbb43c439088.tar.gz |
Add Lastfm proxy
Diffstat (limited to 'rest-extras')
-rw-r--r-- | rest-extras/Makefile.am | 9 | ||||
-rw-r--r-- | rest-extras/lastfm-proxy-call.c | 81 | ||||
-rw-r--r-- | rest-extras/lastfm-proxy-call.h | 68 | ||||
-rw-r--r-- | rest-extras/lastfm-proxy-private.h | 33 | ||||
-rw-r--r-- | rest-extras/lastfm-proxy.c | 379 | ||||
-rw-r--r-- | rest-extras/lastfm-proxy.h | 94 |
6 files changed, 662 insertions, 2 deletions
diff --git a/rest-extras/Makefile.am b/rest-extras/Makefile.am index 2c470f2..26a421f 100644 --- a/rest-extras/Makefile.am +++ b/rest-extras/Makefile.am @@ -3,10 +3,15 @@ CLEANFILES = lib_sources = \ flickr-proxy.c \ flickr-proxy-call.c \ - flickr-proxy-private.h + flickr-proxy-private.h \ + lastfm-proxy.c \ + lastfm-proxy-call.c \ + lastfm-proxy-private.h lib_headers = \ flickr-proxy.h \ - flickr-proxy-call.h + flickr-proxy-call.h \ + lastfm-proxy.h \ + lastfm-proxy-call.h lib_LTLIBRARIES = librest-extras-@API_VERSION@.la diff --git a/rest-extras/lastfm-proxy-call.c b/rest-extras/lastfm-proxy-call.c new file mode 100644 index 0000000..00d28d9 --- /dev/null +++ b/rest-extras/lastfm-proxy-call.c @@ -0,0 +1,81 @@ +/* + * librest - RESTful web services access + * Copyright (c) 2010 Intel Corporation. + * + * Authors: Rob Bradford <rob@linux.intel.com> + * Ross Burton <ross@linux.intel.com> + * + * 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, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <string.h> +#include <libsoup/soup.h> +#include <rest/rest-private.h> +#include <rest/rest-proxy-call.h> +#include "lastfm-proxy-call.h" +#include "lastfm-proxy-private.h" +#include "rest/rest-proxy-call-private.h" +#include "rest/sha1.h" + +G_DEFINE_TYPE (LastfmProxyCall, lastfm_proxy_call, REST_TYPE_PROXY_CALL) + +static gboolean +_prepare (RestProxyCall *call, GError **error) +{ + LastfmProxy *proxy = NULL; + LastfmProxyPrivate *priv; + RestProxyCallPrivate *call_priv; + char *s; + + g_object_get (call, "proxy", &proxy, NULL); + priv = PROXY_GET_PRIVATE (proxy); + call_priv = call->priv; + + /* First reset the URL because Lastfm puts the function in the parameters */ + call_priv->url = g_strdup (_rest_proxy_get_bound_url (REST_PROXY (proxy))); + + rest_proxy_call_add_params (call, + "method", call_priv->function, + "api_key", priv->api_key, + NULL); + + if (priv->session_key) + rest_proxy_call_add_param (call, "sk", priv->session_key); + + s = lastfm_proxy_sign (proxy, call_priv->params); + rest_proxy_call_add_param (call, "api_sig", s); + g_free (s); + + g_object_unref (proxy); + + return TRUE; +} + +static void +lastfm_proxy_call_class_init (LastfmProxyCallClass *klass) +{ + RestProxyCallClass *call_class = REST_PROXY_CALL_CLASS (klass); + + call_class->prepare = _prepare; +} + +static void +lastfm_proxy_call_init (LastfmProxyCall *self) +{ +} + +#if BUILD_TESTS +#warning TODO lastfm signature test cases +#endif diff --git a/rest-extras/lastfm-proxy-call.h b/rest-extras/lastfm-proxy-call.h new file mode 100644 index 0000000..1a1fefb --- /dev/null +++ b/rest-extras/lastfm-proxy-call.h @@ -0,0 +1,68 @@ +/* + * librest - RESTful web services access + * Copyright (c) 2010 Intel Corporation. + * + * Authors: Rob Bradford <rob@linux.intel.com> + * Ross Burton <ross@linux.intel.com> + * + * 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, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef _LASTFM_PROXY_CALL +#define _LASTFM_PROXY_CALL + +#include <rest/rest-proxy-call.h> + +G_BEGIN_DECLS + +#define LASTFM_TYPE_PROXY_CALL lastfm_proxy_call_get_type() + +#define LASTFM_PROXY_CALL(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), LASTFM_TYPE_PROXY_CALL, LastfmProxyCall)) + +#define LASTFM_PROXY_CALL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), LASTFM_TYPE_PROXY_CALL, LastfmProxyCallClass)) + +#define LASTFM_IS_PROXY_CALL(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LASTFM_TYPE_PROXY_CALL)) + +#define LASTFM_IS_PROXY_CALL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), LASTFM_TYPE_PROXY_CALL)) + +#define LASTFM_PROXY_CALL_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), LASTFM_TYPE_PROXY_CALL, LastfmProxyCallClass)) + +/** + * LastfmProxyCall: + * + * #LastfmProxyCall has no publicly available members. + */ +typedef struct { + RestProxyCall parent; +} LastfmProxyCall; + +typedef struct { + RestProxyCallClass parent_class; + /*< private >*/ + /* padding for future expansion */ + gpointer _padding_dummy[8]; +} LastfmProxyCallClass; + +GType lastfm_proxy_call_get_type (void); + +G_END_DECLS + +#endif /* _LASTFM_PROXY_CALL */ + diff --git a/rest-extras/lastfm-proxy-private.h b/rest-extras/lastfm-proxy-private.h new file mode 100644 index 0000000..1002f39 --- /dev/null +++ b/rest-extras/lastfm-proxy-private.h @@ -0,0 +1,33 @@ +/* + * librest - RESTful web services access + * Copyright (c) 2010 Intel Corporation. + * + * Authors: Rob Bradford <rob@linux.intel.com> + * Ross Burton <ross@linux.intel.com> + * + * 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, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include "lastfm-proxy.h" + +#define PROXY_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), LASTFM_TYPE_PROXY, LastfmProxyPrivate)) + +struct _LastfmProxyPrivate { + char *api_key; + char *secret; + char *session_key; +}; + diff --git a/rest-extras/lastfm-proxy.c b/rest-extras/lastfm-proxy.c new file mode 100644 index 0000000..c6fe054 --- /dev/null +++ b/rest-extras/lastfm-proxy.c @@ -0,0 +1,379 @@ +/* + * librest - RESTful web services access + * Copyright (c) 2010 Intel Corporation. + * + * Authors: Rob Bradford <rob@linux.intel.com> + * Ross Burton <ross@linux.intel.com> + * + * 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, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <config.h> +#include <stdlib.h> +#include <string.h> +#include <rest/rest-proxy.h> +#include <libsoup/soup.h> +#include "lastfm-proxy.h" +#include "lastfm-proxy-private.h" +#include "lastfm-proxy-call.h" + +G_DEFINE_TYPE (LastfmProxy, lastfm_proxy, REST_TYPE_PROXY) + +enum { + PROP_0, + PROP_API_KEY, + PROP_SECRET, + PROP_SESSION_KEY, +}; + +GQuark +lastfm_proxy_error_quark (void) +{ + return g_quark_from_static_string ("rest-lastfm-proxy"); +} + +static RestProxyCall * +_new_call (RestProxy *proxy) +{ + RestProxyCall *call; + + call = g_object_new (LASTFM_TYPE_PROXY_CALL, + "proxy", proxy, + NULL); + + return call; +} + +static void +lastfm_proxy_get_property (GObject *object, guint property_id, + GValue *value, GParamSpec *pspec) +{ + LastfmProxyPrivate *priv = PROXY_GET_PRIVATE (object); + + switch (property_id) { + case PROP_API_KEY: + g_value_set_string (value, priv->api_key); + break; + case PROP_SECRET: + g_value_set_string (value, priv->secret); + break; + case PROP_SESSION_KEY: + g_value_set_string (value, priv->session_key); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +lastfm_proxy_set_property (GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec) +{ + LastfmProxyPrivate *priv = PROXY_GET_PRIVATE (object); + + switch (property_id) { + case PROP_API_KEY: + if (priv->api_key) + g_free (priv->api_key); + priv->api_key = g_value_dup_string (value); + break; + case PROP_SECRET: + if (priv->secret) + g_free (priv->secret); + priv->secret = g_value_dup_string (value); + break; + case PROP_SESSION_KEY: + if (priv->session_key) + g_free (priv->session_key); + priv->session_key = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +lastfm_proxy_finalize (GObject *object) +{ + LastfmProxyPrivate *priv = PROXY_GET_PRIVATE (object); + + g_free (priv->api_key); + g_free (priv->secret); + g_free (priv->session_key); + + G_OBJECT_CLASS (lastfm_proxy_parent_class)->finalize (object); +} + +#ifndef G_PARAM_STATIC_STRINGS +#define G_PARAM_STATIC_STRINGS (G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB) +#endif + +static void +lastfm_proxy_class_init (LastfmProxyClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + RestProxyClass *proxy_class = REST_PROXY_CLASS (klass); + GParamSpec *pspec; + + g_type_class_add_private (klass, sizeof (LastfmProxyPrivate)); + + object_class->get_property = lastfm_proxy_get_property; + object_class->set_property = lastfm_proxy_set_property; + object_class->finalize = lastfm_proxy_finalize; + + proxy_class->new_call = _new_call; + + pspec = g_param_spec_string ("api-key", "api-key", + "The API key", NULL, + G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, + PROP_API_KEY, + pspec); + + pspec = g_param_spec_string ("secret", "secret", + "The API key secret", NULL, + G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, + PROP_SECRET, + pspec); + + pspec = g_param_spec_string ("session-key", "session-key", + "The session key", NULL, + G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, + PROP_SESSION_KEY, + pspec); +} + +static void +lastfm_proxy_init (LastfmProxy *self) +{ + self->priv = PROXY_GET_PRIVATE (self); +} + +RestProxy * +lastfm_proxy_new (const char *api_key, + const char *secret) +{ + return lastfm_proxy_new_with_session (api_key, + secret, + NULL); +} + +RestProxy * +lastfm_proxy_new_with_session (const char *api_key, + const char *secret, + const char *session_key) +{ + return g_object_new (LASTFM_TYPE_PROXY, + "api-key", api_key, + "secret", secret, + "session-key", session_key, + "url-format", "http://ws.audioscrobbler.com/2.0/", + "binding-required", FALSE, + NULL); +} + +/** + * lastfm_proxy_get_api_key: + * @proxy: an #LastfmProxy + * + * Get the API key. + * + * Returns: the API key. This string is owned by #LastfmProxy and should not be + * freed. + */ +const char * +lastfm_proxy_get_api_key (LastfmProxy *proxy) +{ + LastfmProxyPrivate *priv = PROXY_GET_PRIVATE (proxy); + return priv->api_key; +} + +/** + * lastfm_proxy_get_secret: + * @proxy: an #LastfmProxy + * + * Get the secret for authentication. + * + * Returns: the secret. This string is owned by #LastfmProxy and should not be + * freed. + */ +const char * +lastfm_proxy_get_secret (LastfmProxy *proxy) +{ + LastfmProxyPrivate *priv = PROXY_GET_PRIVATE (proxy); + return priv->secret; +} + +/** + * lastfm_proxy_get_session_key: + * @proxy: an #LastfmProxy + * + * Get the current session key. + * + * Returns: the session key, or %NULL if there is no session key yet. This string is owned + * by #LastfmProxy and should not be freed. + */ +const char * +lastfm_proxy_get_session_key (LastfmProxy *proxy) +{ + LastfmProxyPrivate *priv = PROXY_GET_PRIVATE (proxy); + return priv->session_key; +} + +/** + * lastfm_proxy_set_session_key: + * @proxy: an #LastfmProxy + * @session_key: the access session_key + * + * Set the session key. + */ +void +lastfm_proxy_set_session_key (LastfmProxy *proxy, const char *session_key) +{ + LastfmProxyPrivate *priv; + + g_return_if_fail (LASTFM_IS_PROXY (proxy)); + priv = PROXY_GET_PRIVATE (proxy); + + if (priv->session_key) + g_free (priv->session_key); + + priv->session_key = g_strdup (session_key); +} + +char * +lastfm_proxy_sign (LastfmProxy *proxy, GHashTable *params) +{ + LastfmProxyPrivate *priv; + GString *s; + GList *keys; + char *md5; + + g_return_val_if_fail (LASTFM_IS_PROXY (proxy), NULL); + g_return_val_if_fail (params, NULL); + + priv = PROXY_GET_PRIVATE (proxy); + + s = g_string_new (NULL); + + keys = g_hash_table_get_keys (params); + keys = g_list_sort (keys, (GCompareFunc)strcmp); + + while (keys) { + const char *key; + const char *value; + + key = keys->data; + value = g_hash_table_lookup (params, key); + + g_string_append_printf (s, "%s%s", key, value); + + keys = g_list_delete_link (keys, keys); + } + + g_string_append (s, priv->secret); + + md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, s->str, s->len); + + g_string_free (s, TRUE); + + return md5; +} + +char * +lastfm_proxy_build_login_url (LastfmProxy *proxy, const char *token) +{ + g_return_val_if_fail (LASTFM_IS_PROXY (proxy), NULL); + g_return_val_if_fail (token, NULL); + + return g_strdup_printf ("http://www.last.fm/api/auth/?api_key=%s&token=%s", + proxy->priv->api_key, + token); +} + +/** + * lastfm_proxy_is_successful: + * @root: The root node of a parsed Lastfm response + * @error: #GError to set if the response was an error + * + * Examines the Lastfm response and if it not a successful reply, set @error and + * return FALSE. + * + * Returns: %TRUE if this response is successful, %FALSE otherwise. + */ +gboolean +lastfm_proxy_is_successful (RestXmlNode *root, GError **error) +{ + RestXmlNode *node; + + g_return_val_if_fail (root, FALSE); + + if (strcmp (root->name, "lfm") != 0) { + g_set_error (error, LASTFM_PROXY_ERROR, 0, + "Unexpected response from Lastfm (root node %s)", + root->name); + return FALSE; + } + + if (strcmp (rest_xml_node_get_attr (root, "status"), "ok") != 0) { + node = rest_xml_node_find (root, "error"); + g_set_error_literal (error,LASTFM_PROXY_ERROR, + atoi (rest_xml_node_get_attr (node, "code")), + node->content); + return FALSE; + } + + return TRUE; +} + +#if BUILD_TESTS +void +test_lastfm_error (void) +{ + RestXmlParser *parser; + RestXmlNode *root; + GError *error; + const char test_1[] = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + "<foobar/>"; + const char test_2[] = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + "<lfm status=\"failed\"><error code=\"10\">Invalid API Key</error></lfm>"; + const char test_3[] = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + "<lfm status=\"ok\">some data</lfm>"; + + parser = rest_xml_parser_new (); + + error = NULL; + root = rest_xml_parser_parse_from_data (parser, test_1, sizeof (test_1) - 1); + lastfm_proxy_is_successful (root, &error); + g_assert_error (error, LASTFM_PROXY_ERROR, 0); + g_error_free (error); + rest_xml_node_unref (root); + + root = rest_xml_parser_parse_from_data (parser, test_2, sizeof (test_2) - 1); + error = NULL; + lastfm_proxy_is_successful (root, &error); + g_assert_error (error, LASTFM_PROXY_ERROR, 10); + rest_xml_node_unref (root); + + error = NULL; + root = rest_xml_parser_parse_from_data (parser, test_3, sizeof (test_3) - 1); + lastfm_proxy_is_successful (root, &error); + g_assert_no_error (error); + g_error_free (error); + rest_xml_node_unref (root); +} +#endif diff --git a/rest-extras/lastfm-proxy.h b/rest-extras/lastfm-proxy.h new file mode 100644 index 0000000..85e612a --- /dev/null +++ b/rest-extras/lastfm-proxy.h @@ -0,0 +1,94 @@ +/* + * librest - RESTful web services access + * Copyright (c) 2010 Intel Corporation. + * + * Authors: Rob Bradford <rob@linux.intel.com> + * Ross Burton <ross@linux.intel.com> + * + * 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, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef _LASTFM_PROXY +#define _LASTFM_PROXY + +#include <rest/rest-proxy.h> +#include <rest/rest-xml-parser.h> + +G_BEGIN_DECLS + +#define LASTFM_TYPE_PROXY lastfm_proxy_get_type() + +#define LASTFM_PROXY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), LASTFM_TYPE_PROXY, LastfmProxy)) + +#define LASTFM_PROXY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), LASTFM_TYPE_PROXY, LastfmProxyClass)) + +#define LASTFM_IS_PROXY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LASTFM_TYPE_PROXY)) + +#define LASTFM_IS_PROXY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), LASTFM_TYPE_PROXY)) + +#define LASTFM_PROXY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), LASTFM_TYPE_PROXY, LastfmProxyClass)) + +typedef struct _LastfmProxyPrivate LastfmProxyPrivate; + +/** + * LastfmProxy: + * + * #LastfmProxy has no publicly available members. + */ +typedef struct { + RestProxy parent; + LastfmProxyPrivate *priv; +} LastfmProxy; + +typedef struct { + RestProxyClass parent_class; + /*< private >*/ + /* padding for future expansion */ + gpointer _padding_dummy[8]; +} LastfmProxyClass; + +#define LASTFM_PROXY_ERROR lastfm_proxy_error_quark() + +GType lastfm_proxy_get_type (void); + +RestProxy* lastfm_proxy_new (const char *api_key, + const char *secret); + +RestProxy* lastfm_proxy_new_with_session (const char *api_key, + const char *secret, + const char *session_key); + +const char * lastfm_proxy_get_api_key (LastfmProxy *proxy); + +const char * lastfm_proxy_get_secret (LastfmProxy *proxy); + +const char * lastfm_proxy_get_session_key (LastfmProxy *proxy); + +void lastfm_proxy_set_session_key (LastfmProxy *proxy, const char *session_key); + +char * lastfm_proxy_sign (LastfmProxy *proxy, GHashTable *params); + +char * lastfm_proxy_build_login_url (LastfmProxy *proxy, const char *token); + +gboolean lastfm_proxy_is_successful (RestXmlNode *root, GError **error); + +G_END_DECLS + +#endif /* _LASTFM_PROXY */ |