Index: plugins/daap/rb-daap-plugin.c
===================================================================
--- plugins/daap/rb-daap-plugin.c	(revision 5895)
+++ plugins/daap/rb-daap-plugin.c	(revision 7205)
@@ -44,6 +44,16 @@
 #include "rb-daap-src.h"
 #include "rb-uri-dialog.h"
 
+#include <unistd.h>
+#include <sys/file.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <gdk/gdk.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
 #include "rb-daap-mdns-browser.h"
 
 /* preferences */
@@ -200,6 +210,8 @@
 		gconf_value_free (value);
 	}
 
+	create_pixbufs (plugin);
+
 	if (enabled) {
 		start_browsing (plugin);
 	}
@@ -209,8 +221,6 @@
 					    (GConfClientNotifyFunc) enable_browsing_changed_cb,
 					    plugin);
 
-	create_pixbufs (plugin);
-
 	g_object_get (shell,
 		      "ui-manager", &uimanager,
 		      NULL);
@@ -478,6 +488,9 @@
 	g_free (service_name);
 }
 
+static void sm_start_sm_daap_services(RBDaapPlugin  * plugin);
+static void sm_stop_sm_daap_services(RBDaapPlugin  * plugin);
+
 static void
 start_browsing (RBDaapPlugin *plugin)
 {
@@ -515,6 +528,7 @@
 							     (GEqualFunc)g_str_equal,
 							     (GDestroyNotify)g_free,
 							     (GDestroyNotify)remove_source);
+	sm_start_sm_daap_services(plugin);
 }
 
 static void
@@ -543,6 +557,8 @@
 
 	g_object_unref (plugin->priv->mdns_browser);
 	plugin->priv->mdns_browser = NULL;
+
+	sm_stop_sm_daap_services(plugin);
 }
 
 static void
@@ -676,17 +692,31 @@
 		  RBDAAPSource *source,
 		  const char *host)
 {
+	char *tmp_host;
 	char *source_host;
+	guint source_port;
+	guint port;
 	gboolean result;
 
 	if (source == NULL || host == NULL) {
 		return FALSE;
 	}
+	tmp_host = g_strdup(host);
+	if (tmp_host == NULL) {
+	    return FALSE;
+	}
 
+	char *s = strchr (tmp_host, ':');
+	*s = '\0';
+	char *p = s + 1;
+	port = (guint)atol(p);
+
 	g_object_get (source, "host", &source_host, NULL);
+	g_object_get (source, "port", &source_port, NULL);
 
-	result = (strcmp (host, source_host) == 0);
+	result = (strcmp (tmp_host, source_host) == 0) && (port == source_port);
 	g_free (source_host);
+	g_free (tmp_host);
 
 	return result;
 }
@@ -702,8 +732,11 @@
 		return NULL;
 	}
 
-	ip = strdup (uri + 7); /* daap:// */
-	s = strchr (ip, ':');
+	ip = g_strdup (uri + 7); /* daap:// */
+	if (ip == NULL) {
+		return NULL;
+	}
+	s = strchr (ip, '/');
 	*s = '\0';
 
 	source = (RBDAAPSource *)g_hash_table_find (plugin->priv->source_lookup, (GHRFunc)source_host_find, ip);
@@ -905,3 +938,401 @@
 	return plugin->priv->preferences;
 }
 
+/****************************************
+    Simplify Media specific code
+*****************************************/
+
+typedef struct {
+	char * service_name;
+	char * host;
+	int port;
+} SMDaapLocation;
+
+typedef struct {
+	GHashTable * services;
+	RBDaapPlugin * plugin;
+	guint event_id;
+} SMHelperData;
+
+typedef enum {SM_CONF, ZING_CONF} SMConfType;
+
+static void sm_remove_old_entry_cb(gpointer key,
+								 SMDaapLocation * source,
+								 SMHelperData * data)
+{
+	rb_debug("Removing service: %s", (char *)key);
+	mdns_service_removed(NULL, key, data->plugin);
+}
+
+static void sm_add_new_entry_cb (gpointer key,
+							  SMDaapLocation *source,
+							  SMHelperData * data)
+{
+	rb_debug("Adding/Updating service: %s", source->service_name);
+	mdns_service_added (NULL,
+			g_strdup(source->service_name),
+			g_strdup(source->service_name),
+			g_strdup(source->host),
+			source->port,
+			FALSE,
+			data->plugin);
+
+	char * new_service_name = (char *)key;
+	SMDaapLocation * nsource = g_hash_table_lookup(data->services, new_service_name);    
+	if (nsource != NULL) {
+		g_hash_table_remove (data->services, new_service_name);
+	}
+
+}
+
+static gboolean sm_get_daap_servers(xmlNode * root, GHashTable * new_services)
+{
+	gboolean foundServers = FALSE;
+	xmlNode *cur_node = root->children;
+   
+	if (!cur_node) {
+		return FALSE;
+	}
+	
+	while(cur_node) {
+		if (cur_node->type == XML_ELEMENT_NODE && (strcmp((char*)cur_node->name, "servers") == 0) ) {
+			foundServers = TRUE;
+			break;
+		}
+		cur_node = cur_node->next;
+	}
+
+	if(!foundServers) {
+		return FALSE;
+	}
+
+	for (; cur_node; cur_node = cur_node->next) {
+		SMDaapLocation * location = g_new0(SMDaapLocation, 1);
+		xmlNode * data = cur_node->children;
+
+		if (!data || strcmp((char *)data->name, "name") || !data->children->content) {
+			g_free(location);
+			return FALSE;
+		}
+
+		location->service_name = g_strdup((char*)data->children->content);
+		data = data->next;
+
+		if (!data || strcmp((char *)data->name, "host") || !data->children->content) {
+			g_free(location);
+			return FALSE;
+		}
+
+		location->host = g_strdup((char *)data->children->content);
+		data = data->next;
+
+		if (!data || strcmp((char *) data->name, "port") || !data->children->content) {
+			g_free(location);
+			return FALSE;
+		}
+
+		location->port = atoi((char *)data->children->content);
+
+		g_hash_table_insert (new_services, g_strdup (location->service_name), location);               
+	}
+
+	return TRUE;
+}
+
+static gboolean sm_parse_daap_server_list(char * buf, GHashTable * new_services)
+{
+	gboolean ret =  TRUE;
+	xmlDoc *doc = NULL;
+	xmlNode *root_element = NULL;
+	
+	/*
+	* this initialize the library and check potential ABI mismatches
+	* between the version it was compiled for and the actual shared
+	* library used.
+	*/
+	LIBXML_TEST_VERSION
+
+	doc = xmlReadMemory(buf, strlen(buf), "noname.xml", NULL, XML_PARSE_NOERROR);
+
+	if (doc == NULL) {
+		rb_debug("Unable to parse XML document");
+		return FALSE;
+	}
+
+	root_element = xmlDocGetRootElement(doc);
+
+	ret = sm_get_daap_servers(root_element, new_services);
+
+	xmlFreeDoc(doc);
+	
+	return ret;
+}
+
+static int sm_get_http_content_length(char * buf) 
+{
+	char * start = strstr(buf, "Content-Length:");
+	if  (start != NULL) {
+		char * end;
+		char tmp[100];
+
+		start += 15; 
+
+		end  = strstr(start, "\r\n");
+		if(end == NULL || end - start > sizeof(tmp) - 1) {
+			return -1;
+		}
+		strncpy(tmp, start, end - start);
+		tmp[end - start] = '\0';
+		return atoi(tmp);
+	}
+	return -1;
+}
+
+static int sm_get_http_headers_length(char * buf)
+{
+	char * start = strstr(buf, "\r\n\r\n");
+	if  (start!= NULL) {
+		return start - buf + 4;
+	}
+	return -1;
+}
+
+static int sm_get_httpd_port_number(SMConfType type) 
+{
+	int default_port = 4689;
+	int port = 0;
+	char path[1024];
+	char *home_path = getenv("HOME");
+	FILE * f = NULL;
+
+	if (home_path == NULL) {
+		rb_debug("Unable to read HOME environment variable.");
+		return default_port;
+	}
+	
+	strncpy(path, home_path, sizeof(path));
+	path[sizeof(path) -1] = '\0';
+	switch (type) {
+		case ZING_CONF:
+			strncat(path, "/.zingagent/ZINGagent.ini", sizeof(path));
+			break;
+		case SM_CONF:
+			strncat(path, "/.simplifymedia/SimplifyMedia.ini", sizeof(path));
+			break;
+		default:
+			rb_debug("Wrong configuration type. Unable to read configuration.");
+			return default_port;
+	}	
+	path[sizeof(path) -1] = '\0';
+	
+	f = fopen(path, "r");
+	
+	if (f != NULL) {
+		char    buf[10240 + 1];
+		int fd = fileno(f);
+		int res = res = flock(fd, LOCK_EX);
+		
+		if(res != 0) {
+			rb_debug("Lock using flock failed");
+			fclose(f);
+			return default_port;
+		}
+		
+		while(fgets(buf, 10240, f)) {
+			buf[10240] = '\0';
+			char * start = strstr(buf, "HTTP Port=");
+			if (start != NULL) {			
+				char * end = strstr(buf, "\n");
+				if (end != NULL) {
+					*end = '\0';
+					port = atoi(start + 10);// 10 is the length of "HTTP Port="
+					rb_debug("Parsed port number %d", port);
+					break;
+				} else {
+					rb_debug("Unable to find the ending new line");
+				}
+			}
+		}
+		
+		res = flock(fd, LOCK_UN);
+		if (res != 0) {
+			rb_debug("Unlock using flock failed");
+		}
+		
+		fclose(f);
+	} else {
+		rb_debug("Unable to open SimplifyMedia.ini file");
+	}
+		
+	if (port == 0) {
+		port = default_port;
+	}	
+	
+	rb_debug("Using httpd port number %d", port);
+	
+	return port;
+}
+
+static gboolean sm_pull_daap_server_list(char * buf, int length, SMConfType type) 
+{
+	int content_length = -1;
+	int headers_length = -1;
+	const char  *sendbuf = "GET http://localhost/daapservers HTTP/1.1\xd\xaHost: localhost\xd\xa\xd\xa";
+	int send_size = strlen(sendbuf);
+	int sent = 0;
+	int received = 0;
+	struct timeval tv;
+
+	struct sockaddr_in clientService;
+	int client_socket;
+	buf[0] = '\0';
+
+	client_socket = socket(AF_INET, SOCK_STREAM, 0 );
+	if (client_socket == -1) {
+		rb_debug("Unable to create socket");
+		return FALSE;
+	}
+	
+	tv.tv_sec = 10;
+	tv.tv_usec = 0;
+	if (setsockopt(client_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
+		rb_debug("Unable to set socket receive timeout");
+	}
+
+	clientService.sin_family = AF_INET;
+	clientService.sin_addr.s_addr = inet_addr("127.0.0.1");
+	clientService.sin_port = htons(sm_get_httpd_port_number(type));
+	if (connect(client_socket, (struct sockaddr *)&clientService, sizeof(clientService))) {
+		close(client_socket);
+		rb_debug("Unable to connect to http server");
+		return FALSE;	
+	}
+
+	while (sent < send_size){
+		int ret = send(client_socket, sendbuf + sent, send_size - sent, 0);
+		if(ret == -1) {
+			close(client_socket);
+			rb_debug("Unable to send request");
+			return FALSE;
+		}
+		sent += ret;
+	}
+
+	while (received < length) {
+		int ret = recv(client_socket, buf + received, length - received, 0);
+
+		if (ret == 0) {
+			break;
+		}
+
+		if (ret == -1) {
+			rb_debug("Socket error");
+			close(client_socket);
+			return FALSE;
+		}
+
+		received += ret;
+		buf[received] = '\0';
+
+		if (content_length == -1) {
+			content_length = sm_get_http_content_length(buf);
+		}	
+		
+		if(headers_length == -1) {
+			headers_length = sm_get_http_headers_length(buf);
+		}
+
+		if (content_length != -1 && headers_length != -1 &&  content_length == received - headers_length) {			
+			break;
+		}
+	}
+	
+	rb_debug("Received: received=%d, headers_length=%d, content_length=%d", received, headers_length, content_length);
+	
+	close(client_socket);
+	return TRUE;
+}
+
+static gboolean sm_retrieve_daap_services(GHashTable * new_services, SMConfType type)
+{
+	char * xmlData = NULL;
+	gboolean ret = TRUE;
+	const int BUF_LENGTH = 30 * 1024;
+	char * buf = g_new0(char, BUF_LENGTH);
+
+	if (!sm_pull_daap_server_list(buf, BUF_LENGTH - 1, type)) {
+		rb_debug("Unable to pull server list");
+		ret = FALSE;
+		goto end;
+	}
+
+	xmlData =  strstr(buf, "<?xml");
+	if (xmlData == NULL) {
+		rb_debug("Unable to identify the xml document");
+		ret =  FALSE;
+		goto end;
+	}
+
+	ret = sm_parse_daap_server_list(xmlData, new_services);
+	
+	if (!ret) {
+		rb_debug("Unable to parse the xml document");
+		ret = FALSE;
+		goto end;
+	}
+
+end:
+	g_free(buf);
+	return ret;
+}
+
+static gboolean sm_daap_services_cb(SMHelperData * data)
+{
+	GDK_THREADS_ENTER ();
+	gboolean ret = FALSE;
+
+	rb_debug("Scheduled callback");
+
+	GHashTable * new_services = g_hash_table_new(g_str_hash, g_str_equal);
+			 
+	/* 1. pull the current list from SM web server. */
+	ret = sm_retrieve_daap_services(new_services, SM_CONF);
+	rb_debug("Retrieved services: %s", (ret)? "TRUE" : "FALSE");
+
+	/* 2. pull the current list from Zing web server. */
+	ret = sm_retrieve_daap_services(new_services, ZING_CONF);
+	rb_debug("Retrieved services: %s", (ret)? "TRUE" : "FALSE");
+
+	/* 3. add new services  */
+	g_hash_table_foreach(new_services, (GHFunc)sm_add_new_entry_cb, data);
+
+	/* 4. remove old services */
+	g_hash_table_foreach(data->services, (GHFunc)sm_remove_old_entry_cb, data);
+
+	/* 5. Cleanup */
+	g_hash_table_destroy(data->services);
+	data->services = new_services;
+
+	GDK_THREADS_LEAVE ();
+	return TRUE;
+}
+
+static SMHelperData * data;
+
+static void sm_start_sm_daap_services(RBDaapPlugin * plugin)
+{
+	data = g_new0(SMHelperData, 1);
+	data->plugin = plugin;
+	data->services = g_hash_table_new(g_str_hash, g_str_equal);
+	
+	sm_daap_services_cb(data);
+	
+	data->event_id = g_timeout_add(15000, (GSourceFunc)sm_daap_services_cb, data);
+}
+
+static void sm_stop_sm_daap_services(RBDaapPlugin * plugin)
+{
+	g_source_remove(data->event_id);
+	g_hash_table_destroy(data->services);
+	g_free(data);
+}
Index: plugins/daap/rb-daap-connection.c
===================================================================
--- plugins/daap/rb-daap-connection.c	(revision 5895)
+++ plugins/daap/rb-daap-connection.c	(revision 7205)
@@ -44,7 +44,7 @@
 #include "rb-debug.h"
 #include "rb-util.h"
 
-#define RB_DAAP_USER_AGENT "iTunes/4.6 (Windows; N)"
+#define RB_DAAP_USER_AGENT "Rhythmbox" //"iTunes/4.6 (Windows; N)"
 
 static void      rb_daap_connection_dispose      (GObject *obj);
 static void      rb_daap_connection_set_property (GObject *object,
@@ -82,12 +82,14 @@
 	gboolean is_connecting;
 
 	SoupSession *session;
+	SoupMessage *message;
 	SoupURI *base_uri;
 	gchar *daap_base_uri;
 
 	gdouble daap_version;
 	guint32 session_id;
 	gint revision_number;
+	gint revision_delta;
 
 	gint request_id;
 	gint database_id;
@@ -337,6 +339,7 @@
 	message = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
 
 	soup_message_headers_append (message->request_headers, "Client-DAAP-Version", 		"3.0");
+	soup_message_headers_append (message->request_headers, "User-Agent", 			RB_DAAP_USER_AGENT);
 	soup_message_headers_append (message->request_headers, "Accept-Language", 		"en-us, en;q=5.0");
 #ifdef HAVE_LIBZ
 	soup_message_headers_append (message->request_headers, "Accept-Encoding",		"gzip");
@@ -675,6 +678,7 @@
 		  priv->base_uri->host,
 		  priv->base_uri->port,
 		  path);
+	priv->message = message;
 	return TRUE;
 }
 
@@ -781,6 +785,7 @@
 {
 	RBDAAPConnectionPrivate *priv = connection->priv;
 	RBDAAPItem *item;
+	gint	prev_revision_number = priv->revision_number;
 
 	if (structure == NULL || SOUP_STATUS_IS_SUCCESSFUL (status) == FALSE) {
 		rb_daap_connection_state_done (connection, FALSE);
@@ -796,6 +801,9 @@
 	}
 
 	priv->revision_number = g_value_get_int (&(item->content));
+	if (priv->revision_number > 2) {
+		priv->revision_delta = prev_revision_number;
+	}
 	rb_daap_connection_state_done (connection, TRUE);
 }
 
@@ -908,7 +916,9 @@
 		return;
 	}
 
-	priv->item_id_to_uri = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)rb_refstring_unref);
+	if ( !priv->item_id_to_uri ) {
+		priv->item_id_to_uri = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)rb_refstring_unref);
+	}
 
 	rb_profile_start ("handling song listing");
 	priv->progress = 0.0f;
@@ -1057,15 +1067,19 @@
 		g_value_unset (&value);
 
 		/* title */
+		g_assert (title != NULL);
 		entry_set_string_prop (priv->db, entry, RHYTHMDB_PROP_TITLE, title);
 
 		/* album */
+		g_assert (album != NULL);
 		entry_set_string_prop (priv->db, entry, RHYTHMDB_PROP_ALBUM, album);
 
 		/* artist */
+		g_assert (artist != NULL);
 		entry_set_string_prop (priv->db, entry, RHYTHMDB_PROP_ARTIST, artist);
 
 		/* genre */
+		g_assert (genre != NULL);
 		entry_set_string_prop (priv->db, entry, RHYTHMDB_PROP_GENRE, genre);
 
 		/* stream URI property is stored as a mountpoint for get_playback_uri */
@@ -1083,6 +1097,48 @@
 		}
 	}
 	rhythmdb_commit (priv->db);
+
+	if ( update_type == 1 ) {
+		// This is a delta update, so look for deleted items
+		GNode *deletedid_node;
+
+		deletedid_node = rb_daap_structure_find_node (structure, RB_DAAP_CC_MUDL);
+		if (deletedid_node != NULL) {
+			gint id = 0;
+
+			for ( i = 0, n = deletedid_node->children ; n ; ++i, n = n->next ) {
+				RhythmDBEntry *entry = NULL;
+				RBRefString *item_uri;
+
+				item = rb_daap_structure_find_item (n, RB_DAAP_CC_MIID);
+				if (item == NULL) {
+					rb_debug ("Could not find dmap.itemid item in /databases/%d/containers",
+						priv->database_id);
+					continue;
+				}
+				id = g_value_get_int (&(item->content));
+
+				item_uri = g_hash_table_lookup (priv->item_id_to_uri, GINT_TO_POINTER (id));
+				if (item_uri == NULL) {
+					rb_debug ("Entry %d s doesn't exist in the database\n", id);
+					continue;
+				}
+
+				entry = rhythmdb_entry_lookup_by_location(priv->db, rb_refstring_get(item_uri));
+				if ( entry == NULL ) {
+					rb_debug ("Entry %d s doesn't exist in the database\n", id);
+					continue;
+				}
+
+				g_hash_table_remove (priv->item_id_to_uri, GINT_TO_POINTER (id));
+
+				rb_debug ("Deleting database item %s", rb_refstring_get(item_uri));
+				rhythmdb_entry_delete(priv->db, entry);
+			}
+		}
+	}
+
+	rhythmdb_commit (priv->db);
 	rb_profile_end ("handling song listing");
 
 	rb_daap_connection_state_done (connection, TRUE);
@@ -1111,14 +1167,25 @@
 {
 	RBDAAPConnectionPrivate *priv = connection->priv;
 	GNode *listing_node;
+	RBDAAPItem *update_item;
 	gint i;
 	GNode *n;
+	gboolean update_delta;
 
 	if (structure == NULL || SOUP_STATUS_IS_SUCCESSFUL (status) == FALSE) {
 		rb_daap_connection_state_done (connection, FALSE);
 		return;
 	}
 
+	update_item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MUTY);
+	if (update_item == NULL) {
+		rb_debug ("Could not find dmap.updatetype item in /databases/%d/items",
+			  priv->database_id);
+		rb_daap_connection_state_done (connection, FALSE);
+		return;
+	}
+	update_delta = g_value_get_char (&(update_item->content));
+
 	listing_node = rb_daap_structure_find_node (structure, RB_DAAP_CC_MLCL);
 	if (listing_node == NULL) {
 		rb_debug ("Could not find dmap.listing item in /databases/%d/containers",
@@ -1159,9 +1226,50 @@
 		playlist->name = name;
 		rb_debug ("Got playlist %p: name %s, id %d", playlist, playlist->name, playlist->id);
 
-		priv->playlists = g_slist_prepend (priv->playlists, playlist);
+		if ( !g_slist_find(priv->playlists, playlist) ) {
+			priv->playlists = g_slist_prepend (priv->playlists, playlist);
+		} else {
+			g_free(playlist);
+		}
 	}
 
+	if (update_delta == TRUE) {
+		// This is a delta update, so look for deleted items
+		GNode *deletedid_node;
+
+		deletedid_node = rb_daap_structure_find_node (structure, RB_DAAP_CC_MUDL);
+		if (deletedid_node != NULL) {
+			RBDAAPItem *item;
+			gint id;
+
+			for ( i = 0, n = deletedid_node->children ; n ; ++i, n = n->next ) {
+				GSList *l;
+
+				item = rb_daap_structure_find_item (n, RB_DAAP_CC_MIID);
+				if (item == NULL) {
+					rb_debug ("Could not find dmap.itemid item in /databases/%d/containers",
+						priv->database_id);
+					continue;
+				}
+				id = g_value_get_int (&(item->content));
+				for (l = priv->playlists; l; l = l->next) {
+					RBDAAPPlaylist *playlist = l->data;
+		
+					if ( playlist->id == id ) {
+						priv->playlists = g_slist_remove (priv->playlists, playlist);
+
+						g_list_foreach (playlist->uris, (GFunc)rb_refstring_unref, NULL);
+						g_list_free (playlist->uris);
+						g_free (playlist->name);
+						g_free (playlist);
+						l->data = NULL;
+						break;
+					}
+				}
+			}
+		}
+	}
+
 	/* Sort the playlists into lexical order. Established DAAP clients already
 	 * do this leading to an absence of sorting functionality in DAAP servers. */
 	priv->playlists = g_slist_sort (priv->playlists, compare_playlists_by_name);
@@ -1222,7 +1330,11 @@
 	}
 	rb_profile_end ("handling playlist entries");
 
-	playlist->uris = g_list_reverse (playlist_uris);
+        if (playlist->uris) {
+		playlist->uris = g_list_prepend(playlist->uris, playlist_uris);
+        } else {
+		playlist->uris = playlist_uris;
+	}
 	rb_daap_connection_state_done (connection, TRUE);
 }
 
@@ -1406,6 +1518,16 @@
 		   hasn't returned yet so we need to force the connection
 		   to finish */
 		priv->state = DAAP_DONE;
+		if (priv->message) {
+			// Set any outstanding message to timeout
+			soup_message_set_status(priv->message, 408);
+#if defined(HAVE_LIBSOUP_2_4)
+			soup_session_cancel_message(priv->session, priv->message, SOUP_STATUS_CANCELLED);
+#else
+			soup_session_cancel_message(priv->session, priv->message);
+#endif
+			priv->message = NULL;
+		}
 		GDK_THREADS_LEAVE ();
 		rb_daap_connection_finish (connection);
 		GDK_THREADS_ENTER ();
@@ -1429,8 +1551,19 @@
 		rb_daap_connection_finish (connection);
 		GDK_THREADS_ENTER ();
 	} else {
-		priv->state = DAAP_LOGOUT;
+		if (priv->message) {
+			// Set any outstanding message to timeout
+			soup_message_set_status(priv->message, 408);
+#if defined(HAVE_LIBSOUP_2_4)
+			soup_session_cancel_message(priv->session, priv->message, SOUP_STATUS_CANCELLED);
+#else
+			soup_session_cancel_message(priv->session, priv->message);
+#endif
+			priv->message = NULL;
+		}
 
+		priv->state = DAAP_DONE;
+
 		priv->do_something_id = g_idle_add ((GSourceFunc) rb_daap_connection_do_something, connection);
 	}
 }
@@ -1450,14 +1583,14 @@
 		switch (priv->state) {
 		case DAAP_GET_PLAYLISTS:
 			if (priv->playlists == NULL)
-				priv->state = DAAP_DONE;
+				priv->state = DAAP_GET_REVISION_NUMBER;
 			else
 				priv->state = DAAP_GET_PLAYLIST_ENTRIES;
 			break;
 		case DAAP_GET_PLAYLIST_ENTRIES:
 			/* keep reading playlists until we've got them all */
 			if (++priv->reading_playlist >= g_slist_length (priv->playlists))
-				priv->state = DAAP_DONE;
+				priv->state = DAAP_GET_REVISION_NUMBER;
 			break;
 
 		case DAAP_LOGOUT:
@@ -1546,12 +1679,20 @@
 			/* FIXME: set state back to GET_PASSWORD to try again */
 			rb_daap_connection_state_done (connection, FALSE);
 		}
+		priv->revision_number = 1;
+		priv->revision_delta = 0;
 
 		break;
 
 	case DAAP_GET_REVISION_NUMBER:
-		rb_debug ("Getting DAAP server database revision number");
-		path = g_strdup_printf ("/update?session-id=%u&revision-number=1", priv->session_id);
+		if (priv->revision_delta > 0) {
+			path = g_strdup_printf ("/update?session-id=%u&revision-number=%d&delta=%d", 
+						priv->session_id, priv->revision_number, priv->revision_delta);
+		} else {
+			path = g_strdup_printf ("/update?session-id=%u&revision-number=%d", 
+						priv->session_id, priv->revision_number);
+		}
+		rb_debug ("Getting DAAP server database revision number %s", path);
 		if (! http_get (connection, path, TRUE, priv->daap_version, 0, FALSE,
 			       (RBDAAPResponseHandler) handle_update, FALSE)) {
 			rb_debug ("Could not get server database revision number");
@@ -1562,8 +1703,13 @@
 
 	case DAAP_GET_DB_INFO:
 		rb_debug ("Getting DAAP database info");
-		path = g_strdup_printf ("/databases?session-id=%u&revision-number=%d",
-					priv->session_id, priv->revision_number);
+		if (priv->revision_delta > 0) {
+			path = g_strdup_printf ("/databases?session-id=%u&revision-number=%d&delta=%d",
+						priv->session_id, priv->revision_number, priv->revision_delta);
+		} else {
+			path = g_strdup_printf ("/databases?session-id=%u&revision-number=%d",
+						priv->session_id, priv->revision_number);
+		}
 		if (! http_get (connection, path, TRUE, priv->daap_version, 0, FALSE,
 			       (RBDAAPResponseHandler) handle_database_info, FALSE)) {
 			rb_debug ("Could not get DAAP database info");
@@ -1574,15 +1720,28 @@
 
 	case DAAP_GET_SONGS:
 		rb_debug ("Getting DAAP song listing");
-		path = g_strdup_printf ("/databases/%i/items?session-id=%u&revision-number=%i"
-				        "&meta=dmap.itemid,dmap.itemname,daap.songalbum,"
-					"daap.songartist,daap.daap.songgenre,daap.songsize,"
-					"daap.songtime,daap.songtrackcount,daap.songtracknumber,"
-					"daap.songyear,daap.songformat,daap.songgenre,"
-					"daap.songbitrate,daap.songdiscnumber,daap.songdataurl",
-					priv->database_id,
-					priv->session_id,
-					priv->revision_number);
+		if (priv->revision_delta > 0) {
+			path = g_strdup_printf ("/databases/%i/items?session-id=%u&revision-number=%i&delta=%d"
+				        	"&meta=dmap.itemid,dmap.itemname,daap.songalbum,"
+						"daap.songartist,daap.daap.songgenre,daap.songsize,"
+						"daap.songtime,daap.songtrackcount,daap.songtracknumber,"
+						"daap.songyear,daap.songformat,daap.songgenre,"
+						"daap.songbitrate,daap.songdiscnumber,daap.songdataurl",
+						priv->database_id,
+						priv->session_id,
+						priv->revision_number,
+						priv->revision_delta);
+		} else {
+			path = g_strdup_printf ("/databases/%i/items?session-id=%u&revision-number=%i"
+				        	"&meta=dmap.itemid,dmap.itemname,daap.songalbum,"
+						"daap.songartist,daap.daap.songgenre,daap.songsize,"
+						"daap.songtime,daap.songtrackcount,daap.songtracknumber,"
+						"daap.songyear,daap.songformat,daap.songgenre,"
+						"daap.songbitrate,daap.songdiscnumber,daap.songdataurl",
+						priv->database_id,
+						priv->session_id,
+						priv->revision_number);
+		}
 		if (! http_get (connection, path, TRUE, priv->daap_version, 0, FALSE,
 			       (RBDAAPResponseHandler) handle_song_listing, TRUE)) {
 			rb_debug ("Could not get DAAP song listing");
@@ -1593,10 +1752,18 @@
 
 	case DAAP_GET_PLAYLISTS:
 		rb_debug ("Getting DAAP playlists");
-		path = g_strdup_printf ("/databases/%d/containers?session-id=%u&revision-number=%d",
-					priv->database_id,
-					priv->session_id,
-					priv->revision_number);
+		if (priv->revision_delta > 0) {
+			path = g_strdup_printf ("/databases/%d/containers?session-id=%u&revision-number=%d&delta=%d",
+						priv->database_id,
+						priv->session_id,
+						priv->revision_number,
+						priv->revision_delta);
+		} else {
+			path = g_strdup_printf ("/databases/%d/containers?session-id=%u&revision-number=%d",
+						priv->database_id,
+						priv->session_id,
+						priv->revision_number);
+		}
 		if (! http_get (connection, path, TRUE, priv->daap_version, 0, FALSE,
 			       (RBDAAPResponseHandler) handle_playlists, TRUE)) {
 			rb_debug ("Could not get DAAP playlists");
@@ -1612,10 +1779,19 @@
 								     priv->reading_playlist);
 			g_assert (playlist);
 			rb_debug ("Reading DAAP playlist %d entries", priv->reading_playlist);
-			path = g_strdup_printf ("/databases/%d/containers/%d/items?session-id=%u&revision-number=%d&meta=dmap.itemid",
-						priv->database_id,
-						playlist->id,
-						priv->session_id, priv->revision_number);
+			if (priv->revision_delta > 0) {
+				path = g_strdup_printf ("/databases/%d/containers/%d/items?session-id=%u&revision-number=%d&delta=%d&meta=dmap.itemid",
+							priv->database_id,
+							playlist->id,
+							priv->session_id, 
+							priv->revision_number, 
+							priv->revision_delta);
+			} else {
+				path = g_strdup_printf ("/databases/%d/containers/%d/items?session-id=%u&revision-number=%d&meta=dmap.itemid",
+							priv->database_id,
+							playlist->id,
+							priv->session_id, priv->revision_number);
+			}
 			if (! http_get (connection, path, TRUE, priv->daap_version, 0, FALSE,
 				       (RBDAAPResponseHandler) handle_playlist_entries, TRUE)) {
 				rb_debug ("Could not get entries for DAAP playlist %d",
@@ -1699,7 +1875,12 @@
 	}
 
 	if (bytes != 0) {
-		g_string_append_printf (headers,"Range: bytes=%"G_GINT64_FORMAT"-\r\n", bytes);
+		if ( bytes > 2 ) {
+		    g_string_append_printf (headers,"Range: bytes=%"G_GINT64_FORMAT"-\r\n", bytes);
+		} else {
+		    gint64 start = 0, count = 1;
+		    g_string_append_printf (headers,"Range: bytes=%"G_GINT64_FORMAT"-%"G_GINT64_FORMAT"\r\n", start, count);
+   		}
 	}
 
 	s = headers->str;
Index: plugins/daap/rb-daap-source.c
===================================================================
--- plugins/daap/rb-daap-source.c	(revision 5895)
+++ plugins/daap/rb-daap-source.c	(revision 7205)
@@ -67,6 +67,7 @@
 static char * rb_daap_source_get_paned_key (RBBrowserSource *source);
 static void rb_daap_source_get_status (RBSource *source, char **text, char **progress_text, float *progress);
 static char * rb_daap_source_get_playback_uri (RhythmDBEntry *entry, gpointer data);
+static void _add_location_to_playlist (RBRefString *uri, RBStaticPlaylistSource *source);
 
 #define CONF_STATE_SORTING CONF_PREFIX "/state/daap/sorting"
 #define CONF_STATE_PANED_POSITION CONF_PREFIX "/state/daap/paned_position"
@@ -424,6 +425,72 @@
 		source->priv->connection_status = _("Connecting to music share");
 		break;
 	case DAAP_GET_REVISION_NUMBER:
+		{
+			RBDAAPSource *daap_source = RB_DAAP_SOURCE (source);
+			RBShell *shell = NULL;
+			GSList *playlists;
+			GSList *l;
+			GSList *ps;
+			RhythmDBEntryType entry_type;
+
+			g_object_get (daap_source,
+		      		"shell", &shell,
+		      		"entry-type", &entry_type,
+		      		NULL);
+			playlists = rb_daap_connection_get_playlists (RB_DAAP_CONNECTION (daap_source->priv->connection));
+			for (l = playlists; l != NULL; l = g_slist_next (l)) {
+				RBDAAPPlaylist *playlist = l->data;
+				RBSource *playlist_source;
+		
+				// Determine if this playlist is already in the playlist sources
+				for (ps = daap_source->priv->playlist_sources; ps!= NULL; ps = ps->next) {
+					RBSource *playlist_source = RB_SOURCE (ps->data);
+					char *name;
+
+					g_object_get (playlist_source, "name", &name, NULL);
+					if ( strcmp(playlist->name, name) == 0 ) {
+						g_free (name);
+						break;
+					}
+					g_free (name);
+				}
+
+				if ( ps != NULL ) {
+					playlist_source = RB_SOURCE(ps->data);
+					g_list_foreach (playlist->uris, (GFunc)_add_location_to_playlist, playlist_source);
+				} else {
+					playlist_source = rb_static_playlist_source_new (shell, playlist->name, FALSE, entry_type);
+					g_list_foreach (playlist->uris, (GFunc)_add_location_to_playlist, playlist_source);
+
+					rb_shell_append_source (shell, playlist_source, RB_SOURCE (daap_source));
+					daap_source->priv->playlist_sources = g_slist_prepend (daap_source->priv->playlist_sources, playlist_source);
+				}
+			}
+
+			// Now remove any deleted playlists
+			for (ps = daap_source->priv->playlist_sources; ps!= NULL; ps = ps->next) {
+				RBSource *playlist_source = RB_SOURCE (ps->data);
+				char *name = NULL;
+
+				g_object_get (playlist_source, "name", &name, NULL);
+
+				for (l = playlists; l != NULL; l = g_slist_next (l)) {
+					RBDAAPPlaylist *playlist = l->data;
+					if ( strcmp(playlist->name, name) == 0 ) {
+						break;
+					}
+				}
+				g_free (name);
+
+				if (l == NULL) {
+					rb_source_delete_thyself (playlist_source);
+					daap_source->priv->playlist_sources = g_slist_remove (daap_source->priv->playlist_sources, playlist_source);
+				}
+			}
+			g_object_unref (shell);
+			g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
+		}
+		break;
 	case DAAP_GET_DB_INFO:
 	case DAAP_GET_SONGS:
 	case DAAP_GET_PLAYLISTS:
@@ -495,6 +562,10 @@
 static void
 _add_location_to_playlist (RBRefString *uri, RBStaticPlaylistSource *source)
 {
+	if (uri == NULL) {
+		rb_debug("note: _add_location_to_playlist URI==NULL\n");
+		return;
+	}
 	rb_static_playlist_source_add_location (source, rb_refstring_get (uri), -1);
 }
 
@@ -608,6 +679,8 @@
 
 	rb_debug ("DAAP source disconnected");
 
+	daap_source->priv->disconnecting = FALSE;
+
 	release_connection (daap_source);
 
 	g_object_unref (source);
Index: plugins/daap/rb-daap-mdns-browser-avahi.c
===================================================================
--- plugins/daap/rb-daap-mdns-browser-avahi.c	(revision 5895)
+++ plugins/daap/rb-daap-mdns-browser-avahi.c	(revision 7205)
@@ -161,6 +161,7 @@
 		char    *name = NULL;
 		char     host [AVAHI_ADDRESS_STR_MAX];
 		gboolean pp = FALSE;
+		gboolean localok = FALSE;
 
 		if (text) {
 			AvahiStringList *l;
@@ -182,6 +183,8 @@
 					}
 				} else if (strcmp (key, "Machine Name") == 0) {
 					name = g_strdup (value);
+				} else if (strcmp (key, "Machine ID") == 0) {
+					localok = TRUE;
 				}
 
 				g_free (key);
@@ -193,16 +196,32 @@
 			name = g_strdup (service_name);
 		}
 
+
+		// test if the service is local
+		gboolean local;
+#ifdef HAVE_AVAHI_0_5
+		local = avahi_client_is_service_local (service_resolver->priv->client, interface, protocol, name, type, domain);
+#endif
+#ifdef HAVE_AVAHI_0_6
+		local = ((flags & AVAHI_LOOKUP_RESULT_LOCAL) != 0);
+#endif
 		avahi_address_snprint (host, AVAHI_ADDRESS_STR_MAX, address);
 
-		g_signal_emit (browser,
-			       signals [SERVICE_ADDED],
-			       0,
-			       service_name,
-			       name,
-			       host,
-			       port,
-			       pp);
+		if (localok || !local) {
+			if (local) {
+				rb_debug("Allowing local service: '%s'\n", name);
+			}
+			g_signal_emit (browser,
+				       signals [SERVICE_ADDED],
+				       0,
+				       service_name,
+				       name,
+				       host,
+				       port,
+				       pp);
+		} else {
+			rb_debug("Excluding local service: '%s'\n", name);
+		}
 
 		g_free (name);
 	}
@@ -266,6 +285,7 @@
 #endif
 	   RBDaapMdnsBrowser     *browser)
 {
+#ifdef DISABLE_LOCAL_SHARING
 	gboolean local;
 
 #ifdef HAVE_AVAHI_0_5
@@ -278,6 +298,7 @@
 		rb_debug ("Ignoring local service %s", name);
 		return;
 	}
+#endif
 
 	if (event == AVAHI_BROWSER_NEW) {
 		browser_add_service (browser, name);
Index: plugins/daap/rb-daap-src.c
===================================================================
--- plugins/daap/rb-daap-src.c	(revision 5895)
+++ plugins/daap/rb-daap-src.c	(revision 7205)
@@ -316,17 +316,14 @@
 		}
 	}
 
-	while (bytes_read < count) {
-		ssize_t ret = read (src->sock_fd, buf + bytes_read, count - bytes_read);
+	ssize_t ret = read (src->sock_fd, buf + bytes_read, count - bytes_read);
 
-		if (ret < 0) {
-			GST_WARNING ("error while reading: %s", g_strerror (errno));
-			return ret;
-		}
-		if (ret == 0)
-			break;
-		bytes_read += ret;
+	if (ret < 0) {
+		GST_WARNING ("error while reading: %s", g_strerror (errno));
+		return ret;
 	}
+	if (ret > 0)
+	    bytes_read += ret;
 
 	GST_DEBUG_OBJECT (src, "read %d bytes succesfully", bytes_read);
 	return bytes_read;
@@ -423,7 +420,7 @@
 }
 
 static gboolean
-rb_daap_src_open (RBDAAPSrc *src)
+rb_daap_src_open (RBDAAPSrc *src, gboolean headersonly)
 {
 	int ret;
 	struct sockaddr_in server;
@@ -493,6 +490,9 @@
 		g_warning ("Unable to lookup source for URI: %s", src->daap_uri);
 		return FALSE;
 	}
+	if (headersonly) {
+		src->seek_bytes = 2;
+	}
 
 	/* The following can fail if the source is no longer connected */
 	headers = rb_daap_source_get_headers (source, src->daap_uri, src->seek_bytes);
@@ -659,7 +659,7 @@
 }
 
 static gboolean
-rb_daap_src_start (GstBaseSrc *bsrc)
+rb_daap_src_start_ex (GstBaseSrc *bsrc, gboolean headersonly)
 {
 	RBDAAPSrc *src = RB_DAAP_SRC (bsrc);
 	if (src->sock_fd != -1) {
@@ -668,7 +668,7 @@
 
 	src->curoffset = 0;
 
-	if (rb_daap_src_open (src)) {
+	if (rb_daap_src_open (src, headersonly)) {
 		src->buffer = src->buffer_base;
 		src->curoffset = src->seek_bytes;
 		if (src->chunked) {
@@ -682,6 +682,12 @@
 }
 
 static gboolean
+rb_daap_src_start (GstBaseSrc *bsrc)
+{
+	return rb_daap_src_start_ex (bsrc, TRUE);
+}
+
+static gboolean
 rb_daap_src_stop (GstBaseSrc *bsrc)
 {
 	/* don't do anything - this seems to get called during setup, but
@@ -703,7 +709,7 @@
 			close (src->sock_fd);
 			src->sock_fd = -1;
 		}
-		if (!rb_daap_src_start (GST_BASE_SRC (src)))
+		if (!rb_daap_src_start_ex (GST_BASE_SRC (src), FALSE))
 			return GST_FLOW_ERROR;
 		src->do_seek = FALSE;
 	}
