/* XMMS2 - X Music Multiplexer System * Copyright (C) 2003-2007 XMMS2 Team * * PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!! * * 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.1 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. */ #include "xmms/xmms_xformplugin.h" #include "xmms/xmms_log.h" #include "xmms/xmms_bindata.h" #include /* * Function prototypes */ static gboolean xmms_lastfm_init (xmms_xform_t *xform); static void xmms_lastfm_destroy (xmms_xform_t *decoder); static gboolean xmms_lastfm_plugin_setup (xmms_xform_plugin_t *xform_plugin); static gchar *xmms_lastfm_handshake (xmms_xform_t *xform, CURL *curl, GString *buffer); static gboolean xmms_lastfm_adjust (xmms_xform_t *xform, CURL *curl, GString *buffer); static size_t xmms_lastfm_feed_buffer (void *ptr, size_t size, size_t nmemb, void *stream); /* * Plugin header */ XMMS_XFORM_PLUGIN ("lastfm", "last.fm transport", XMMS_VERSION, "last.fm stream transport", xmms_lastfm_plugin_setup); static gboolean xmms_lastfm_plugin_setup (xmms_xform_plugin_t *xform_plugin) { xmms_xform_methods_t methods; XMMS_XFORM_METHODS_INIT (methods); methods.init = xmms_lastfm_init; methods.destroy = xmms_lastfm_destroy; xmms_xform_plugin_methods_set (xform_plugin, &methods); xmms_xform_plugin_config_property_register (xform_plugin, "username", "user", NULL, NULL); xmms_xform_plugin_config_property_register (xform_plugin, "password", "password", NULL, NULL); xmms_xform_plugin_indata_add (xform_plugin, XMMS_STREAM_TYPE_MIMETYPE, "application/x-url", XMMS_STREAM_TYPE_URL, "lastfm://*", XMMS_STREAM_TYPE_END); return TRUE; } /* * Member functions */ static gboolean xmms_lastfm_init (xmms_xform_t *xform) { gboolean ret = FALSE; GString *buffer; CURL *curl; gchar *url; g_return_val_if_fail (xform, FALSE); buffer = g_string_new (NULL); curl = curl_easy_init (); curl_easy_setopt (curl, CURLOPT_USERAGENT, "XMMS2/" XMMS_VERSION); curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, 15); curl_easy_setopt (curl, CURLOPT_WRITEDATA, buffer); curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, xmms_lastfm_feed_buffer); url = xmms_lastfm_handshake (xform, curl, buffer); if (!url) { xmms_log_error ("Last.fm handshake failed"); goto cleanup; } if (!xmms_lastfm_adjust (xform, curl, buffer)) { xmms_log_error ("Last.fm could not tune in given channel"); goto cleanup; } xmms_xform_outdata_type_add (xform, XMMS_STREAM_TYPE_MIMETYPE, "application/x-url", XMMS_STREAM_TYPE_URL, url, XMMS_STREAM_TYPE_END); ret = TRUE; cleanup: if (url) { g_free (url); } curl_easy_cleanup (curl); g_string_free (buffer, TRUE); return ret; } static void xmms_lastfm_destroy (xmms_xform_t *decoder) { /* a destroy function is currently required */ } static gchar * xmms_lastfm_handshake (xmms_xform_t *xform, CURL *curl, GString *buffer) { const gchar *handshake_fmt = "http://ws.audioscrobbler.com/radio/handshake.php" "?version=0.1" "&platform=linux" "&username=%s" "&passwordmd5=%s" "&debug=0"; const gchar *username, *password; xmms_config_property_t *val; gchar **split; gchar *tmp; gint i; gchar hash[33]; gchar *ret = NULL; g_return_val_if_fail (curl, NULL); g_return_val_if_fail (xform, NULL); val = xmms_xform_config_lookup (xform, "username"); username = xmms_config_property_get_string (val); val = xmms_xform_config_lookup (xform, "password"); password = xmms_config_property_get_string (val); xmms_bindata_calculate_md5 ((const guchar *) password, strlen (password), hash); tmp = g_strdup_printf (handshake_fmt, username, hash); curl_easy_setopt (curl, CURLOPT_URL, tmp); if (curl_easy_perform (curl) == CURLE_OK) { split = g_strsplit (buffer->str, "\n", 0); for (i = 0; split && split[i]; i++) { if (g_str_has_prefix (split[i], "session=")) { xmms_xform_metadata_set_str (xform, "session", split[i] + 8); } else if (g_str_has_prefix (split[i], "stream_url=")) { ret = g_strdup (split[i] + 11); } } g_strfreev (split); } g_free (tmp); g_string_erase (buffer, 0, -1); return ret; } static gboolean xmms_lastfm_adjust (xmms_xform_t *xform, CURL *curl, GString *buffer) { const gchar *request_fmt = "http://ws.audioscrobbler.com/radio/adjust.php" "?session=%s" "&url=%s" "&debug=0"; const gchar *url, *session; gchar *tmp; gboolean ret = FALSE; g_return_val_if_fail (curl, FALSE); g_return_val_if_fail (xform, FALSE); url = xmms_xform_indata_get_str (xform, XMMS_STREAM_TYPE_URL); session = xmms_xform_metadata_get_str (xform, "session"); tmp = g_strdup_printf (request_fmt, session, url); curl_easy_setopt (curl, CURLOPT_URL, tmp); if (curl_easy_perform (curl) == CURLE_OK) { if (g_str_has_prefix (buffer->str, "response=OK")) { ret = TRUE; } } g_free (tmp); g_string_erase (buffer, 0, -1); return ret; } static size_t xmms_lastfm_feed_buffer (void *ptr, size_t size, size_t nmemb, void *udata) { GString *data = (GString *) udata; g_string_append_len (data, ptr, nmemb); return size * nmemb; }