[libdmapsharing] Refactor DMAPShare, DAAPShare and DPAPShare to increase code reuse.



commit 557dd4970f77105556d90b8e43e892bc3c32efaf
Author: W. Michael Petullo <mike flyn org>
Date:   Mon May 24 11:31:25 2010 -0500

    Refactor DMAPShare, DAAPShare and DPAPShare to increase code reuse.
    
    This was done to support Alexandre Rosenfeld's Google Summer of Code
    effort to add DACP support to libdmapsharing. It should now be easier
    to implement DACPShare because more code has been moved to DMAPShare. The
    lines of code in DAAPShare and DPAPShare have been reduced by several
    hundred lines. I would expect a similar effect on DACPShare.
    Signed-off-by: W. Michael Petullo <mike flyn org>

 ChangeLog                   |    5 +
 libdmapsharing/daap-share.c |  536 +++++++++++++------------------------------
 libdmapsharing/dmap-db.c    |    2 +-
 libdmapsharing/dmap-db.h    |    2 +-
 libdmapsharing/dmap-share.c |  410 +++++++++++++++++++++++++++++++--
 libdmapsharing/dmap-share.h |   52 ++++-
 libdmapsharing/dpap-share.c |  373 +++++-------------------------
 7 files changed, 653 insertions(+), 727 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 5a298e4..9041915 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+24 May 2010 W. Michael Petullo <mike flyn org>
+
+	* Refactor DMAPShare, DAAPShare and DPAPShare to move code
+	to DMAPShare and increase code reuse.
+
 22 May 2010 W. Michael Petullo <mike flyn org>
 
 	* Add new to_blob/new_from_blob interface to D[AP]PRecord.
diff --git a/libdmapsharing/daap-share.c b/libdmapsharing/daap-share.c
index 6a8f867..744dd3c 100644
--- a/libdmapsharing/daap-share.c
+++ b/libdmapsharing/daap-share.c
@@ -61,35 +61,36 @@ void daap_share_server_info (DMAPShare         *share,
 			     const char        *path,
 			     GHashTable        *query,
 			     SoupClientContext *context);
-void daap_share_databases (DMAPShare         *share,
-			     SoupServer        *server,
-	  		     SoupMessage       *message,
-			     const char        *path,
-			     GHashTable        *query,
-			     SoupClientContext *context);
-void daap_share_message_add_standard_headers (SoupMessage *message);
+void daap_share_message_add_standard_headers (DMAPShare *share,
+					      SoupMessage *message);
+static void databases_browse_xxx (DMAPShare *share,
+				  SoupServer *server,
+				  SoupMessage *msg,
+				  const char *path,
+				  GHashTable *query,
+				  SoupClientContext *context);
+static void databases_items_xxx (DMAPShare *share,
+				 SoupServer *server,
+				 SoupMessage *msg,
+				 const char *path,
+				 GHashTable *query,
+				 SoupClientContext *context);
+static struct DMAPMetaDataMap *get_meta_data_map (DMAPShare *share);
+static void add_entry_to_mlcl (gpointer id,
+			       DMAPRecord *record,
+			       gpointer mb);
 
 #define DAAP_TYPE_OF_SERVICE "_daap._tcp"
 #define DAAP_PORT 3689
 
 struct DAAPSharePrivate {
-	/* db things */
-	DMAPDb *db;
-	DMAPContainerDb *container_db;
-
-	/* FIXME: eventually, this should be determined dynamically, based
-	 * on what client has connected and its supported mimetypes.
-	 */
-	gchar *transcode_mimetype;
+	gchar unused;
 };
 
 #define DAAP_SHARE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TYPE_DAAP_SHARE, DAAPSharePrivate))
 
 enum {
 	PROP_0,
-	PROP_DB,
-	PROP_CONTAINER_DB,
-	PROP_TRANSCODE_MIMETYPE
 };
 
 /* Provide two items as user_data to callback. */
@@ -114,35 +115,14 @@ daap_share_class_init (DAAPShareClass *klass)
 	object_class->set_property = daap_share_set_property;
 	object_class->dispose = daap_share_dispose;
 
-	parent_class->get_desired_port    = daap_share_get_desired_port;
-	parent_class->get_type_of_service = daap_share_get_type_of_service;
+	parent_class->get_desired_port     = daap_share_get_desired_port;
+	parent_class->get_type_of_service  = daap_share_get_type_of_service;
 	parent_class->message_add_standard_headers = daap_share_message_add_standard_headers;
-	parent_class->server_info         = daap_share_server_info;
-	parent_class->databases           = daap_share_databases;
-
-	/* FIXME?: */
-	g_object_class_install_property (object_class,
-                                         PROP_DB,
-                                         g_param_spec_pointer ("db",
-                                                              "DB",
-                                                              "DB object",
-                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
-	g_object_class_install_property (object_class,
-                                         PROP_CONTAINER_DB,
-                                         g_param_spec_pointer ("container-db",
-                                                              "Container DB",
-                                                              "Container DB object",
-                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
-	/* FIXME?: */
-	g_object_class_install_property (object_class,
-                                         PROP_TRANSCODE_MIMETYPE,
-                                         g_param_spec_string ("transcode-mimetype",
-                                                             "Transcode mimetype",
-							     "Set mimetype of stream after transcoding",
-                                                             NULL,
-                                                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+	parent_class->get_meta_data_map    = get_meta_data_map;
+	parent_class->add_entry_to_mlcl    = add_entry_to_mlcl;
+	parent_class->databases_browse_xxx = databases_browse_xxx;
+	parent_class->databases_items_xxx  = databases_items_xxx;
+	parent_class->server_info          = daap_share_server_info;
 
 	g_type_class_add_private (klass, sizeof (DAAPSharePrivate));
 }
@@ -163,16 +143,6 @@ daap_share_set_property (GObject *object,
 	DAAPShare *share = DAAP_SHARE (object);
 
 	switch (prop_id) {
-	/* FIXME: */
-	case PROP_DB:
-		share->priv->db = (DMAPDb *) g_value_get_pointer (value);
-		break;
-	case PROP_CONTAINER_DB:
-		share->priv->container_db = (DMAPContainerDb *) g_value_get_pointer (value);
-		break;
-	case PROP_TRANSCODE_MIMETYPE:
-		share->priv->transcode_mimetype = (gchar *) g_value_dup_string (value);
-		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -188,15 +158,6 @@ daap_share_get_property (GObject *object,
 	DAAPShare *share = DAAP_SHARE (object);
 
 	switch (prop_id) {
-	case PROP_DB:
-		g_value_set_pointer (value, share->priv->db);
-		break;
-	case PROP_CONTAINER_DB:
-		g_value_set_pointer (value, share->priv->container_db);
-		break;
-	case PROP_TRANSCODE_MIMETYPE:
-		g_value_set_string (value, share->priv->transcode_mimetype);
-		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -247,13 +208,11 @@ daap_share_new (const char *name,
 }
 
 void
-daap_share_message_add_standard_headers (SoupMessage *message)
+daap_share_message_add_standard_headers (DMAPShare *share, SoupMessage *message)
 {
 	soup_message_headers_append (message->response_headers, "DMAP-Server", "libdmapsharing" VERSION);
 }
 
-#define DAAP_STATUS_OK 200
-
 #define DMAP_VERSION 2.0
 #define DAAP_VERSION 3.0
 #define DAAP_TIMEOUT 1800
@@ -304,7 +263,7 @@ daap_share_server_info (DMAPShare *share,
 	g_object_get ((gpointer) share, "name", &nameprop, NULL);
 
 	msrv = dmap_structure_add (NULL, DMAP_CC_MSRV);
-	dmap_structure_add (msrv, DMAP_CC_MSTT, (gint32) DAAP_STATUS_OK);
+	dmap_structure_add (msrv, DMAP_CC_MSTT, (gint32) DMAP_STATUS_OK);
 	dmap_structure_add (msrv, DMAP_CC_MPRO, (gdouble) DAAP_VERSION);
 	dmap_structure_add (msrv, DMAP_CC_APRO, (gdouble) DAAP_VERSION);
 	/* 2/3 is for itunes 4.8 (at least).  its determined by the
@@ -417,7 +376,8 @@ static struct DMAPMetaDataMap meta_data_map[] = {
     	{"daap.songyear",		SONG_YEAR},
 	{"daap.sortalbum",		SONG_SORT_ALBUM},
 	{"daap.sortartist",		SONG_SORT_ARTIST},
-	{"com.apple.itunes.has-video",	SONG_HAS_VIDEO}};
+	{"com.apple.itunes.has-video",	SONG_HAS_VIDEO},
+	{ NULL,				0}};
 
 #define DAAP_ITEM_KIND_AUDIO 2
 #define DAAP_SONG_DATA_KIND_NONE 0
@@ -461,7 +421,7 @@ chunked_message_finished (SoupMessage *message, ChunkData *cd)
 }
 
 static void
-send_chunked_file (SoupServer *server, SoupMessage *message, DAAPRecord *record, guint64 filesize, guint64 offset, gchar *transcode_mimetype)
+send_chunked_file (SoupServer *server, SoupMessage *message, DAAPRecord *record, guint64 filesize, guint64 offset, const gchar *transcode_mimetype)
 {
 	GInputStream *stream;
 	gboolean has_video;
@@ -566,11 +526,12 @@ send_chunked_file (SoupServer *server, SoupMessage *message, DAAPRecord *record,
 }
 
 static void
-add_entry_to_mlcl (gpointer id, DMAPRecord *record, gpointer _mb)
+add_entry_to_mlcl (gpointer id,
+		   DMAPRecord *record,
+		   gpointer _mb)
 {
 	GNode *mlit;
 	struct MLCL_Bits *mb;
-
 	mb = (struct MLCL_Bits *) _mb;
 	mlit = dmap_structure_add (mb->mlcl, DMAP_CC_MLIT);
 
@@ -712,7 +673,6 @@ add_entry_to_mlcl (gpointer id, DMAPRecord *record, gpointer _mb)
 			g_warning ("Sort album requested but not available");
 	}
 
-
 	return;
 }
 
@@ -743,6 +703,16 @@ album_tabulator (gpointer id, DMAPRecord *record, GHashTable *ht)
 		g_hash_table_insert (ht, (gchar *) album, NULL);
 }
 
+static void
+add_to_category_listing (gpointer key, gpointer value, gpointer user_data)
+{
+	GNode *mlit;
+	GNode *node = (GNode *) user_data;
+
+	mlit = dmap_structure_add (node, DMAP_CC_MLIT);
+	dmap_structure_add (mlit, DMAP_RAW, (char *) key);
+}
+
 static gchar *
 get_album (DAAPRecord *record)
 {
@@ -859,226 +829,13 @@ free_filter (GSList *filter)
 }
 
 static void
-add_to_category_listing (gpointer key, gpointer value, gpointer user_data)
-{
-	GNode *mlit;
-	GNode *node = (GNode *) user_data;
-
-	mlit = dmap_structure_add (node, DMAP_CC_MLIT);
-	dmap_structure_add (mlit, DMAP_RAW, (char *) key);
-}
-
-static void
-debug_param (gpointer key, gpointer val, gpointer user_data)
-{
-	g_debug ("%s %s", (char *) key, (char *) val);
-}
-
-void
-daap_share_databases (DMAPShare *share,
-	      SoupServer        *server,
-	      SoupMessage       *message,
-	      const char        *path,
-	      GHashTable        *query,
-	      SoupClientContext *context)
-
+databases_browse_xxx (DMAPShare *share,
+		      SoupServer *server,
+		      SoupMessage *msg,
+		      const char *path,
+		      GHashTable *query,
+		      SoupClientContext *context)
 {
-	const char *rest_of_path;
-
-	g_debug ("Path is %s.", path);
-	g_hash_table_foreach (query, debug_param, NULL);
-
-	if (! _dmap_share_session_id_validate (share, context, message, query, NULL)) {
-		soup_message_set_status (message, SOUP_STATUS_FORBIDDEN);
-		return;
-	}
-
-	rest_of_path = strchr (path + 1, '/');
-
-	if (rest_of_path == NULL) {
-	/* AVDB server databases
-	 * 	MSTT status
-	 * 	MUTY update type
-	 * 	MTCO specified total count
-	 * 	MRCO returned count
-	 * 	MLCL listing
-	 * 		MLIT listing item
-	 * 			MIID item id
-	 * 			MPER persistent id
-	 * 			MINM item name
-	 * 			MIMC item count
-	 * 			MCTC container count
-	 */
-		gchar *nameprop;
-		GNode *avdb;
-		GNode *mlcl;
-		GNode *mlit;
-
-		g_object_get ((gpointer) share, "name", &nameprop, NULL);
-
-		avdb = dmap_structure_add (NULL, DMAP_CC_AVDB);
-		dmap_structure_add (avdb, DMAP_CC_MSTT, (gint32) DAAP_STATUS_OK);
-		dmap_structure_add (avdb, DMAP_CC_MUTY, 0);
-		dmap_structure_add (avdb, DMAP_CC_MTCO, (gint32) 1);
-		dmap_structure_add (avdb, DMAP_CC_MRCO, (gint32) 1);
-		mlcl = dmap_structure_add (avdb, DMAP_CC_MLCL);
-		mlit = dmap_structure_add (mlcl, DMAP_CC_MLIT);
-		dmap_structure_add (mlit, DMAP_CC_MIID, (gint32) 1);
-		dmap_structure_add (mlit, DMAP_CC_MPER, (gint64) 1);
-		dmap_structure_add (mlit, DMAP_CC_MINM, nameprop);
-		dmap_structure_add (mlit, DMAP_CC_MIMC, dmap_db_count (DAAP_SHARE (share)->priv->db));
-		dmap_structure_add (mlit, DMAP_CC_MCTC, (gint32) 1);
-
-		_dmap_share_message_set_from_dmap_structure (share, message, avdb);
-		dmap_structure_destroy (avdb);
-
-		g_free (nameprop);
-	} else if (g_ascii_strcasecmp ("/1/items", rest_of_path) == 0) {
-	/* ADBS database songs
-	 * 	MSTT status
-	 * 	MUTY update type
-	 * 	MTCO specified total count
-	 * 	MRCO returned count
-	 * 	MLCL listing
-	 * 		MLIT
-	 * 			attrs
-	 * 		MLIT
-	 * 		...
-	 */
-		GNode *adbs;
-		gchar *record_query;
-		GHashTable *records = NULL;
-		gint32 num_songs;
-		struct MLCL_Bits mb = {NULL,0};
-
-		record_query = g_hash_table_lookup (query, "query");
-		if (record_query) {
-			GSList *filter_def;
-			filter_def = build_filter (record_query);
-			records = _dmap_db_apply_filter (DMAP_DB (DAAP_SHARE (share)->priv->db), filter_def);
-			g_debug ("Found %d records", g_hash_table_size (records));
-			num_songs = g_hash_table_size (records);
-			free_filter (filter_def);
-		} else {
-			num_songs = dmap_db_count (DAAP_SHARE (share)->priv->db);
-		}
-
-		mb.bits = _dmap_share_parse_meta (query, meta_data_map, G_N_ELEMENTS (meta_data_map));
-
-		adbs = dmap_structure_add (NULL, DMAP_CC_ADBS);
-		dmap_structure_add (adbs, DMAP_CC_MSTT, (gint32) DAAP_STATUS_OK);
-		dmap_structure_add (adbs, DMAP_CC_MUTY, 0);
-		dmap_structure_add (adbs, DMAP_CC_MTCO, (gint32) num_songs);
-		dmap_structure_add (adbs, DMAP_CC_MRCO, (gint32) num_songs);
-		mb.mlcl = dmap_structure_add (adbs, DMAP_CC_MLCL);
-
-		if (record_query) {
-			g_hash_table_foreach (records, (GHFunc) add_entry_to_mlcl, &mb);
-			/* Free hash table but not data: */
-			g_hash_table_destroy (records);
-		} else {
-			dmap_db_foreach (DAAP_SHARE (share)->priv->db, (GHFunc) add_entry_to_mlcl, &mb);
-		}
-
-		_dmap_share_message_set_from_dmap_structure (share, message, adbs);
-		dmap_structure_destroy (adbs);
-		adbs = NULL;
-	} else if (g_ascii_strcasecmp ("/1/containers", rest_of_path) == 0) {
-	/* APLY database playlists
-	 * 	MSTT status
-	 * 	MUTY update type
-	 * 	MTCO specified total count
-	 * 	MRCO returned count
-	 * 	MLCL listing
-	 * 		MLIT listing item
-	 * 			MIID item id
-	 * 			MPER persistent item id
-	 * 			MINM item name
-	 * 			MIMC item count
-	 * 			ABPL baseplaylist (only for base)
-	 * 		MLIT
-	 * 		...
-	 */
-		gchar *nameprop;
-		GNode *aply;
-		GNode *mlcl;
-		GNode *mlit;
-
-		g_object_get ((gpointer) share, "name", &nameprop, NULL);
-
-		aply = dmap_structure_add (NULL, DMAP_CC_APLY);
-		dmap_structure_add (aply, DMAP_CC_MSTT, (gint32) DAAP_STATUS_OK);
-		dmap_structure_add (aply, DMAP_CC_MUTY, 0);
-		dmap_structure_add (aply, DMAP_CC_MTCO, (gint32) dmap_container_db_count (DAAP_SHARE (share)->priv->container_db) + 1);
-		dmap_structure_add (aply, DMAP_CC_MRCO, (gint32) dmap_container_db_count (DAAP_SHARE (share)->priv->container_db) + 1);
-		mlcl = dmap_structure_add (aply, DMAP_CC_MLCL);
-
-		/* Base playlist: */
-		mlit = dmap_structure_add (mlcl, DMAP_CC_MLIT);
-		dmap_structure_add (mlit, DMAP_CC_MIID, (gint32) 1);
-		dmap_structure_add (mlit, DMAP_CC_MPER, (gint64) 1);
-		dmap_structure_add (mlit, DMAP_CC_MINM, nameprop);
-		dmap_structure_add (mlit, DMAP_CC_MIMC, dmap_db_count (DAAP_SHARE (share)->priv->db));
-		dmap_structure_add (mlit, DMAP_CC_ABPL, (gchar) 1);
-
-		dmap_container_db_foreach (DAAP_SHARE (share)->priv->container_db, (GHFunc) _dmap_share_add_playlist_to_mlcl, (gpointer) mlcl);
-
-		_dmap_share_message_set_from_dmap_structure (share, message, aply);
-		dmap_structure_destroy (aply);
-
-		g_free (nameprop);
-	} else if (g_ascii_strncasecmp ("/1/containers/", rest_of_path, 14) == 0) {
-	/* APSO playlist songs
-	 * 	MSTT status
-	 * 	MUTY update type
-	 * 	MTCO specified total count
-	 * 	MRCO returned count
-	 * 	MLCL listing
-	 * 		MLIT listing item
-	 * 			MIKD item kind
-	 * 			MIID item id
-	 * 			MCTI container item id
-	 * 		MLIT
-	 * 		...
-	 */
-		GNode *apso;
-		struct MLCL_Bits mb = {NULL,0};
-		gint pl_id = atoi (rest_of_path + 14);
-
-		mb.bits = _dmap_share_parse_meta (query, meta_data_map, G_N_ELEMENTS (meta_data_map));
-
-		apso = dmap_structure_add (NULL, DMAP_CC_APSO);
-		dmap_structure_add (apso, DMAP_CC_MSTT, (gint32) DAAP_STATUS_OK);
-		dmap_structure_add (apso, DMAP_CC_MUTY, 0);
-
-		if (pl_id == 1) {
-			gint32 num_songs = dmap_db_count (DAAP_SHARE (share)->priv->db);
-			dmap_structure_add (apso, DMAP_CC_MTCO, (gint32) num_songs);
-			dmap_structure_add (apso, DMAP_CC_MRCO, (gint32) num_songs);
-			mb.mlcl = dmap_structure_add (apso, DMAP_CC_MLCL);
-
-			dmap_db_foreach (DAAP_SHARE (share)->priv->db, (GHFunc) add_entry_to_mlcl, &mb);
-		} else {
-			DMAPContainerRecord *record;
-			const DMAPDb *entries;
-			guint num_songs;
-			
-			record = dmap_container_db_lookup_by_id (DAAP_SHARE (share)->priv->container_db, pl_id);
-			entries = dmap_container_record_get_entries (record);
-			num_songs = dmap_db_count (entries);
-			
-			dmap_structure_add (apso, DMAP_CC_MTCO, (gint32) num_songs);
-			dmap_structure_add (apso, DMAP_CC_MRCO, (gint32) num_songs);
-			mb.mlcl = dmap_structure_add (apso, DMAP_CC_MLCL);
-
-			dmap_db_foreach (entries, (GHFunc) add_entry_to_mlcl, &mb);
-
-			g_object_unref (record);
-		}
-
-		_dmap_share_message_set_from_dmap_structure (share, message, apso);
-		dmap_structure_destroy (apso);
-	} else if (g_ascii_strncasecmp ("/1/browse/", rest_of_path, 9) == 0) {
 	/* ABRO database browse
          *      MSTT status
          *      MUTY update type
@@ -1088,99 +845,120 @@ daap_share_databases (DMAPShare *share,
          *              MLIT listing item
          *              ...
          */
-                GNode *abro, *node;
-		gchar *filter;
-		GSList *filter_def;
-		GHashTable *filtered;
-		guint num_genre;
-		const gchar *browse_category;
-		GHashTable *category_items;
-		DMAPContentCode category_cc;
-
-		browse_category = rest_of_path + 10;
-		category_items = g_hash_table_new (g_str_hash, g_str_equal);
-
-		filter = g_hash_table_lookup (query, "filter");
-		filter_def = build_filter (filter);
-		filtered = _dmap_db_apply_filter (DMAP_DB (DAAP_SHARE (share)->priv->db), filter_def);
-
-		if (g_ascii_strcasecmp (browse_category, "genres") == 0) {
-			g_hash_table_foreach (filtered, (GHFunc) genre_tabulator, category_items);
-			category_cc = DMAP_CC_ABGN;
-		} else if (g_ascii_strcasecmp (browse_category, "artists") == 0) {
-			g_hash_table_foreach (filtered, (GHFunc) artist_tabulator, category_items);
-			category_cc = DMAP_CC_ABAR;
-		} else if (g_ascii_strcasecmp (browse_category, "albums") == 0) {
-			g_hash_table_foreach (filtered, (GHFunc) album_tabulator, category_items);
-			category_cc = DMAP_CC_ABAL;
-		} else {
-			g_warning ("Unsupported browse category: %s",
-				   browse_category);
-			goto _bad_category;
-		}
+	DMAPDb *db;
+	const gchar *rest_of_path;
+	GNode *abro, *node;
+	gchar *filter;
+	GSList *filter_def;
+	GHashTable *filtered;
+	guint num_genre;
+	const gchar *browse_category;
+	GHashTable *category_items;
+	DMAPContentCode category_cc;
 
-		abro = dmap_structure_add (NULL, DMAP_CC_ABRO);
-		dmap_structure_add (abro, DMAP_CC_MSTT, (gint32) DAAP_STATUS_OK);
-		dmap_structure_add (abro, DMAP_CC_MUTY, 0);
-
-		num_genre = g_hash_table_size (category_items);
-		dmap_structure_add (abro, DMAP_CC_MTCO, (gint32) num_genre);
-		dmap_structure_add (abro, DMAP_CC_MRCO, (gint32) num_genre);
-
-		node = dmap_structure_add (abro, category_cc);
-
-		g_hash_table_foreach (category_items,
-				      add_to_category_listing,
-				      node);
-
-		_dmap_share_message_set_from_dmap_structure (share, message, abro);
-                dmap_structure_destroy (abro);
-	_bad_category:
-		free_filter (filter_def);
-		/* Free's hash table but not data (points into real DB): */
-		g_hash_table_destroy (filtered);
-		g_hash_table_destroy (category_items);
-
-	} else if (g_ascii_strncasecmp ("/1/items/", rest_of_path, 9) == 0) {
-	/* just the file :) */
-		const gchar *id_str;
-		gint id;
-		const gchar *location;
-		const gchar *range_header;
-		guint64 filesize;
-		guint64 offset = 0;
-		DAAPRecord *record;
+	rest_of_path = strchr (path + 1, '/');
+	browse_category = rest_of_path + 10;
+	category_items = g_hash_table_new (g_str_hash, g_str_equal);
+
+	filter = g_hash_table_lookup (query, "filter");
+	filter_def = build_filter (filter);
+	g_object_get (share, "db", &db, NULL);
+	filtered = dmap_db_apply_filter (db, filter_def);
+
+	if (g_ascii_strcasecmp (browse_category, "genres") == 0) {
+		g_hash_table_foreach (filtered, (GHFunc) genre_tabulator, category_items);
+		category_cc = DMAP_CC_ABGN;
+	} else if (g_ascii_strcasecmp (browse_category, "artists") == 0) {
+		g_hash_table_foreach (filtered, (GHFunc) artist_tabulator, category_items);
+		category_cc = DMAP_CC_ABAR;
+	} else if (g_ascii_strcasecmp (browse_category, "albums") == 0) {
+		g_hash_table_foreach (filtered, (GHFunc) album_tabulator, category_items);
+		category_cc = DMAP_CC_ABAL;
+	} else {
+		g_warning ("Unsupported browse category: %s",
+			   browse_category);
+		goto _bad_category;
+	}
 
-		id_str = rest_of_path + 9;
-		id = atoi (id_str);
+	abro = dmap_structure_add (NULL, DMAP_CC_ABRO);
+	dmap_structure_add (abro, DMAP_CC_MSTT, (gint32) DMAP_STATUS_OK);
+	dmap_structure_add (abro, DMAP_CC_MUTY, 0);
 
-		record = DAAP_RECORD (dmap_db_lookup_by_id (DAAP_SHARE (share)->priv->db, id));
-		g_object_get (record, "location", &location, NULL);
-		g_object_get (record, "filesize", &filesize, NULL);
+	num_genre = g_hash_table_size (category_items);
+	dmap_structure_add (abro, DMAP_CC_MTCO, (gint32) num_genre);
+	dmap_structure_add (abro, DMAP_CC_MRCO, (gint32) num_genre);
+
+	node = dmap_structure_add (abro, category_cc);
 
-		daap_share_message_add_standard_headers (message);
-		soup_message_headers_append (message->response_headers, "Accept-Ranges", "bytes");
+	g_hash_table_foreach (category_items,
+			      add_to_category_listing,
+			      node);
 
-		range_header = soup_message_headers_get (message->request_headers, "Range");
-		if (range_header) {
-			const gchar *s;
-			gchar *content_range;
+	_dmap_share_message_set_from_dmap_structure (share, msg, abro);
+	dmap_structure_destroy (abro);
+_bad_category:
+	free_filter (filter_def);
+	/* Free's hash table but not data (points into real DB): */
+	g_hash_table_destroy (filtered);
+	g_hash_table_destroy (category_items);
+}
 
-			s = range_header + 6; /* bytes= */
-			offset = atoll (s);
+static void
+databases_items_xxx (DMAPShare *share,
+		     SoupServer *server,
+		     SoupMessage *msg,
+		     const char *path,
+		     GHashTable *query,
+		     SoupClientContext *context)
+{
+	DMAPDb *db;
+	const gchar *transcode_mimetype;
+	const gchar *rest_of_path;
+	const gchar *id_str;
+	gint id;
+	const gchar *location;
+	const gchar *range_header;
+	guint64 filesize;
+	guint64 offset = 0;
+	DAAPRecord *record;
 
-			content_range = g_strdup_printf ("bytes %" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, offset, filesize, filesize);
-			soup_message_headers_append (message->response_headers, "Content-Range", content_range);
-			g_debug ("Content range is %s.", content_range);
-			g_free (content_range);
-			soup_message_set_status (message, SOUP_STATUS_PARTIAL_CONTENT);
-		} else {
-			soup_message_set_status (message, SOUP_STATUS_OK);
-		}
-		send_chunked_file (server, message, record, filesize, offset, DAAP_SHARE (share)->priv->transcode_mimetype);
-		
-		g_object_unref (record);
+	rest_of_path = strchr (path + 1, '/');
+	id_str = rest_of_path + 9;
+	id = atoi (id_str);
+
+	g_object_get (share, "db", &db, NULL);
+	record = DAAP_RECORD (dmap_db_lookup_by_id (db, id));
+	g_object_get (record, "location", &location, NULL);
+	g_object_get (record, "filesize", &filesize, NULL);
+
+	DMAP_SHARE_GET_CLASS (share)->message_add_standard_headers
+				(share, msg);
+	soup_message_headers_append (msg->response_headers, "Accept-Ranges", "bytes");
+
+	range_header = soup_message_headers_get (msg->request_headers, "Range");
+	if (range_header) {
+		const gchar *s;
+		gchar *content_range;
+
+		s = range_header + 6; /* bytes= */
+		offset = atoll (s);
+
+		content_range = g_strdup_printf ("bytes %" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, offset, filesize, filesize);
+		soup_message_headers_append (msg->response_headers, "Content-Range", content_range);
+		g_debug ("Content range is %s.", content_range);
+		g_free (content_range);
+		soup_message_set_status (msg, SOUP_STATUS_PARTIAL_CONTENT);
 	} else {
-		g_warning ("Unhandled: %s\n", path);
+		soup_message_set_status (msg, SOUP_STATUS_OK);
 	}
+	g_object_get (share, "transcode-mimetype", &transcode_mimetype, NULL);
+	send_chunked_file (server, msg, record, filesize, offset, transcode_mimetype);
+	
+	g_object_unref (record);
+}
+
+static struct DMAPMetaDataMap *
+get_meta_data_map (DMAPShare *share)
+{
+	return meta_data_map;
 }
diff --git a/libdmapsharing/dmap-db.c b/libdmapsharing/dmap-db.c
index 29a4307..1cbe538 100644
--- a/libdmapsharing/dmap-db.c
+++ b/libdmapsharing/dmap-db.c
@@ -179,7 +179,7 @@ apply_filter (gpointer id, DMAPRecord *record, gpointer data)
 }
 
 GHashTable *
-_dmap_db_apply_filter (DMAPDb *db, GSList *filter_def)
+dmap_db_apply_filter (DMAPDb *db, GSList *filter_def)
 {
 	GHashTable *ht;
 	FilterData data;
diff --git a/libdmapsharing/dmap-db.h b/libdmapsharing/dmap-db.h
index 342b136..3679b88 100644
--- a/libdmapsharing/dmap-db.h
+++ b/libdmapsharing/dmap-db.h
@@ -143,7 +143,7 @@ gulong      dmap_db_count	    (const DMAPDb *db);
 
 gchar     **_dmap_db_strsplit_using_quotes (const gchar *str);
 
-GHashTable *_dmap_db_apply_filter    (DMAPDb *db, GSList *filter_def);
+GHashTable *dmap_db_apply_filter    (DMAPDb *db, GSList *filter_def);
 
 #endif /* __DMAP_DB_H */
 
diff --git a/libdmapsharing/dmap-share.c b/libdmapsharing/dmap-share.c
index 4e3e161..72de797 100644
--- a/libdmapsharing/dmap-share.c
+++ b/libdmapsharing/dmap-share.c
@@ -36,10 +36,10 @@
 #define TYPE_OF_SERVICE "_daap._tcp"
 #define STANDARD_DAAP_PORT 3689
 #define STANDARD_DPAP_PORT 8770
-#define DMAP_STATUS_OK 200
 #define DMAP_VERSION 2.0
 #define DAAP_VERSION 3.0
 #define DMAP_TIMEOUT 1800
+#define DMAP_STATUS_OK 200
 
 typedef enum {
 	DMAP_SHARE_AUTH_METHOD_NONE              = 0,
@@ -53,24 +53,36 @@ enum {
 	PROP_PASSWORD,
 	PROP_REVISION_NUMBER,
 	PROP_AUTH_METHOD,
+	PROP_DB,
+	PROP_CONTAINER_DB,
+	PROP_TRANSCODE_MIMETYPE
 };
 
 struct DMAPSharePrivate {
 	gchar *name;
 	guint port;
 	char *password;
+
+	/* FIXME: eventually, this should be determined dynamically, based
+	 * on what client has connected and its supported mimetypes.
+	 */
 	char *transcode_mimetype;
+
 	DMAPShareAuthMethod auth_method;
 
-	/* mdns/dns-sd publishing things */
+	/* mDNS/DNS-SD publishing things */
 	gboolean server_active;
 	gboolean published;
 	DmapMdnsPublisher *publisher;
 
-	/* http server things */
+	/* HTTP server things */
 	SoupServer *server;
 	guint revision_number;
 
+	/* The media database */
+	DMAPDb *db;
+	DMAPContainerDb *container_db;
+
 	GHashTable *session_ids;
 };
 
@@ -416,6 +428,16 @@ _dmap_share_set_property (GObject *object,
 	case PROP_PASSWORD:
 		_dmap_share_set_password (share, g_value_get_string (value));
 		break;
+	case PROP_DB:
+                share->priv->db = (DMAPDb *) g_value_get_pointer (value);
+                break;
+        case PROP_CONTAINER_DB:
+                share->priv->container_db = (DMAPContainerDb *) g_value_get_pointer (value);
+                break;
+        case PROP_TRANSCODE_MIMETYPE:
+		/* FIXME: get or dup? */
+                share->priv->transcode_mimetype = g_value_dup_string (value);
+                break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -447,6 +469,15 @@ _dmap_share_get_property (GObject *object,
 				  _dmap_share_get_auth_method
 					(DMAP_SHARE (object)));
 		break;
+	case PROP_DB:
+                g_value_set_pointer (value, share->priv->db);
+                break;
+        case PROP_CONTAINER_DB:
+                g_value_set_pointer (value, share->priv->container_db);
+                break;
+        case PROP_TRANSCODE_MIMETYPE:
+                g_value_set_string (value, share->priv->transcode_mimetype);
+                break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -487,19 +518,22 @@ dmap_share_class_init (DMAPShareClass *klass)
 	object_class->finalize     = _dmap_share_finalize;
 
 	/* Pure virtual methods: */
-	klass->get_desired_port    = NULL;
-	klass->get_type_of_service = NULL;
+	klass->get_desired_port     = NULL;
+	klass->get_type_of_service  = NULL;
 	klass->message_add_standard_headers = NULL;
-	klass->server_info         = NULL;
-	klass->content_codes       = _dmap_share_content_codes;
-	klass->login               = _dmap_share_login;
-	klass->logout              = _dmap_share_logout;
-	klass->update              = _dmap_share_update;
-	klass->databases           = NULL;
+	klass->get_meta_data_map    = NULL;
+	klass->add_entry_to_mlcl    = NULL;
+	klass->databases_browse_xxx = NULL;
+	klass->databases_items_xxx  = NULL;
+	klass->content_codes        = _dmap_share_content_codes;
+	klass->login                = _dmap_share_login;
+	klass->logout               = _dmap_share_logout;
+	klass->update               = _dmap_share_update;
 
 	/* Virtual methods: */
 	klass->published      = _dmap_share_published;
 	klass->name_collision = _dmap_share_name_collision;
+	klass->databases      = _dmap_share_databases;
 
 	g_object_class_install_property (object_class,
 					 PROP_NAME,
@@ -535,7 +569,27 @@ dmap_share_class_init (DMAPShareClass *klass)
 						DMAP_SHARE_AUTH_METHOD_PASSWORD,
 						0,
 						G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+                                         PROP_DB,
+                                         g_param_spec_pointer ("db",
+                                                              "DB",
+                                                              "DB object",
+                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+        g_object_class_install_property (object_class,
+                                         PROP_CONTAINER_DB,
+                                         g_param_spec_pointer ("container-db",
+                                                              "Container DB",
+                                                              "Container DB object",
+                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
+	g_object_class_install_property (object_class,
+                                         PROP_TRANSCODE_MIMETYPE,
+                                         g_param_spec_string ("transcode-mimetype",
+                                                             "Transcode mimetype",
+                                                             "Set mimetype of stream after transcoding",
+                                                             NULL,
+                                                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
 	g_type_class_add_private (klass, sizeof (DMAPSharePrivate));
 }
@@ -736,7 +790,8 @@ _dmap_share_message_set_from_dmap_structure (DMAPShare *share,
 	soup_message_set_response (message, "application/x-dmap-tagged",
 				   SOUP_MEMORY_TAKE, resp, length);
 
-	DMAP_SHARE_GET_CLASS (share)->message_add_standard_headers (message);
+	DMAP_SHARE_GET_CLASS (share)->message_add_standard_headers (share,
+								    message);
 
 	soup_message_set_status (message, SOUP_STATUS_OK);
 }
@@ -956,7 +1011,7 @@ _dmap_share_update (DMAPShare *share,
 }
 
 bitwise
-_dmap_share_parse_meta_str (const char *attrs, struct DMAPMetaDataMap *mdm, guint mdmlen)
+_dmap_share_parse_meta_str (const char *attrs, struct DMAPMetaDataMap *mdm)
 {
 	guint i;
 	bitwise bits = 0;
@@ -972,7 +1027,7 @@ _dmap_share_parse_meta_str (const char *attrs, struct DMAPMetaDataMap *mdm, guin
 		for (i = 0; attrsv[i]; i++) {
 			guint j;
 
-			for (j = 0; j < mdmlen; j++) {
+			for (j = 0; mdm[j].tag; j++) {
 				if (strcmp (mdm[j].tag, attrsv[i]) == 0) {
 					bits |= (((bitwise) 1) << mdm[j].md);
 				}
@@ -985,7 +1040,7 @@ _dmap_share_parse_meta_str (const char *attrs, struct DMAPMetaDataMap *mdm, guin
 }
 
 bitwise
-_dmap_share_parse_meta (GHashTable *query, struct DMAPMetaDataMap *mdm, guint mdmlen)
+_dmap_share_parse_meta (GHashTable *query, struct DMAPMetaDataMap *mdm)
 {
 	const gchar *attrs;
 
@@ -993,7 +1048,7 @@ _dmap_share_parse_meta (GHashTable *query, struct DMAPMetaDataMap *mdm, guint md
 	if (attrs == NULL) {
 		return 0;
 	}
-	return _dmap_share_parse_meta_str (attrs, mdm, mdmlen);
+	return _dmap_share_parse_meta_str (attrs, mdm);
 }
 
 void
@@ -1021,3 +1076,326 @@ _dmap_share_add_playlist_to_mlcl (gpointer id, DMAPContainerRecord *record, gpoi
 
 	return;
 } 
+
+/* FIXME: Handle ('...') and share code with DAAPShare. */
+static GSList *
+build_filter (gchar *filterstr)
+{
+	/* Produces a list of lists, each being a filter definition that may
+	 * be one or more filter criteria.
+	 */
+
+	/* A filter string looks like (iTunes):
+	 * 'daap.songgenre:Other'+'daap.songartist:Band'.
+	 * or (Roku):
+	 * 'daap.songgenre:Other' 'daap.songartist:Band'.
+	 * or
+         * 'dmap.itemid:1000'
+	 * or
+         * 'dmap.itemid:1000','dmap:itemid:1001'
+	 * or
+	 * 'daap.songgenre:Foo'+'daap.songartist:Bar'+'daap.songalbum:Baz'
+	 * or (iPhoto '09)
+	 * ('daap.idemid:1000','dmap:itemid:1001')
+         */
+
+	size_t len;
+	GSList *list = NULL;
+
+	g_debug ("Filter string is %s.", filterstr);
+
+	len = strlen (filterstr);
+	filterstr = *filterstr == '(' ? filterstr + 1 : filterstr;
+	/* FIXME: I thought this should be -1, but there is a trailing ' ' in iTunes '09: */
+	filterstr[len - 2] = filterstr[len - 2] == ')' ? 0x00 : filterstr[len - 2];
+
+	if (filterstr != NULL) {
+		int i;
+		gchar **t1 = g_strsplit (filterstr, ",", 0);
+
+		for (i = 0; t1[i]; i++) {
+			int j;
+			GSList *filter = NULL;
+			gchar **t2;
+
+			t2 = _dmap_db_strsplit_using_quotes (t1[i]);
+
+			for (j = 0; t2[j]; j++) {
+				FilterDefinition *def;
+				gchar **t3;
+
+				t3 = g_strsplit (t2[j], ":", 0);
+
+				if (g_strcasecmp ("dmap.itemid", t3[0]) == 0) {
+					def = g_new0 (FilterDefinition, 1);
+					def->value = g_strdup (t3[1]);
+					def->record_get_value = NULL;
+				} else {
+					g_warning ("Unknown category: %s", t3[0]);
+					def = NULL;
+				}
+
+				if (def != NULL)
+					filter = g_slist_append (filter, def);
+
+				g_strfreev (t3);
+			}
+
+			list = g_slist_append (list, filter);
+
+			g_strfreev (t2);
+		}
+		g_strfreev (t1);
+	}
+
+        return list;
+}
+
+static void
+free_filter (GSList *filter)
+{
+        GSList *ptr1, *ptr2;
+
+        for (ptr1 = filter; ptr1 != NULL; ptr1 = ptr1->next) {
+                for (ptr2 = ptr1->data; ptr2 != NULL; ptr2 = ptr2->next) {
+                        g_free (((FilterDefinition *) ptr2->data)->value);
+                        g_free (ptr2->data);
+                }
+        }
+}
+
+static void
+debug_param (gpointer key, gpointer val, gpointer user_data)
+{
+        g_debug ("%s %s", (char *) key, (char *) val);
+}
+
+void
+_dmap_share_databases (DMAPShare *share,
+		       SoupServer        *server,
+		       SoupMessage       *message,
+		       const char        *path,
+		       GHashTable        *query,
+		       SoupClientContext *context)
+
+{
+	const char *rest_of_path;
+
+	g_debug ("Path is %s.", path);
+	g_hash_table_foreach (query, debug_param, NULL);
+
+	if (! _dmap_share_session_id_validate (share, context, message, query, NULL)) {
+		soup_message_set_status (message, SOUP_STATUS_FORBIDDEN);
+		return;
+	}
+
+	rest_of_path = strchr (path + 1, '/');
+
+	if (rest_of_path == NULL) {
+	/* AVDB server databases
+	 * 	MSTT status
+	 * 	MUTY update type
+	 * 	MTCO specified total count
+	 * 	MRCO returned count
+	 * 	MLCL listing
+	 * 		MLIT listing item
+	 * 			MIID item id
+	 * 			MPER persistent id
+	 * 			MINM item name
+	 * 			MIMC item count
+	 * 			MCTC container count
+	 */
+		gchar *nameprop;
+		GNode *avdb;
+		GNode *mlcl;
+		GNode *mlit;
+
+		g_object_get ((gpointer) share, "name", &nameprop, NULL);
+
+		avdb = dmap_structure_add (NULL, DMAP_CC_AVDB);
+		dmap_structure_add (avdb, DMAP_CC_MSTT, (gint32) DMAP_STATUS_OK);
+		dmap_structure_add (avdb, DMAP_CC_MUTY, 0);
+		dmap_structure_add (avdb, DMAP_CC_MTCO, (gint32) 1);
+		dmap_structure_add (avdb, DMAP_CC_MRCO, (gint32) 1);
+		mlcl = dmap_structure_add (avdb, DMAP_CC_MLCL);
+		mlit = dmap_structure_add (mlcl, DMAP_CC_MLIT);
+		dmap_structure_add (mlit, DMAP_CC_MIID, (gint32) 1);
+		dmap_structure_add (mlit, DMAP_CC_MPER, (gint64) 1);
+		dmap_structure_add (mlit, DMAP_CC_MINM, nameprop);
+		dmap_structure_add (mlit, DMAP_CC_MIMC, dmap_db_count (share->priv->db));
+		dmap_structure_add (mlit, DMAP_CC_MCTC, (gint32) 1);
+
+		_dmap_share_message_set_from_dmap_structure (share, message, avdb);
+		dmap_structure_destroy (avdb);
+
+		g_free (nameprop);
+	} else if (g_ascii_strcasecmp ("/1/items", rest_of_path) == 0) {
+	/* ADBS database songs
+	 * 	MSTT status
+	 * 	MUTY update type
+	 * 	MTCO specified total count
+	 * 	MRCO returned count
+	 * 	MLCL listing
+	 * 		MLIT
+	 * 			attrs
+	 * 		MLIT
+	 * 		...
+	 */
+		GNode *adbs;
+		gchar *record_query;
+		GHashTable *records = NULL;
+		struct DMAPMetaDataMap *map;
+		gint32 num_songs;
+		struct MLCL_Bits mb = {NULL,0};
+
+		record_query = g_hash_table_lookup (query, "query");
+		if (record_query) {
+			GSList *filter_def;
+			filter_def = build_filter (record_query);
+			records = dmap_db_apply_filter (DMAP_DB (share->priv->db), filter_def);
+			g_debug ("Found %d records", g_hash_table_size (records));
+			num_songs = g_hash_table_size (records);
+			free_filter (filter_def);
+		} else {
+			num_songs = dmap_db_count (share->priv->db);
+		}
+
+		map = DMAP_SHARE_GET_CLASS (share)->get_meta_data_map (share);
+		mb.bits = _dmap_share_parse_meta (query, map);
+
+		adbs = dmap_structure_add (NULL, DMAP_CC_ADBS);
+		dmap_structure_add (adbs, DMAP_CC_MSTT, (gint32) DMAP_STATUS_OK);
+		dmap_structure_add (adbs, DMAP_CC_MUTY, 0);
+		dmap_structure_add (adbs, DMAP_CC_MTCO, (gint32) num_songs);
+		dmap_structure_add (adbs, DMAP_CC_MRCO, (gint32) num_songs);
+		mb.mlcl = dmap_structure_add (adbs, DMAP_CC_MLCL);
+
+		if (record_query) {
+			g_hash_table_foreach (records, (GHFunc) DMAP_SHARE_GET_CLASS (share)->add_entry_to_mlcl, &mb);
+			/* Free hash table but not data: */
+			g_hash_table_destroy (records);
+		} else {
+			dmap_db_foreach (share->priv->db, (GHFunc) DMAP_SHARE_GET_CLASS (share)->add_entry_to_mlcl, &mb);
+		}
+
+		_dmap_share_message_set_from_dmap_structure (share, message, adbs);
+		dmap_structure_destroy (adbs);
+		adbs = NULL;
+	} else if (g_ascii_strcasecmp ("/1/containers", rest_of_path) == 0) {
+	/* APLY database playlists
+	 * 	MSTT status
+	 * 	MUTY update type
+	 * 	MTCO specified total count
+	 * 	MRCO returned count
+	 * 	MLCL listing
+	 * 		MLIT listing item
+	 * 			MIID item id
+	 * 			MPER persistent item id
+	 * 			MINM item name
+	 * 			MIMC item count
+	 * 			ABPL baseplaylist (only for base)
+	 * 		MLIT
+	 * 		...
+	 */
+		gchar *nameprop;
+		GNode *aply;
+		GNode *mlcl;
+		GNode *mlit;
+
+		g_object_get ((gpointer) share, "name", &nameprop, NULL);
+
+		aply = dmap_structure_add (NULL, DMAP_CC_APLY);
+		dmap_structure_add (aply, DMAP_CC_MSTT, (gint32) DMAP_STATUS_OK);
+		dmap_structure_add (aply, DMAP_CC_MUTY, 0);
+		dmap_structure_add (aply, DMAP_CC_MTCO, (gint32) dmap_container_db_count (share->priv->container_db) + 1);
+		dmap_structure_add (aply, DMAP_CC_MRCO, (gint32) dmap_container_db_count (share->priv->container_db) + 1);
+		mlcl = dmap_structure_add (aply, DMAP_CC_MLCL);
+
+		/* Base playlist: */
+		mlit = dmap_structure_add (mlcl, DMAP_CC_MLIT);
+		dmap_structure_add (mlit, DMAP_CC_MIID, (gint32) 1);
+		dmap_structure_add (mlit, DMAP_CC_MPER, (gint64) 1);
+		dmap_structure_add (mlit, DMAP_CC_MINM, nameprop);
+		dmap_structure_add (mlit, DMAP_CC_MIMC, dmap_db_count (share->priv->db));
+		dmap_structure_add (mlit, DMAP_CC_ABPL, (gchar) 1);
+
+		dmap_container_db_foreach (share->priv->container_db, (GHFunc) _dmap_share_add_playlist_to_mlcl, (gpointer) mlcl);
+
+		_dmap_share_message_set_from_dmap_structure (share, message, aply);
+		dmap_structure_destroy (aply);
+
+		g_free (nameprop);
+	} else if (g_ascii_strncasecmp ("/1/containers/", rest_of_path, 14) == 0) {
+	/* APSO playlist songs
+	 * 	MSTT status
+	 * 	MUTY update type
+	 * 	MTCO specified total count
+	 * 	MRCO returned count
+	 * 	MLCL listing
+	 * 		MLIT listing item
+	 * 			MIKD item kind
+	 * 			MIID item id
+	 * 			MCTI container item id
+	 * 		MLIT
+	 * 		...
+	 */
+		GNode *apso;
+		struct DMAPMetaDataMap *map;
+		struct MLCL_Bits mb = {NULL,0};
+		gint pl_id = atoi (rest_of_path + 14);
+
+		map = DMAP_SHARE_GET_CLASS (share)->get_meta_data_map (share);
+		mb.bits = _dmap_share_parse_meta (query, map);
+
+		apso = dmap_structure_add (NULL, DMAP_CC_APSO);
+		dmap_structure_add (apso, DMAP_CC_MSTT, (gint32) DMAP_STATUS_OK);
+		dmap_structure_add (apso, DMAP_CC_MUTY, 0);
+
+		if (pl_id == 1) {
+			gint32 num_songs = dmap_db_count (share->priv->db);
+			dmap_structure_add (apso, DMAP_CC_MTCO, (gint32) num_songs);
+			dmap_structure_add (apso, DMAP_CC_MRCO, (gint32) num_songs);
+			mb.mlcl = dmap_structure_add (apso, DMAP_CC_MLCL);
+
+			dmap_db_foreach (share->priv->db, (GHFunc) DMAP_SHARE_GET_CLASS (share)->add_entry_to_mlcl, &mb);
+		} else {
+			DMAPContainerRecord *record;
+			const DMAPDb *entries;
+			guint num_songs;
+			
+			record = dmap_container_db_lookup_by_id (share->priv->container_db, pl_id);
+			entries = dmap_container_record_get_entries (record);
+			num_songs = dmap_db_count (entries);
+			
+			dmap_structure_add (apso, DMAP_CC_MTCO, (gint32) num_songs);
+			dmap_structure_add (apso, DMAP_CC_MRCO, (gint32) num_songs);
+			mb.mlcl = dmap_structure_add (apso, DMAP_CC_MLCL);
+
+			dmap_db_foreach (entries, (GHFunc) DMAP_SHARE_GET_CLASS (share)->add_entry_to_mlcl, &mb);
+
+			g_object_unref (record);
+		}
+
+		_dmap_share_message_set_from_dmap_structure (share, message, apso);
+		dmap_structure_destroy (apso);
+	} else if (g_ascii_strncasecmp ("/1/browse/", rest_of_path, 9) == 0) {
+		 DMAP_SHARE_GET_CLASS (share)->databases_browse_xxx (
+			share,
+			server,
+			message,
+			path,
+			query,
+			context);
+	} else if (g_ascii_strncasecmp ("/1/items/", rest_of_path, 9) == 0) {
+	/* just the file :) */
+		 DMAP_SHARE_GET_CLASS (share)->databases_items_xxx (
+			share,
+			server,
+			message,
+			path,
+			query,
+			context);
+	} else {
+		g_warning ("Unhandled: %s\n", path);
+	}
+}
diff --git a/libdmapsharing/dmap-share.h b/libdmapsharing/dmap-share.h
index b24effb..08aed91 100644
--- a/libdmapsharing/dmap-share.h
+++ b/libdmapsharing/dmap-share.h
@@ -92,6 +92,7 @@ G_BEGIN_DECLS
 				 TYPE_DMAP_SHARE, DMAPShareClass))
 
 #define DMAP_SHARE_CHUNK_SIZE 16384
+#define DMAP_STATUS_OK 200
 
 typedef struct DMAPSharePrivate DMAPSharePrivate;
 
@@ -100,13 +101,32 @@ typedef struct {
 	DMAPSharePrivate *priv;
 } DMAPShare;
 
+typedef struct _DMAPMetaDataMap DMAPMetaDataMap;
+
 typedef struct {
 	GObjectClass parent;
 
 	/* Pure virtual methods: */
-	guint        (*get_desired_port)    (DMAPShare *share);
-	const char * (*get_type_of_service) (DMAPShare *share);
-	void         (*message_add_standard_headers) (SoupMessage *message);
+	guint             (*get_desired_port)             (DMAPShare *share);
+	const char *      (*get_type_of_service)          (DMAPShare *share);
+	void              (*message_add_standard_headers) (DMAPShare *share,
+							   SoupMessage *msg);
+	struct DMAPMetaDataMap * (*get_meta_data_map)     (DMAPShare *share);
+	void              (*add_entry_to_mlcl)            (gpointer id,
+							   DMAPRecord *record,
+							   gpointer mb);
+	void		  (*databases_browse_xxx)	  (DMAPShare *share,
+							   SoupServer *server,
+							   SoupMessage *msg,
+							   const char *path,
+							   GHashTable *query,
+							   SoupClientContext *context);
+	void		  (*databases_items_xxx)	  (DMAPShare *share,
+							   SoupServer *server,
+							   SoupMessage *msg,
+							   const char *path,
+							   GHashTable *query,
+							   SoupClientContext *context);
 
 	/* Pure virtual methods: libsoup callbacks */
 	void	  (*server_info)   (DMAPShare *share, SoupServer *server,
@@ -129,10 +149,6 @@ typedef struct {
 				    SoupMessage *message, const char *path,
 				    GHashTable *query, SoupClientContext *ctx);
 
-	void	  (*databases)     (DMAPShare *share, SoupServer *server,
-				    SoupMessage *message, const char *path,
-				    GHashTable *query, SoupClientContext *ctx);
-
 	/* Virtual methods: MDNS callbacks */
 	void	  (*published)	   (DMAPShare         *share,
 				    DmapMdnsPublisher *publisher,
@@ -141,6 +157,14 @@ typedef struct {
 	void	  (*name_collision)(DMAPShare	      *share,
 				    DmapMdnsPublisher *publisher,
 	              		    const char        *name);
+
+	/* Virtual methods: */
+	void	  (*databases)     (DMAPShare	      *share,
+				    SoupServer        *server,
+				    SoupMessage       *message,
+				    const char        *path,
+				    GHashTable        *query,
+				    SoupClientContext *context);
 } DMAPShareClass;
 
 struct DMAPMetaDataMap {
@@ -186,12 +210,10 @@ void     _dmap_share_message_set_from_dmap_structure (DMAPShare *share,
 						     GNode *structure);
 
 bitwise  _dmap_share_parse_meta (GHashTable *query,
-				struct DMAPMetaDataMap *mdm,
-				guint mdmlen);
+				struct DMAPMetaDataMap *mdm);
 
 bitwise  _dmap_share_parse_meta_str (const char *attrs,
-				    struct DMAPMetaDataMap *mdm,
-				    guint mdmlen);
+				    struct DMAPMetaDataMap *mdm);
 
 void _dmap_share_add_playlist_to_mlcl (gpointer id,
 				       DMAPContainerRecord *record,
@@ -236,6 +258,14 @@ void _dmap_share_name_collision(DMAPShare         *share,
 			       DmapMdnsPublisher *publisher,
 			       const char        *name);
 
+void
+_dmap_share_databases (DMAPShare *share,
+                       SoupServer        *server,
+		       SoupMessage       *message,
+		       const char        *path,
+		       GHashTable        *query,
+		       SoupClientContext *context);
+
 #endif /* __DMAP_SHARE_H */
 
 G_END_DECLS
diff --git a/libdmapsharing/dpap-share.c b/libdmapsharing/dpap-share.c
index 036321e..a31ae6d 100644
--- a/libdmapsharing/dpap-share.c
+++ b/libdmapsharing/dpap-share.c
@@ -63,21 +63,30 @@ void dpap_share_server_info (DMAPShare         *share,
 			     const char        *path,
 			     GHashTable        *query,
 			     SoupClientContext *context);
-void dpap_share_databases (DMAPShare         *share,
-			     SoupServer        *server,
-	  		     SoupMessage       *message,
-			     const char        *path,
-			     GHashTable        *query,
-			     SoupClientContext *context);
-void dpap_share_message_add_standard_headers (SoupMessage *message);
+void dpap_share_message_add_standard_headers (DMAPShare *share,
+					      SoupMessage *message);
+static void databases_browse_xxx (DMAPShare *share,
+				  SoupServer *server,
+				  SoupMessage *msg,
+				  const char *path,
+				  GHashTable *query,
+				  SoupClientContext *context);
+static void databases_items_xxx (DMAPShare *share,
+				 SoupServer *server,
+				 SoupMessage *msg,
+				 const char *path,
+				 GHashTable *query,
+				 SoupClientContext *context);
+static struct DMAPMetaDataMap *get_meta_data_map (DMAPShare *share);
+static void add_entry_to_mlcl (gpointer id,
+			       DMAPRecord *record,
+			       gpointer mb);
 
 #define DPAP_TYPE_OF_SERVICE "_dpap._tcp"
 #define DPAP_PORT 8770
 
 struct DPAPSharePrivate {
-	/* db things */
-	DMAPDb *db;
-	DMAPContainerDb *container_db;
+	gchar unused;
 };
 
 /* Mmap'ed full image file. Global so that it may be free'ed in a different
@@ -89,8 +98,6 @@ static GMappedFile *mapped_file = NULL;
 
 enum {
 	PROP_0,
-	PROP_DB,
-	PROP_CONTAINER_DB,
 };
 
 G_DEFINE_TYPE (DPAPShare, dpap_share, TYPE_DMAP_SHARE)
@@ -105,26 +112,14 @@ dpap_share_class_init (DPAPShareClass *klass)
 	object_class->set_property = dpap_share_set_property;
 	object_class->dispose = dpap_share_dispose;
 
-	parent_class->get_desired_port    = dpap_share_get_desired_port;
-	parent_class->get_type_of_service = dpap_share_get_type_of_service;
+	parent_class->get_desired_port     = dpap_share_get_desired_port;
+	parent_class->get_type_of_service  = dpap_share_get_type_of_service;
 	parent_class->message_add_standard_headers = dpap_share_message_add_standard_headers;
-	parent_class->server_info         = dpap_share_server_info;
-	parent_class->databases           = dpap_share_databases;
-
-	/* FIXME?: */
-	g_object_class_install_property (object_class,
-                                         PROP_DB,
-                                         g_param_spec_pointer ("db",
-                                                              "DB",
-                                                              "DB object",
-                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
-	g_object_class_install_property (object_class,
-                                         PROP_CONTAINER_DB,
-                                         g_param_spec_pointer ("container-db",
-                                                              "Container DB",
-                                                              "Container DB object",
-                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+	parent_class->get_meta_data_map     = get_meta_data_map;
+	parent_class->add_entry_to_mlcl    = add_entry_to_mlcl;
+	parent_class->databases_browse_xxx = databases_browse_xxx;
+	parent_class->databases_items_xxx  = databases_items_xxx;
+	parent_class->server_info          = dpap_share_server_info;
 
 	g_type_class_add_private (klass, sizeof (DPAPSharePrivate));
 }
@@ -146,12 +141,6 @@ dpap_share_set_property (GObject *object,
 
 	switch (prop_id) {
 	/* FIXME: */
-	case PROP_DB:
-		share->priv->db = (DMAPDb *) g_value_get_pointer (value);
-		break;
-	case PROP_CONTAINER_DB:
-		share->priv->container_db = (DMAPContainerDb *) g_value_get_pointer (value);
-		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -167,12 +156,6 @@ dpap_share_get_property (GObject *object,
 	DPAPShare *share = DPAP_SHARE (object);
 
 	switch (prop_id) {
-	case PROP_DB:
-		g_value_set_pointer (value, share->priv->db);
-		break;
-	case PROP_CONTAINER_DB:
-		g_value_set_pointer (value, share->priv->container_db);
-		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -211,13 +194,11 @@ dpap_share_new (const char *name,
 }
 
 void
-dpap_share_message_add_standard_headers (SoupMessage *message)
+dpap_share_message_add_standard_headers (DMAPShare *share, SoupMessage *message)
 {
 	soup_message_headers_append (message->response_headers, "DPAP-Server", "libdmapsharing" VERSION);
 }
 
-#define DPAP_STATUS_OK 200
-
 #define DMAP_VERSION 2.0
 #define DPAP_VERSION 1.1
 #define DPAP_TIMEOUT 1800
@@ -268,7 +249,7 @@ dpap_share_server_info (DMAPShare *share,
 	g_object_get ((gpointer) share, "name", &nameprop, NULL);
 
 	msrv = dmap_structure_add (NULL, DMAP_CC_MSRV);
-	dmap_structure_add (msrv, DMAP_CC_MSTT, (gint32) DPAP_STATUS_OK);
+	dmap_structure_add (msrv, DMAP_CC_MSTT, (gint32) DMAP_STATUS_OK);
 	dmap_structure_add (msrv, DMAP_CC_MPRO, (gdouble) DMAP_VERSION);
 	dmap_structure_add (msrv, DMAP_CC_PPRO, (gdouble) DPAP_VERSION);
 	dmap_structure_add (msrv, DMAP_CC_MINM, nameprop);
@@ -366,7 +347,9 @@ file_to_mmap (const char *location)
 }
 
 static void
-add_entry_to_mlcl (gpointer id, DMAPRecord *record, gpointer _mb)
+add_entry_to_mlcl (gpointer id,
+		   DMAPRecord *record,
+		   gpointer _mb)
 {
 	struct MLCL_Bits *mb = (struct MLCL_Bits *) _mb;
 
@@ -463,284 +446,36 @@ add_entry_to_mlcl (gpointer id, DMAPRecord *record, gpointer _mb)
 	return;
 }
 
-/* FIXME: Handle ('...') and share code with DAAPShare. */
-static GSList *
-build_filter (gchar *filterstr)
-{
-	/* Produces a list of lists, each being a filter definition that may
-	 * be one or more filter criteria.
-	 */
-
-	/* A filter string looks like (iTunes):
-	 * 'daap.songgenre:Other'+'daap.songartist:Band'.
-	 * or (Roku):
-	 * 'daap.songgenre:Other' 'daap.songartist:Band'.
-	 * or
-         * 'dmap.itemid:1000'
-	 * or
-         * 'dmap.itemid:1000','dmap:itemid:1001'
-	 * or
-	 * 'daap.songgenre:Foo'+'daap.songartist:Bar'+'daap.songalbum:Baz'
-	 * or (iPhoto '09)
-	 * ('daap.idemid:1000','dmap:itemid:1001')
-         */
-
-	size_t len;
-	GSList *list = NULL;
-
-	g_debug ("Filter string is %s.", filterstr);
-
-	len = strlen (filterstr);
-	filterstr = *filterstr == '(' ? filterstr + 1 : filterstr;
-	/* FIXME: I thought this should be -1, but there is a trailing ' ' in iTunes '09: */
-	filterstr[len - 2] = filterstr[len - 2] == ')' ? 0x00 : filterstr[len - 2];
-
-	if (filterstr != NULL) {
-		int i;
-		gchar **t1 = g_strsplit (filterstr, ",", 0);
-
-		for (i = 0; t1[i]; i++) {
-			int j;
-			GSList *filter = NULL;
-			gchar **t2;
-
-			t2 = _dmap_db_strsplit_using_quotes (t1[i]);
-
-			for (j = 0; t2[j]; j++) {
-				FilterDefinition *def;
-				gchar **t3;
-
-				t3 = g_strsplit (t2[j], ":", 0);
-
-				if (g_strcasecmp ("dmap.itemid", t3[0]) == 0) {
-					def = g_new0 (FilterDefinition, 1);
-					def->value = g_strdup (t3[1]);
-					def->record_get_value = NULL;
-				} else {
-					g_warning ("Unknown category: %s", t3[0]);
-					def = NULL;
-				}
-
-				if (def != NULL)
-					filter = g_slist_append (filter, def);
-
-				g_strfreev (t3);
-			}
-
-			list = g_slist_append (list, filter);
-
-			g_strfreev (t2);
-		}
-		g_strfreev (t1);
-	}
-
-        return list;
-}
-
 static void
 debug_param (gpointer key, gpointer val, gpointer user_data)
 {
         g_debug ("%s %s", (char *) key, (char *) val);
 }
 
-void
-dpap_share_databases (DMAPShare *share,
-	      SoupServer        *server,
-	      SoupMessage       *message,
-	      const char        *path,
-	      GHashTable        *query,
-	      SoupClientContext *context)
-
+static void
+databases_browse_xxx (DMAPShare *share,
+                      SoupServer *server,
+                      SoupMessage *msg,
+                      const char *path,
+                      GHashTable *query,
+                      SoupClientContext *context)
 {
-	const char *rest_of_path;
-
-	g_debug ("Path is %s.", path);
-	g_hash_table_foreach (query, debug_param, NULL);
-
-	if (! _dmap_share_session_id_validate (share, context, message, query, NULL)) {
-		soup_message_set_status (message, SOUP_STATUS_FORBIDDEN);
-		return;
-	}
-
-	rest_of_path = strchr (path + 1, '/');
-
-	if (rest_of_path == NULL) {
-	/* AVDB server databases
-	 * 	MSTT status
-	 * 	MUTY update type
-	 * 	MTCO specified total count
-	 * 	MRCO returned count
-	 * 	MLCL listing
-	 * 		MLIT listing item
-	 * 			MIID item id
-	 * 			MPER persistent id
-	 * 			MINM item name
-	 * 			MIMC item count
-	 * 			MCTC container count
-	 */
-		gchar *nameprop;
-		GNode *avdb;
-		GNode *mlcl;
-		GNode *mlit;
-
-		g_object_get ((gpointer) share, "name", &nameprop, NULL);
-
-		avdb = dmap_structure_add (NULL, DMAP_CC_AVDB);
-		dmap_structure_add (avdb, DMAP_CC_MSTT, (gint32) DPAP_STATUS_OK);
-		dmap_structure_add (avdb, DMAP_CC_MUTY, 0);
-		dmap_structure_add (avdb, DMAP_CC_MTCO, (gint32) 1);
-		dmap_structure_add (avdb, DMAP_CC_MRCO, (gint32) 1);
-		mlcl = dmap_structure_add (avdb, DMAP_CC_MLCL);
-		mlit = dmap_structure_add (mlcl, DMAP_CC_MLIT);
-		dmap_structure_add (mlit, DMAP_CC_MIID, (gint32) 1);
-		dmap_structure_add (mlit, DMAP_CC_MPER, (gint64) 1);
-		dmap_structure_add (mlit, DMAP_CC_MINM, nameprop);
-		dmap_structure_add (mlit, DMAP_CC_MIMC, dmap_db_count (DPAP_SHARE (share)->priv->db));
-		dmap_structure_add (mlit, DMAP_CC_MCTC, (gint32) 1);
-
-		_dmap_share_message_set_from_dmap_structure (share, message, avdb);
-		dmap_structure_destroy (avdb);
-
-		g_free (nameprop);
-	} else if (g_ascii_strcasecmp ("/1/items", rest_of_path) == 0) {
-	/* ADBS database songs
-	 * 	MSTT status
-	 * 	MUTY update type
-	 * 	MTCO specified total count
-	 * 	MRCO returned count
-	 * 	MLCL listing
-	 * 		MLIT
-	 * 			attrs
-	 * 		MLIT
-	 * 		...
-	 */
-		GNode *adbs;
-		gchar *record_query;
-		gint32 num_songs = dmap_db_count (DPAP_SHARE (share)->priv->db);
-		struct MLCL_Bits mb = {NULL,0};
-
-		mb.bits = _dmap_share_parse_meta (query, meta_data_map, G_N_ELEMENTS (meta_data_map));
-
-		adbs = dmap_structure_add (NULL, DMAP_CC_ADBS);
-		dmap_structure_add (adbs, DMAP_CC_MSTT, (gint32) DPAP_STATUS_OK);
-		dmap_structure_add (adbs, DMAP_CC_MUTY, 0);
-		dmap_structure_add (adbs, DMAP_CC_MTCO, (gint32) num_songs);
-		dmap_structure_add (adbs, DMAP_CC_MRCO, (gint32) num_songs);
-		mb.mlcl = dmap_structure_add (adbs, DMAP_CC_MLCL);
-
-		record_query = g_hash_table_lookup (query, "query");
-
-		if (record_query) {
-			GHashTable *records;
-			GSList *filter_def;
-
-			/* FIXME: fix memory leaks (DAAP too): */
-			filter_def = build_filter (record_query);
-			records = _dmap_db_apply_filter (DMAP_DB (DPAP_SHARE (share)->priv->db), filter_def);
-			g_hash_table_foreach (records, (GHFunc) add_entry_to_mlcl, &mb);
-			/* FIXME: need to free hash table keys but not records */
-		} else {
-			g_warning ("Missing query parameter");
-		}
-
-		_dmap_share_message_set_from_dmap_structure (share, message, adbs);
-		dmap_structure_destroy (adbs);
-		adbs = NULL;
-	} else if (g_ascii_strcasecmp ("/1/containers", rest_of_path) == 0) {
-	/* APLY database playlists
-	 * 	MSTT status
-	 * 	MUTY update type
-	 * 	MTCO specified total count
-	 * 	MRCO returned count
-	 * 	MLCL listing
-	 * 		MLIT listing item
-	 * 			MIID item id
-	 * 			MPER persistent item id
-	 * 			MINM item name
-	 * 			MIMC item count
-	 * 			ABPL baseplaylist (only for base)
-	 * 		MLIT
-	 * 		...
-	 */
-		gchar *nameprop;
-		GNode *aply;
-		GNode *mlcl;
-		GNode *mlit;
-
-		g_object_get ((gpointer) share, "name", &nameprop, NULL);
-
-		aply = dmap_structure_add (NULL, DMAP_CC_APLY);
-		dmap_structure_add (aply, DMAP_CC_MSTT, (gint32) DPAP_STATUS_OK);
-		dmap_structure_add (aply, DMAP_CC_MUTY, 0);
-		dmap_structure_add (aply, DMAP_CC_MTCO, (gint32) 1);
-		dmap_structure_add (aply, DMAP_CC_MRCO, (gint32) 1);
-		mlcl = dmap_structure_add (aply, DMAP_CC_MLCL);
-
-		mlit = dmap_structure_add (mlcl, DMAP_CC_MLIT);
-		dmap_structure_add (mlit, DMAP_CC_MIID, (gint32) 1);
-		dmap_structure_add (mlit, DMAP_CC_MPER, (gint64) 1);
-		dmap_structure_add (mlit, DMAP_CC_MINM, nameprop);
-		dmap_structure_add (mlit, DMAP_CC_MIMC, dmap_db_count (DPAP_SHARE (share)->priv->db));
-		dmap_structure_add (mlit, DMAP_CC_ABPL, (gchar) 1); /* base album (AKA playlist) */
-
-		dmap_container_db_foreach (DPAP_SHARE (share)->priv->container_db, (GHFunc) _dmap_share_add_playlist_to_mlcl, (gpointer) mlcl);
-
-		_dmap_share_message_set_from_dmap_structure (share, message, aply);
-		dmap_structure_destroy (aply);
-
-		g_free (nameprop);
-	} else if (g_ascii_strncasecmp ("/1/containers/", rest_of_path, 14) == 0) {
-	/* APSO playlist songs
-	 * 	MSTT status
-	 * 	MUTY update type
-	 * 	MTCO specified total count
-	 * 	MRCO returned count
-	 * 	MLCL listing
-	 * 		MLIT listing item
-	 * 			MIKD item kind
-	 * 			MIID item id
-	 * 			MCTI container item id
-	 * 		MLIT
-	 * 		...
-	 */
-		GNode *apso;
-		struct MLCL_Bits mb = {NULL,0};
-		gint pl_id = atoi (rest_of_path + 14);
-
-		mb.bits = _dmap_share_parse_meta (query, meta_data_map, G_N_ELEMENTS (meta_data_map));
-
-		apso = dmap_structure_add (NULL, DMAP_CC_APSO);
-		dmap_structure_add (apso, DMAP_CC_MSTT, (gint32) DPAP_STATUS_OK);
-		dmap_structure_add (apso, DMAP_CC_MUTY, 0);
-
-		if (pl_id == 1) {
-			gint32 num_songs = dmap_db_count (DPAP_SHARE (share)->priv->db);
-			dmap_structure_add (apso, DMAP_CC_MTCO, (gint32) num_songs);
-			dmap_structure_add (apso, DMAP_CC_MRCO, (gint32) num_songs);
-			mb.mlcl = dmap_structure_add (apso, DMAP_CC_MLCL);
-
-			dmap_db_foreach (DPAP_SHARE (share)->priv->db, (GHFunc) add_entry_to_mlcl, &mb);
-		} else {
-			DMAPContainerRecord *record;
-                        const DMAPDb *entries;
-                        guint num_songs;
-
-                        record = dmap_container_db_lookup_by_id (DPAP_SHARE (share)->priv->container_db, pl_id);
-			entries = dmap_container_record_get_entries (record);
-			num_songs = dmap_db_count (entries);
-
-                        dmap_structure_add (apso, DMAP_CC_MTCO, (gint32) num_songs);
-                        dmap_structure_add (apso, DMAP_CC_MRCO, (gint32) num_songs);
-                        mb.mlcl = dmap_structure_add (apso, DMAP_CC_MLCL);
-
-			dmap_db_foreach (entries, (GHFunc) add_entry_to_mlcl, (gpointer) &mb);
+	g_warning ("Unhandled: %s\n", path);
+}
 
-			g_object_unref (record);
-		}
+static void
+databases_items_xxx  (DMAPShare *share,
+                      SoupServer *server,
+                      SoupMessage *msg,
+                      const char *path,
+                      GHashTable *query,
+                      SoupClientContext *context)
+{
+	g_warning ("Unhandled: %s\n", path);
+}
 
-		_dmap_share_message_set_from_dmap_structure (share, message, apso);
-		dmap_structure_destroy (apso);
-	} else {
-		g_warning ("Unhandled: %s\n", path);
-	}
+static struct DMAPMetaDataMap *
+get_meta_data_map (DMAPShare *share)
+{
+        return meta_data_map;
 }



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]