xmms2-scrobbler-0.4.0/0000755000175000017500000000000011316704031014062 5ustar tilmantilmanxmms2-scrobbler-0.4.0/src/0000755000175000017500000000000011316704001014646 5ustar tilmantilmanxmms2-scrobbler-0.4.0/src/strbuf.c0000644000175000017500000000565411316704001016331 0ustar tilmantilman/* * Copyright (c) 2008 Tilman Sauerbeck (tilman at xmms org) * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include "strbuf.h" #define GOODCHAR(a) ((((a) >= 'a') && ((a) <= 'z')) || \ (((a) >= 'A') && ((a) <= 'Z')) || \ (((a) >= '0') && ((a) <= '9')) || \ ((a) == ':') || \ ((a) == '/') || \ ((a) == '-') || \ ((a) == '.') || \ ((a) == '_')) StrBuf * strbuf_new (void) { StrBuf *sb; sb = malloc (sizeof (StrBuf)); sb->allocated = 256; sb->buf = malloc (sb->allocated); sb->length = 0; sb->buf[sb->length] = 0; return sb; } void strbuf_free (StrBuf *sb) { free (sb->buf); free (sb); } static void resize (StrBuf *sb, size_t len) { int needed = sb->length + len + 1; if (needed > sb->allocated) { int alloc; for (alloc = sb->allocated; needed > alloc; alloc *= 2) ; sb->buf = realloc (sb->buf, alloc); sb->allocated = alloc; } } void strbuf_append (StrBuf *sb, const char *other) { size_t len; len = strlen (other); resize (sb, len); memcpy (sb->buf + sb->length, other, len + 1); sb->length += len; } void strbuf_append_encoded (StrBuf *sb, const uint8_t *other) { static const char hex[16] = "0123456789abcdef"; char *dest; int len = 0; for (const uint8_t *src = other; *src; src++) { if (GOODCHAR (*src)) len++; else if (*src == ' ') len++; else len += 3; } resize (sb, len); dest = sb->buf + sb->length; for (const uint8_t *src = other; *src; src++) { if (GOODCHAR (*src)) { *dest++ = *src; } else if (*src == ' ') { *dest++ = '+'; } else { *dest++ = '%'; *dest++ = hex[((*src & 0xf0) >> 4)]; *dest++ = hex[(*src & 0x0f)]; } } *dest++ = 0; sb->length += len; } void strbuf_truncate (StrBuf *sb, int length) { sb->length = length; sb->buf[sb->length] = 0; } xmms2-scrobbler-0.4.0/src/list.h0000644000175000017500000000247211316704001015777 0ustar tilmantilman/* * Copyright (c) 2009 Tilman Sauerbeck (tilman at xmms org) * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef _LIST_H #define _LIST_H typedef struct __List { struct __List *next; void *data; } List; List *list_prepend (List *list, void *data); List *list_remove_head (List *list); #endif xmms2-scrobbler-0.4.0/src/xmms2-scrobbler.c0000644000175000017500000005416311316704001020044 0ustar tilmantilman/* * Copyright (c) 2008 Tilman Sauerbeck (tilman at xmms org) * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "list.h" #include "queue.h" #include "submission.h" #include "md5.h" #define PROTOCOL "1.2" #define CLIENT_ID "xm2" #define VERSION "0.2.9" #define INVALID_MEDIA_ID -1 typedef struct { char name[NAME_MAX + 1]; int hard_failure_count; char user[64], hashed_password[33]; char session_id[256], np_url[256], subm_url[256]; char handshake_url[256]; Queue submissions; pthread_t thread; pthread_mutex_t submissions_mutex; pthread_cond_t cond; bool need_handshake; bool submission_was_success; bool shutdown_thread; } Server; static void handle_queue_line (const char *line, void *user_data); static xmmsc_connection_t *conn; static int32_t current_id = INVALID_MEDIA_ID; static uint32_t seconds_played; static time_t started_playing, last_unpause; static List *servers; static char proxy_host[128]; static int proxy_port; static bool keep_running = true; static struct sigaction sig; static Server * server_new (const char *name) { Server *server; server = malloc (sizeof (Server)); strncpy (server->name, name, sizeof (server->name)); server->name[sizeof (server->name) - 1] = 0; pthread_mutex_init (&server->submissions_mutex, NULL); pthread_cond_init (&server->cond, NULL); server->need_handshake = true; server->shutdown_thread = false; queue_init (&server->submissions); return server; } static void server_free (Server *server) { pthread_mutex_destroy (&server->submissions_mutex); pthread_cond_destroy (&server->cond); free (server); } static bool server_check_config (Server *server) { bool config_ok = true; if (!*server->user) { fprintf (stderr, "[%s] username not specified\n", server->name); config_ok = false; } if (!*server->hashed_password) { fprintf (stderr, "[%s] password not specified\n", server->name); config_ok = false; } if (!*server->handshake_url) { fprintf (stderr, "[%s] handshake URL not specified\n", server->name); config_ok = false; } return config_ok; } static void signal_handler (int sig) { if (sig == SIGINT) keep_running = false; } static size_t handle_handshake_reponse (void *rawptr, size_t size, size_t nmemb, void *data) { Server *server = data; size_t total = size * nmemb, left = total; char *ptr = rawptr, *newline; int len; newline = memchr (ptr, '\n', left); if (!newline) { fprintf (stderr, "no newline (1)\n"); return total; } *newline = 0; if (strcmp (ptr, "OK")) { fprintf (stderr, "handshake failed\n"); return total; } len = newline - ptr + 1; left -= len; ptr += len; newline = memchr (ptr, '\n', left); if (!newline) { fprintf (stderr, "no newline (1)\n"); return total; } *newline = 0; len = newline - ptr + 1; if (len > 255) { fprintf (stderr, "session ID is too long (%i characters)\n", len); return total; } strcpy (server->session_id, ptr); left -= len; ptr += len; /* now playing URL */ newline = memchr (ptr, '\n', left); if (!newline) { fprintf (stderr, "no newline (2)\n"); return total; } *newline = 0; len = newline - ptr + 1; if (len > 255) { fprintf (stderr, "now_playing URL is too long " "(%i characters)\n", len); return total; } strcpy (server->np_url, ptr); left -= len; ptr += len; /* submission URL */ newline = memchr (ptr, '\n', left); if (!newline) { printf("no newline (3)\n"); return total; } *newline = 0; len = newline - ptr + 1; if (len > 255) { fprintf (stderr, "submission URL is too long " "(%i characters)\n", len); return total; } strcpy (server->subm_url, ptr); fprintf (stderr, "got:\n'%s' '%s' '%s'\n", server->session_id, server->np_url, server->subm_url); server->need_handshake = false; server->hard_failure_count = 0; return total; } static size_t handle_submission_reponse (void *ptr, size_t size, size_t nmemb, void *data) { Server *server = data; size_t total = size * nmemb; char *newline; newline = memchr (ptr, '\n', total); if (!newline) { fprintf (stderr, "[%s] no newline\n", server->name); return total; } *newline = 0; fprintf (stderr, "[%s] response: '%s'\n", server->name, (char *) ptr); if (!strcmp (ptr, "BADSESSION")) { /* need to perform handshake again */ server->need_handshake = true; fprintf (stderr, "[%s] BADSESSION\n", server->name); } else if (!strcmp (ptr, "OK")) { /* submission succeeded */ fprintf (stderr, "[%s] success \\o/\n", server->name); server->submission_was_success = true; } else if (total >= strlen ("FAILED ")) { fprintf (stderr, "[%s] couldn't submit: '%s'\n", server->name, (char *) ptr); } return total; } static void set_proxy (Server *server, CURL *curl) { if (!*proxy_host) return; curl_easy_setopt (curl, CURLOPT_PROXY, proxy_host); if (proxy_port) curl_easy_setopt (curl, CURLOPT_PROXYPORT, (long) proxy_port); } /* perform the handshake and return true on success, false otherwise. */ static bool do_handshake (Server *server) { CURL *curl; char hashed[64], post_data[512]; time_t timestamp; int pos; timestamp = time (NULL); /* copy over the hashed password */ memcpy (hashed, server->hashed_password, 32); /* append the timestamp */ snprintf (&hashed[32], sizeof (hashed) - 32, "%lu", timestamp); pos = snprintf (post_data, sizeof (post_data), "%s/" "?hs=true&p=" PROTOCOL "&c=" CLIENT_ID "&v=" VERSION "&u=%s&t=%lu&a=", server->handshake_url, server->user, timestamp); /* hash the hashed password and timestamp and append the hex string * to 'post_data'. * FIXME: check for buffer overflow. */ md5 (hashed, &post_data[pos]); curl = curl_easy_init (); set_proxy (server, curl); curl_easy_setopt (curl, CURLOPT_URL, post_data); curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, handle_handshake_reponse); curl_easy_setopt (curl, CURLOPT_WRITEDATA, server); curl_easy_perform (curl); curl_easy_cleanup (curl); return !server->need_handshake; } static bool handshake_if_needed (Server *server) { int delay = 30; while (server->need_handshake) { struct timespec ts; bool shutdown; if (do_handshake (server)) return true; delay *= 2; /* there's a maximum delay of two hours */ if (delay > 7200) delay = 7200; #ifdef CLOCK_REALTIME clock_gettime (CLOCK_REALTIME, &ts); #else struct timeval tv; gettimeofday (&tv, NULL); ts.tv_sec = tv.tv_sec; ts.tv_nsec = tv.tv_usec * 1000; #endif ts.tv_sec += delay; int e; do { pthread_mutex_lock (&server->submissions_mutex); e = pthread_cond_timedwait (&server->cond, &server->submissions_mutex, &ts); shutdown = server->shutdown_thread; pthread_mutex_unlock (&server->submissions_mutex); if (shutdown) return false; } while (e != ETIMEDOUT); } return true; } static void * curl_thread (void *arg) { Server *server = arg; CURL *curl; fprintf (stderr, "starting thread for %s\n", server->name); pthread_mutex_lock (&server->submissions_mutex); while (!server->shutdown_thread) { /* check whether there's data waiting to be * submitted. */ if (!queue_peek (&server->submissions)) { pthread_cond_wait (&server->cond, &server->submissions_mutex); continue; } pthread_mutex_unlock (&server->submissions_mutex); curl = curl_easy_init (); set_proxy (server, curl); curl_easy_setopt (curl, CURLOPT_POST, 1); curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, handle_submission_reponse); curl_easy_setopt (curl, CURLOPT_WRITEDATA, server); while (true) { Submission *submission; bool shutdown; int orig_length; server->submission_was_success = false; pthread_mutex_lock (&server->submissions_mutex); submission = queue_peek (&server->submissions); shutdown = server->shutdown_thread; pthread_mutex_unlock (&server->submissions_mutex); if (!submission || shutdown) break; if (!handshake_if_needed (server)) break; /* append the current session id, but remember * the current length of the string first. */ orig_length = submission->sb->length; strbuf_append (submission->sb, "&s="); strbuf_append (submission->sb, server->session_id); fprintf (stderr, "[%s] submitting '%s'\n", server->name, submission->sb->buf); if (submission->type == SUBMISSION_TYPE_NOW_PLAYING) curl_easy_setopt (curl, CURLOPT_URL, server->np_url); else curl_easy_setopt (curl, CURLOPT_URL, server->subm_url); curl_easy_setopt (curl, CURLOPT_POSTFIELDS, submission->sb->buf); curl_easy_perform (curl); if (!server->submission_was_success && !server->need_handshake && ++server->hard_failure_count == 3) server->need_handshake = true; if (server->submission_was_success || submission->type == SUBMISSION_TYPE_NOW_PLAYING) { /* if the submission was successful, or if it was a * now-playing submission, remove the item from the * queue. */ pthread_mutex_lock (&server->submissions_mutex); queue_pop (&server->submissions); pthread_mutex_unlock (&server->submissions_mutex); submission_free (submission); } else if (!server->submission_was_success) { /* if the (profile) submission wasn't successful, * remove the session id from the string again. */ strbuf_truncate (submission->sb, orig_length); } } curl_easy_cleanup (curl); pthread_mutex_lock (&server->submissions_mutex); } return NULL; } static void enqueue (Server *server, Submission *submission) { pthread_mutex_lock (&server->submissions_mutex); queue_push (&server->submissions, submission); pthread_cond_signal (&server->cond); pthread_mutex_unlock (&server->submissions_mutex); } static void submit_now_playing (xmmsv_t *val) { Submission *submission; xmmsv_t *dict; dict = xmmsv_propdict_to_dict (val, NULL); submission = now_playing_submission_new (dict); xmmsv_unref (dict); if (submission) { enqueue (servers->data, submission); for (List *l = servers->next; l; l = l->next) { Server *server = l->data; enqueue (server, submission_clone (submission)); } } } static bool submit_to_profile (xmmsv_t *val) { Submission *submission; xmmsv_t *dict; dict = xmmsv_propdict_to_dict (val, NULL); submission = profile_submission_new (dict, seconds_played, started_playing); xmmsv_unref (dict); if (submission) { enqueue (servers->data, submission); for (List *l = servers->next; l; l = l->next) { Server *server = l->data; enqueue (server, submission_clone (submission)); } } return !!submission; } static int on_medialib_get_info2 (xmmsv_t *val, void *udata) { bool reset_current_id = XPOINTER_TO_INT (udata); seconds_played += time (NULL) - last_unpause; fprintf (stderr, "submitting: seconds_played %i\n", seconds_played); /* if we could submit this song we might have to reset * 'current_id', so we don't submit it again. */ if (submit_to_profile (val) && reset_current_id) current_id = INVALID_MEDIA_ID; return 0; } static int on_medialib_get_info (xmmsv_t *val, void *udata) { submit_now_playing (val); fprintf (stderr, "resetting seconds_played\n"); last_unpause = started_playing = time (NULL); seconds_played = 0; return 0; } static void maybe_submit_to_profile (bool reset_current_id) { xmmsc_result_t *mediainfo_result; /* check whether we're interesting in this track at all */ if (current_id == INVALID_MEDIA_ID) return; mediainfo_result = xmmsc_medialib_get_info (conn, current_id); xmmsc_result_notifier_set (mediainfo_result, on_medialib_get_info2, XINT_TO_POINTER (reset_current_id)); xmmsc_result_unref (mediainfo_result); } static int on_playback_current_id (xmmsv_t *val, void *udata) { xmmsc_result_t *mediainfo_result; int32_t id = INVALID_MEDIA_ID; /* if the submission works, we must NOT reset current_id * because we set it a couple of lines below (to the new * song's ID). we must not overwrite that value. */ maybe_submit_to_profile (false); /* get the new song's medialib id. */ xmmsv_get_int (val, &id); fprintf (stderr, "now playing %u\n", id); current_id = id; /* request information about this song. */ mediainfo_result = xmmsc_medialib_get_info (conn, id); xmmsc_result_notifier_set (mediainfo_result, on_medialib_get_info, NULL); xmmsc_result_unref (mediainfo_result); return 1; } static int on_playback_status (xmmsv_t *val, void *udata) { int s, status; s = xmmsv_get_int (val, &status); if (!s) return 1; switch (status) { case XMMS_PLAYBACK_STATUS_STOP: case XMMS_PLAYBACK_STATUS_PAUSE: /* if we could submit this song we need to reset * 'current_id', so we don't submit it again. */ maybe_submit_to_profile (true); break; case XMMS_PLAYBACK_STATUS_PLAY: last_unpause = time (NULL); break; } return 1; } static int on_quit (xmmsv_t *val, void *udata) { keep_running = false; return 0; } static void on_disconnect (void *udata) { keep_running = false; } static void strchomp (char *s, size_t *length) { size_t l = *length; while (l > 0 && isspace (s[l - 1])) s[--l] = 0; *length = l; } static void for_each_line (FILE *fp, void (*callback) (const char *line, void *user_data), void *user_data) { char buf[4096]; while (fgets (buf, sizeof (buf), fp)) { size_t length = strlen (buf); if (length < 2) continue; strchomp (buf, &length); callback (buf, user_data); } } static void handle_config_line (const char *line, void *user_data) { if (!strncmp (line, "proxy: ", 7)) { strncpy (proxy_host, &line[7], sizeof (proxy_host)); proxy_host[sizeof (proxy_host) - 1] = 0; } else if (!strncmp (line, "proxy_port: ", 12)) proxy_port = atoi (&line[12]); } static void handle_server_config_line (const char *line, void *user_data) { Server *server = user_data; if (!strncmp (line, "handshake_url: ", 15)) { strncpy (server->handshake_url, &line[15], sizeof (server->handshake_url)); server->handshake_url[sizeof (server->handshake_url) - 1] = 0; } else if (!strncmp (line, "user: ", 6)) { strncpy (server->user, &line[6], sizeof (server->user)); server->user[sizeof (server->user) - 1] = 0; } else if (!strncmp (line, "password: ", 10)) { /* we only ever need the hashed password :) */ md5 (&line[10], server->hashed_password); } } static bool load_config () { FILE *fp; DIR *dp; struct dirent *dirent; const char *dir; char buf[XMMS_PATH_MAX], config_dir[PATH_MAX], filename[PATH_MAX]; dir = xmmsc_userconfdir_get (buf, sizeof (buf)); if (!dir) { fprintf (stderr, "cannot get userconfdir\n"); return false; } snprintf (config_dir, sizeof (config_dir), "%s/clients/xmms2-scrobbler", buf); snprintf (filename, sizeof (filename), "%s/config", config_dir); fp = fopen (filename, "r"); if (fp) { for_each_line (fp, handle_config_line, NULL); fclose (fp); } dp = opendir (config_dir); if (!dp) { fprintf (stderr, "cannot open config directory '%s'\n", config_dir); return false; } while ((dirent = readdir (dp))) { Server *server; struct stat st; int e; if (dirent->d_name[0] == '.') continue; snprintf (filename, sizeof (filename), "%s/%s", config_dir, dirent->d_name); e = stat (filename, &st); if (e || !S_ISDIR (st.st_mode)) continue; snprintf (filename, sizeof (filename), "%s/%s/config", config_dir, dirent->d_name); server = server_new (dirent->d_name); if (!server) continue; fp = fopen (filename, "r"); if (!fp) { fprintf (stderr, "cannot open config file: '%s'\n", filename); server_free (server); continue; } for_each_line (fp, handle_server_config_line, server); fclose (fp); if (!server_check_config (server)) { fprintf (stderr, "ignoring %s\n", server->name); server_free (server); continue; } fprintf (stderr, "registering %s\n", server->name); servers = list_prepend (servers, server); snprintf (filename, sizeof (filename), "%s/%s/queue", config_dir, dirent->d_name); fp = fopen (filename, "r"); if (!fp) fprintf (stderr, "warning: cannot open queue '%s' for reading\n", filename); else { for_each_line (fp, handle_queue_line, server); fclose (fp); } } closedir (dp); return true; } static void handle_queue_line (const char *line, void *user_data) { Server *server = user_data; StrBuf *sb; sb = strbuf_new (); strbuf_append (sb, line); queue_push (&server->submissions, submission_new (sb, SUBMISSION_TYPE_PROFILE)); } static void save_profile_submissions_queue (Server *server) { FILE *fp; const char *dir; char buf[XMMS_PATH_MAX]; char filename[PATH_MAX]; dir = xmmsc_userconfdir_get (buf, sizeof (buf)); if (!dir) { fprintf (stderr, "cannot get userconfdir\n"); return; } snprintf (filename, sizeof (filename), "%s/clients/xmms2-scrobbler/%s/queue", buf, server->name); fp = fopen (filename, "w"); if (!fp) { fprintf (stderr, "cannot open queue '%s' for writing\n", filename); return; } while (true) { Submission *s; s = queue_pop (&server->submissions); if (!s) break; if (s->type == SUBMISSION_TYPE_PROFILE) fprintf (fp, "%s\n", s->sb->buf); submission_free (s); } fclose (fp); } static void main_loop () { struct pollfd fds; fds.fd = xmmsc_io_fd_get (conn); while (keep_running) { fds.events = POLLIN | POLLHUP | POLLERR; fds.revents = 0; if (xmmsc_io_want_out (conn)) fds.events |= POLLOUT; int e = poll (&fds, 1, -1); if (e == -1) xmmsc_io_disconnect (conn); else if ((fds.revents & POLLERR) == POLLERR) xmmsc_io_disconnect (conn); else if ((fds.revents & POLLHUP) == POLLHUP) xmmsc_io_disconnect (conn); else { if ((fds.revents & POLLOUT) == POLLOUT) xmmsc_io_out_handle (conn); if ((fds.revents & POLLIN) == POLLIN) xmmsc_io_in_handle (conn); } } } static void start_logging () { int fd; const char *dir; char buf[XMMS_PATH_MAX]; dir = xmmsc_userconfdir_get (buf, sizeof (buf)); if (!dir) { fprintf (stderr, "cannot get userconfdir\n"); return; } chdir (dir); fd = creat ("clients/xmms2-scrobbler/logfile.log", 0640); if (fd > -1) { /* redirect stderr to the log file. */ dup2 (fd, 2); } } int main (int argc, char **argv) { xmmsc_result_t *current_id_broadcast; xmmsc_result_t *playback_status_broadcast; xmmsc_result_t *quit_broadcast; int s; sig.sa_handler = &signal_handler; sigaction (SIGINT, &sig, 0); start_logging (); if (!load_config ()) return EXIT_FAILURE; if (!servers) { fprintf (stderr, "*** No subdirectories found in " ".../clients/xmms2-scrobbler\n" "*** See README for how to configure XMMS2-Scrobbler.\n"); return EXIT_FAILURE; } conn = xmmsc_init ("XMMS2-Scrobbler"); if (!conn) { fprintf (stderr, "OOM\n"); return EXIT_FAILURE; } s = xmmsc_connect (conn, NULL); if (!s) { fprintf (stderr, "cannot connect to xmms2d\n"); xmmsc_unref (conn); return EXIT_FAILURE; } curl_global_init (CURL_GLOBAL_NOTHING); for (List *l = servers; l; l = l->next) { Server *server = l->data; pthread_create (&server->thread, NULL, curl_thread, server); } /* register the various broadcasts that we're interested in */ current_id_broadcast = xmmsc_broadcast_playback_current_id (conn); xmmsc_result_notifier_set (current_id_broadcast, on_playback_current_id, NULL); xmmsc_result_unref (current_id_broadcast); playback_status_broadcast = xmmsc_broadcast_playback_status (conn); xmmsc_result_notifier_set (playback_status_broadcast, on_playback_status, NULL); xmmsc_result_unref (playback_status_broadcast); quit_broadcast = xmmsc_broadcast_quit (conn); xmmsc_result_notifier_set (quit_broadcast, on_quit, NULL); xmmsc_result_unref (quit_broadcast); xmmsc_disconnect_callback_set (conn, on_disconnect, NULL); main_loop (); /* tell the curl threads to stop working */ for (List *l = servers; l; l = l->next) { Server *server = l->data; pthread_mutex_lock (&server->submissions_mutex); server->shutdown_thread = true; pthread_cond_signal (&server->cond); pthread_mutex_unlock (&server->submissions_mutex); } /* and wait until they are gone */ for (List *l = servers; l; l = l->next) { Server *server = l->data; pthread_join (server->thread, NULL); } curl_global_cleanup (); #if 0 /* disconnect broadcasts and signals */ xmmsc_result_disconnect (current_id_broadcast); xmmsc_result_disconnect (playback_status_broadcast); xmmsc_result_disconnect (quit_broadcast); #endif xmmsc_unref (conn); while (servers) { Server *server = servers->data; save_profile_submissions_queue (server); server_free (servers->data); servers = list_remove_head (servers); } return EXIT_SUCCESS; } xmms2-scrobbler-0.4.0/src/queue.h0000644000175000017500000000266511316704001016154 0ustar tilmantilman/* * Copyright (c) 2008 Tilman Sauerbeck (tilman at xmms org) * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef _QUEUE_H #define _QUEUE_H typedef struct __QueueItem { struct __QueueItem *next; void *data; } QueueItem; typedef struct { QueueItem *head; QueueItem *tail; } Queue; void queue_init (Queue *q); void queue_push (Queue *q, void *data); void *queue_pop (Queue *q); void *queue_peek (Queue *q); #endif xmms2-scrobbler-0.4.0/src/submission.c0000644000175000017500000001164111316704001017210 0ustar tilmantilman/* * Copyright (c) 2008 Tilman Sauerbeck (tilman at xmms org) * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include "submission.h" Submission * submission_new (StrBuf *sb, SubmissionType type) { Submission *submission; submission = malloc (sizeof (Submission)); submission->sb = sb; submission->type = type; return submission; } Submission * now_playing_submission_new (xmmsv_t *dict) { StrBuf *sb; const char *artist, *title, *val_s; int32_t val_i; int s; /* artist is required */ s = xmmsv_dict_entry_get_string (dict, "artist", &artist); if (!s) return NULL; /* title is required */ s = xmmsv_dict_entry_get_string (dict, "title", &title); if (!s) return NULL; sb = strbuf_new (); /* note that the session id isn't written here yet, we'll do * that just before we submit the data. */ /* artist */ strbuf_append (sb, "a="); strbuf_append_encoded (sb, (const uint8_t *) artist); /* title */ strbuf_append (sb, "&t="); strbuf_append_encoded (sb, (const uint8_t *) title); /* album */ strbuf_append (sb, "&b="); s = xmmsv_dict_entry_get_string (dict, "album", &val_s); if (s) strbuf_append_encoded (sb, (const uint8_t *) val_s); /* duration in seconds */ strbuf_append (sb, "&l="); s = xmmsv_dict_entry_get_int (dict, "duration", &val_i); if (s) { char buf32[32]; sprintf (buf32, "%i", val_i / 1000); strbuf_append (sb, buf32); } /* position of the track on the album. * xmms2 doesn't enforce any format for this field, so we're not * submitting it at all. */ strbuf_append (sb, "&n="); /* musicbrainz track id */ strbuf_append (sb, "&m="); s = xmmsv_dict_entry_get_string (dict, "track_id", &val_s); if (s) strbuf_append (sb, val_s); return submission_new (sb, SUBMISSION_TYPE_NOW_PLAYING); } Submission * profile_submission_new (xmmsv_t *dict, uint32_t seconds_played, time_t started_playing) { StrBuf *sb; const char *artist, *title, *val_s; char buf32[32]; int32_t val_i; int s; /* duration in seconds is required */ s = xmmsv_dict_entry_get_int (dict, "duration", &val_i); if (!s) return NULL; if (seconds_played < 240 && seconds_played < (val_i / 2000)) { fprintf (stderr, "seconds_played FAIL: %u\n", seconds_played); return NULL; } /* artist is required */ s = xmmsv_dict_entry_get_string (dict, "artist", &artist); if (!s) return NULL; /* title is required */ s = xmmsv_dict_entry_get_string (dict, "title", &title); if (!s) return NULL; sb = strbuf_new (); /* note that the session id isn't written here yet, we'll do * that just before we submit the data. */ /* artist */ strbuf_append (sb, "a[0]="); strbuf_append_encoded (sb, (const uint8_t *) artist); /* title */ strbuf_append (sb, "&t[0]="); strbuf_append_encoded (sb, (const uint8_t *) title); /* timestamp */ sprintf (buf32, "%lu", started_playing); strbuf_append (sb, "&i[0]="); strbuf_append (sb, buf32); /* source: chosen by user */ strbuf_append (sb, "&o[0]=P"); /* rating: unknown */ strbuf_append (sb, "&r[0]="); /* duration in seconds */ sprintf (buf32, "%i", val_i / 1000); strbuf_append (sb, "&l[0]="); strbuf_append (sb, buf32); /* album */ strbuf_append (sb, "&b[0]="); s = xmmsv_dict_entry_get_string (dict, "album", &val_s); if (s) { strbuf_append_encoded (sb, (const uint8_t *) val_s); } /* position of the track on the album. * xmms2 doesn't enforce any format for this field, so we're not * submitting it at all. */ strbuf_append (sb, "&n[0]="); /* musicbrainz track id */ strbuf_append (sb, "&m[0]="); s = xmmsv_dict_entry_get_string (dict, "track_id", &val_s); if (s) strbuf_append (sb, val_s); return submission_new (sb, SUBMISSION_TYPE_PROFILE); } Submission * submission_clone (Submission *s) { StrBuf *sb; sb = strbuf_new (); strbuf_append (sb, s->sb->buf); return submission_new (sb, s->type); } void submission_free (Submission *s) { strbuf_free (s->sb); free (s); } xmms2-scrobbler-0.4.0/src/strbuf.h0000644000175000017500000000273511316704001016333 0ustar tilmantilman/* * Copyright (c) 2008 Tilman Sauerbeck (tilman at xmms org) * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef _STRBUF_H #define _STRBUF_H #include typedef struct { char *buf; int allocated; int length; } StrBuf; StrBuf *strbuf_new (void); void strbuf_free (StrBuf *sb); void strbuf_append (StrBuf *sb, const char *other); void strbuf_append_encoded (StrBuf *sb, const uint8_t *other); void strbuf_truncate (StrBuf *sb, int length); #endif xmms2-scrobbler-0.4.0/src/md5.h0000644000175000017500000000232011316704001015501 0ustar tilmantilman/* * Copyright (c) 2008 Tilman Sauerbeck (tilman at xmms org) * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef _MD5_H #define _MD5_H void md5 (const char *input, char output[33]); #endif xmms2-scrobbler-0.4.0/src/queue.c0000644000175000017500000000352311316704001016141 0ustar tilmantilman/* * Copyright (c) 2008 Tilman Sauerbeck (tilman at xmms org) * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include "queue.h" void queue_init (Queue *q) { q->head = q->tail = NULL; } void queue_push (Queue *q, void *data) { QueueItem *item; item = malloc (sizeof (QueueItem)); item->next = NULL; item->data = data; if (q->tail) q->tail->next = item; q->tail = item; if (!q->head) q->head = item; } void * queue_pop (Queue *q) { QueueItem *item; void *data; if (!q->head) return NULL; item = q->head; data = item->data; q->head = q->head->next; /* if we just removed the last item in the queue, * we also need to reset the tail pointer. */ if (!q->head) q->tail = NULL; free (item); return data; } void * queue_peek (Queue *q) { return q->head ? q->head->data : NULL; } xmms2-scrobbler-0.4.0/src/submission.h0000644000175000017500000000333011316704001017211 0ustar tilmantilman/* * Copyright (c) 2008 Tilman Sauerbeck (tilman at xmms org) * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef _SUBMISSION_H #define _SUBMISSION_H #include #include #include #include "strbuf.h" typedef enum { SUBMISSION_TYPE_NOW_PLAYING, SUBMISSION_TYPE_PROFILE } SubmissionType; typedef struct { StrBuf *sb; SubmissionType type; } Submission; Submission *submission_new (StrBuf *sb, SubmissionType type); Submission *now_playing_submission_new (xmmsv_t *dict); Submission *profile_submission_new (xmmsv_t *dict, uint32_t seconds_played, time_t started_playing); Submission *submission_clone (Submission *s); void submission_free (Submission *s); #endif xmms2-scrobbler-0.4.0/src/list.c0000644000175000017500000000264211316704001015771 0ustar tilmantilman/* * Copyright (c) 2008 Tilman Sauerbeck (tilman at xmms org) * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include "list.h" List * list_prepend (List *list, void *data) { List *link; link = malloc (sizeof (List)); link->next = list; link->data = data; return link; } List * list_remove_head (List *list) { List *new_head = list->next; free (list); return new_head; } xmms2-scrobbler-0.4.0/src/md5.c0000644000175000017500000001770311316704001015507 0ustar tilmantilman/* * This code implements the MD5 message-digest algorithm. * The algorithm is due to Ron Rivest. This code was * written by Colin Plumb in 1993, no copyright is claimed. * This code is in the public domain; do with it what you wish. * * Equivalent code is available from RSA Data Security, Inc. * This code has been tested against that, and is equivalent, * except that you don't need to include two pages of legalese * with every copy. * * To compute the message digest of a chunk of bytes, declare an * MD5Context structure, pass it to md5_init, call md5_update as * needed on buffers full of bytes, and then call md5_final, which * will fill a supplied 16-byte array with the digest. */ #include #include #include typedef struct { uint32_t buf[4]; uint32_t bits[2]; uint8_t in[64]; } MD5Context; #ifndef WORDS_BIGENDIAN # define byte_reverse(buf, len) /* Nothing */ #else /* WORDS_BIGENDIAN */ /* * Note: this code is harmless on little-endian machines. */ static void byte_reverse (uint8_t *buf, uint32_t longs) { uint32_t t; do { t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 | ((unsigned) buf[1] << 8 | buf[0]); *(uint32_t *) buf = t; buf += 4; } while (--longs); } #endif /*! WORDS_BIGENDIAN */ /* * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious * initialization constants. */ static void md5_init (MD5Context *ctx) { ctx->buf[0] = 0x67452301; ctx->buf[1] = 0xefcdab89; ctx->buf[2] = 0x98badcfe; ctx->buf[3] = 0x10325476; ctx->bits[0] = 0; ctx->bits[1] = 0; } /* The four core functions - F1 is optimized somewhat */ #define F1(x, y, z) (z ^ (x & (y ^ z))) #define F2(x, y, z) F1(z, x, y) #define F3(x, y, z) (x ^ y ^ z) #define F4(x, y, z) (y ^ (x | ~z)) /* This is the central step in the MD5 algorithm. */ #define MD5STEP(f, w, x, y, z, data, s) \ (w += f(x, y, z) + data, w = w << s | w >> (32 - s), w += x) /* * The core of the MD5 algorithm, this alters an existing MD5 hash to * reflect the addition of 16 longwords of new data. MD5Update blocks * the data and converts bytes into longwords for this routine. */ static void md5_transform (uint32_t buf[4], const uint32_t in[16]) { uint32_t a, b, c, d; a = buf[0]; b = buf[1]; c = buf[2]; d = buf[3]; MD5STEP (F1, a, b, c, d, in[0] + 0xd76aa478, 7); MD5STEP (F1, d, a, b, c, in[1] + 0xe8c7b756, 12); MD5STEP (F1, c, d, a, b, in[2] + 0x242070db, 17); MD5STEP (F1, b, c, d, a, in[3] + 0xc1bdceee, 22); MD5STEP (F1, a, b, c, d, in[4] + 0xf57c0faf, 7); MD5STEP (F1, d, a, b, c, in[5] + 0x4787c62a, 12); MD5STEP (F1, c, d, a, b, in[6] + 0xa8304613, 17); MD5STEP (F1, b, c, d, a, in[7] + 0xfd469501, 22); MD5STEP (F1, a, b, c, d, in[8] + 0x698098d8, 7); MD5STEP (F1, d, a, b, c, in[9] + 0x8b44f7af, 12); MD5STEP (F1, c, d, a, b, in[10] + 0xffff5bb1, 17); MD5STEP (F1, b, c, d, a, in[11] + 0x895cd7be, 22); MD5STEP (F1, a, b, c, d, in[12] + 0x6b901122, 7); MD5STEP (F1, d, a, b, c, in[13] + 0xfd987193, 12); MD5STEP (F1, c, d, a, b, in[14] + 0xa679438e, 17); MD5STEP (F1, b, c, d, a, in[15] + 0x49b40821, 22); MD5STEP (F2, a, b, c, d, in[1] + 0xf61e2562, 5); MD5STEP (F2, d, a, b, c, in[6] + 0xc040b340, 9); MD5STEP (F2, c, d, a, b, in[11] + 0x265e5a51, 14); MD5STEP (F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); MD5STEP (F2, a, b, c, d, in[5] + 0xd62f105d, 5); MD5STEP (F2, d, a, b, c, in[10] + 0x02441453, 9); MD5STEP (F2, c, d, a, b, in[15] + 0xd8a1e681, 14); MD5STEP (F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); MD5STEP (F2, a, b, c, d, in[9] + 0x21e1cde6, 5); MD5STEP (F2, d, a, b, c, in[14] + 0xc33707d6, 9); MD5STEP (F2, c, d, a, b, in[3] + 0xf4d50d87, 14); MD5STEP (F2, b, c, d, a, in[8] + 0x455a14ed, 20); MD5STEP (F2, a, b, c, d, in[13] + 0xa9e3e905, 5); MD5STEP (F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); MD5STEP (F2, c, d, a, b, in[7] + 0x676f02d9, 14); MD5STEP (F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); MD5STEP (F3, a, b, c, d, in[5] + 0xfffa3942, 4); MD5STEP (F3, d, a, b, c, in[8] + 0x8771f681, 11); MD5STEP (F3, c, d, a, b, in[11] + 0x6d9d6122, 16); MD5STEP (F3, b, c, d, a, in[14] + 0xfde5380c, 23); MD5STEP (F3, a, b, c, d, in[1] + 0xa4beea44, 4); MD5STEP (F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); MD5STEP (F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); MD5STEP (F3, b, c, d, a, in[10] + 0xbebfbc70, 23); MD5STEP (F3, a, b, c, d, in[13] + 0x289b7ec6, 4); MD5STEP (F3, d, a, b, c, in[0] + 0xeaa127fa, 11); MD5STEP (F3, c, d, a, b, in[3] + 0xd4ef3085, 16); MD5STEP (F3, b, c, d, a, in[6] + 0x04881d05, 23); MD5STEP (F3, a, b, c, d, in[9] + 0xd9d4d039, 4); MD5STEP (F3, d, a, b, c, in[12] + 0xe6db99e5, 11); MD5STEP (F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); MD5STEP (F3, b, c, d, a, in[2] + 0xc4ac5665, 23); MD5STEP (F4, a, b, c, d, in[0] + 0xf4292244, 6); MD5STEP (F4, d, a, b, c, in[7] + 0x432aff97, 10); MD5STEP (F4, c, d, a, b, in[14] + 0xab9423a7, 15); MD5STEP (F4, b, c, d, a, in[5] + 0xfc93a039, 21); MD5STEP (F4, a, b, c, d, in[12] + 0x655b59c3, 6); MD5STEP (F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); MD5STEP (F4, c, d, a, b, in[10] + 0xffeff47d, 15); MD5STEP (F4, b, c, d, a, in[1] + 0x85845dd1, 21); MD5STEP (F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); MD5STEP (F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); MD5STEP (F4, c, d, a, b, in[6] + 0xa3014314, 15); MD5STEP (F4, b, c, d, a, in[13] + 0x4e0811a1, 21); MD5STEP (F4, a, b, c, d, in[4] + 0xf7537e82, 6); MD5STEP (F4, d, a, b, c, in[11] + 0xbd3af235, 10); MD5STEP (F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); MD5STEP (F4, b, c, d, a, in[9] + 0xeb86d391, 21); buf[0] += a; buf[1] += b; buf[2] += c; buf[3] += d; } /* * Update context to reflect the concatenation of another buffer full * of bytes. */ static void md5_update (MD5Context *ctx, uint8_t const *buf, uint32_t len) { uint32_t t; /* Update bitcount */ t = ctx->bits[0]; if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) ctx->bits[1]++; /* Carry from low to high */ ctx->bits[1] += len >> 29; t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ /* Handle any leading odd-sized chunks */ if (t) { uint8_t *p = (uint8_t *) ctx->in + t; t = 64 - t; if (len < t) { memcpy (p, buf, len); return; } memcpy (p, buf, t); byte_reverse (ctx->in, 16); md5_transform (ctx->buf, (uint32_t *) ctx->in); buf += t; len -= t; } /* Process data in 64-byte chunks */ while (len >= 64) { memcpy (ctx->in, buf, 64); byte_reverse (ctx->in, 16); md5_transform (ctx->buf, (uint32_t *) ctx->in); buf += 64; len -= 64; } /* Handle any remaining bytes of data. */ memcpy (ctx->in, buf, len); } /* * Final wrapup - pad to 64-byte boundary with the bit pattern * 1 0* (64-bit count of bits processed, MSB-first) */ static void md5_final (uint8_t digest[16], MD5Context *ctx) { unsigned count; uint8_t *p; /* Compute number of bytes mod 64 */ count = (ctx->bits[0] >> 3) & 0x3F; /* Set the first char of padding to 0x80. This is safe since there * is always at least one byte free */ p = ctx->in + count; *p++ = 0x80; /* Bytes of padding needed to make 64 bytes */ count = 64 - 1 - count; /* Pad out to 56 mod 64 */ if (count < 8) { /* Two lots of padding: Pad the first block to 64 bytes */ memset (p, 0, count); byte_reverse (ctx->in, 16); md5_transform (ctx->buf, (uint32_t *) ctx->in); /* Now fill the next block with 56 bytes */ memset (ctx->in, 0, 56); } else /* Pad block to 56 bytes */ memset (p, 0, count - 8); byte_reverse (ctx->in, 14); /* Append length in bits and transform */ ((uint32_t *) ctx->in)[14] = ctx->bits[0]; ((uint32_t *) ctx->in)[15] = ctx->bits[1]; md5_transform (ctx->buf, (uint32_t *) ctx->in); byte_reverse ((uint8_t *) ctx->buf, 4); memcpy (digest, ctx->buf, 16); } void md5 (const char *input, char output[33]) { static const char hex[] = "0123456789abcdef"; MD5Context ctx; uint8_t hash[16]; int i; md5_init (&ctx); md5_update (&ctx, (uint8_t *) input, strlen (input)); md5_final (hash, &ctx); for (i = 0; i < 16; i++) { output[2 * i] = hex[hash[i] >> 4]; output[2 * i + 1] = hex[hash[i] & 0x0f]; } output[32] = 0; } xmms2-scrobbler-0.4.0/AUTHORS0000644000175000017500000000005411316704001015126 0ustar tilmantilmanTilman Sauerbeck (tilman at code-monkey de) xmms2-scrobbler-0.4.0/ChangeLog0000644000175000017500000004175211316704031015645 0ustar tilmantilmancommit 5d16742f897f645dc01c4dc7d2ec2592d30579d7 Author: Tilman Sauerbeck Date: Wed Dec 30 18:04:01 2009 +0100 Bumped version to 0.4.0. commit de43f705f365429f57e3f7eae00f8dd8b0e6d8c4 Author: Tilman Sauerbeck Date: Tue Dec 22 19:03:46 2009 +0100 Pre-multiscrobbling versions are 0.3.x, not 0.2.x. commit 200a566979836a129930a809b568da04aae0fd6e Author: Tilman Sauerbeck Date: Tue Dec 22 18:53:09 2009 +0100 Bumped version to 0.3.9. commit e719dfeeac1c1dd76ed780f727ff1b221898ca6b Author: Tilman Sauerbeck Date: Sun Dec 6 20:25:16 2009 +0100 Code cleanup. commit 46a4228cb9391540ab17fa1a03ff5b6c59368d46 Author: Tilman Sauerbeck Date: Sun Dec 6 20:24:11 2009 +0100 Not being able to open a queue file for reading isn't fatal. commit 17347cb30c8779ebff2e9e54434865a929c20de8 Author: Tilman Sauerbeck Date: Sun Nov 22 19:02:36 2009 +0100 Implemented support for "multi-scrobbling". XMMS2-Scrobbler can now talk to more than one AudioScrobbler server. For example it's possible to have it submit data to both last.fm and libre.fm. commit d2dab1ef64f367ce283e980fcb07dc511e1eb611 Author: Tilman Sauerbeck Date: Sun Nov 22 18:21:25 2009 +0100 Added submission_clone(). commit 10280d54c3fffc25167406f8939240cce0d9ff7c Author: Tilman Sauerbeck Date: Sun Nov 22 18:20:42 2009 +0100 Added a primitive linked list implementation. commit 9873a9174e280064cfe4c00bb273d6ff27b0405d Author: Tilman Sauerbeck Date: Fri Oct 30 20:36:12 2009 +0100 Bumped version to 0.3.1. commit 79a063031649db537c4c1f4bca85941c43a0f6ae Author: Tilman Sauerbeck Date: Fri Oct 30 20:31:54 2009 +0100 Properly remove trailing whitespace from config file entries. commit 93ca06927c9135ab970d4583be0f2921ef51c633 Author: Tilman Sauerbeck Date: Fri Sep 25 10:16:42 2009 +0200 Fixed the build on big endian machines (make BIG_ENDIAN=1). commit 8d245b96b4223bbc461cb8a287dde9fbca4f7bb7 Author: Tilman Sauerbeck Date: Sat May 9 21:28:25 2009 +0200 Don't error out in the 'clean' target if bin/ doesn't exit. commit 542f2499736fc78cdbcbf51788f892ed35b5d02e Author: Tilman Sauerbeck Date: Sat May 9 21:27:29 2009 +0200 Added the 'dist' make target and set the package version to 0.3.0. commit b57b54586f8977d54916437ded8e62749a020687 Author: Tilman Sauerbeck Date: Sat May 9 21:10:05 2009 +0200 Put the xmms2-scrobbler binary in bin/ like README says. commit 31d3818cd3df623d54d2a402f83bf33a8c7dfd8f Author: Tilman Sauerbeck Date: Sat May 9 20:59:38 2009 +0200 Redirect stderr to the log file. commit 6bde761af57d0a421322e3e6e3a599318d2a7edd Author: Tilman Sauerbeck Date: Sat May 9 20:37:56 2009 +0200 Debugging output now goes to stderr instead of stdout. commit 6ad9ffe992849ecd5a931c7ec098ac0d55912b59 Author: Tilman Sauerbeck Date: Mon Mar 30 20:31:24 2009 +0200 Replace xmmsv_get_uint() calls by xmmsv_get_int() calls. commit 6f594f25d05dd8d3d3f2e5dd0d9acfe3fdf405a6 Author: Tilman Sauerbeck Date: Fri May 1 22:14:33 2009 +0200 Evaluate $LDFLAGS in the Makefile. commit 7b75aa58940e36620ab8606103ec24a97c181d6a Author: Tilman Sauerbeck Date: Sun Mar 29 20:26:30 2009 +0200 Fixed the build for systems that don't have clock_gettime(). commit ea5a426cc6304a679ec841264656eb9e71bd1921 Author: Tilman Sauerbeck Date: Wed Feb 11 19:22:45 2009 +0100 Reflect xmmsv API update: s/xmmsv_get_dict_entry/xmmsv_dict_entry_get/. commit ab058922eb739f9ce8193e06f3195f06d858cf7c Author: Tilman Sauerbeck Date: Wed Dec 17 12:22:27 2008 +0100 Added proxy support. Based on a patch by Auke Schrijnen. commit e00f560cae4dacc59a2cb644bda8f68c65be1dc7 Author: Tilman Sauerbeck Date: Sun Dec 14 11:27:54 2008 +0100 Use the default source preferences. commit 29e8762ffe36cccd8429e74eec7e615f7ecd1379 Author: Tilman Sauerbeck Date: Sat Oct 25 18:25:10 2008 +0200 Ported xmms2-scrobbler to rv-split. commit b048b2d55daa5039ebeeb55e411778c13a89828a Author: Jonne Lehtinen Date: Sun Sep 28 21:05:54 2008 +0300 Handle SIGINT properly. commit 0d114affe48b1c573644bdc91515da9b88d70d5a Author: Tilman Sauerbeck Date: Sun Sep 28 20:05:08 2008 +0200 Get the buffer's address _after_ resize() is called. resize() might call realloc on the buffer which will of course change its address, so we may not get that address before the resize() call. commit 3522e44bf0fde30554f714cefa40dcd97ecd8a9e Author: Erik Massop Date: Tue Sep 23 17:08:42 2008 +0200 Stop when the daemon disconnects without a quit-signal. commit be1d71e79a7d4308a9f8469439b8ee348df5e357 Author: Tilman Sauerbeck Date: Fri Aug 8 19:05:13 2008 +0200 Fixed curl failure handling. commit 54abde14b052174d4dcc975bf8001ca8056edd0c Author: Tilman Sauerbeck Date: Sun Aug 3 21:20:11 2008 +0200 Implemented failure handling as described by the protocol spec. commit 40277cc737eab051878c06fdafc69215ca323174 Author: Tilman Sauerbeck Date: Sun Aug 3 18:31:47 2008 +0200 Initial commit of the C rewrite. commit 6505184a8fe88e6231225122c78a4590a44f4f0e Author: Tilman Sauerbeck Date: Sun Apr 20 11:10:32 2008 +0200 Bumped version to 0.2.2. commit b13deec1da9e608fdcee2bfeb4fcb05dac13ba0d Author: Tilman Sauerbeck Date: Sun Apr 20 11:10:15 2008 +0200 Fixed lock file handling to actually prohibit multiple instances. commit ce1a3869e89b0a770ba1e40584463020ae545b06 Author: Tilman Sauerbeck Date: Fri Apr 18 14:37:11 2008 +0200 Added .gitignore. commit 965748e031cb03d3393a6c10c0b77f13eef1329e Author: Tilman Sauerbeck Date: Fri Apr 18 14:36:55 2008 +0200 Include a git-generated ChangeLog in the packages. commit 259de98fe6c6b945141d44661cad9b6f38a59668 Author: Tilman Sauerbeck Date: Fri Apr 18 14:30:32 2008 +0200 Bumped version to 0.2.1. commit 019144be963e3fa4e7cbfb7363d0f7df59f7ac1a Author: Florian Ragwitz Date: Sun Apr 13 09:54:32 2008 +0200 Ignore stale lock files. commit 41a53b6039ee6b1848c8ed12ca1af0cb70288afd Author: Tilman Sauerbeck Date: Thu Oct 18 21:25:46 2007 +0200 Bumped version to 0.2.0. commit 743c4f3754d5bd47fb7085a15fb3b3a677391038 Author: Tilman Sauerbeck Date: Thu Oct 18 21:24:03 2007 +0200 Apply the filters to now-playing, too. commit db2d04b81dcb9cc34282982c0cc15dd83ec27172 Author: Tilman Sauerbeck Date: Fri Oct 12 13:56:35 2007 +0200 Removed Time#interval_passed as it's not used anymore. commit ff6303bb6e93205b19e295fdcafa960469a8b507 Author: Tilman Sauerbeck Date: Fri Oct 12 13:13:37 2007 +0200 Submit songs as "chosen by the user" rather than "unknown". At the moment, last.fm doesn't display songs whose source is "unknown". commit 9e5f866c6a6ff9754751094f5e08099acf5cee0b Author: Tilman Sauerbeck Date: Fri Oct 12 13:08:48 2007 +0200 Submit songs after they have been played as required by protocol v1.2. commit cd2720fc639e618dcf959608f711f1bad5b096b9 Author: Tilman Sauerbeck Date: Fri Oct 12 11:43:32 2007 +0200 Simplified the delay scheme for submissions. After a successful submission, we're waiting 1 second now, and after submission failures we're waiting 60 seconds. commit c2662eb94ff0021213bace8022a4e913beca3790 Author: Tilman Sauerbeck Date: Fri Oct 12 10:05:37 2007 +0200 Added support for now-playing notifications. commit 8a38eee7356d438b42d4e504bd9138bdbda1ad93 Author: Tilman Sauerbeck Date: Thu Oct 11 20:40:02 2007 +0200 Use &method(:foo) in a few places instead of blocks that only call foo. commit f694c6f902a61515a64dd20465f9906a5ff807c1 Author: Tilman Sauerbeck Date: Thu Oct 11 17:45:42 2007 +0200 Started moving to protocol 1.2. commit f8893ca0edec81f0ac6c7cead62dee9fbf62cc5f Author: Tilman Sauerbeck Date: Thu Mar 29 19:14:04 2007 +0200 Improved on the garbage regexp for the config file. Noticed by Dan Chokola. commit 7d8ef0673be6d64c175846e291a428ec0e5d7be5 Author: Tilman Sauerbeck Date: Sun Mar 18 19:36:07 2007 +0100 Removed pre-DrGonzo code and documentation. commit 60dd4bb4783cc2daa0d778d0f35b2b0e4104cfd7 Author: Tilman Sauerbeck Date: Sun Mar 18 19:32:54 2007 +0100 Note that the filters are installed as well. commit c17b2f1045694ea19d9abd8caa061a966115fe8d Author: Tilman Sauerbeck Date: Sun Mar 18 19:30:15 2007 +0100 Distribute and install filters. commit c2933d3ed01d0b83f1b0d4ada01b95d85bcb2553 Author: Tilman Sauerbeck Date: Sun Mar 18 19:19:47 2007 +0100 Bumped version to 0.1.3. commit d9ed217d8aafda8edcd0f335e15fd30a0947106e Author: Tilman Sauerbeck Date: Mon Mar 12 23:13:50 2007 +0100 Don't CGI.escape a hexstring. Spotted by Jason Salaz. commit 5b55b7018306a13d999d4e9bedd2b490b0c1eb96 Author: Tilman Sauerbeck Date: Mon Mar 12 23:07:24 2007 +0100 Use Digest.hexdigest rather than Digest.new. This makes xmms2-scrobbler work with Ruby 1.8.6. commit 76c4c19eb0b0f4175019938551bf2563946fca30 Author: Tilman Sauerbeck Date: Sun Nov 12 11:33:03 2006 +0100 Sleep 0.5 seconds before restarting the playback_playtime signal. This helps reduce CPU load. commit 74c029544fa347e9be5aa74617e5ae5c6d3c3773 Author: Tilman Sauerbeck Date: Sat Nov 11 22:31:34 2006 +0100 Shorten this code a bit. commit c17b0b4470c391f08a97ab0dd6bdb75c7c747b66 Author: Tilman Sauerbeck Date: Sat Nov 11 22:30:53 2006 +0100 Prevent the filters from crashing from querying unknown properties. commit c574c10487c870e2f42fc2d56dc714a92ee91167 Author: Tilman Sauerbeck Date: Sat Sep 30 13:35:13 2006 +0200 Don't use Array#shift, since it's broken in some versions of Ruby. commit 5002e2820394ddd474d63fd8d437fe2093e3c31c Author: Tilman Sauerbeck Date: Sat Sep 9 18:55:58 2006 +0200 Pass the full propdict to the filters instead of the minimal hash. commit 9ea13c24af2cbf9faaf05a8d071cf5d54ab55235 Author: Tilman Sauerbeck Date: Fri Sep 8 14:44:11 2006 +0200 the empty-string-fallback isn't needed in the musicbrainz filter commit 598d1e646f80e30df9c405de3c57cb24d27fdb98 Author: Tilman Sauerbeck Date: Fri Sep 8 14:12:42 2006 +0200 Added filter capability. commit 4e644ea0e122d1589c1ebb021941729c93135bc9 Author: Tilman Sauerbeck Date: Fri Sep 8 11:05:21 2006 +0200 Added proxy supported (contributed by Alexander Rigbo). Bumped version to 0.1.2. commit f5b1cf8c6317609e98657c52d4eb5a7751472fe2 Author: Tilman Sauerbeck Date: Thu Sep 7 15:35:16 2006 +0200 userconfdir returns an absolute path now. commit 86c66aa8a6c338409a43bab99ae8e29825f986bb Author: Tilman Sauerbeck Date: Wed Sep 6 23:30:57 2006 +0200 mkdir_p is in FileUtils, not File commit 591ebc6e0489b49fbd8c1ba7780513e6b4b07385 Author: Tilman Sauerbeck Date: Wed Sep 6 18:01:22 2006 +0200 Create our config directory if it doesn't exist yet. commit cedf7af0f303fde66aa2a80a23a6d492f054f1e5 Author: Tilman Sauerbeck Date: Sun Aug 27 17:49:38 2006 +0200 Bumped version to 0.1.1 commit fbd8d3449567a182bce3e547d4456f8e28264360 Author: Tilman Sauerbeck Date: Sun Aug 27 17:49:05 2006 +0200 Fixed userconfdir fallback method definition. commit a4452f766fea7385d2ed6d5da46fcff157833610 Author: Tilman Sauerbeck Date: Fri Aug 25 20:19:36 2006 +0200 Added Rakefile, prepared README for release. commit e5a93d1e0a6f00cfcd9b1d90b90fdb2951fb039e Author: Tilman Sauerbeck Date: Fri Aug 25 19:45:54 2006 +0200 Added license stuff. commit 42c8bb28ffd79f40d20d72a6d5230a1626e67ca2 Author: Tilman Sauerbeck Date: Sun Jul 30 13:07:20 2006 +0200 Added userconfdir support. commit d2765943cb3946108ded06a79fea614384fdd45c Author: Tilman Sauerbeck Date: Mon Jul 17 17:59:21 2006 +0200 don't block songs that are longer than 30 minutes. where did i get this idea from? thanks to jason for spotting this commit bf98997a0c77e4dba7f62a0554bef444f5ce0d25 Author: Tilman Sauerbeck Date: Wed Jun 21 18:45:20 2006 +0200 use the announced host to submit our tracks commit 34e4f702f1089864f512a4eb49a7b244e343be2f Author: Tilman Sauerbeck Date: Sat May 20 15:04:03 2006 +0200 Applied a compatibility fix for DrDolittle. commit 696c4e769e8bbf0ef88c75a827fc2925f1915ce9 Author: Tilman Sauerbeck Date: Tue May 2 21:27:12 2006 +0200 Ignore broken queue.yaml's. commit a58e2d6b0d7bf614b3b979d6357a89f2e7cba1f4 Author: Tilman Sauerbeck Date: Tue Apr 11 23:57:35 2006 +0200 Fix propdict access to work wrt source preference voodoo. commit 5689c8f774ebbd9612319266deacbcb235bad42c Author: Tilman Sauerbeck Date: Sat Mar 11 20:04:32 2006 +0100 Updated for Ruby bindings breakage. commit ff061819100c5966ed6ce65d2fae71b262c7df36 Author: Tilman Sauerbeck Date: Sat Feb 25 11:05:34 2006 +0100 Remove the lockfile with an at_exit handler. commit 3c94b72781ea03c05e50922fe4f4c7a9dc54150d Author: Tilman Sauerbeck Date: Sun Feb 12 17:24:41 2006 +0100 Moved the documentation to README and added AUTHORS. commit b912985ebc328d152f3b82f3069394579ae2740b Author: Tilman Sauerbeck Date: Sun Feb 12 16:56:19 2006 +0100 Replace the timer with signal_playback_playtime. commit cf5f0e5e2c3be7c866ee23533e2cae880073cd91 Author: Tilman Sauerbeck Date: Sun Feb 5 11:21:37 2006 +0100 Use 'env' to spawn the ruby executable; don't hardcode the path. commit a033a2363f0257ca25865f724aaba25a3edbbe04 Author: Tilman Sauerbeck Date: Sun Feb 5 11:17:51 2006 +0100 Code cleanup. commit c4555ca6d8f7719186dd74603da0d55775773204 Author: Tilman Sauerbeck Date: Fri Dec 23 15:57:29 2005 +0100 Update to new propdict API. commit d3e68609acb6514eaa812788a4369b3bf525823a Author: Tilman Sauerbeck Date: Sat Oct 1 16:53:39 2005 +0200 Only allow one instance of xmms2-scrobbler at the same time. commit 85a73b84e757c6c047bd462aabb7f1b6811336ca Author: Tilman Sauerbeck Date: Sat Oct 1 15:55:23 2005 +0200 Optional variables need to be set to an empty string in the submission query. commit b6af3717faca3274f56eea2ec014073829e50b12 Author: Tilman Sauerbeck Date: Sat Oct 1 15:52:11 2005 +0200 Album is optional, too. commit 10588ed6925e50f36902dea1b326f159eecaf690 Author: Tilman Sauerbeck Date: Sat Oct 1 15:35:47 2005 +0200 Don't set the MusicBrainz track ID to an empty string. commit 40ba80a239b4d8c6d42bca9733d7238215cacdbf Author: Tilman Sauerbeck Date: Sat Oct 1 14:54:33 2005 +0200 Run the handshake sequence again if the server kicked us out. commit c95d5913808c74d1a6aec9b2cbf9fa36f9350b29 Author: Tilman Sauerbeck Date: Fri Sep 30 22:10:41 2005 +0200 Submit MusicBrainz track ID if available. commit 837363332a97906ed1648eb34668df07f386d14d Author: Tilman Sauerbeck Date: Sat Sep 10 14:44:15 2005 +0200 Don't try to submit song data on shutdown. commit 8476b4f7d6cc61f38e4fcaadf5f6c96926557f2f Author: Tilman Sauerbeck Date: Sat Sep 10 14:41:03 2005 +0200 Start the submission queue once we're logged in, not as soon as it's loaded. commit 9d40340ae480c7652bbe4ce32c8d7aa26165ec7a Author: Tilman Sauerbeck Date: Mon Sep 5 20:07:00 2005 +0200 API update. commit 48bf27dec78b794b28cf622f6036c85771935f5e Author: Tilman Sauerbeck Date: Mon Sep 5 20:06:25 2005 +0200 Don't die if we cannot retrieve the current playtime. commit d0a55bcb2a5ee2047c86963aad15823fed82ef68 Author: Tilman Sauerbeck Date: Wed Aug 24 21:16:13 2005 +0200 Initial import. commit 3e003950c38a7de6bde072b09c7874c24522437f Author: root Date: Wed Aug 24 18:36:08 2005 +0200 Initial commit xmms2-scrobbler-0.4.0/README0000644000175000017500000000523111316704001014740 0ustar tilmantilmanXMMS2-Scrobbler =============== About ----- XMMS2-Scrobbler is a client for XMMS2 that feeds information about the songs you played to last.fm, formerly known as AudioScrobbler. XMMS2-Scrobbler supports "multi-scrobbling", i.e. it can talk to more than one AudioScrobbler server. The latest version can be found at http://code-monkey.de/pages/xmms2-scrobbler. Dependencies ------------ XMMS2-Scrobbler obviously depends on libxmmsclient (ships with XMMS2). It also depends on CURL. That's it. Installation ------------ Run "make" to build xmms2-scrobbler. Run "make install" to install xmms2-scrobbler. The usual variables PREFIX and DESTDIR are evaluated. Alternatively, just copy the bin/xmms2-scrobbler file anywhere you like. Usage ----- First, you need to set up XMMS2-Scrobbler's config files. Config values that are specific to the AudioScrobbler server go in ~/.config/xmms2/clients/xmms2-scrobbler/$SERVER_NAME/config. You will usually have .../clients/xmms2-scrobbler/lastfm/config and maybe .../clients/xmms2-scrobbler/librefm/config. These server-specific config files contain your username and password and the URL to use: mkdir ~/.config/xmms2/clients/xmms2-scrobbler/lastfm echo -e "user: foo\npassword: bar\nhandshake_url: http://post.audioscrobbler.com\n" > \ ~/.config/xmms2/clients/xmms2-scrobbler/lastfm/config For libre.fm, use handshake_url: http://turtle.libre.fm Optionally, if you're behind a proxy, you'll need to tell XMMS2-Scrobbler about that proxy. This information applies to all servers and so goes in .../clients/xmms2-scrobbler/config. echo -e "proxy: my.proxy\nproxy_port: 8080\n" >> \ ~/.config/xmms2/clients/xmms2-scrobbler/config Next, create a symlink to the script in ~/.config/xmms2/startup.d. This will make xmms2d start xmms2-scrobbler on startup. When xmms2d is killed, xmms2-scrobbler will exit automatically. In case anything doesn't work as it should, have a look at ~/.config/xmms2/clients/xmms2-scrobbler/logfile.log. Upgrading from 0.3.x -------------------- The 0.3 series of XMMS2-Scrobbler didn't use server-specific config files. To upgrade your configuration, do like this: cd ~/.config/xmms2/clients/xmms2-scrobbler # Create a config directory for the last.fm server mkdir lastfm # Copy the old config to the last.fm-specific config file grep -v ^proxy config > lastfm/config # Remove the last.fm-specific lines from the generic config file grep ^proxy config > proxy_config mv proxy_config config # Move the old queue file to the lastfm directory mv queue lastfm/ # Append the handshake_url config value echo -e "\nhandshake_url: http://post.audioscrobbler.com\n" >> \ lastfm/config xmms2-scrobbler-0.4.0/COPYING0000644000175000017500000000210411316704001015107 0ustar tilmantilmanCopyright (c) 2004-2006 Tilman Sauerbeck (tilman at code-monkey de) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. xmms2-scrobbler-0.4.0/Makefile0000644000175000017500000000253711316704001015526 0ustar tilmantilmanCFLAGS += -std=gnu99 -Wall -Wwrite-strings -pthread ENDIAN_CFLAGS= PREFIX ?= /usr/local VERSION := 0.4.0 TARBALL := xmms2-scrobbler-$(VERSION).tar.gz XMMS_CFLAGS := `pkg-config xmms2-client --cflags` XMMS_LDFLAGS := `pkg-config xmms2-client --libs` CURL_CFLAGS := `pkg-config libcurl --cflags` CURL_LDFLAGS := `pkg-config libcurl --libs` ifndef VERBOSE QUIET_CC = @echo ' ' CC $@; QUIET_LINK = @echo ' ' LINK $@; QUIET_MKDIR = @echo ' ' MKDIR $@; endif ifdef BIG_ENDIAN ENDIAN_CFLAGS=-DWORDS_BIGENDIAN endif BINARY := bin/xmms2-scrobbler OBJECTS := src/xmms2-scrobbler.o \ src/list.o \ src/queue.o \ src/strbuf.o \ src/md5.o \ src/submission.o all: $(BINARY) install: $(BINARY) install -d $(DESTDIR)$(PREFIX)/bin install -m 755 $(BINARY) $(DESTDIR)$(PREFIX)/bin $(BINARY): $(OBJECTS) bin $(QUIET_LINK)$(CC) $(LDFLAGS) $(XMMS_LDFLAGS) $(CURL_LDFLAGS) $(OBJECTS) -o $@ src/%.o : src/%.c $(QUIET_CC)$(CC) $(CFLAGS) $(XMMS_CFLAGS) $(CURL_CFLAGS) $(ENDIAN_CFLAGS) -o $@ -c $< bin: $(QUIET_MKDIR)mkdir bin dist: rm -rf $(TARBALL) xmms2-scrobbler-$(VERSION) git archive --format=tar --prefix=xmms2-scrobbler-$(VERSION)/ HEAD | tar -x git log > xmms2-scrobbler-$(VERSION)/ChangeLog tar czvf $(TARBALL) xmms2-scrobbler-$(VERSION) rm -rf xmms2-scrobbler-$(VERSION) clean: rm -rf $(OBJECTS) bin xmms2-scrobbler-0.4.0/.gitignore0000644000175000017500000000005211316704001016044 0ustar tilmantilmanChangeLog bin pkg src/xmms2-scrobbler *.o