Skip to content
Browse files

Update scrobsub with latest code

See http://github.com/mxcl/scrobsub

Also yes, this does mean I have stopped using the subtree merge strategy. It
didn't seem worth it, although admittedly that's because I fucked it up… :P
  • Loading branch information...
1 parent fe88023 commit 51fd1bfabc88283e0bf4af3392f81c48584542b0 @mxcl mxcl committed
View
8 resolvers/audioscrobbler/CMakeLists.txt
@@ -4,10 +4,10 @@ IF(NOT WIN32)
ADD_LIBRARY( audioscrobbler SHARED audioscrobbler_plugin.cpp
scrobsub.c
relay.c
- portable/auth-libxml2.c
- portable/http-curl.c
- portable/md5.c
- portable/persistence-simple.c )
+ auth-libxml2.c
+ http-curl.c
+ md5.c
+ persistence-simple.c )
ADD_DEFINITIONS( -w -std=c99
-DSCROBSUB_SHARED_SECRET="e800f85bb46beeca0af4175724e97b2e"
View
63 resolvers/audioscrobbler/README
@@ -1,8 +1,11 @@
-ScrobSub 2
-==========
-A cross-platform plain-c library that allows you to submit scrobbles to Last.fm
-using the official Audioscrobbler application, or if that isn't installed,
-independently.
+ScrobSub
+========
+A cross-platform plain-c library that allows you to submit scrobbles to
+Last.fm using the official Audioscrobbler application, or if that isn't
+installed, independently.
+
+The license is BSD, so use it in commercial or open source software as you see
+fit.
Max Howell <max@last.fm>
@@ -13,21 +16,50 @@ This does not compile to a dynamic library. You need to compile it into your
application. Indeed, (on Linux) this is duplication, but it's a very small
amount of code, and we provide many options for which libraries it uses.
-You need to compile in scrobsub.c and some of the implementation files. You
-can pick and choose from the portable/ directory. Or for instance use
-cocoa/scrobsub.m, although the Cocoa implementation requires a Cocoa run loop.
+scrosub.c does the actual scrobbling, however it depends on a number of
+platform specific scrobsub_* functions. If you like you can write your own (if
+perhaps you'd contribute them to the project?), however we provide default
+implementations for all major platforms:
+
+Linux
+-----
+gcc -std=c99 scrobsub.c auth-libxml2 http-curl md5.c relay.c -lcrypto -lxml2 -lcurl
+
+OS X
+----
+Tiger and above are shipped with all the required libraries from the Linux
+example so you can use those. This is preferable if you don't run a Cocoa
+event loop. If you do though, we provide a Cocoa implementation:
+
+gcc scrobsub.c scrobsub.m md5.c relay.c -FCocoa
-So pick whichever suits you, or write a new one (there's only a few functions to
-reimplement) and submit it.
+Windows
+-------
+cl /TP scrobsub.c scrobsub-winsock.c md5.c relay.c
-You also will need to define a number of preprocessor constants:
+Note that scrobsub.c is C99, and Visual Studio still isn't compliant, so you
+need to compile with the /TP switch; this compiles the files as though they
+are C++. You can still link to the C-runtime though.
+
+All platforms
+-------------
+You need to define a number of preprocessor constants:
SCROBSUB_CLIENT_VERSION
SCROBSUB_CLIENT_ID
SCROBSUB_API_KEY
SCROBSUB_SHARED_SECRET
-What these represent should be apparent once you have read: http://last.fm/api
+What these represent should be apparent after reading: http://last.fm/api
+
+Relay only
+----------
+If you have already built a scrobbling solution and want to stick with it then
+you may consider only compiling in the relay code. At program start call
+scrobsub_launch_audioscrobbler(), if that function returns false use your own
+code otherwise use the scrobsub_relay functions. The rationale for this is
+that if the user has installed the official client then they probably would
+like you to use it.
Practical concerns
@@ -37,3 +69,10 @@ them, you will need to call these functions in a separate thread. Or you can
rely on the user installing the official Last.fm Audioscrobbler application:
#define SCROBSUB_ALWAYS_RELAY 1
+
+
+Development Notes
+=================
+Efficiency in memory consumption was prioritised because we want to be as
+little a burden to other software as possible. As a result the code is
+quite unreadable. Sorry about that.
View
43 ...rs/audioscrobbler/portable/auth-libxml2.c → resolvers/audioscrobbler/auth-libxml2.c
@@ -1,22 +1,29 @@
-/***************************************************************************
- * Copyright 2005-2009 Last.fm Ltd. *
- * *
- * 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., *
- * 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. *
- ***************************************************************************/
+/*
+ Copyright 2009 Last.fm Ltd.
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ This file was originally created by Max Howell <max@last.fm>
+*/
#include "scrobsub.h"
#include <libxml/parser.h>
#include <libxml/xpath.h>
View
69 resolvers/audioscrobbler/http-curl.c
@@ -0,0 +1,69 @@
+/*
+ Copyright 2009 Last.fm Ltd.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ This file was originally created by Max Howell <max@last.fm>
+*/
+#include "scrobsub.h"
+#include <string.h>
+#include <curl/curl.h>
+
+//TODO user-agent
+
+static int n;
+
+static size_t curl_writer(void* in, size_t size, size_t N, void* out)
+{
+ size_t x;
+ N *= size;
+ for(x=0; x<N; ++x){
+ if (n-- <= 0) break;
+ *(char*)out++ = *(char*)in++;
+ }
+
+ return x;
+}
+
+void scrobsub_get(char response[256], const char* url)
+{
+ n = 256;
+
+ CURL* h = curl_easy_init(); //TODO may return NULL
+ curl_easy_setopt(h, CURLOPT_URL, url);
+ curl_easy_setopt(h, CURLOPT_WRITEFUNCTION, curl_writer);
+ curl_easy_setopt(h, CURLOPT_WRITEDATA, response);
+ CURLcode result = curl_easy_perform(h);
+ curl_easy_cleanup(h);
+
+ response[255-n] = '\0'; // curl_writer won't null terminate
+}
+
+void scrobsub_post(char response[256], const char* url, const char* post_data)
+{
+ CURL* h = curl_easy_init(); //TODO may return NULL
+ curl_easy_setopt(h, CURLOPT_POSTFIELDS, post_data);
+ curl_easy_setopt(h, CURLOPT_URL, url);
+ curl_easy_perform(h);
+ curl_easy_cleanup(h);
+ strcpy(response, "OK\n"); //TODO
+}
View
57 resolvers/audioscrobbler/md5.c
@@ -0,0 +1,57 @@
+/*
+ Copyright 2009 Last.fm Ltd.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ This file was originally created by Max Howell <max@last.fm>
+*/
+#if __APPLE__
+#include <CommonCrypto/CommonDigest.h>
+#elif WIN32
+#else
+#include <openssl/md5.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+
+
+void scrobsub_md5(char out[33], const char* in)
+{
+#if WIN32
+ #error Pls to implement
+#else
+ #if __APPLE__
+ unsigned char d[CC_MD5_DIGEST_LENGTH];
+ CC_MD5(in, strlen(in), d);
+ #else
+ MD5_CTX c;
+ unsigned char d[MD5_DIGEST_LENGTH];
+
+ MD5_Init(&c);
+ MD5_Update(&c, in, strlen(in));
+ MD5_Final(d, &c);
+ #endif
+ snprintf(out, 33, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
+ d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7],
+ d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]);
+#endif
+}
View
45 ...ioscrobbler/portable/persistence-simple.c → ...lvers/audioscrobbler/persistence-simple.c
@@ -1,28 +1,33 @@
-/***************************************************************************
- * Copyright 2005-2009 Last.fm Ltd. *
- * *
- * 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., *
- * 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. *
- ***************************************************************************/
+/*
+ Copyright 2009 Last.fm Ltd.
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ This file was originally created by Max Howell <max@last.fm>
+*/
#include "scrobsub.h"
#include <stdio.h>
#include <string.h>
-#include <stdlib.h> // needed for getenv() on linux
-
static FILE* fopen_session_file(const char* mode)
{
View
62 resolvers/audioscrobbler/portable/http-curl.c
@@ -1,62 +0,0 @@
-/***************************************************************************
- * Copyright 2005-2009 Last.fm Ltd. *
- * *
- * 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., *
- * 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. *
- ***************************************************************************/
-
-#include "scrobsub.h"
-#include <string.h>
-#include <curl/curl.h>
-
-//TODO user-agent
-
-static int n;
-
-static size_t curl_writer(void* in, size_t size, size_t N, void* out)
-{
- size_t x;
- N *= size;
- for(x=0; x<N; ++x){
- if (n-- <= 0) break;
- *(char*)out++ = *(char*)in++;
- }
-
- return x;
-}
-
-void scrobsub_get(char response[256], const char* url)
-{
- n = 256;
-
- CURL* h = curl_easy_init(); //TODO may return NULL
- curl_easy_setopt(h, CURLOPT_URL, url);
- curl_easy_setopt(h, CURLOPT_WRITEFUNCTION, curl_writer);
- curl_easy_setopt(h, CURLOPT_WRITEDATA, response);
- CURLcode result = curl_easy_perform(h);
- curl_easy_cleanup(h);
-
- response[255-n] = '\0'; // curl_writer won't null terminate
-}
-
-void scrobsub_post(char response[256], const char* url, const char* post_data)
-{
- CURL* h = curl_easy_init(); //TODO may return NULL
- curl_easy_setopt(h, CURLOPT_POSTFIELDS, post_data);
- curl_easy_setopt(h, CURLOPT_URL, url);
- curl_easy_perform(h);
- curl_easy_cleanup(h);
- strcpy(response, "OK\n"); //TODO
-}
View
50 resolvers/audioscrobbler/portable/md5.c
@@ -1,50 +0,0 @@
-/***************************************************************************
- * Copyright 2009 Last.fm Ltd. *
- * *
- * 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., *
- * 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. *
- ***************************************************************************/
-
-#if __APPLE__
-#include <CommonCrypto/CommonDigest.h>
-#elif WIN32
-#else
-#include <openssl/md5.h>
-#endif
-#include <stdio.h>
-#include <string.h>
-
-
-void scrobsub_md5(char out[33], const char* in)
-{
-#if WIN32
- #error Pls to implement
-#else
- #if __APPLE__
- unsigned char d[CC_MD5_DIGEST_LENGTH];
- CC_MD5(in, strlen(in), d);
- #else
- MD5_CTX c;
- unsigned char d[MD5_DIGEST_LENGTH];
-
- MD5_Init(&c);
- MD5_Update(&c, in, strlen(in));
- MD5_Final(d, &c);
- #endif
- snprintf(out, 33, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
- d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7],
- d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]);
-#endif
-}
View
101 resolvers/audioscrobbler/relay.c
@@ -1,36 +1,48 @@
-/***************************************************************************
- * Copyright 2005-2009 Last.fm Ltd. *
- * *
- * 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., *
- * 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. *
- ***************************************************************************/
+/*
+ Copyright 2009 Last.fm Ltd.
-// Created by Max Howell <max@last.fm>
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ This file was originally created by Max Howell <max@last.fm>
+*/
#include "scrobsub.h"
#if __APPLE__
#include <ApplicationServices/ApplicationServices.h>
#endif
+#if __APPLE__
+bool scrobsub_fsref(FSRef* fsref)
+{
+ OSStatus err = LSFindApplicationForInfo(kLSUnknownCreator, CFSTR("fm.last.Audioscrobbler"), NULL, fsref, NULL);
+ return err != kLSApplicationNotFoundErr;
+}
+#endif
+
/** returns false if Audioscrobbler is not installed */
bool scrobsub_launch_audioscrobbler()
{
#if __APPLE__
FSRef fsref;
- OSStatus err = LSFindApplicationForInfo(kLSUnknownCreator, CFSTR("fm.last.Audioscrobbler"), NULL, &fsref, NULL);
- if (err == kLSApplicationNotFoundErr)
+ if (!scrobsub_fsref(&fsref))
return false;
LSApplicationParameters p = {0};
@@ -38,15 +50,14 @@ bool scrobsub_launch_audioscrobbler()
p.application = &fsref;
LSOpenApplication( &p, NULL ); //won't launch if already running
return true; //TODO if failed to launch we should log it
-#else
- return false;
#endif
}
#if __APPLE__
static void script(const char* cmd)
{
- char a[] = "osascript -e 'tell application \"Audioscrobbler\" to ";
+ // the $ allows us to escape single quotes inside a single quoted string
+ char a[] = "osascript -e $'tell application \"Audioscrobbler\" to ";
char b[sizeof(a)+strlen(cmd)+2];
strcpy(b, a);
strcat(b, cmd);
@@ -63,18 +74,40 @@ void scrobsub_relay(int state)
}
}
-void scrobsub_relay_start(const char* artist, const char* title, int duration)
+static inline uint strcat_escape_quotes(char* dst, char* src)
{
- #define FORMAT "start \""SCROBSUB_CLIENT_ID"\" with \"%s\" by \"%s\" duration %d"
- char s[sizeof(FORMAT)+strlen(artist)+strlen(title)];
- snprintf(s, sizeof(s), FORMAT, title, artist, duration);
- script(s);
+ // get to the end of the dst string first
+ while(*dst)
+ dst++;
+
+ char* c;
+ while(c = *src++){
+ if(c == '\'' || c == '"')
+ *dst++ = '\\';
+ *dst++ = c;
+ }
+ *dst = '\0';
}
-#else
-void scrobsub_relay(int state)
-{}
+void scrobsub_relay_start(const char* artist, const char* title, int durationi)
+{
+ #define START "start \"" SCROBSUB_CLIENT_ID "\" with \""
+ #define BY "\" by \""
+ #define DURATION "\" duration %d " //strlen("%d")+3 = 5 digits, thus up to 99,999 seconds
-void scrobsub_relay_start(const char* artist, const char* title, int duration)
-{}
+ const uint N = sizeof(START BY DURATION) +
+ strlen(artist)*2 + // double the length of maybe-quoted strings
+ strlen(title)*2;
+ char s[N];
+ strcpy(s, START);
+ strcat_escape_quotes(s, title);
+ strcat(s, BY);
+ strcat_escape_quotes(s, artist);
+
+ char durations[] = DURATION;
+ snprintf(durations, sizeof(durations), DURATION, durationi);
+ strcat(s, durations);
+
+ script(s);
+}
#endif //__APPLE__
View
184 resolvers/audioscrobbler/scrobsub.c
@@ -1,45 +1,54 @@
-/***************************************************************************
- * Copyright 2005-2009 Last.fm Ltd. *
- * *
- * 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., *
- * 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. *
- ***************************************************************************/
-
-// Created by Max Howell <max@last.fm>
+/*
+ Copyright 2009 Last.fm Ltd.
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ This file was originally created by Max Howell <max@last.fm>
+*/
#include "scrobsub.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
+#ifdef _WIN32
+#include <malloc.h>
+#define snprintf _snprintf
+#endif
static time_t start_time = 0;
static time_t pause_time = 0;
static int state = SCROBSUB_STOPPED;
static char* session_id = 0;
-static unsigned int N = 0;
+static unsigned int N = 0; // cached length of artist+track+album+mbid
static char* np_url = 0;
static char* submit_url = 0;
static char rating = ' ';
-static char* artist;
-static char* track;
-static char* album;
-static char* mbid;
-static unsigned int duration;
-static unsigned int track_number;
+static char* artist = 0;
+static char* track = 0;
+static char* album = 0;
+static char* mbid = 0;
+static unsigned int duration = 0;
+static unsigned int track_number = 0;
void(*scrobsub_callback)(int event, const char* message);
void scrobsub_get(char* response, const char* url);
@@ -48,10 +57,10 @@ bool scrobsub_retrieve_credentials();
bool scrobsub_launch_audioscrobbler();
#if SCROBSUB_NO_RELAY
-// compiler will optimise this stuff away now
+// compiler will optimise this stuff away now (in theory)
#define relay false
#define scrobsub_relay(x)
-#define scrobsub_relay_start(x, y, z);
+#define scrobsub_relay_start(x, y, z)
#else
static bool relay = true;
void scrobsub_relay(int);
@@ -62,6 +71,7 @@ char* scrobsub_session_key = 0;
char* scrobsub_username = 0;
+/** worth noting that this is UTC and not local */
static time_t now()
{
time_t t;
@@ -77,9 +87,14 @@ void scrobsub_init(void(*callback)(int, const char*))
#if !SCROBSUB_NO_RELAY
// will return true if audioscrobbler is installed
relay = scrobsub_launch_audioscrobbler();
-#endif
+#endif
if(!relay && !scrobsub_retrieve_credentials())
(callback)(SCROBSUB_AUTH_REQUIRED, 0);
+
+ artist = (char*)malloc(0); // these must always point at malloc'd data
+ track = (char*)malloc(0);
+ album = (char*)malloc(0);
+ mbid = (char*)malloc(0);
}
static void get_handshake_auth(char out[33], time_t time)
@@ -103,18 +118,60 @@ static bool ok(char* response)
return response[0] == 'O' && response[1] == 'K' && response[2] == '\n';
}
-static void handshake()
+static char* escape(const char* in)
+{
+ // code from QByteArray.cpp, Qt 4.4.0
+
+ const char hexnumbers[] = "0123456789ABCDEF";
+ #define toHexHelper(c) hexnumbers[(c) & 0xf]
+
+ int const n = strlen(in);
+#if SCROBSUB_NO_C99
+ // we only use alloca on Windows as its use is discouraged on BSD
+ char* outs = (char*)alloca(n);
+#else
+ char outs[n*3];
+#endif
+
+ char* out = outs;
+ for(int i = 0; i < n; ++i){
+ char c = *in++;
+ if(c >= 0x61 && c <= 0x7A // ALPHA
+ || c >= 0x41 && c <= 0x5A // ALPHA
+ || c >= 0x30 && c <= 0x39 // DIGIT
+ || c == 0x2D // -
+ || c == 0x2E // .
+ || c == 0x5F // _
+ || c == 0x7E)// ~
+ *out++ = c;
+ else{
+ *out++ = '%';
+ *out++ = toHexHelper((c & 0xf0) >> 4);
+ *out++ = toHexHelper(c & 0xf);
+ }
+ }
+ *out = '\0';
+
+ return strdup(outs);
+}
+
+static bool handshake()
{
scrobsub_finish_auth();
if (!scrobsub_session_key || !scrobsub_username)
- return; //TODO auth required
+ return false; //TODO auth required AGAIN
+ char* username = escape(scrobsub_username);
time_t time = now();
char auth[33];
get_handshake_auth(auth, time);
- int n = 34+8+8+6+11+3+strlen(scrobsub_username)+13+32+9+32+4+32+1;
+ int n = 34+8+8+6+11+3+strlen(username)+13+32+9+32+4+32+1;
+#if SCROBSUB_NO_C99
+ char* url = (char*)alloca(n); //alloca discouraged on BSD
+#else
char url[n];
+#endif
n = snprintf(url, n, "http://post.audioscrobbler.com:80/"
"?hs=true"
@@ -126,21 +183,26 @@ static void handshake()
"&a=%s" // length 32
"&api_key=" SCROBSUB_API_KEY // length 32
"&sk=%s", // length 32
- scrobsub_username, time, auth, scrobsub_session_key);
- if (n<0) return; //TODO error callback
+ username, time, auth, scrobsub_session_key);
+ free(username);
+
+ if (n<0) return false; //TODO error callback
char responses[256];
- char* response = responses;
scrobsub_get(responses, url);
-
+
+ char* response = responses;
if(ok(response)){
response += 3;
session_id = handshake_response_strdup(&response);
np_url = handshake_response_strdup(&response);
submit_url = handshake_response_strdup(&response);
- }else
+ return true;
+ }else{
//TODO better
(scrobsub_callback)(SCROBSUB_ERROR_RESPONSE, response);
+ return false;
+ }
}
static unsigned int scrobble_time(unsigned int duration)
@@ -157,11 +219,15 @@ static void submit()
if(state == SCROBSUB_PAUSED)
scrobsub_resume(); //sets pause time correctly
//FIXME the second resolution issue can introduce rounding errors
- if(now() - (start_time + pause_time) < scrobble_time(duration))
+ if(now()-(start_time+pause_time) < scrobble_time(duration))
return;
int n = 32+N+4+2+10+1+1 +2+9*6;
+#if SCROBSUB_NO_C99
+ char* post_data = (char*)alloca(n); //alloca discouraged on BSD
+#else
char post_data[n];
+#endif
n = snprintf(post_data, n,
"s=%s"
@@ -176,7 +242,7 @@ static void submit()
"&r[0]=%c",
session_id, artist, track, album, duration, track_number, mbid, start_time, 'P', rating);
- for (int x=0; x<2; ++x){
+ for (int x=0; x<2; ++x){
char response[128];
scrobsub_post(response, submit_url, post_data);
@@ -187,6 +253,17 @@ static void submit()
}
}
+void scrobsub_change_metadata(const char* _artist, const char* _track, const char* _album)
+{
+ free(artist);
+ free(track);
+ free(album);
+ artist = escape(_artist);
+ track = escape(_track);
+ album = escape(_album);
+ N = strlen(artist)+strlen(track)+strlen(album)+strlen(mbid);
+}
+
void scrobsub_start(const char* _artist, const char* _track, unsigned int _duration, const char* _album, unsigned int _track_number, const char* _mbid)
{
if(_duration>9999) _duration = 9999;
@@ -197,34 +274,37 @@ void scrobsub_start(const char* _artist, const char* _track, unsigned int _durat
state = SCROBSUB_PLAYING;
return;
}
-
- if (!session_id)
- handshake();
+
+ if (!session_id && !handshake())
+ return;
if (state != SCROBSUB_STOPPED)
submit();
state = SCROBSUB_PLAYING;
- artist = strdup(_artist);
- track = strdup(_track);
- album = strdup(_album);
+ free(mbid);
mbid = strdup(_mbid);
duration = _duration;
track_number = _track_number;
rating = ' ';
-
+ scrobsub_change_metadata(_artist, _track, _album);
start_time = now();
-
- N = strlen(_artist)+strlen(_track)+strlen(_album)+strlen(_mbid);
+ pause_time = 0;
//TODO, don't emit np if user is skipping fast, then you need a timer
// static time_t previous_np = 0;
// time_t time = now();
// if(time - previous_np < 4)
+ //NOTE I pushed this into our GUIs, maybe that is all that matters
//TODO don't submit track number if 0
- char post_data[32+4+2+N+2+6*3];
+ #define POST_DATA_LENGTH 32+4+2+N+2+6*3
+#if SCROBSUB_NO_C99
+ char* post_data = (char*)alloca(POST_DATA_LENGTH); //alloca discouraged on BSD
+#else
+ char post_data[POST_DATA_LENGTH];
+#endif
snprintf(post_data, sizeof(post_data),
"s=%s"
"&a=%s"
@@ -279,9 +359,9 @@ void scrobsub_stop()
else if(state != SCROBSUB_STOPPED){
submit();
state = SCROBSUB_STOPPED;
- free( artist ); free( track ); free( album ); free( mbid );
- artist = track = album = mbid = 0;
duration = track_number = 0;
+ // indeed don't free or set artist, track etc. to NULL, they must always
+ // point to somthing malloc'd. We free in scrobsub_start()
}
}
View
122 resolvers/audioscrobbler/scrobsub.h
@@ -1,78 +1,98 @@
-/***************************************************************************
- * Copyright 2005-2009 Last.fm Ltd. *
- * *
- * 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., *
- * 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. *
- ***************************************************************************/
-
-// Created by Max Howell <max@last.fm>
-
+/*
+ Copyright 2009 Last.fm Ltd.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
#ifndef __SCROBSUB_H__
#define __SCROBSUB_H__
-#include <stdbool.h>
+#if defined(_MSC_VER) && _MSC_VER <= 1400
+ #define SCROBSUB_NO_C99 1
+#endif
+
+#if SCROBSUB_NO_C99
+ #if !__cplusplus && !defined(bool)
+ #define bool int
+ #define false 0
+ #define true 1
+ #endif
+#else
+ #include <stdbool.h>
+#endif
+
#if __cplusplus
extern "C" {
#endif
-/** the callback must be set, when the callback is called, you get one of the
- * SCROBSUB_ values. */
+/** Indeed, call this at your earliest convenience! */
void scrobsub_init(void(*callback)(int event, const char* message));
-/** you need to call scrobsub_auth, but you can do it whenever you want,
- * although, no scrobbling will happen until then */
+/** @see scrobsub_auth. Scrobbling will not function until your application is
+ * auth'd. However we will be caching scrobbles in memory. */
#define SCROBSUB_AUTH_REQUIRED 0
-/** the char* paramater will be the error string */
+/** The char* paramater will be the error string */
#define SCROBSUB_ERROR_RESPONSE 1
-/** the user needs to visit @p url within one hour for authentication to succeed */
+/** The URL at url needs to be visited by the user in a web browser withing an
+ * hour to authorize your application for scrobbling. @see
+ * SCROBSUB_AUTH_REQUIRED. */
void scrobsub_auth(char url[110]);
-
-/** once the user has visited the above page, we need to request their session
+/** Once the user has visited the above page, we need to request their session
* key, you can either do this yourself, or scrobsub will do it as required,
* mostly you won't need to call this function
* @returns true if authentication was successful */
bool scrobsub_finish_auth();
-/** A new track started. scrobsub takes copies of the strings. All strings must
- * be UTF8.
- * artist, track and duration are mandatory
- * album and mbid can be "" (do not pass NULL or 0)
- * if track_number is 0 it is ignored (yes, we know some albums have a zeroth track)
- * valid MBIDs are always 38 characters, be wary of that
- */
+/** A new track started.
+ * We dup' the strings, so don't worry yourself with memory woes.
+ * All strings must be UTF8.
+ * artist, track and duration are mandatory.
+ * album and mbid can be "" (do *not* pass NULL or 0).
+ * A value of 0 for track_number will be ignored.
+ * You should note that valid MBIDs are always 38 characters. */
void scrobsub_start(const char* artist, const char* track, unsigned int duration, const char* album, unsigned int track_number, const char* mbid);
-/** the thing that we're scrobbling got paused. This is not a toggle, when/if
+/** The track we are scrobbling became paused. This is NOT a toggle! When/if
* the track is unpaused, call resume. We insist on this distinction because
* we want you to be exact! */
void scrobsub_pause();
-/** the thing that we're scrobbling was unpaused */
+/** The thing that we're scrobbling was unpaused */
void scrobsub_resume();
-/** only call this when playback stops, if a new track is about to start, call
+/** Only call this when playback stops, if a new track is about to start, call
* scrobsub_start() instead */
void scrobsub_stop();
-/** marks the current track as loved, it is worth noting, you also have to call
- * the Last.fm track.love webservice separately (scrobsub doesn't do it for
- * you). This stupid system will be like this forever prolly. Sorry about that.
+
+/** Marks the current track as loved, it is worth noting, you also have to call
+ * the Last.fm track.love webservice separately (scrobsub doesn't do it for
+ * you). This should be fixed with Audioscrobbler protocol 2.0
*/
void scrobsub_love();
+/** Support this if you can and it is appropriate */
+void scrobsub_change_metadata(const char* artist, const char* track, const char* album);
+
#define SCROBSUB_STOPPED 0
#define SCROBSUB_PLAYING 1
@@ -81,16 +101,24 @@ void scrobsub_love();
int scrobsub_state();
-/** returns 0 if you need to auth, or the user still hasn't allowed the auth
- * attempt */
+/** If these are NULL then authorization has not yet been completed */
extern char* scrobsub_session_key;
extern char* scrobsub_username;
-/** for your convenience, we need it, so maybe you can use it too */
+/** For your convenience, we need it, so maybe you can use it too. We use the
+ * platform native library to generate this md5 */
void scrobsub_md5(char out[33], const char* in);
+
+/** This will always be the same as the protocol version we implement */
+#define SCROBSUB_VERSION 0x00010201
+#define SCROBSUB_VERSION_STRING "1.2.1"
+#define SCROBSUB_MAJOR_VERSION 1
+#define SCROBSUB_MINOR_VERSION 2
+#define SCROBSUB_PATCH_VERSION 1
+
#if __cplusplus
}
#endif
-#endif //__SCROBSUB_H__
+#endif

0 comments on commit 51fd1bf

Please sign in to comment.
Something went wrong with that request. Please try again.