hythmbox-2.98/plugins/audioscrobbler/rb-audioscrobbler.c

No issues found

   1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
   2  *
   3  *  Copyright (C) 2005 Alex Revo <xiphoidappendix@gmail.com>,
   4  *		       Ruben Vermeersch <ruben@Lambda1.be>
   5  *            (C) 2007 Christophe Fergeau <teuf@gnome.org>
   6  *
   7  *  This program is free software; you can redistribute it and/or modify
   8  *  it under the terms of the GNU General Public License as published by
   9  *  the Free Software Foundation; either version 2 of the License, or
  10  *  (at your option) any later version.
  11  *
  12  *  The Rhythmbox authors hereby grant permission for non-GPL compatible
  13  *  GStreamer plugins to be used and distributed together with GStreamer
  14  *  and Rhythmbox. This permission is above and beyond the permissions granted
  15  *  by the GPL license by which Rhythmbox is covered. If you modify this code
  16  *  you may extend this exception to your version of the code, but you are not
  17  *  obligated to do so. If you do not wish to do so, delete this exception
  18  *  statement from your version.
  19  *
  20  *  This program is distributed in the hope that it will be useful,
  21  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  22  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  23  *  GNU General Public License for more details.
  24  *
  25  *  You should have received a copy of the GNU General Public License
  26  *  along with this program; if not, write to the Free Software
  27  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
  28  *
  29  */
  30 
  31 #define __EXTENSIONS__
  32 
  33 #include "config.h"
  34 
  35 #include <errno.h>
  36 
  37 #include <string.h>
  38 #include <time.h>
  39 
  40 #include <glib.h>
  41 #include <glib/gi18n.h>
  42 #include <glib/gprintf.h>
  43 #include <gtk/gtk.h>
  44 
  45 #include <libsoup/soup.h>
  46 #include <libsoup/soup-gnome.h>
  47 
  48 #include "rb-audioscrobbler.h"
  49 #include "rb-debug.h"
  50 #include "rb-file-helpers.h"
  51 #include "rb-builder-helpers.h"
  52 #include "rb-shell.h"
  53 #include "rb-shell-player.h"
  54 #include "rb-source.h"
  55 #include "rb-cut-and-paste-code.h"
  56 #include "rb-util.h"
  57 #include "rb-podcast-entry-types.h"
  58 #include "rb-marshal.h"
  59 #include "rb-audioscrobbler-entry.h"
  60 
  61 #define CLIENT_ID "rbx"
  62 #define CLIENT_VERSION VERSION
  63 
  64 #define MAX_QUEUE_SIZE 1000
  65 #define MAX_SUBMIT_SIZE	50
  66 #define INITIAL_HANDSHAKE_DELAY 60
  67 #define MAX_HANDSHAKE_DELAY 120*60
  68 
  69 #define SCROBBLER_VERSION "1.2.1"
  70 
  71 #define USER_AGENT	"Rhythmbox/" VERSION
  72 
  73 struct _RBAudioscrobblerPrivate
  74 {
  75 	RBAudioscrobblerService *service;
  76 
  77 	RBShellPlayer *shell_player;
  78 
  79 	/* Data for the prefs pane */
  80 	guint submit_count;
  81 	char *submit_time;
  82 	guint queue_count;
  83 	enum {
  84 		STATUS_OK = 0,
  85 		HANDSHAKING,
  86 		REQUEST_FAILED,
  87 		BADAUTH,
  88 		BAD_TIMESTAMP,
  89 		CLIENT_BANNED,
  90 		GIVEN_UP
  91 	} status;
  92 	char *status_msg;
  93 
  94 	/* Submission queue */
  95 	GQueue *queue;
  96 	/* Entries currently being submitted */
  97 	GQueue *submission;
  98 
  99 	guint failures;
 100 	guint handshake_delay;
 101 	/* Handshake has been done? */
 102 	gboolean handshake;
 103 	time_t handshake_next;
 104 
 105 	/* Only write the queue to a file if it has been changed */
 106 	gboolean queue_changed;
 107 
 108 	/* Authentication cookie + authentication info */
 109 	gchar *sessionid;
 110 	gchar *username;
 111 	gchar *session_key;
 112 	gchar *submit_url;
 113 	gchar *nowplaying_url;
 114 
 115 	/* Currently playing song info, if NULL this means the currently
 116 	 * playing song isn't eligible to be queued
 117 	 */
 118 	AudioscrobblerEntry *currently_playing;
 119 	guint current_elapsed;
 120 	gboolean now_playing_updated;
 121 
 122 	guint timeout_id;
 123 
 124 	/* HTTP requests session */
 125 	SoupSession *soup_session;
 126 
 127 	/* callback for songs that were played offline (eg on an iPod) */
 128 	gulong offline_play_notify_id;
 129 };
 130 
 131 #define RB_AUDIOSCROBBLER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_AUDIOSCROBBLER, RBAudioscrobblerPrivate))
 132 
 133 
 134 static gboolean	     rb_audioscrobbler_load_queue (RBAudioscrobbler *audioscrobbler);
 135 static int	     rb_audioscrobbler_save_queue (RBAudioscrobbler *audioscrobbler);
 136 static void	     rb_audioscrobbler_print_queue (RBAudioscrobbler *audioscrobbler, gboolean submission);
 137 static void	     rb_audioscrobbler_free_queue_entries (RBAudioscrobbler *audioscrobbler, GQueue **queue);
 138 
 139 static void	     rb_audioscrobbler_get_property (GObject *object,
 140 						    guint prop_id,
 141 						    GValue *value,
 142 						    GParamSpec *pspec);
 143 static void	     rb_audioscrobbler_set_property (GObject *object,
 144 						    guint prop_id,
 145 						    const GValue *value,
 146 						    GParamSpec *pspec);
 147 static void	     rb_audioscrobbler_dispose (GObject *object);
 148 static void	     rb_audioscrobbler_finalize (GObject *object);
 149 
 150 static void	     rb_audioscrobbler_add_timeout (RBAudioscrobbler *audioscrobbler);
 151 static gboolean	     rb_audioscrobbler_timeout_cb (RBAudioscrobbler *audioscrobbler);
 152 
 153 static void	     rb_audioscrobbler_parse_response (RBAudioscrobbler *audioscrobbler, SoupMessage *msg, gboolean handshake);
 154 
 155 static void	     rb_audioscrobbler_do_handshake (RBAudioscrobbler *audioscrobbler);
 156 static void	     rb_audioscrobbler_submit_queue (RBAudioscrobbler *audioscrobbler);
 157 static void	     rb_audioscrobbler_perform (RBAudioscrobbler *audioscrobbler,
 158 						char *url,
 159 						char *post_data,
 160 						SoupSessionCallback response_handler);
 161 static void	     rb_audioscrobbler_do_handshake_cb (SoupSession *session, SoupMessage *msg, gpointer user_data);
 162 static void	     rb_audioscrobbler_submit_queue_cb (SoupSession *session, SoupMessage *msg, gpointer user_data);
 163 static void	     rb_audioscrobbler_nowplaying_cb (SoupSession *session, SoupMessage *msg, gpointer user_data);
 164 
 165 static void	     rb_audioscrobbler_song_changed_cb (RBShellPlayer *player,
 166 							RhythmDBEntry *entry,
 167 							RBAudioscrobbler *audioscrobbler);
 168 static void          rb_audioscrobbler_offline_play_notify_cb (RhythmDB *db,
 169 							       RhythmDBEntry *rb_entry,
 170 							       const gchar *property_name,
 171 							       const GValue *metadata,
 172 							       RBAudioscrobbler *audioscrobbler);
 173 
 174 static void          rb_audioscrobbler_nowplaying (RBAudioscrobbler *audioscrobbler, AudioscrobblerEntry *entry);
 175 
 176 
 177 
 178 
 179 enum
 180 {
 181 	PROP_0,
 182 	PROP_SERVICE,
 183 	PROP_SHELL_PLAYER,
 184 	PROP_USERNAME,
 185 	PROP_SESSION_KEY,
 186 };
 187 
 188 enum
 189 {
 190 	AUTHENTICATION_ERROR,
 191 	STATISTICS_CHANGED,
 192 	LAST_SIGNAL
 193 };
 194 
 195 static guint rb_audioscrobbler_signals[LAST_SIGNAL] = { 0 };
 196 
 197 G_DEFINE_DYNAMIC_TYPE (RBAudioscrobbler, rb_audioscrobbler, G_TYPE_OBJECT)
 198 
 199 
 200 static void
 201 rb_audioscrobbler_constructed (GObject *object)
 202 {
 203 	RBAudioscrobbler *audioscrobbler;
 204 	RhythmDB *db;
 205 	RhythmDBEntry *playing_entry;
 206 
 207 	RB_CHAIN_GOBJECT_METHOD (rb_audioscrobbler_parent_class, constructed, object);
 208 
 209 	audioscrobbler = RB_AUDIOSCROBBLER (object);
 210 
 211 	rb_audioscrobbler_load_queue (audioscrobbler);
 212 	rb_audioscrobbler_add_timeout (audioscrobbler);
 213 	rb_audioscrobbler_statistics_changed (audioscrobbler);
 214 
 215 	g_object_get (audioscrobbler->priv->shell_player, "db", &db, NULL);
 216 
 217 	audioscrobbler->priv->offline_play_notify_id = 
 218 		g_signal_connect_object (db, 
 219 					 "entry-extra-metadata-notify::rb:offlinePlay",
 220 					 (GCallback)rb_audioscrobbler_offline_play_notify_cb, 
 221 					 audioscrobbler, 0);
 222 
 223 	/* if an entry is currently being played then handle it */
 224 	playing_entry = rb_shell_player_get_playing_entry (audioscrobbler->priv->shell_player);
 225 	if (playing_entry != NULL) {
 226 		rb_audioscrobbler_song_changed_cb (audioscrobbler->priv->shell_player,
 227 		                                   playing_entry,
 228 		                                   audioscrobbler);
 229 		rhythmdb_entry_unref (playing_entry);
 230 	}
 231 
 232 	g_object_unref (db);
 233 }
 234 
 235 /* Class-related functions: */
 236 static void
 237 rb_audioscrobbler_class_init (RBAudioscrobblerClass *klass)
 238 {
 239 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 240 
 241 	object_class->constructed = rb_audioscrobbler_constructed;
 242 	object_class->dispose = rb_audioscrobbler_dispose;
 243 	object_class->finalize = rb_audioscrobbler_finalize;
 244 
 245 	object_class->set_property = rb_audioscrobbler_set_property;
 246 	object_class->get_property = rb_audioscrobbler_get_property;
 247 
 248 	g_object_class_install_property (object_class,
 249 	                                 PROP_SERVICE,
 250 	                                 g_param_spec_object ("service",
 251 	                                                      "Service",
 252 	                                                      "Audioscrobbler service to scrobble to",
 253 	                                                      RB_TYPE_AUDIOSCROBBLER_SERVICE,
 254                                                               G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
 255 
 256 	g_object_class_install_property (object_class,
 257 					 PROP_SHELL_PLAYER,
 258 					 g_param_spec_object ("shell-player",
 259 							      "RBShellPlayer",
 260 							      "RBShellPlayer object",
 261 							      RB_TYPE_SHELL_PLAYER,
 262 							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 263 
 264 	g_object_class_install_property (object_class,
 265 	                                 PROP_USERNAME,
 266 	                                 g_param_spec_string ("username",
 267 	                                                      "Username",
 268 	                                                      "Username of the user who is scrobbling data",
 269 	                                                      NULL,
 270                                                               G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
 271 
 272 	g_object_class_install_property (object_class,
 273 	                                 PROP_SESSION_KEY,
 274 	                                 g_param_spec_string ("session-key",
 275 	                                                      "Session Key",
 276 	                                                      "Session key used to authenticate the user",
 277 	                                                      NULL,
 278                                                               G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
 279 
 280 	/**
 281 	 * RBAudioscrobbler::authentication-error:
 282 	 * @account: the #RBAudioscrobblerAccount
 283 	 * @status: new status
 284 	 *
 285 	 * Emitted when an authentication error occurs.
 286 	 */
 287 	rb_audioscrobbler_signals[AUTHENTICATION_ERROR] =
 288 		g_signal_new ("authentication-error",
 289 			      G_OBJECT_CLASS_TYPE (object_class),
 290 			      G_SIGNAL_RUN_LAST,
 291 			      G_STRUCT_OFFSET (RBAudioscrobblerClass, authentication_error),
 292 			      NULL, NULL,
 293 			      g_cclosure_marshal_VOID__VOID,
 294 			      G_TYPE_NONE,
 295 			      0);
 296 
 297 	/**
 298 	 * RBAudioscrobbler::statistics-changed:
 299 	 * @status_msg: description of the status
 300 	 * @queued_count: the number of tracks queued for submission
 301 	 * @submit_count: the number of tracks already submitted this session
 302 	 * @submit_time: the time at which the last submission was made
 303 	 *
 304 	 * Emitted when the scrobbling session's statistics change.
 305 	 */
 306 	rb_audioscrobbler_signals[STATISTICS_CHANGED] =
 307 		g_signal_new ("statistics-changed",
 308 			      G_OBJECT_CLASS_TYPE (object_class),
 309 			      G_SIGNAL_RUN_LAST,
 310 			      G_STRUCT_OFFSET (RBAudioscrobblerClass, statistics_changed),
 311 			      NULL, NULL,
 312 			      rb_marshal_VOID__STRING_UINT_UINT_STRING,
 313 			      G_TYPE_NONE,
 314 		              4,
 315 			      G_TYPE_STRING,
 316 		              G_TYPE_UINT,
 317 		              G_TYPE_UINT,
 318 		              G_TYPE_STRING);
 319 
 320 	g_type_class_add_private (klass, sizeof (RBAudioscrobblerPrivate));
 321 }
 322 
 323 static void
 324 rb_audioscrobbler_class_finalize (RBAudioscrobblerClass *klass)
 325 {
 326 }
 327 
 328 static void
 329 rb_audioscrobbler_init (RBAudioscrobbler *audioscrobbler)
 330 {
 331 	rb_debug ("Initialising Audioscrobbler");
 332 	rb_debug ("Plugin ID: %s, Version %s (Protocol %s)",
 333 		  CLIENT_ID, CLIENT_VERSION, SCROBBLER_VERSION);
 334 
 335 	audioscrobbler->priv = RB_AUDIOSCROBBLER_GET_PRIVATE (audioscrobbler);
 336 
 337 	audioscrobbler->priv->queue = g_queue_new();
 338 	audioscrobbler->priv->submission = g_queue_new();
 339 	audioscrobbler->priv->sessionid = g_strdup ("");
 340 	audioscrobbler->priv->username = NULL;
 341 	audioscrobbler->priv->session_key = NULL;
 342 	audioscrobbler->priv->submit_url = g_strdup ("");
 343 	audioscrobbler->priv->nowplaying_url = g_strdup ("");
 344 }
 345 
 346 static void
 347 rb_audioscrobbler_dispose (GObject *object)
 348 {
 349 	RBAudioscrobbler *audioscrobbler;
 350 
 351 	g_return_if_fail (object != NULL);
 352 	g_return_if_fail (RB_IS_AUDIOSCROBBLER (object));
 353 
 354 	audioscrobbler = RB_AUDIOSCROBBLER (object);
 355 	
 356 	rb_debug ("disposing audioscrobbler");
 357 
 358 	/* Save any remaining entries */
 359 	rb_audioscrobbler_save_queue (audioscrobbler);
 360 
 361 	if (audioscrobbler->priv->offline_play_notify_id != 0) {
 362 		RhythmDB *db;
 363 
 364 		g_object_get (G_OBJECT (audioscrobbler->priv->shell_player),
 365 			      "db", &db, 
 366 			      NULL);
 367 		g_signal_handler_disconnect (db, audioscrobbler->priv->offline_play_notify_id);
 368 		audioscrobbler->priv->offline_play_notify_id = 0;
 369 		g_object_unref (db);
 370 	}
 371 
 372 	if (audioscrobbler->priv->timeout_id != 0) {
 373 		g_source_remove (audioscrobbler->priv->timeout_id);
 374 		audioscrobbler->priv->timeout_id = 0;
 375 	}
 376 
 377 	if (audioscrobbler->priv->soup_session != NULL) {
 378 		soup_session_abort (audioscrobbler->priv->soup_session);
 379 		g_object_unref (audioscrobbler->priv->soup_session);
 380 		audioscrobbler->priv->soup_session = NULL;
 381 	}
 382 
 383 	if (audioscrobbler->priv->service != NULL) {
 384 		g_object_unref (audioscrobbler->priv->service);
 385 		audioscrobbler->priv->service = NULL;
 386 	}
 387 
 388 	if (audioscrobbler->priv->shell_player != NULL) {
 389 		g_object_unref (audioscrobbler->priv->shell_player);
 390 		audioscrobbler->priv->shell_player = NULL;
 391 	}
 392 
 393 	G_OBJECT_CLASS (rb_audioscrobbler_parent_class)->dispose (object);
 394 }
 395 
 396 static void
 397 rb_audioscrobbler_finalize (GObject *object)
 398 {
 399 	RBAudioscrobbler *audioscrobbler;
 400 
 401 	rb_debug ("Finalizing Audioscrobbler");
 402 
 403 	g_return_if_fail (object != NULL);
 404 	g_return_if_fail (RB_IS_AUDIOSCROBBLER (object));
 405 
 406 	audioscrobbler = RB_AUDIOSCROBBLER (object);
 407 
 408 	g_free (audioscrobbler->priv->sessionid);
 409 	g_free (audioscrobbler->priv->username);
 410 	g_free (audioscrobbler->priv->session_key);
 411 	g_free (audioscrobbler->priv->submit_url);
 412 	g_free (audioscrobbler->priv->nowplaying_url);
 413 
 414 	if (audioscrobbler->priv->currently_playing != NULL) {
 415 		rb_audioscrobbler_entry_free (audioscrobbler->priv->currently_playing);
 416 		audioscrobbler->priv->currently_playing = NULL;
 417 	}
 418 
 419 	rb_audioscrobbler_free_queue_entries (audioscrobbler, &audioscrobbler->priv->queue);
 420 	rb_audioscrobbler_free_queue_entries (audioscrobbler, &audioscrobbler->priv->submission);
 421 
 422 	G_OBJECT_CLASS (rb_audioscrobbler_parent_class)->finalize (object);
 423 }
 424 
 425 RBAudioscrobbler*
 426 rb_audioscrobbler_new (RBAudioscrobblerService *service,
 427                        RBShellPlayer *shell_player,
 428                        const char *username,
 429                        const char *session_key)
 430 {
 431 	return g_object_new (RB_TYPE_AUDIOSCROBBLER,
 432 	                     "service", service,
 433 			     "shell-player", shell_player,
 434 	                     "username", username,
 435 	                     "session_key", session_key,
 436 			     NULL);
 437 }
 438 
 439 static void
 440 rb_audioscrobbler_set_property (GObject *object,
 441 				guint prop_id,
 442 				const GValue *value,
 443 				GParamSpec *pspec)
 444 {
 445 	RBAudioscrobbler *audioscrobbler = RB_AUDIOSCROBBLER (object);
 446 
 447 	switch (prop_id) {
 448 	case PROP_SERVICE:
 449 		audioscrobbler->priv->service = g_value_dup_object (value);
 450 		break;
 451 	case PROP_SHELL_PLAYER:
 452 		audioscrobbler->priv->shell_player = g_value_get_object (value);
 453 		g_object_ref (G_OBJECT (audioscrobbler->priv->shell_player));
 454 		g_signal_connect_object (G_OBJECT (audioscrobbler->priv->shell_player),
 455 					 "playing-song-changed",
 456 					 G_CALLBACK (rb_audioscrobbler_song_changed_cb),
 457 					 audioscrobbler, 0);
 458 		break;
 459 	case PROP_USERNAME:
 460 		audioscrobbler->priv->username = g_value_dup_string (value);
 461 		break;
 462 	case PROP_SESSION_KEY:
 463 		audioscrobbler->priv->session_key = g_value_dup_string (value);
 464 		break;
 465 	default:
 466 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 467 		break;
 468 	}
 469 }
 470 
 471 static void
 472 rb_audioscrobbler_get_property (GObject *object,
 473 				guint prop_id,
 474 				GValue *value,
 475 				GParamSpec *pspec)
 476 {
 477 	RBAudioscrobbler *audioscrobbler = RB_AUDIOSCROBBLER (object);
 478 
 479 	switch (prop_id) {
 480 	case PROP_SHELL_PLAYER:
 481 		g_value_set_object (value, audioscrobbler->priv->shell_player);
 482 		break;
 483 	default:
 484 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 485 		break;
 486 	}
 487 }
 488 
 489 /* emits the statistics-changed signal */
 490 void
 491 rb_audioscrobbler_statistics_changed (RBAudioscrobbler *audioscrobbler)
 492 {
 493 	const char *status;
 494 	char *status_msg;
 495 
 496 	switch (audioscrobbler->priv->status) {
 497 	case STATUS_OK:
 498 		status = _("OK");
 499 		break;
 500 	case HANDSHAKING:
 501 		status = _("Logging in");
 502 		break;
 503 	case REQUEST_FAILED:
 504 		status = _("Request failed");
 505 		break;
 506 	case BADAUTH:
 507 		status = _("Authentication error");
 508 		break;
 509 	case BAD_TIMESTAMP:
 510 		status = _("Clock is not set correctly");
 511 		break;
 512 	case CLIENT_BANNED:
 513 		status = _("This version of Rhythmbox has been banned.");
 514 		break;
 515 	case GIVEN_UP:
 516 		status = _("Track submission failed too many times");
 517 		break;
 518 	default:
 519 		g_assert_not_reached ();
 520 		break;
 521 	}
 522 
 523 	if (audioscrobbler->priv->status_msg && audioscrobbler->priv->status_msg[0] != '\0') {
 524 		status_msg = g_strdup_printf ("%s: %s", status, audioscrobbler->priv->status_msg);
 525 	} else {
 526 		status_msg = g_strdup (status);
 527 	}
 528 
 529 	g_signal_emit_by_name (audioscrobbler, "statistics-changed",
 530 	                       status_msg, audioscrobbler->priv->queue_count,
 531 	                       audioscrobbler->priv->submit_count, audioscrobbler->priv->submit_time);
 532 
 533 	g_free (status_msg);
 534 }
 535 
 536 /* Add the audioscrobbler thread timer */
 537 static void
 538 rb_audioscrobbler_add_timeout (RBAudioscrobbler *audioscrobbler)
 539 {
 540 	if (!audioscrobbler->priv->timeout_id) {
 541 		rb_debug ("Adding Audioscrobbler timer (15 seconds)");
 542 		audioscrobbler->priv->timeout_id = 
 543 			g_timeout_add_seconds (15,
 544 					       (GSourceFunc) rb_audioscrobbler_timeout_cb,
 545 					       audioscrobbler);
 546 	}
 547 }
 548 
 549 static gboolean
 550 rb_audioscrobbler_is_queueable (RhythmDBEntry *entry)
 551 {
 552 	const char *title;
 553 	const char *artist;
 554 	gulong duration;
 555 	RhythmDBEntryType *type;
 556 	RhythmDBEntryCategory category;
 557 
 558 	/* First, check if the entry is appropriate for sending to 
 559 	 * audioscrobbler
 560 	 */
 561 	type = rhythmdb_entry_get_entry_type (entry);
 562 	g_object_get (type, "category", &category, NULL);
 563 	if (category != RHYTHMDB_ENTRY_NORMAL) {
 564 		rb_debug ("entry %s is not queueable: category not NORMAL", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION));
 565 		return FALSE;
 566 	}
 567 	if (type == RHYTHMDB_ENTRY_TYPE_PODCAST_POST) {
 568 		rb_debug ("entry %s is not queueable: is a podcast post", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION));
 569 		return FALSE;
 570 	}
 571 	if (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_PLAYBACK_ERROR)) {
 572 		rb_debug ("entry %s is not queueable: has playback error (%s)",
 573 			  rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION),
 574 			  rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_PLAYBACK_ERROR));
 575 		return FALSE;
 576 	}
 577 
 578 	title = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE);
 579 	artist = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST);
 580 	duration = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DURATION);
 581 
 582 	/* The specification (v1.1) tells us to ignore entries that do not
 583 	 * meet these conditions
 584 	 */
 585 	if (duration < 30) {
 586 		rb_debug ("entry %s not queueable: shorter than 30 seconds", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION));
 587 		return FALSE;
 588 	}
 589 	if (strcmp (artist, _("Unknown")) == 0) {
 590 		rb_debug ("entry %s not queueable: artist is %s (unknown)", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION), artist);
 591 		return FALSE;
 592 	}
 593 	if (strcmp (title, _("Unknown")) == 0) {
 594 		rb_debug ("entry %s not queueable: title is %s (unknown)", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION), title);
 595 		return FALSE;
 596 	}
 597 
 598 	rb_debug ("entry %s is queueable", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION));
 599 	return TRUE;
 600 }
 601 
 602 
 603 static void
 604 rb_audioscrobbler_add_to_queue (RBAudioscrobbler *audioscrobbler,
 605 				AudioscrobblerEntry *entry)
 606 {
 607 	if (g_queue_get_length (audioscrobbler->priv->queue) >= MAX_QUEUE_SIZE) {
 608 		AudioscrobblerEntry *oldest;
 609 
 610 		rb_debug ("queue limit reached.  dropping oldest entry.");
 611 		oldest = g_queue_pop_head (audioscrobbler->priv->queue);
 612 		rb_audioscrobbler_entry_free (oldest);
 613 	} else {
 614 		audioscrobbler->priv->queue_count++;
 615 	}
 616 
 617 	g_queue_push_tail (audioscrobbler->priv->queue, entry);
 618 	audioscrobbler->priv->queue_changed = TRUE;
 619 }
 620 
 621 static void
 622 maybe_add_current_song_to_queue (RBAudioscrobbler *audioscrobbler)
 623 {
 624 	gboolean got_elapsed;
 625 	guint elapsed;
 626 	AudioscrobblerEntry *cur_entry;
 627 
 628 	cur_entry = audioscrobbler->priv->currently_playing;
 629 	if (cur_entry == NULL) {
 630 		return;
 631 	}
 632 
 633 	got_elapsed = rb_shell_player_get_playing_time (audioscrobbler->priv->shell_player,
 634 							&elapsed,
 635 							NULL);
 636 	if (got_elapsed) {
 637 		int elapsed_delta = elapsed - audioscrobbler->priv->current_elapsed;
 638 		audioscrobbler->priv->current_elapsed = elapsed;
 639 		
 640 		if ((elapsed >= cur_entry->length / 2 || elapsed >= 240) && elapsed_delta < 20) {
 641 			rb_debug ("Adding currently playing song to queue");
 642 			rb_audioscrobbler_add_to_queue (audioscrobbler, cur_entry);
 643 			audioscrobbler->priv->currently_playing = NULL;
 644 
 645 			rb_audioscrobbler_statistics_changed (audioscrobbler);
 646 		} else if (elapsed_delta > 20) {
 647 			rb_debug ("Skipping detected; not submitting current song");
 648 			/* not sure about this - what if I skip to somewhere towards
 649 			 * the end, but then go back and listen to the whole song?
 650 			 */
 651 			rb_audioscrobbler_entry_free (audioscrobbler->priv->currently_playing);
 652 			audioscrobbler->priv->currently_playing = NULL;
 653 		}
 654 	}
 655 }
 656 
 657 /* updates the queue and submits entries as required */
 658 static gboolean
 659 rb_audioscrobbler_timeout_cb (RBAudioscrobbler *audioscrobbler)
 660 {
 661 	maybe_add_current_song_to_queue (audioscrobbler);
 662 
 663 	/* do handshake if we need to */
 664 	rb_audioscrobbler_do_handshake (audioscrobbler);
 665 
 666 	if ((audioscrobbler->priv->now_playing_updated == FALSE) &&
 667 	    (audioscrobbler->priv->currently_playing != NULL) &&
 668 	    audioscrobbler->priv->handshake) {
 669 		rb_debug ("Sending now playing data");
 670 		audioscrobbler->priv->now_playing_updated = TRUE;
 671 		rb_audioscrobbler_nowplaying (audioscrobbler, audioscrobbler->priv->currently_playing);
 672 	}
 673 
 674 	/* if there's something in the queue, submit it if we can, save it otherwise */
 675 	if (!g_queue_is_empty(audioscrobbler->priv->queue)) {
 676 		if (audioscrobbler->priv->handshake) {
 677 			rb_audioscrobbler_submit_queue (audioscrobbler);
 678 		} else {
 679 			rb_audioscrobbler_save_queue (audioscrobbler);
 680 		}
 681 	}
 682 	return TRUE;
 683 }
 684 
 685 static void
 686 rb_audioscrobbler_offline_play_notify_cb (RhythmDB *db,
 687 					  RhythmDBEntry *rb_entry,
 688 					  const gchar *property_name,
 689 					  const GValue *metadata,
 690 					  RBAudioscrobbler *audioscrobbler)
 691 {
 692 	g_return_if_fail (G_VALUE_HOLDS_ULONG (metadata));
 693 
 694 	/* FIXME: do sanity checks on play_date value? */
 695 	if (rb_audioscrobbler_is_queueable (rb_entry)) {
 696 		AudioscrobblerEntry *as_entry;
 697 		
 698 		as_entry = rb_audioscrobbler_entry_create (rb_entry, audioscrobbler->priv->service);
 699 		as_entry->play_time = g_value_get_ulong (metadata);
 700 		rb_audioscrobbler_add_to_queue (audioscrobbler, as_entry);
 701 	}
 702 }
 703 
 704 static void
 705 rb_audioscrobbler_parse_response (RBAudioscrobbler *audioscrobbler, SoupMessage *msg, gboolean handshake)
 706 {
 707 	gboolean successful;
 708 
 709 	rb_debug ("Parsing response, status=%d Reason: %s", msg->status_code, msg->reason_phrase);
 710 
 711 	successful = FALSE;
 712 	if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code) && msg->response_body->length != 0)
 713 		successful = TRUE;
 714 
 715 	if (successful) {
 716 		gchar **breaks;
 717 
 718 		breaks = g_strsplit (msg->response_body->data, "\n", 0);
 719 
 720 		g_free (audioscrobbler->priv->status_msg);
 721 		audioscrobbler->priv->status = STATUS_OK;
 722 		audioscrobbler->priv->status_msg = NULL;
 723 
 724 		if (g_str_has_prefix (breaks[0], "OK")) {
 725 			rb_debug ("OK");
 726 			if (handshake) {
 727 				if (g_strv_length (breaks) < 4) {
 728 					g_warning ("Unexpectedly short successful last.fm handshake response:\n%s",
 729 						   msg->response_body->data);
 730 					audioscrobbler->priv->status = REQUEST_FAILED;
 731 				} else {
 732 					g_free (audioscrobbler->priv->sessionid);
 733 					g_free (audioscrobbler->priv->nowplaying_url);
 734 					g_free (audioscrobbler->priv->submit_url);
 735 					audioscrobbler->priv->sessionid = g_strdup (breaks[1]);
 736 					audioscrobbler->priv->nowplaying_url = g_strdup (breaks[2]);
 737 					audioscrobbler->priv->submit_url = g_strdup (breaks[3]);
 738 				}
 739 			}
 740 		} else if (g_str_has_prefix (breaks[0], "BANNED")) {
 741 			rb_debug ("Client banned");
 742 			audioscrobbler->priv->status = CLIENT_BANNED;
 743 		} else if (g_str_has_prefix (breaks[0], "BADAUTH")) {
 744 			rb_debug ("Bad authorization");
 745 			audioscrobbler->priv->status = BADAUTH;
 746 			/* emit an authentication error signal.
 747 			 * this is the only error which needs to be addressed from outside this class */
 748 			g_signal_emit (audioscrobbler, rb_audioscrobbler_signals[AUTHENTICATION_ERROR], 0);
 749 		} else if (g_str_has_prefix (breaks[0], "BADTIME")) {
 750 			rb_debug ("Bad timestamp");
 751 			audioscrobbler->priv->status = BAD_TIMESTAMP;
 752 		} else if (g_str_has_prefix (breaks[0], "FAILED")) {
 753 			rb_debug ("Server failure:\n \tMessage: %s", breaks[0]);
 754 			audioscrobbler->priv->status = REQUEST_FAILED;
 755 			/* this is probably going to be ugly, but there isn't much we can do */
 756 			if (strlen (breaks[0]) > strlen ("FAILED ")) {
 757 				audioscrobbler->priv->status_msg = g_strdup (breaks[0] + strlen ("FAILED "));
 758 			}
 759 		} else {
 760 			g_warning ("Unexpected last.fm response:\n%s",
 761 				   msg->response_body->data);
 762 			audioscrobbler->priv->status = REQUEST_FAILED;
 763 		}
 764 
 765 		g_strfreev (breaks);
 766 	} else {
 767 		audioscrobbler->priv->status = REQUEST_FAILED;
 768 		audioscrobbler->priv->status_msg = g_strdup (msg->reason_phrase);
 769 	}
 770 }
 771 
 772 static gboolean
 773 idle_unref_cb (GObject *object)
 774 {
 775 	g_object_unref (object);
 776 	return FALSE;
 777 }
 778 
 779 /*
 780  * NOTE: the caller *must* unref the audioscrobbler object in an idle
 781  * handler created in the callback.
 782  */
 783 static void
 784 rb_audioscrobbler_perform (RBAudioscrobbler *audioscrobbler,
 785 			   char *url,
 786 			   char *post_data,
 787 			   SoupSessionCallback response_handler)
 788 {
 789 	SoupMessage *msg;
 790 
 791 	msg = soup_message_new (post_data == NULL ? "GET" : "POST", url);
 792 	soup_message_headers_append (msg->request_headers, "User-Agent", USER_AGENT);
 793 
 794 	if (post_data != NULL) {
 795 		rb_debug ("Submitting to Audioscrobbler: %s", post_data);
 796 		soup_message_set_request (msg,
 797 					  "application/x-www-form-urlencoded",
 798 					  SOUP_MEMORY_TAKE,
 799 					  post_data,
 800 					  strlen (post_data));
 801 	}
 802 
 803 	/* create soup session, if we haven't got one yet */
 804 	if (!audioscrobbler->priv->soup_session) {
 805 		audioscrobbler->priv->soup_session =
 806 			soup_session_async_new_with_options (
 807 					SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_GNOME_FEATURES_2_26,
 808 					NULL);
 809 	}
 810 
 811 	soup_session_queue_message (audioscrobbler->priv->soup_session,
 812 				    msg,
 813 				    response_handler,
 814 				    g_object_ref (audioscrobbler));
 815 }
 816 
 817 static gboolean
 818 rb_audioscrobbler_should_handshake (RBAudioscrobbler *audioscrobbler)
 819 {
 820 	/* Perform handshake if necessary. Only perform handshake if
 821 	 *   - we have no current handshake; AND
 822 	 *   - we have waited the appropriate amount of time between
 823 	 *     handshakes; AND
 824 	 *   - we have a username; AND
 825 	 *   - we have a session key
 826 	 */
 827 	if (audioscrobbler->priv->handshake) {
 828 		return FALSE;
 829 	}
 830 
 831 	if (time (NULL) < audioscrobbler->priv->handshake_next) {
 832 		rb_debug ("Too soon; time=%ld, handshake_next=%ld",
 833 			  (long)time (NULL),
 834 			  (long)audioscrobbler->priv->handshake_next);
 835 		return FALSE;
 836 	}
 837 
 838 	if ((audioscrobbler->priv->username == NULL) ||
 839 	    (strcmp (audioscrobbler->priv->username, "") == 0)) {
 840 		rb_debug ("No username set");
 841 		return FALSE;
 842 	}
 843 
 844 	if ((audioscrobbler->priv->session_key == NULL) ||
 845 	    (strcmp (audioscrobbler->priv->session_key, "") == 0)) {
 846 		return FALSE;
 847 	}
 848 
 849 	return TRUE;
 850 }
 851 
 852 static void
 853 rb_audioscrobbler_do_handshake (RBAudioscrobbler *audioscrobbler)
 854 {
 855 	gchar *username;
 856 	gchar *url;
 857 	gchar *auth;
 858 	gchar *autharg;
 859 	guint timestamp;
 860 
 861 	if (!rb_audioscrobbler_should_handshake (audioscrobbler)) {
 862 		return;
 863 	}
 864 
 865 	username = soup_uri_encode (audioscrobbler->priv->username, EXTRA_URI_ENCODE_CHARS);
 866 	timestamp = time (NULL);
 867 
 868 	autharg = g_strdup_printf ("%s%d",
 869 		                   rb_audioscrobbler_service_get_api_secret (audioscrobbler->priv->service),
 870 		                   timestamp);
 871 	auth = g_compute_checksum_for_string (G_CHECKSUM_MD5, autharg, -1);
 872 
 873 	url = g_strdup_printf ("%s?hs=true&p=%s&c=%s&v=%s&u=%s&t=%d&a=%s&api_key=%s&sk=%s",
 874 			       rb_audioscrobbler_service_get_scrobbler_url (audioscrobbler->priv->service),
 875 			       SCROBBLER_VERSION,
 876 			       CLIENT_ID,
 877 			       CLIENT_VERSION,
 878 			       username,
 879 			       timestamp,
 880 			       auth,
 881 		               rb_audioscrobbler_service_get_api_key (audioscrobbler->priv->service),
 882 		               audioscrobbler->priv->session_key);
 883 
 884 	g_free (auth);
 885 	g_free (autharg);
 886 	g_free (username);
 887 
 888 	rb_debug ("Performing handshake with Audioscrobbler server: %s", url);
 889 
 890 	audioscrobbler->priv->status = HANDSHAKING;
 891 	rb_audioscrobbler_statistics_changed (audioscrobbler);
 892 
 893 	rb_audioscrobbler_perform (audioscrobbler,
 894 				   url,
 895 				   NULL,
 896 				   rb_audioscrobbler_do_handshake_cb);
 897 
 898 	g_free (url);
 899 }
 900 
 901 
 902 static void
 903 rb_audioscrobbler_do_handshake_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
 904 {
 905 	RBAudioscrobbler *audioscrobbler = RB_AUDIOSCROBBLER(user_data);
 906 
 907 	rb_debug ("Handshake response");
 908 	rb_audioscrobbler_parse_response (audioscrobbler, msg, TRUE);
 909 	rb_audioscrobbler_statistics_changed (audioscrobbler);
 910 
 911 	switch (audioscrobbler->priv->status) {
 912 	case STATUS_OK:
 913 		audioscrobbler->priv->handshake = TRUE;
 914 		audioscrobbler->priv->handshake_delay = INITIAL_HANDSHAKE_DELAY;
 915 		audioscrobbler->priv->failures = 0;
 916 		break;
 917 	default:
 918 		rb_debug ("Handshake failed");
 919 		++audioscrobbler->priv->failures;
 920 
 921 		audioscrobbler->priv->handshake_next = time (NULL) + audioscrobbler->priv->handshake_delay;
 922 
 923 		audioscrobbler->priv->handshake_delay *= 2;
 924 		if (audioscrobbler->priv->handshake_delay > MAX_HANDSHAKE_DELAY) {
 925 			audioscrobbler->priv->handshake_delay = MAX_HANDSHAKE_DELAY;
 926 		}
 927 		rb_debug ("handshake delay is now %d minutes", audioscrobbler->priv->handshake_delay/60);
 928 		break;
 929 	}
 930 
 931 	g_idle_add ((GSourceFunc) idle_unref_cb, audioscrobbler);
 932 }
 933 
 934 static gchar *
 935 rb_audioscrobbler_build_post_data (RBAudioscrobbler *audioscrobbler)
 936 {
 937 	g_return_val_if_fail (!g_queue_is_empty (audioscrobbler->priv->queue), NULL);
 938 
 939 	gchar *post_data = g_strdup_printf ("s=%s", audioscrobbler->priv->sessionid);
 940 	int i = 0;
 941 	do {
 942 		AudioscrobblerEntry *entry;
 943 		AudioscrobblerEncodedEntry *encoded;
 944 		gchar *new;
 945 
 946 		/* remove first queue entry */
 947 		entry = g_queue_pop_head (audioscrobbler->priv->queue);
 948 		encoded = rb_audioscrobbler_entry_encode (entry);
 949 		new = g_strdup_printf ("%s&a[%d]=%s&t[%d]=%s&b[%d]=%s&m[%d]=%s&l[%d]=%d&i[%d]=%s&o[%d]=%s&n[%d]=%s&r[%d]=",
 950 				       post_data,
 951 				       i, encoded->artist,
 952 				       i, encoded->title,
 953 				       i, encoded->album,
 954 				       i, encoded->mbid,
 955 				       i, encoded->length,
 956 				       i, encoded->timestamp,
 957 				       i, encoded->source,
 958 				       i, encoded->track,
 959 				       i);
 960 		rb_audioscrobbler_encoded_entry_free (encoded);
 961 		g_free (post_data);
 962 		post_data = new;
 963 
 964 		/* add to submission list */
 965 		g_queue_push_tail (audioscrobbler->priv->submission, entry);
 966 		i++;
 967 	} while ((!g_queue_is_empty (audioscrobbler->priv->queue)) && (i < MAX_SUBMIT_SIZE));
 968 	
 969 	return post_data;
 970 }
 971 
 972 static void
 973 rb_audioscrobbler_submit_queue (RBAudioscrobbler *audioscrobbler)
 974 {
 975 	if (audioscrobbler->priv->sessionid != NULL) {
 976 		gchar *post_data;
 977 	
 978 		post_data = rb_audioscrobbler_build_post_data (audioscrobbler);
 979 
 980 		rb_debug ("Submitting queue to Audioscrobbler");
 981 		rb_audioscrobbler_print_queue (audioscrobbler, TRUE);
 982 
 983 		rb_audioscrobbler_perform (audioscrobbler,
 984 					   audioscrobbler->priv->submit_url,
 985 					   post_data,
 986 					   rb_audioscrobbler_submit_queue_cb);
 987 		 /* libsoup will free post_data when the request is finished */
 988 	}
 989 }
 990 
 991 static void
 992 rb_g_queue_concat (GQueue *q1, GQueue *q2)
 993 {
 994 	GList *elem;
 995 
 996 	while (!g_queue_is_empty (q2)) {
 997 		elem = g_queue_pop_head_link (q2);
 998 		g_queue_push_tail_link (q1, elem);
 999 	}
1000 }
1001 
1002 static void
1003 rb_audioscrobbler_submit_queue_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
1004 {
1005 	RBAudioscrobbler *audioscrobbler = RB_AUDIOSCROBBLER (user_data);
1006 
1007 	rb_debug ("Submission response");
1008 	rb_audioscrobbler_parse_response (audioscrobbler, msg, FALSE);
1009 
1010 	if (audioscrobbler->priv->status == STATUS_OK) {
1011 		rb_debug ("Queue submitted successfully");
1012 		rb_audioscrobbler_free_queue_entries (audioscrobbler, &audioscrobbler->priv->submission);
1013 		audioscrobbler->priv->submission = g_queue_new ();
1014 		rb_audioscrobbler_save_queue (audioscrobbler);
1015 
1016 		audioscrobbler->priv->submit_count += audioscrobbler->priv->queue_count;
1017 		audioscrobbler->priv->queue_count = 0;
1018 
1019 		g_free (audioscrobbler->priv->submit_time);
1020 		audioscrobbler->priv->submit_time = rb_utf_friendly_time (time (NULL));
1021 	} else {
1022 		++audioscrobbler->priv->failures;
1023 
1024 		/* add failed submission entries back to queue */
1025 		rb_g_queue_concat (audioscrobbler->priv->submission, 
1026 				   audioscrobbler->priv->queue);
1027 		g_assert (g_queue_is_empty (audioscrobbler->priv->queue));
1028 		g_queue_free (audioscrobbler->priv->queue);
1029 		audioscrobbler->priv->queue = audioscrobbler->priv->submission;
1030 		audioscrobbler->priv->submission = g_queue_new ();;
1031 		rb_audioscrobbler_save_queue (audioscrobbler);
1032 
1033 		rb_audioscrobbler_print_queue (audioscrobbler, FALSE);
1034 
1035 		if (audioscrobbler->priv->failures >= 3) {
1036 			rb_debug ("Queue submission has failed %d times; caching tracks locally",
1037 				  audioscrobbler->priv->failures);
1038 			g_free (audioscrobbler->priv->status_msg);
1039 
1040 			audioscrobbler->priv->handshake = FALSE;
1041 			audioscrobbler->priv->status = GIVEN_UP;
1042 			audioscrobbler->priv->status_msg = NULL;
1043 		} else {
1044 			rb_debug ("Queue submission failed %d times", audioscrobbler->priv->failures);
1045 		}
1046 	}
1047 
1048 	rb_audioscrobbler_statistics_changed (audioscrobbler);
1049 	g_idle_add ((GSourceFunc) idle_unref_cb, audioscrobbler);
1050 }
1051 
1052 static void
1053 rb_audioscrobbler_song_changed_cb (RBShellPlayer *player,
1054 				   RhythmDBEntry *entry,
1055 				   RBAudioscrobbler *audioscrobbler)
1056 {
1057 	gboolean got_time;
1058 	guint playing_time;
1059 
1060 	if (audioscrobbler->priv->currently_playing != NULL) {
1061 		rb_audioscrobbler_entry_free (audioscrobbler->priv->currently_playing);
1062 		audioscrobbler->priv->currently_playing = NULL;
1063 	}
1064 
1065 	if (entry == NULL) {
1066 		rb_debug ("called with no playing entry");
1067 		return;
1068 	}
1069 	rb_debug ("new entry: %s", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION));
1070 
1071 	got_time = rb_shell_player_get_playing_time (audioscrobbler->priv->shell_player,
1072 						     &playing_time,
1073 						     NULL);
1074 	if (got_time) {
1075 		audioscrobbler->priv->current_elapsed = (int) playing_time;
1076 	} else {
1077 		rb_debug ("didn't get playing time; assuming 0");
1078 		audioscrobbler->priv->current_elapsed = 0;
1079 	}
1080 
1081 	if (rb_audioscrobbler_is_queueable (entry) && (got_time == FALSE || playing_time < 15)) {
1082 		AudioscrobblerEntry *as_entry;
1083 
1084 		/* even if it's the same song, it's being played again from
1085 		 * the start so we can queue it again.
1086 		 */
1087 		as_entry = rb_audioscrobbler_entry_create (entry, audioscrobbler->priv->service);
1088 		as_entry->play_time = time (NULL);
1089 		audioscrobbler->priv->currently_playing = as_entry;
1090 		audioscrobbler->priv->now_playing_updated = FALSE;
1091 	}
1092 }
1093 
1094 
1095 /* Queue functions: */
1096 static gboolean
1097 rb_audioscrobbler_load_queue (RBAudioscrobbler *audioscrobbler)
1098 {
1099 	char *pathname;
1100 	GFile *file;
1101 	GError *error = NULL;
1102 	char *data;
1103 	char *start;
1104 	char *end;
1105 	gsize size;
1106 
1107 	/* we don't really care about errors enough to report them here */
1108 	pathname = g_build_filename (rb_user_data_dir (),
1109 	                             "audioscrobbler",
1110 	                             "submission-queues",
1111 	                             rb_audioscrobbler_service_get_name (audioscrobbler->priv->service),
1112 	                             audioscrobbler->priv->username,
1113 	                             NULL);
1114 	file = g_file_new_for_path (pathname);
1115 	rb_debug ("loading Audioscrobbler queue from \"%s\"", pathname);
1116 	g_free (pathname);
1117 
1118 	if (g_file_load_contents (file, NULL, &data, &size, NULL, &error) == FALSE) {
1119 		rb_debug ("unable to load audioscrobbler queue: %s", error->message);
1120 		g_error_free (error);
1121 		return FALSE;
1122 	}
1123 
1124 	start = data;
1125 	while (start < (data + size)) {
1126 		AudioscrobblerEntry *entry;
1127 
1128 		/* find the end of the line, to terminate the string */
1129 		end = g_utf8_strchr (start, -1, '\n');
1130 		if (end == NULL)
1131 			break;
1132 		*end = 0;
1133 
1134 		entry = rb_audioscrobbler_entry_load_from_string (start);
1135 		if (entry) {
1136 			g_queue_push_tail (audioscrobbler->priv->queue,
1137 					   entry);
1138 			audioscrobbler->priv->queue_count++;
1139 		}
1140 
1141 		start = end + 1;
1142 	}
1143 
1144 	g_free (data);
1145 	return TRUE;
1146 }
1147 
1148 static gboolean
1149 rb_audioscrobbler_save_queue (RBAudioscrobbler *audioscrobbler)
1150 {
1151 	char *pathname;
1152 	char *uri;
1153 	GFile *file;
1154 	GError *error = NULL;
1155 	GList *l;
1156 	GString *str;
1157 
1158 	if (!audioscrobbler->priv->queue_changed) {
1159 		return TRUE;
1160 	}
1161 
1162 	str = g_string_new ("");
1163 	for (l = audioscrobbler->priv->queue->head; l != NULL; l = g_list_next (l)) {
1164 		AudioscrobblerEntry *entry;
1165 
1166 		entry = (AudioscrobblerEntry *) l->data;
1167 		rb_audioscrobbler_entry_save_to_string (str, entry);
1168 	}
1169 
1170 	/* we don't really care about errors enough to report them here */
1171 	pathname = g_build_filename (rb_user_data_dir (),
1172 	                             "audioscrobbler",
1173 	                             "submission-queues",
1174 	                             rb_audioscrobbler_service_get_name (audioscrobbler->priv->service),
1175 	                             audioscrobbler->priv->username,
1176 	                             NULL);
1177 	rb_debug ("Saving Audioscrobbler queue to \"%s\"", pathname);
1178 
1179 	uri = g_filename_to_uri (pathname, NULL, NULL);
1180 	rb_uri_create_parent_dirs (uri, &error);
1181 
1182 	file = g_file_new_for_path (pathname);
1183 	g_free (pathname);
1184 	g_free (uri);
1185 
1186 	error = NULL;
1187 	g_file_replace_contents (file,
1188 				 str->str, str->len,
1189 				 NULL,
1190 				 FALSE,
1191 				 G_FILE_CREATE_NONE,
1192 				 NULL,
1193 				 NULL,
1194 				 &error);
1195 	g_string_free (str, TRUE);
1196 
1197 	if (error == NULL) {
1198 		audioscrobbler->priv->queue_changed = FALSE;
1199 		return TRUE;
1200 	} else {
1201 		rb_debug ("error saving audioscrobbler queue: %s",
1202 			  error->message);
1203 		g_error_free (error);
1204 		return FALSE;
1205 	}
1206 }
1207 
1208 static void
1209 rb_audioscrobbler_print_queue (RBAudioscrobbler *audioscrobbler, gboolean submission)
1210 {
1211 	GList *l;
1212 	AudioscrobblerEntry *entry;
1213 	int i = 0;
1214 
1215 	if (submission) {
1216 		l = audioscrobbler->priv->submission->head;
1217 		rb_debug ("Audioscrobbler submission (%d entries): ", 
1218 			  g_queue_get_length (audioscrobbler->priv->submission));
1219 
1220 	} else {
1221 		l = audioscrobbler->priv->queue->head;
1222 		rb_debug ("Audioscrobbler queue (%d entries): ", 
1223 			  g_queue_get_length (audioscrobbler->priv->queue));
1224 	}
1225 
1226 	for (; l != NULL; l = g_list_next (l)) {
1227 		entry = (AudioscrobblerEntry *) l->data;
1228 		rb_audioscrobbler_entry_debug (entry, ++i);
1229 	}
1230 }
1231 
1232 static void
1233 rb_audioscrobbler_free_queue_entries (RBAudioscrobbler *audioscrobbler, GQueue **queue)
1234 {
1235 	g_queue_foreach (*queue, (GFunc) rb_audioscrobbler_entry_free, NULL);
1236 	g_queue_free (*queue);
1237 	*queue = NULL;
1238 
1239 	audioscrobbler->priv->queue_changed = TRUE;
1240 }
1241 
1242 static void
1243 rb_audioscrobbler_nowplaying (RBAudioscrobbler *audioscrobbler, AudioscrobblerEntry *entry)
1244 {
1245 	AudioscrobblerEncodedEntry *encoded;
1246 	gchar *post_data;
1247 
1248 	if (audioscrobbler->priv->handshake) {
1249 		encoded = rb_audioscrobbler_entry_encode (entry);
1250 
1251 		post_data = g_strdup_printf ("s=%s&a=%s&t=%s&b=%s&l=%d&n=%s&m=%s",
1252 					     audioscrobbler->priv->sessionid,
1253 					     encoded->artist,
1254 					     encoded->title,
1255 					     encoded->album,
1256 					     encoded->length,
1257 					     encoded->track,
1258 					     encoded->mbid);
1259 
1260 		rb_audioscrobbler_perform (audioscrobbler,
1261 					   audioscrobbler->priv->nowplaying_url,
1262 					   post_data,
1263 					   rb_audioscrobbler_nowplaying_cb);
1264 
1265 		rb_audioscrobbler_encoded_entry_free (encoded);
1266 	}
1267 }
1268 
1269 static void
1270 rb_audioscrobbler_nowplaying_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
1271 {
1272 	RBAudioscrobbler *audioscrobbler = RB_AUDIOSCROBBLER (user_data);
1273 	rb_debug ("Now playing response");
1274 	rb_audioscrobbler_parse_response (audioscrobbler, msg, FALSE);
1275 
1276 	if (audioscrobbler->priv->status == STATUS_OK) {
1277 		rb_debug("Submission success!");
1278 	} else {
1279 		rb_debug("Error submitting now playing information.");
1280 	}
1281 
1282 	g_idle_add ((GSourceFunc) idle_unref_cb, audioscrobbler);
1283 }
1284 
1285 void
1286 _rb_audioscrobbler_register_type (GTypeModule *module)
1287 {
1288 	rb_audioscrobbler_register_type (module);
1289 }