[libdmapsharing] Make some functions static



commit 9d377f803d09cd3fb69f1d8db55e17f3575fa0b1
Author: W. Michael Petullo <mike flyn org>
Date:   Fri Dec 6 22:33:58 2019 -0500

    Make some functions static
    
    Signed-off-by: W. Michael Petullo <mike flyn org>

 libdmapsharing/dmap-av-share.c      |   67 +-
 libdmapsharing/dmap-image-share.c   |   39 +-
 libdmapsharing/dmap-share-private.h |   53 -
 libdmapsharing/dmap-share.c         | 3211 +++++++++++++++++------------------
 4 files changed, 1654 insertions(+), 1716 deletions(-)
---
diff --git a/libdmapsharing/dmap-av-share.c b/libdmapsharing/dmap-av-share.c
index 0dedb6e..1bf3ed4 100644
--- a/libdmapsharing/dmap-av-share.c
+++ b/libdmapsharing/dmap-av-share.c
@@ -44,13 +44,13 @@
 #include <libdmapsharing/dmap-gst-input-stream.h>
 #endif /* HAVE_GSTREAMERAPP */
 
-guint dmap_av_share_get_desired_port (DmapShare * share);
-const char *dmap_av_share_get_type_of_service (DmapShare * share);
-void dmap_av_share_server_info (DmapShare * share,
-                            SoupMessage * message,
-                            const char *path);
-void dmap_av_share_message_add_standard_headers (DmapShare * share,
-                                             SoupMessage * message);
+static guint _get_desired_port (DmapShare * share);
+static const char *_get_type_of_service (DmapShare * share);
+static void _server_info (DmapShare * share,
+                          SoupMessage * message,
+                          const char *path);
+static void _message_add_standard_headers (DmapShare * share,
+                                           SoupMessage * message);
 static void _databases_browse_xxx (DmapShare * share,
                                    SoupMessage * msg,
                                    const char *path,
@@ -73,15 +73,14 @@ dmap_av_share_class_init (DmapAvShareClass * klass)
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
        DmapShareClass *parent_class = DMAP_SHARE_CLASS (object_class);
 
-       parent_class->get_desired_port = dmap_av_share_get_desired_port;
-       parent_class->get_type_of_service = dmap_av_share_get_type_of_service;
-       parent_class->message_add_standard_headers =
-               dmap_av_share_message_add_standard_headers;
+       parent_class->get_desired_port = _get_desired_port;
+       parent_class->get_type_of_service = _get_type_of_service;
+       parent_class->message_add_standard_headers = _message_add_standard_headers;
        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 = dmap_av_share_server_info;
+       parent_class->server_info = _server_info;
 }
 
 static void
@@ -105,9 +104,9 @@ dmap_av_share_new (const char *name,
                                            transcode_mimetype, NULL));
 }
 
-void
-dmap_av_share_message_add_standard_headers (G_GNUC_UNUSED DmapShare * share,
-                                        SoupMessage * message)
+static void
+_message_add_standard_headers (G_GNUC_UNUSED DmapShare * share,
+                               SoupMessage * message)
 {
        soup_message_headers_append (message->response_headers, "DMAP-Server",
                                     "libdmapsharing" VERSION);
@@ -117,22 +116,22 @@ dmap_av_share_message_add_standard_headers (G_GNUC_UNUSED DmapShare * share,
 #define DAAP_VERSION 3.0
 #define DAAP_TIMEOUT 1800
 
-guint
-dmap_av_share_get_desired_port (G_GNUC_UNUSED DmapShare * share)
+static guint
+_get_desired_port (G_GNUC_UNUSED DmapShare * share)
 {
        return DAAP_PORT;
 }
 
-const char *
-dmap_av_share_get_type_of_service (G_GNUC_UNUSED DmapShare * share)
+static const char *
+_get_type_of_service (G_GNUC_UNUSED DmapShare * share)
 {
        return DAAP_TYPE_OF_SERVICE;
 }
 
-void
-dmap_av_share_server_info (DmapShare * share,
-                           SoupMessage * message,
-                           const char *path)
+static void
+_server_info (DmapShare * share,
+              SoupMessage * message,
+              const char *path)
 {
 /* MSRV        server info response
  *     MSTT status
@@ -1350,25 +1349,25 @@ START_TEST(_should_transcode_test_yes_trancode_mimetype_to_mp4)
 }
 END_TEST
 
-START_TEST(dmap_av_share_get_desired_port_test)
+START_TEST(_get_desired_port_test)
 {
-       DmapShare *share = _build_share_test("dmap_av_share_get_desired_port_test");
-       ck_assert_int_eq(DAAP_PORT, dmap_av_share_get_desired_port(share));
+       DmapShare *share = _build_share_test("_get_desired_port_test");
+       ck_assert_int_eq(DAAP_PORT, _get_desired_port(share));
        g_object_unref(share);
 }
 END_TEST
 
-START_TEST(dmap_av_share_get_type_of_service_test)
+START_TEST(_get_type_of_service_test)
 {
-       DmapShare *share = _build_share_test("dmap_av_share_get_type_of_service_test");
-       ck_assert_str_eq(DAAP_TYPE_OF_SERVICE, dmap_av_share_get_type_of_service(share));
+       DmapShare *share = _build_share_test("_get_type_of_service_test");
+       ck_assert_str_eq(DAAP_TYPE_OF_SERVICE, _get_type_of_service(share));
        g_object_unref(share);
 }
 END_TEST
 
-START_TEST(dmap_av_share_server_info_test)
+START_TEST(_server_info_test)
 {
-       char *nameprop = "dmap_av_share_server_info_test";
+       char *nameprop = "_server_info_test";
        DmapShare *share;
        SoupMessage *message;
        SoupMessageBody *body;
@@ -1384,7 +1383,7 @@ START_TEST(dmap_av_share_server_info_test)
        /* Causes auth. method to be set to DMAP_SHARE_AUTH_METHOD_PASSWORD. */
        g_object_set(share, "password", "password", NULL);
 
-       dmap_av_share_server_info(share, message, "/");
+       _server_info(share, message, "/");
 
        g_object_get(message, "response-body", &body, NULL);
        buffer = soup_message_body_flatten(body);
@@ -1444,14 +1443,14 @@ START_TEST(dmap_av_share_server_info_test)
 }
 END_TEST
 
-START_TEST(dmap_av_share_message_add_standard_headers_test)
+START_TEST(_message_add_standard_headers_test)
 {
        const char *header;
        DmapShare *share;
        SoupMessage *message;
        SoupMessageHeaders *headers;
 
-       share = _build_share_test("dmap_av_share_message_add_standard_headers_test");
+       share = _build_share_test("_message_add_standard_headers_test");
        message = soup_message_new(SOUP_METHOD_GET, "http://test/";);
 
        soup_message_headers_append(message->response_headers,
diff --git a/libdmapsharing/dmap-image-share.c b/libdmapsharing/dmap-image-share.c
index a98bc9e..80671c5 100644
--- a/libdmapsharing/dmap-image-share.c
+++ b/libdmapsharing/dmap-image-share.c
@@ -45,13 +45,10 @@
 #include <libdmapsharing/dmap-private-utils.h>
 #include <libdmapsharing/dmap-structure.h>
 
-guint dmap_image_share_get_desired_port (DmapShare * share);
-const char *dmap_image_share_get_type_of_service (DmapShare * share);
-void dmap_image_share_server_info (DmapShare * share,
-                                   SoupMessage * message,
-                                   const char *path);
-void dmap_image_share_message_add_standard_headers (DmapShare * share,
-                                             SoupMessage * message);
+static guint _get_desired_port (DmapShare * share);
+static const char *_get_type_of_service (DmapShare * share);
+static void _server_info (DmapShare * share, SoupMessage * message, const char *path);
+static void _message_add_standard_headers (DmapShare * share, SoupMessage * message);
 
 #define DPAP_TYPE_OF_SERVICE "_dpap._tcp"
 #define DPAP_PORT 8770
@@ -429,15 +426,14 @@ dmap_image_share_class_init (DmapImageShareClass * klass)
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
        DmapShareClass *parent_class = DMAP_SHARE_CLASS (object_class);
 
-       parent_class->get_desired_port = dmap_image_share_get_desired_port;
-       parent_class->get_type_of_service = dmap_image_share_get_type_of_service;
-       parent_class->message_add_standard_headers =
-               dmap_image_share_message_add_standard_headers;
+       parent_class->get_desired_port = _get_desired_port;
+       parent_class->get_type_of_service = _get_type_of_service;
+       parent_class->message_add_standard_headers = _message_add_standard_headers;
        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 = dmap_image_share_server_info;
+       parent_class->server_info = _server_info;
 }
 
 static void
@@ -468,9 +464,8 @@ dmap_image_share_new (const char *name,
                                               NULL));
 }
 
-void
-dmap_image_share_message_add_standard_headers (G_GNUC_UNUSED DmapShare * share,
-                                               SoupMessage * message)
+static void
+_message_add_standard_headers (G_GNUC_UNUSED DmapShare * share, SoupMessage * message)
 {
        soup_message_headers_append (message->response_headers, "DPAP-Server",
                                     "libdmapsharing" VERSION);
@@ -480,22 +475,20 @@ dmap_image_share_message_add_standard_headers (G_GNUC_UNUSED DmapShare * share,
 #define DPAP_VERSION 1.1
 #define DPAP_TIMEOUT 1800
 
-guint
-dmap_image_share_get_desired_port (G_GNUC_UNUSED DmapShare * share)
+static guint
+_get_desired_port (G_GNUC_UNUSED DmapShare * share)
 {
        return DPAP_PORT;
 }
 
-const char *
-dmap_image_share_get_type_of_service (G_GNUC_UNUSED DmapShare * share)
+static const char *
+_get_type_of_service (G_GNUC_UNUSED DmapShare * share)
 {
        return DPAP_TYPE_OF_SERVICE;
 }
 
-void
-dmap_image_share_server_info (DmapShare * share,
-                              SoupMessage * message,
-                              const char *path)
+static void
+_server_info (DmapShare * share, SoupMessage * message, const char *path)
 {
 /* MSRV        server info response
  *     MSTT status
diff --git a/libdmapsharing/dmap-share-private.h b/libdmapsharing/dmap-share-private.h
index 38f4a8a..cf17bc2 100644
--- a/libdmapsharing/dmap-share-private.h
+++ b/libdmapsharing/dmap-share-private.h
@@ -40,78 +40,25 @@ G_BEGIN_DECLS
 /* Non-virtual methods */
 guint dmap_share_get_auth_method (DmapShare * share);
 
-guint dmap_share_get_revision_number (DmapShare * share);
-
-gboolean dmap_share_get_revision_number_from_query (GHashTable * query,
-                                                    guint * number);
-
 gboolean dmap_share_session_id_validate (DmapShare * share,
                                          SoupClientContext * context,
                                          GHashTable * query,
                                          guint32 * id);
 
-guint32 dmap_share_session_id_create (DmapShare * share,
-                                      SoupClientContext * ctx);
-
 gboolean dmap_share_client_requested (DmapBits bits, gint field);
 
-gboolean dmap_share_uri_is_local (const char *text_uri);
-
-gboolean dmap_share_soup_auth_filter (SoupAuthDomain * auth_domain,
-                                      SoupMessage * msg, gpointer user_data);
-
 void dmap_share_message_set_from_dmap_structure (DmapShare * share,
                                                  SoupMessage * message,
                                                  GNode * structure);
 
-DmapBits dmap_share_parse_meta (GHashTable * query,
-                               struct DmapMetaDataMap *mdm);
-
-DmapBits dmap_share_parse_meta_str (const char *attrs,
-                                   struct DmapMetaDataMap *mdm);
-
-void dmap_share_add_playlist_to_mlcl (guint id,
-                                      DmapContainerRecord * record,
-                                      gpointer mb);
-
 GSList *dmap_share_build_filter (gchar * filterstr);
 
-/* Virtual methods (libsoup callbacks with default implementation): */
-void dmap_share_content_codes (DmapShare * share,
-                               SoupMessage * message,
-                               const char *path,
-                               SoupClientContext * context);
-
 void dmap_share_login (DmapShare * share,
                        SoupMessage * message,
                        const char *path,
                        GHashTable * query,
                        SoupClientContext * context);
 
-void dmap_share_logout (DmapShare * share,
-                        SoupMessage * message,
-                        const char *path,
-                        GHashTable * query,
-                        SoupClientContext * context);
-
-void dmap_share_update (DmapShare * share,
-                        SoupServer * server,
-                        SoupMessage * message,
-                        const char *path,
-                        GHashTable * query);
-
-void dmap_share_databases (DmapShare * share,
-                      SoupServer * server,
-                      SoupMessage * message,
-                      const char *path,
-                      GHashTable * query, SoupClientContext * context);
-
-void dmap_share_ctrl_int (DmapShare * share,
-                          SoupServer * server,
-                          SoupMessage * message,
-                          const char *path,
-                          GHashTable * query, SoupClientContext * context);
-
 /* Virtual methods: MDNS callbacks */
 void dmap_share_name_collision (DmapShare * share,
                                 DmapMdnsPublisher * publisher,
diff --git a/libdmapsharing/dmap-share.c b/libdmapsharing/dmap-share.c
index 310fe7c..780f706 100644
--- a/libdmapsharing/dmap-share.c
+++ b/libdmapsharing/dmap-share.c
@@ -62,6 +62,13 @@ enum
 
 static guint _signals[LAST_SIGNAL] = { 0, };
 
+typedef struct {
+       gchar *name;
+       gint64 group_id;
+       gchar *artist;
+       int count;
+} GroupInfo;
+
 struct DmapSharePrivate
 {
        gchar *name;
@@ -153,6 +160,48 @@ _server_info_adapter (G_GNUC_UNUSED SoupServer * server,
        DMAP_SHARE_GET_CLASS (share)->server_info (share, message, path);
 }
 
+static void
+_content_codes (DmapShare * share,
+                SoupMessage * message,
+                const char *path,
+                G_GNUC_UNUSED SoupClientContext * context)
+{
+/* MCCR content codes response
+ *     MSTT status
+ *     MDCL dictionary
+ *             MCNM content codes number
+ *             MCNA content codes name
+ *             MCTY content codes type
+ *     MDCL dictionary
+ *     ...
+ */
+       const DmapContentCodeDefinition *defs;
+       guint num_defs = 0;
+       guint i;
+       GNode *mccr;
+
+       g_debug ("Path is %s.", path);
+
+       defs = dmap_structure_content_codes (&num_defs);
+
+       mccr = dmap_structure_add (NULL, DMAP_CC_MCCR);
+       dmap_structure_add (mccr, DMAP_CC_MSTT, (gint32) SOUP_STATUS_OK);
+
+       for (i = 0; i < num_defs; i++) {
+               GNode *mdcl;
+
+               mdcl = dmap_structure_add (mccr, DMAP_CC_MDCL);
+               dmap_structure_add (mdcl, DMAP_CC_MCNM,
+                                   dmap_structure_cc_string_as_int32 (defs[i].string));
+               dmap_structure_add (mdcl, DMAP_CC_MCNA, defs[i].name);
+               dmap_structure_add (mdcl, DMAP_CC_MCTY,
+                                   (gint32) defs[i].type);
+       }
+
+       dmap_share_message_set_from_dmap_structure (share, message, mccr);
+       dmap_structure_destroy (mccr);
+}
+
 static void
 _content_codes_adapter (G_GNUC_UNUSED SoupServer * server,
                         SoupMessage * message,
@@ -178,6 +227,36 @@ _login_adapter (G_GNUC_UNUSED SoupServer * server,
        DMAP_SHARE_GET_CLASS (share)->login (share, message, path, query, context);
 }
 
+static void
+_session_id_remove (DmapShare * share,
+                    guint32 id)
+{
+       g_hash_table_remove (share->priv->session_ids, GUINT_TO_POINTER (id));
+}
+
+static void
+_logout (DmapShare * share,
+         SoupMessage * message,
+         const char *path,
+         GHashTable * query, SoupClientContext * context)
+{
+       int status;
+       guint32 id;
+
+       g_debug ("Path is %s.", path);
+
+       if (dmap_share_session_id_validate
+           (share, context, query, &id)) {
+               _session_id_remove (share, id);
+
+               status = SOUP_STATUS_NO_CONTENT;
+       } else {
+               status = SOUP_STATUS_FORBIDDEN;
+       }
+
+       soup_message_set_status (message, status);
+}
+
 static void
 _logout_adapter (G_GNUC_UNUSED SoupServer * server,
                  SoupMessage * message,
@@ -189,6 +268,78 @@ _logout_adapter (G_GNUC_UNUSED SoupServer * server,
        DMAP_SHARE_GET_CLASS (share)->logout (share, message, path, query, context);
 }
 
+static guint
+_get_revision_number (DmapShare * share)
+{
+       return share->priv->revision_number;
+}
+
+static gboolean
+_get_revision_number_from_query (GHashTable * query,
+                                 guint * number)
+{
+       gboolean ok = FALSE;
+       char *revision_number_str;
+       guint revision_number;
+
+       revision_number_str = g_hash_table_lookup (query, "revision-number");
+       if (revision_number_str == NULL) {
+               g_warning
+                       ("Client asked for an update without a rev. number");
+               goto done;
+       }
+
+       revision_number = strtoul (revision_number_str, NULL, 10);
+       if (number != NULL) {
+               *number = revision_number;
+       }
+
+       ok = TRUE;
+
+done:
+       return ok;
+}
+
+static void
+_update (DmapShare * share,
+         SoupServer * server,
+         SoupMessage * message,
+         const char *path,
+         GHashTable * query)
+{
+       guint revision_number;
+       gboolean res;
+
+       g_debug ("Path is %s.", path);
+
+       res = _get_revision_number_from_query (query, &revision_number);
+
+       if (res && revision_number != _get_revision_number (share)) {
+               /* MUPD update response
+                *      MSTT status
+                *      MUSR server revision
+                */
+               GNode *mupd;
+
+               mupd = dmap_structure_add (NULL, DMAP_CC_MUPD);
+               dmap_structure_add (mupd, DMAP_CC_MSTT,
+                                   (gint32) SOUP_STATUS_OK);
+               dmap_structure_add (mupd, DMAP_CC_MUSR,
+                                   (gint32)
+                                   _get_revision_number (share));
+
+               dmap_share_message_set_from_dmap_structure (share, message,
+                                                            mupd);
+               dmap_structure_destroy (mupd);
+       } else {
+               /* FIXME: This seems like a bug. It just leaks the
+                * message (and socket) without ever replying.
+                */
+               g_object_ref (message);
+               soup_server_pause_message (server, message);
+       }
+}
+
 static void
 _update_adapter (SoupServer * server,
                  SoupMessage * message,
@@ -203,778 +354,860 @@ _update_adapter (SoupServer * server,
 }
 
 static void
-_databases_adapter (SoupServer * server,
-                    SoupMessage * message,
-                    const char *path,
-                    GHashTable * query,
-                    SoupClientContext * context,
-                    DmapShare * share)
+_debug_param (gpointer key, gpointer val, G_GNUC_UNUSED gpointer user_data)
 {
-       DMAP_SHARE_GET_CLASS (share)->databases (share,
-                                                server,
-                                                message,
-                                                path, query, context);
+       g_debug ("%s %s", (char *) key, (char *) val);
 }
 
 static void
-_ctrl_int_adapter (SoupServer * server,
-                   SoupMessage * message,
-                   const char *path,
-                   GHashTable * query,
-                   SoupClientContext * context,
-                   DmapShare * share)
+_add_playlist_to_mlcl (G_GNUC_UNUSED guint id,
+                       DmapContainerRecord * record,
+                       gpointer _mb)
 {
-       DMAP_SHARE_GET_CLASS (share)->ctrl_int (share,
-                                               server,
-                                               message,
-                                               path, query, context);
-}
+       /* MLIT listing item
+        * MIID item id
+        * MPER persistent item id
+        * MINM item name
+        * MIMC item count
+        */
+       GNode *mlit;
+       guint num_songs;
+       gchar *name;
+       struct DmapMlclBits *mb = (struct DmapMlclBits *) _mb;
 
-static void
-_set_name (DmapShare * share, const char *name)
-{
-       GError *error;
+       num_songs = dmap_container_record_get_entry_count (record);
+       g_object_get (record, "name", &name, NULL);
 
-       g_return_if_fail (share != NULL);
+       /* FIXME: ITEM_ID, etc. is defined in DmapAvShare, so I can't use
+        * with dmap_share_client_requested() here (see add_entry_to_mlcl())
+        */
 
-       g_free (share->priv->name);
-       share->priv->name = g_strdup (name);
+       mlit = dmap_structure_add (mb->mlcl, DMAP_CC_MLIT);
+       dmap_structure_add (mlit, DMAP_CC_MIID,
+                           dmap_container_record_get_id (record));
+       /* we don't have a persistant ID for playlists, unfortunately */
+       dmap_structure_add (mlit, DMAP_CC_MPER,
+                           (gint64) dmap_container_record_get_id (record));
+       dmap_structure_add (mlit, DMAP_CC_MINM, name);
+       dmap_structure_add (mlit, DMAP_CC_MIMC, (gint32) num_songs);
 
-       if (share->priv->published) {
-               error = NULL;
-               dmap_mdns_publisher_rename_at_port (share->priv->
-                                                   publisher,
-                                                   share->priv->port,
-                                                   name,
-                                                   &error);
-               if (error != NULL) {
-                       g_warning ("Unable to change MDNS service name: %s",
-                                  error->message);
-                       g_error_free (error);
-               }
-       }
-}
+       /* FIXME: Is this getting music-specific? */
+       dmap_structure_add (mlit, DMAP_CC_FQUESCH, 0);
+       dmap_structure_add (mlit, DMAP_CC_MPCO, 0);
+       dmap_structure_add (mlit, DMAP_CC_AESP, 0);
+       dmap_structure_add (mlit, DMAP_CC_AEPP, 0);
+       dmap_structure_add (mlit, DMAP_CC_AEPS, 0);
+       dmap_structure_add (mlit, DMAP_CC_AESG, 0);
 
-static void
-_published (DmapShare * share,
-            G_GNUC_UNUSED DmapMdnsPublisher * publisher,
-            const char *name)
-{
-       if (share->priv->name == NULL || name == NULL) {
-               return;
-       }
+       g_free (name);
 
-       if (strcmp (share->priv->name, name) == 0) {
-               g_debug ("mDNS publish successful");
-               share->priv->published = TRUE;
-       }
+       return;
 }
 
 static void
-_published_adapter (DmapMdnsPublisher * publisher,
-                    const char *name,
-                    DmapShare * share)
+_write_dmap_preamble (SoupMessage * message, GNode * node)
 {
-       DMAP_SHARE_GET_CLASS (share)->published (share, publisher, name);
+       guint length;
+       gchar *data = dmap_structure_serialize (node, &length);
+
+       soup_message_body_append (message->response_body,
+                                 SOUP_MEMORY_TAKE, data, length);
+       dmap_structure_destroy (node);
 }
 
 static void
-_name_collision (DmapShare * share,
-                 G_GNUC_UNUSED DmapMdnsPublisher * publisher,
-                 const char *name)
+_write_next_mlit (SoupMessage * message, struct share_bitwise_t *share_bitwise)
 {
-       g_assert(NULL != name);
-       g_assert(NULL != share->priv->name);
+       if (share_bitwise->id_list == NULL) {
+               g_debug ("No more ID's, sending message complete.");
+               soup_message_body_complete (message->response_body);
+       } else {
+               gchar *data = NULL;
+               guint length;
+               DmapRecord *record;
+               struct DmapMlclBits mb = { NULL, 0, NULL };
 
-       g_warning ("Duplicate share name on mDNS; renaming share to %s", name);
+               record = share_bitwise->lookup_by_id (share_bitwise->db,
+                                                     GPOINTER_TO_UINT
+                                                     (share_bitwise->
+                                                      id_list->data));
 
-       _set_name (DMAP_SHARE (share), name);
+               mb.bits = share_bitwise->mb.bits;
+               mb.mlcl = dmap_structure_add (NULL, DMAP_CC_MLCL);
+               mb.share = share_bitwise->mb.share;
 
-       return;
-}
+               DMAP_SHARE_GET_CLASS (share_bitwise->mb.share)->
+                       add_entry_to_mlcl (GPOINTER_TO_UINT(share_bitwise->id_list->data), record, &mb);
+               data = dmap_structure_serialize (g_node_first_child (mb.mlcl),
+                                                &length);
 
-static void
-_name_collision_adapter (DmapMdnsPublisher * publisher,
-                       const char *name, DmapShare * share)
-{
-       DMAP_SHARE_GET_CLASS (share)->name_collision (share, publisher, name);
-}
-
-gboolean
-dmap_share_serve (DmapShare *share, GError **error)
-{
-       guint desired_port = DMAP_SHARE_GET_CLASS (share)->get_desired_port (share);
-       gboolean password_required, ok = FALSE;
-       GSList *listening_uri_list;
-       SoupURI *listening_uri;
-       gboolean ret;
-       GError *error2 = NULL;
-
-       password_required = (share->priv->auth_method != DMAP_SHARE_AUTH_METHOD_NONE);
+               soup_message_body_append (message->response_body,
+                                         SOUP_MEMORY_TAKE, data, length);
+               g_debug ("Sending ID %u.",
+                        GPOINTER_TO_UINT (share_bitwise->id_list->data));
+               dmap_structure_destroy (mb.mlcl);
 
-       if (password_required) {
-               SoupAuthDomain *auth_domain;
+               share_bitwise->id_list =
+                       g_slist_remove (share_bitwise->id_list,
+                                       share_bitwise->id_list->data);
 
-               auth_domain =
-                       soup_auth_domain_basic_new (SOUP_AUTH_DOMAIN_REALM,
-                                                   "Music Sharing",
-                                                   SOUP_AUTH_DOMAIN_ADD_PATH,
-                                                   "/login",
-                                                   SOUP_AUTH_DOMAIN_ADD_PATH,
-                                                   "/update",
-                                                   SOUP_AUTH_DOMAIN_ADD_PATH,
-                                                   "/database",
-                                                   SOUP_AUTH_DOMAIN_FILTER,
-                                                   dmap_share_soup_auth_filter,
-                                                   NULL);
-               soup_auth_domain_basic_set_auth_callback (auth_domain,
-                                                         (SoupAuthDomainBasicAuthCallback)
-                                                         _soup_auth_callback,
-                                                         g_object_ref
-                                                         (share),
-                                                         g_object_unref);
-               soup_server_add_auth_domain (share->priv->server, auth_domain);
+               g_object_unref (record);
        }
 
-       soup_server_add_handler (share->priv->server, "/server-info",
-                                (SoupServerCallback) _server_info_adapter,
-                                share, NULL);
-       soup_server_add_handler (share->priv->server, "/content-codes",
-                                (SoupServerCallback) _content_codes_adapter,
-                                share, NULL);
-       soup_server_add_handler (share->priv->server, "/login",
-                                (SoupServerCallback) _login_adapter,
-                                share, NULL);
-       soup_server_add_handler (share->priv->server, "/logout",
-                                (SoupServerCallback) _logout_adapter,
-                                share, NULL);
-       soup_server_add_handler (share->priv->server, "/update",
-                                (SoupServerCallback) _update_adapter,
-                                share, NULL);
-       soup_server_add_handler (share->priv->server, "/databases",
-                                (SoupServerCallback) _databases_adapter,
-                                share, NULL);
-       soup_server_add_handler (share->priv->server, "/ctrl-int",
-                                (SoupServerCallback) _ctrl_int_adapter,
-                                share, NULL);
+       soup_server_unpause_message (share_bitwise->share->priv->server, message);
+}
 
-       ret = soup_server_listen_all (share->priv->server, desired_port, 0, &error2);
-       if (ret == FALSE) {
-               g_debug ("Unable to start music sharing server on port %d: %s. "
-                        "Trying any open IPv6 port", desired_port, error2->message);
-               g_error_free(error2);
+static void
+_group_items (G_GNUC_UNUSED gpointer key, DmapRecord * record, GHashTable * groups)
+{
+       gchar *album, *artist;
+       GroupInfo *group_info;
+       gint64 group_id;
 
-               ret = soup_server_listen_all (share->priv->server, SOUP_ADDRESS_ANY_PORT,
-                                             0, error);
+       g_object_get (record, "songartist", &artist, "songalbum", &album,
+                     "songalbumid", &group_id, NULL);
+       if (!album) {
+               g_free (artist);
+               return;
        }
-
-       listening_uri_list = soup_server_get_uris (share->priv->server);
-       if (ret == FALSE || listening_uri_list == NULL) {
-               goto done;
+       group_info = g_hash_table_lookup (groups, album);
+       if (!group_info) {
+               group_info = g_new0 (GroupInfo, 1);
+               g_hash_table_insert (groups, album, group_info);
+               // They will be freed when the hash table is freed.
+               group_info->name = album;
+               group_info->artist = artist;
+               group_info->group_id = group_id;
+       } else {
+               g_free (album);
+               g_free (artist);
        }
+       (group_info->count)++;
+}
 
-       /* We can only expose one port, so no point checking more than one URI
-        * here. Maybe it somehow is listening on a different port for IPv4 vs.
-        * IPv6, but there's not much we can do.
-        */
-       listening_uri = listening_uri_list->data;
-       share->priv->port = soup_uri_get_port (listening_uri);
-       g_slist_free_full (listening_uri_list, (GDestroyNotify) soup_uri_free);
+static gint
+_group_info_cmp (gconstpointer group1, gconstpointer group2)
+{
+       return g_ascii_strcasecmp (((GroupInfo *) group1)->name,
+                                  ((GroupInfo *) group2)->name);
+}
 
-       g_debug ("Started DMAP server on port %u", share->priv->port);
+static DmapRecord *
+_lookup_adapter (GHashTable * ht, guint id)
+{
+       /* NOTE: each time this is called by _write_next_mlit(), the
+        * returned value will be unref'ed by _write_next_mlit(). We
+        * also need to destroy the GHashTable, so bump up the reference
+        * count so that both can happen. */
+       return g_object_ref (g_hash_table_lookup (ht, GUINT_TO_POINTER (id)));
+}
 
-       share->priv->server_active = TRUE;
+static void
+_accumulate_mlcl_size_and_ids (guint id,
+                               DmapRecord * record,
+                               struct share_bitwise_t *share_bitwise)
+{
+       share_bitwise->id_list = g_slist_append (share_bitwise->id_list, GUINT_TO_POINTER(id));
 
-       ok = TRUE;
+       /* Make copy and set mlcl to NULL so real MLCL does not get changed */
+       struct DmapMlclBits mb_copy = share_bitwise->mb;
 
-done:
-       g_assert((!ok && (NULL == error || NULL != *error))
-             || ( ok && (NULL == error || NULL == *error)));
+       mb_copy.mlcl = dmap_structure_add (NULL, DMAP_CC_MLCL);;
 
-       return ok;
+       DMAP_SHARE_GET_CLASS (share_bitwise->mb.share)->add_entry_to_mlcl (id,
+                                                                          record,
+                                                                         &mb_copy);
+       share_bitwise->size += dmap_structure_get_size (mb_copy.mlcl);
+
+       /* Minus eight because we do not want to add size of MLCL CC field + size field n times,
+        * where n == number of records.
+        */
+       share_bitwise->size -= 8;
+
+       /* Destroy created structures as we go. */
+       dmap_structure_destroy (mb_copy.mlcl);
 }
 
-static gboolean
-_server_stop (DmapShare * share)
+static void
+_accumulate_mlcl_size_and_ids_adapter (gpointer id,
+                                       DmapRecord * record,
+                                       struct share_bitwise_t *share_bitwise)
 {
-       g_debug ("Stopping music sharing server on port %d",
-                share->priv->port);
-
-       if (share->priv->server) {
-               soup_server_disconnect (share->priv->server);
-       }
+       _accumulate_mlcl_size_and_ids(GPOINTER_TO_UINT(id),
+                                     record,
+                                     share_bitwise);
+}
 
-       if (share->priv->session_ids) {
-               g_hash_table_remove_all (share->priv->session_ids);
+static void
+_chunked_message_finished (G_GNUC_UNUSED SoupMessage * message,
+                           struct share_bitwise_t *share_bitwise)
+{
+       g_debug ("Finished sending chunked data.");
+       if (share_bitwise->destroy) {
+               share_bitwise->destroy (share_bitwise->db);
        }
-
-       share->priv->server_active = FALSE;
-
-       return TRUE;
+       g_free (share_bitwise);
 }
 
-gboolean
-dmap_share_publish (DmapShare *share, GError **error)
+static DmapBits
+_parse_meta_str (const char *attrs, struct DmapMetaDataMap *mdm)
 {
-       gboolean ok;
-       gboolean password_required;
+       guint i;
+       DmapBits bits = 0;
 
-       password_required =
-               (share->priv->auth_method != DMAP_SHARE_AUTH_METHOD_NONE);
+       /* iTunes 8 uses meta=all for /databases/1/items query: */
+       if (strcmp (attrs, "all") == 0) {
+               bits = ~0;
+       } else {
+               gchar **attrsv;
 
-       ok = dmap_mdns_publisher_publish (share->priv->publisher,
-                                         share->priv->name,
-                                         share->priv->port,
-                                         DMAP_SHARE_GET_CLASS (share)->
-                                         get_type_of_service (share),
-                                         password_required,
-                                         share->priv->txt_records, error);
+               attrsv = g_strsplit (attrs, ",", -1);
 
-       if (ok == FALSE) {
-               goto done;
-       }
+               for (i = 0; attrsv[i]; i++) {
+                       guint j;
+                       gboolean found = FALSE;
 
-       g_debug ("Published DMAP server information to mdns");
+                       for (j = 0; mdm[j].tag; j++) {
+                               if (strcmp (mdm[j].tag, attrsv[i]) == 0) {
+                                       bits |= (((DmapBits) 1) << mdm[j].md);
+                                       found = TRUE;
+                               }
+                       }
 
-done:
-       return ok;
+                       if (found == FALSE) {
+                               g_debug ("Unknown meta request: %s",
+                                        attrsv[i]);
+                       }
+               }
+               g_strfreev (attrsv);
+       }
+
+       return bits;
 }
 
-static gboolean
-_publish_stop (DmapShare * share)
+static DmapBits
+_parse_meta (GHashTable * query, struct DmapMetaDataMap * mdm)
 {
-       GError *error;
-       gboolean ok = FALSE;
+       DmapBits bits = 0;
+       const gchar *attrs;
 
-       if (!share->priv->publisher) {
-               share->priv->published = FALSE;
+       attrs = g_hash_table_lookup (query, "meta");
+       if (attrs == NULL) {
                goto done;
        }
 
-       error = NULL;
-       ok = dmap_mdns_publisher_withdraw (share->priv->publisher,
-                                          share->priv->port,
-                                         &error);
-       if (error != NULL) {
-               g_warning
-                       ("Unable to withdraw music sharing service: %s",
-                        error->message);
-               g_error_free (error);
-       }
+       bits = _parse_meta_str (attrs, mdm);
 
 done:
-       return ok;
+       return bits;
 }
 
 static void
-_restart (DmapShare * share)
+_databases (DmapShare * share,
+            SoupServer * server,
+            SoupMessage * message,
+            const char *path,
+            GHashTable * query, SoupClientContext * context)
 {
-       GError *error = NULL;
-       gboolean res;
+       const char *rest_of_path;
 
-       _server_stop (share);
-       res = dmap_share_serve (share, &error);
-       if (NULL != error) {
-               g_signal_emit (share, _signals[ERROR], 0, error);
-               _publish_stop (share);
-       } else {
-               g_assert(FALSE != res);
-
-               /* To update information just publish again */
-               dmap_share_publish (share, NULL);
-       }
-}
-
-static void
-_maybe_restart (DmapShare * share)
-{
-       if (share->priv->published) {
-               _restart (share);
-       }
-}
+       g_debug ("Path is %s.", path);
+       g_hash_table_foreach (query, _debug_param, NULL);
 
-static void
-_set_password (DmapShare * share, const char *password)
-{
-       if (share->priv->password && password &&
-           0 == strcmp (password, share->priv->password)) {
+       if (!dmap_share_session_id_validate
+           (share, context, query, NULL)) {
+               soup_message_set_status (message, SOUP_STATUS_FORBIDDEN);
                goto done;
        }
 
-       g_free (share->priv->password);
-       share->priv->password = g_strdup (password);
-       if (password != NULL) {
-               share->priv->auth_method = DMAP_SHARE_AUTH_METHOD_PASSWORD;
-       } else {
-               share->priv->auth_method = DMAP_SHARE_AUTH_METHOD_NONE;
-       }
+       rest_of_path = strchr (path + 1, '/');
 
-       _maybe_restart (share);
+       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
+                */
+               GNode *avdb;
+               GNode *mlcl;
+               GNode *mlit;
 
-done:
-       return;
-}
+               avdb = dmap_structure_add (NULL, DMAP_CC_AVDB);
+               dmap_structure_add (avdb, DMAP_CC_MSTT,
+                                   (gint32) SOUP_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, share->priv->name);
+               dmap_structure_add (mlit, DMAP_CC_MIMC,
+                                   dmap_db_count (share->priv->db));
+               dmap_structure_add (mlit, DMAP_CC_MCTC, (gint32) 1);
 
-static void
-_set_property (GObject * object,
-                         guint prop_id,
-                         const GValue * value, GParamSpec * pspec)
-{
-       DmapShare *share = DMAP_SHARE (object);
+               dmap_share_message_set_from_dmap_structure (share, message,
+                                                            avdb);
+               dmap_structure_destroy (avdb);
+       } else if (g_ascii_strcasecmp ("/1/groups", rest_of_path) == 0) {
+               /* ADBS database songs
+                *      MSTT status
+                *      MUTY update type
+                *      MTCO specified total count
+                *      MRCO returned count
+                *      MLCL listing
+                *              MLIT
+                *                      attrs
+                *              MLIT
+                *              ...
+                */
 
-       switch (prop_id) {
-       case PROP_NAME:
-               _set_name (share, g_value_get_string (value));
-               break;
-       case PROP_PASSWORD:
-               _set_password (share, g_value_get_string (value));
-               break;
-       case PROP_DB:
-               if (share->priv->db) {
-                       g_object_unref(share->priv->db);
-               }
-               share->priv->db = g_value_dup_object (value);
-               break;
-       case PROP_CONTAINER_DB:
-               if (share->priv->container_db) {
-                       g_object_unref(share->priv->container_db);
+               GSList *filter_def;
+               gchar *record_query;
+               GHashTable *records = NULL;
+               GHashTable *groups;
+               GList *values;
+               GList *value;
+               gchar *sort_by;
+               GroupInfo *group_info;
+               GNode *agal;
+               GNode *mlcl;
+               GNode *mlit;
+               gint num;
+
+               if (g_strcmp0
+                   (g_hash_table_lookup (query, "group-type"),
+                    "albums") != 0) {
+                       g_warning ("Unsupported grouping");
+                       soup_message_set_status (message,
+                                                SOUP_STATUS_INTERNAL_SERVER_ERROR);
+                       goto done;
                }
-               share->priv->container_db = g_value_dup_object (value);
-               break;
-       case PROP_TRANSCODE_MIMETYPE:
-               g_free(share->priv->transcode_mimetype);
-               share->priv->transcode_mimetype = g_value_dup_string (value);
-               break;
-       case PROP_TXT_RECORDS:
-               g_strfreev (share->priv->txt_records);
-               share->priv->txt_records = g_value_dup_boxed (value);
-               break;
-       default:
-               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-               break;
-       }
-}
 
-static void
-_get_property (GObject * object,
-                         guint prop_id, GValue * value, GParamSpec * pspec)
-{
-       DmapShare *share = DMAP_SHARE (object);
+               record_query = g_hash_table_lookup (query, "query");
+               filter_def = dmap_share_build_filter (record_query);
+               records =
+                       dmap_db_apply_filter (DMAP_DB (share->priv->db),
+                                             filter_def);
 
-       switch (prop_id) {
-       case PROP_SERVER:
-               g_value_set_object (value, share->priv->server);
-               return;
-       case PROP_NAME:
-               g_value_set_string (value, share->priv->name);
-               break;
-       case PROP_PASSWORD:
-               g_value_set_string (value, share->priv->password);
-               break;
-       case PROP_REVISION_NUMBER:
-               g_value_set_uint (value,
-                                 dmap_share_get_revision_number
-                                 (DMAP_SHARE (object)));
-               break;
-       case PROP_AUTH_METHOD:
-               g_value_set_uint (value,
-                                 dmap_share_get_auth_method
-                                 (DMAP_SHARE (object)));
-               break;
-       case PROP_DB:
-               g_value_set_object (value, share->priv->db);
-               break;
-       case PROP_CONTAINER_DB:
-               g_value_set_object (value, share->priv->container_db);
-               break;
-       case PROP_TRANSCODE_MIMETYPE:
-               g_value_set_string (value, share->priv->transcode_mimetype);
-               break;
-       case PROP_TXT_RECORDS:
-               g_value_set_boxed (value, share->priv->txt_records);
-               break;
-       default:
-               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-               break;
-       }
-}
+               groups = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                               g_free, g_free);
+               g_hash_table_foreach (records, (GHFunc) _group_items, groups);
 
-static void
-_dispose (GObject * object)
-{
-       DmapShare *share = DMAP_SHARE (object);
+               agal = dmap_structure_add (NULL, DMAP_CC_AGAL);
+               dmap_structure_add (agal, DMAP_CC_MSTT,
+                                   (gint32) SOUP_STATUS_OK);
+               dmap_structure_add (agal, DMAP_CC_MUTY, 0);
 
-       if (share->priv->published) {
-               _publish_stop (share);
-       }
+               num = g_hash_table_size (groups);
+               dmap_structure_add (agal, DMAP_CC_MTCO, (gint32) num);
+               dmap_structure_add (agal, DMAP_CC_MRCO, (gint32) num);
 
-       if (share->priv->server_active) {
-               _server_stop (share);
-       }
+               mlcl = dmap_structure_add (agal, DMAP_CC_MLCL);
 
-       g_clear_object (&share->priv->publisher);
-       g_clear_object (&share->priv->server);
-       g_clear_object (&share->priv->db);
-       g_clear_object (&share->priv->container_db);
+               values = g_hash_table_get_values (groups);
+               if (g_hash_table_lookup (query, "include-sort-headers")) {
+                       sort_by = g_hash_table_lookup (query, "sort");
+                       if (g_strcmp0 (sort_by, "album") == 0) {
+                               values = g_list_sort (values, _group_info_cmp);
+                       } else {
+                               g_warning ("Unknown sort column: %s",
+                                          sort_by);
+                       }
+               }
 
-       G_OBJECT_CLASS (dmap_share_parent_class)->dispose (object);
-}
+               for (value = values; value; value = g_list_next (value)) {
+                       group_info = (GroupInfo *) value->data;
+                       mlit = dmap_structure_add (mlcl, DMAP_CC_MLIT);
+                       dmap_structure_add (mlit, DMAP_CC_MIID,
+                                           (gint) group_info->group_id);
+                       dmap_structure_add (mlit, DMAP_CC_MPER,
+                                           group_info->group_id);
+                       dmap_structure_add (mlit, DMAP_CC_MINM,
+                                           group_info->name);
+                       dmap_structure_add (mlit, DMAP_CC_ASAA,
+                                           group_info->artist);
+                       dmap_structure_add (mlit, DMAP_CC_MIMC,
+                                           (gint32) group_info->count);
 
-static void
-_finalize (GObject * object)
-{
-       DmapShare *share = DMAP_SHARE (object);
+                       // Free this now, since the hash free func won't.
+                       // Name will be freed when the hash table keys are freed.
+                       g_free (group_info->artist);
+               }
 
-       g_debug ("Finalizing DmapShare");
+               g_list_free (values);
+               dmap_share_free_filter (filter_def);
 
-       g_hash_table_destroy (share->priv->session_ids);
-       share->priv->session_ids = NULL;
+               dmap_share_message_set_from_dmap_structure (share, message,
+                                                            agal);
 
-       g_free (share->priv->name);
-       g_free (share->priv->password);
-       g_free (share->priv->transcode_mimetype);
-       g_strfreev (share->priv->txt_records);
+               g_hash_table_destroy (records);
+               g_hash_table_destroy (groups);
+               dmap_structure_destroy (agal);
+       } 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 DmapMlclBits mb = { NULL, 0, NULL };
+               struct share_bitwise_t *share_bitwise;
 
-       G_OBJECT_CLASS (dmap_share_parent_class)->finalize (object);
-}
+               record_query = g_hash_table_lookup (query, "query");
+               if (record_query) {
+                       GSList *filter_def;
 
-static void
-dmap_share_class_init (DmapShareClass * klass)
-{
-       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+                       filter_def = dmap_share_build_filter (record_query);
+                       records =
+                               dmap_db_apply_filter (DMAP_DB
+                                                     (share->priv->db),
+                                                     filter_def);
+                       num_songs = g_hash_table_size (records);
+                       g_debug ("Found %d records", num_songs);
+                       dmap_share_free_filter (filter_def);
+               } else {
+                       num_songs = dmap_db_count (share->priv->db);
+               }
 
-       object_class->get_property = _get_property;
-       object_class->set_property = _set_property;
-       object_class->dispose = _dispose;
-       object_class->finalize = _finalize;
+               map = DMAP_SHARE_GET_CLASS (share)->get_meta_data_map (share);
+               mb.bits = _parse_meta (query, map);
+               mb.share = share;
 
-       /* Pure virtual methods: */
-       klass->get_desired_port = NULL;
-       klass->get_type_of_service = NULL;
-       klass->message_add_standard_headers = NULL;
-       klass->get_meta_data_map = NULL;
-       klass->add_entry_to_mlcl = NULL;
-       klass->databases_browse_xxx = NULL;
-       klass->databases_items_xxx = NULL;
+               /* NOTE:
+                * We previously simply called foreach...add_entry_to_mlcl and later serialized the entire
+                * structure. This has the disadvantage that the entire response must be in memory before
+                * libsoup sends it to the client.
+                *
+                * Now, we go through the database in multiple passes (as an interim solution):
+                *
+                * 1. Accumulate the eventual size of the MLCL by creating and then free'ing each MLIT.
+                * 2. Generate the DAAP preamble ending with the MLCL (with size fudged for ADBS and MLCL).
+                * 3. Setup libsoup response headers, etc.
+                * 4. Setup callback to transmit DAAP preamble (_write_dmap_preamble)
+                * 5. Setup callback to transmit MLIT's (_write_next_mlit)
+                */
 
-       /* Virtual methods: */
-       klass->content_codes = dmap_share_content_codes;
-       klass->login = dmap_share_login;
-       klass->logout = dmap_share_logout;
-       klass->update = dmap_share_update;
-       klass->published = _published;
-       klass->name_collision = _name_collision;
-       klass->databases = dmap_share_databases;
-       klass->ctrl_int = dmap_share_ctrl_int;
+               /* 1: */
+               share_bitwise = g_new0 (struct share_bitwise_t, 1);
 
-       g_object_class_install_property (object_class,
-                                        PROP_SERVER,
-                                        g_param_spec_object ("server",
-                                                             "Soup Server",
-                                                             "Soup server",
-                                                             SOUP_TYPE_SERVER,
-                                                             G_PARAM_READABLE));
+               share_bitwise->share = share;
+               share_bitwise->mb = mb;
+               share_bitwise->id_list = NULL;
+               share_bitwise->size = 0;
+               if (record_query) {
+                       share_bitwise->db = records;
+                       share_bitwise->lookup_by_id = (ShareBitwiseLookupByIdFunc)
+                               _lookup_adapter;
+                       share_bitwise->destroy = (ShareBitwiseDestroyFunc) g_hash_table_destroy;
+                       g_hash_table_foreach (records,
+                                            (GHFunc) _accumulate_mlcl_size_and_ids_adapter,
+                                             share_bitwise);
+               } else {
+                       share_bitwise->db = share->priv->db;
+                       share_bitwise->lookup_by_id = (ShareBitwiseLookupByIdFunc) dmap_db_lookup_by_id;
+                       share_bitwise->destroy = NULL;
+                       dmap_db_foreach (share->priv->db,
+                                       (DmapIdRecordFunc) _accumulate_mlcl_size_and_ids,
+                                        share_bitwise);
+               }
 
-       g_object_class_install_property (object_class,
-                                        PROP_NAME,
-                                        g_param_spec_string ("name",
-                                                             "Name",
-                                                             "Share Name",
-                                                             NULL,
-                                                             G_PARAM_READWRITE));
-       g_object_class_install_property (object_class,
-                                        PROP_PASSWORD,
-                                        g_param_spec_string ("password",
-                                                             "Authentication password",
-                                                             "Authentication password",
-                                                             NULL,
-                                                             G_PARAM_READWRITE));
+               /* 2: */
+               adbs = dmap_structure_add (NULL, DMAP_CC_ADBS);
+               dmap_structure_add (adbs, DMAP_CC_MSTT,
+                                   (gint32) SOUP_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);
+               dmap_structure_increase_by_predicted_size (adbs,
+                                                          share_bitwise->
+                                                          size);
+               dmap_structure_increase_by_predicted_size (mb.mlcl,
+                                                          share_bitwise->
+                                                          size);
 
-       g_object_class_install_property (object_class,
-                                        PROP_REVISION_NUMBER,
-                                        g_param_spec_uint ("revision-number",
-                                                           "Revision number",
-                                                           "Revision number",
-                                                           0,
-                                                           G_MAXINT,
-                                                           0,
-                                                           G_PARAM_READWRITE));
+               /* 3: */
+               /* Free memory after each chunk sent out over network. */
+               soup_message_body_set_accumulate (message->response_body,
+                                                 FALSE);
+               soup_message_headers_append (message->response_headers,
+                                            "Content-Type",
+                                            "application/x-dmap-tagged");
+               DMAP_SHARE_GET_CLASS (share)->
+                       message_add_standard_headers (share, message);
+               soup_message_headers_set_content_length (message->
+                                                        response_headers,
+                                                        dmap_structure_get_size
+                                                        (adbs));
+               soup_message_set_status (message, SOUP_STATUS_OK);
 
-       g_object_class_install_property (object_class,
-                                        PROP_AUTH_METHOD,
-                                        g_param_spec_uint ("auth-method",
-                                                           "Authentication method",
-                                                           "Authentication method",
-                                                           DMAP_SHARE_AUTH_METHOD_NONE,
-                                                           DMAP_SHARE_AUTH_METHOD_PASSWORD,
-                                                           0,
-                                                           G_PARAM_READWRITE));
-       g_object_class_install_property (object_class,
-                                        PROP_DB,
-                                        g_param_spec_object ("db",
-                                                             "DB",
-                                                             "DB object",
-                                                              DMAP_TYPE_DB,
-                                                              G_PARAM_READWRITE |
-                                                              G_PARAM_CONSTRUCT_ONLY));
+               /* 4: */
+               g_signal_connect (message, "wrote_headers",
+                                 G_CALLBACK (_write_dmap_preamble), adbs);
 
-       g_object_class_install_property (object_class,
-                                        PROP_CONTAINER_DB,
-                                        g_param_spec_object ("container-db",
-                                                             "Container DB",
-                                                             "Container DB object",
-                                                              DMAP_TYPE_CONTAINER_DB,
-                                                              G_PARAM_READWRITE |
-                                                              G_PARAM_CONSTRUCT_ONLY));
+               /* 5: */
+               g_signal_connect (message, "wrote_chunk",
+                                 G_CALLBACK (_write_next_mlit),
+                                 share_bitwise);
+               g_signal_connect (message, "finished",
+                                 G_CALLBACK (_chunked_message_finished),
+                                 share_bitwise);
 
-       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));
+       } 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
+                *              ...
+                */
+               GNode *aply;
+               GNode *mlit;
+               struct DmapMetaDataMap *map;
+               struct DmapMlclBits mb = { NULL, 0, NULL };
 
-       g_object_class_install_property (object_class,
-                                        PROP_TXT_RECORDS,
-                                        g_param_spec_boxed ("txt-records",
-                                                            "TXT-Records",
-                                                            "Set TXT-Records used for MDNS publishing",
-                                                            G_TYPE_STRV,
-                                                            G_PARAM_READWRITE));
+               map = DMAP_SHARE_GET_CLASS (share)->get_meta_data_map (share);
+               mb.bits = _parse_meta (query, map);
+               mb.share = share;
 
-       _signals[ERROR] =
-               g_signal_new ("error",
-                              G_TYPE_FROM_CLASS (object_class),
-                              G_SIGNAL_RUN_FIRST,
-                              0, NULL, NULL,
-                              g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
-                              G_TYPE_POINTER);
-}
+               aply = dmap_structure_add (NULL, DMAP_CC_APLY);
+               dmap_structure_add (aply, DMAP_CC_MSTT,
+                                   (gint32) SOUP_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);
+               mb.mlcl = dmap_structure_add (aply, DMAP_CC_MLCL);
 
-static void
-dmap_share_init (DmapShare * share)
-{
-       share->priv = DMAP_SHARE_GET_PRIVATE (share);
+               /* Base playlist (playlist 1 contains all songs): */
+               mlit = dmap_structure_add (mb.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, share->priv->name);
+               dmap_structure_add (mlit, DMAP_CC_MIMC,
+                                   dmap_db_count (share->priv->db));
+               dmap_structure_add (mlit, DMAP_CC_FQUESCH, 0);
+               dmap_structure_add (mlit, DMAP_CC_MPCO, 0);
+               dmap_structure_add (mlit, DMAP_CC_AESP, 0);
+               dmap_structure_add (mlit, DMAP_CC_AEPP, 0);
+               dmap_structure_add (mlit, DMAP_CC_AEPS, 0);
+               dmap_structure_add (mlit, DMAP_CC_AESG, 0);
 
-       share->priv->revision_number = 5;
-       share->priv->auth_method = DMAP_SHARE_AUTH_METHOD_NONE;
-       share->priv->publisher = dmap_mdns_publisher_new ();
-       share->priv->server = soup_server_new (NULL, NULL);
+               dmap_structure_add (mlit, DMAP_CC_ABPL, (gchar) 1);
 
-       /* using direct since there is no g_uint_hash or g_uint_equal */
-       share->priv->session_ids =
-               g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
-                                      g_free);
+               dmap_container_db_foreach (share->priv->container_db,
+                                          (DmapIdContainerRecordFunc)
+                                          _add_playlist_to_mlcl,
+                                          &mb);
 
-       g_signal_connect_object (share->priv->publisher,
-                                "published",
-                                G_CALLBACK (_published_adapter), share, 0);
-       g_signal_connect_object (share->priv->publisher,
-                                "name-collision",
-                                G_CALLBACK (_name_collision_adapter),
-                                share, 0);
-}
+               dmap_share_message_set_from_dmap_structure (share, message,
+                                                            aply);
+               dmap_structure_destroy (aply);
+       } 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 DmapMlclBits mb = { NULL, 0, NULL };
+               guint pl_id;
+               gchar *record_query;
+               GSList *filter_def;
+               GHashTable *records;
 
-guint
-dmap_share_get_auth_method (DmapShare * share)
-{
-       return share->priv->auth_method;
-}
+               map = DMAP_SHARE_GET_CLASS (share)->get_meta_data_map (share);
+               mb.bits = _parse_meta (query, map);
+               mb.share = share;
 
-guint
-dmap_share_get_revision_number (DmapShare * share)
-{
-       return share->priv->revision_number;
-}
+               apso = dmap_structure_add (NULL, DMAP_CC_APSO);
+               dmap_structure_add (apso, DMAP_CC_MSTT,
+                                   (gint32) SOUP_STATUS_OK);
+               dmap_structure_add (apso, DMAP_CC_MUTY, 0);
 
-static gboolean
-_get_session_id (GHashTable * query, guint32 * id)
-{
-       gboolean ok = FALSE;
-       char *session_id_str;
-       guint32 session_id;
+               if (g_ascii_strcasecmp ("/1/items", rest_of_path + 13) == 0) {
+                       GList *id;
+                       gchar *sort_by;
+                       GList *keys;
 
-       session_id_str = g_hash_table_lookup (query, "session-id");
-       if (session_id_str == NULL) {
-               g_warning ("Session id not found.");
-               goto done;
-       }
+                       record_query = g_hash_table_lookup (query, "query");
+                       filter_def = dmap_share_build_filter (record_query);
+                       records =
+                               dmap_db_apply_filter (DMAP_DB
+                                                     (share->priv->db),
+                                                     filter_def);
+                       gint32 num_songs = g_hash_table_size (records);
 
-       session_id = (guint32) strtoul (session_id_str, NULL, 10);
-       if (id != NULL) {
-               *id = session_id;
-       }
+                       g_debug ("Found %d records", num_songs);
+                       dmap_share_free_filter (filter_def);
 
-       ok = TRUE;
+                       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);
 
-done:
-       return ok;
-}
+                       sort_by = g_hash_table_lookup (query, "sort");
+                       keys = g_hash_table_get_keys (records);
+                       if (g_strcmp0 (sort_by, "album") == 0) {
+                               keys = g_list_sort_with_data (keys,
+                                                             (GCompareDataFunc)
+                                                             dmap_av_record_cmp_by_album,
+                                                             share->priv->
+                                                             db);
+                       } else if (sort_by != NULL) {
+                               g_warning ("Unknown sort column: %s",
+                                          sort_by);
+                       }
 
-gboolean
-dmap_share_get_revision_number_from_query (GHashTable * query,
-                                           guint * number)
-{
-       gboolean ok = FALSE;
-       char *revision_number_str;
-       guint revision_number;
+                       for (id = keys; id; id = id->next) {
+                               (*
+                                (DMAP_SHARE_GET_CLASS (share)->
+                                 add_entry_to_mlcl)) (GPOINTER_TO_UINT(id->data),
+                                                      g_hash_table_lookup
+                                                      (records, id->data),
+                                                      &mb);
+                       }
 
-       revision_number_str = g_hash_table_lookup (query, "revision-number");
-       if (revision_number_str == NULL) {
-               g_warning
-                       ("Client asked for an update without a rev. number");
-               goto done;
-       }
+                       g_list_free (keys);
+                       g_hash_table_destroy (records);
+               } else {
+                       pl_id = strtoul (rest_of_path + 14, NULL, 10);
+                       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);
 
-       revision_number = strtoul (revision_number_str, NULL, 10);
-       if (number != NULL) {
-               *number = revision_number;
-       }
+                               dmap_db_foreach (share->priv->db,
+                                                DMAP_SHARE_GET_CLASS
+                                                (share)->add_entry_to_mlcl,
+                                                &mb);
+                       } else {
+                               DmapContainerRecord *record;
+                               DmapDb *entries;
+                               guint num_songs;
 
-       ok = TRUE;
+                               record = dmap_container_db_lookup_by_id
+                                       (share->priv->container_db, pl_id);
+                               entries =
+                                       dmap_container_record_get_entries
+                                       (record);
+                               /* FIXME: what if entries is NULL (handled in dmapd but should be [also] 
handled here)? */
+                               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,
+                                                DMAP_SHARE_GET_CLASS
+                                                (share)->add_entry_to_mlcl,
+                                                &mb);
+
+                               g_object_unref (entries);
+                               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,
+                                                                   message,
+                                                                   path,
+                                                                   query);
+       } 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);
+       } else if (g_str_has_prefix (rest_of_path, "/1/groups/") &&
+                  g_str_has_suffix (rest_of_path, "/extra_data/artwork")) {
+               /* We don't yet implement cover requests here, say no cover */
+               g_debug ("Assuming no artwork for requested group/album");
+               soup_message_set_status (message, SOUP_STATUS_NOT_FOUND);
+       } else {
+               g_warning ("Unhandled: %s", path);
+       }
 
 done:
-       return ok;
+       return;
 }
 
-gboolean
-dmap_share_session_id_validate (DmapShare * share,
-                                SoupClientContext * context,
-                                GHashTable * query, guint32 * id)
+static void
+_databases_adapter (SoupServer * server,
+                    SoupMessage * message,
+                    const char *path,
+                    GHashTable * query,
+                    SoupClientContext * context,
+                    DmapShare * share)
 {
-       gboolean ok = FALSE;
-       guint32 session_id;
-       gboolean res;
-       const char *addr;
-       const char *remote_address;
+       DMAP_SHARE_GET_CLASS (share)->databases (share,
+                                                server,
+                                                message,
+                                                path, query, context);
+}
 
-       if (id) {
-               *id = 0;
+static void
+_ctrl_int (G_GNUC_UNUSED DmapShare * share,
+           G_GNUC_UNUSED SoupServer * server,
+           G_GNUC_UNUSED SoupMessage * message,
+           const char *path,
+           GHashTable * query,
+           G_GNUC_UNUSED SoupClientContext * context)
+{
+       g_debug ("Path is %s.", path);
+       if (query) {
+               g_hash_table_foreach (query, _debug_param, NULL);
        }
 
-       res = _get_session_id (query, &session_id);
-       if (!res) {
-               g_warning ("Validation failed: Unable to parse session id");
-               goto done;
-       }
+       g_debug ("ctrl-int not implemented");
+}
 
-       /* check hash for remote address */
-       addr = g_hash_table_lookup (share->priv->session_ids,
-                                   GUINT_TO_POINTER (session_id));
-       if (addr == NULL) {
-               g_warning
-                       ("Validation failed: Unable to lookup session id %u",
-                        session_id);
-               goto done;
-       }
-
-       remote_address = soup_client_context_get_host (context);
-       g_debug ("Validating session id %u from %s matches %s",
-                session_id, remote_address, addr);
-       if (remote_address == NULL || strcmp (addr, remote_address) != 0) {
-               g_warning
-                       ("Validation failed: Remote address does not match stored address");
-               goto done;
-       }
-
-       if (id) {
-               *id = session_id;
-       }
-
-       ok = TRUE;
-
-done:
-       return ok;
+static void
+_ctrl_int_adapter (SoupServer * server,
+                   SoupMessage * message,
+                   const char *path,
+                   GHashTable * query,
+                   SoupClientContext * context,
+                   DmapShare * share)
+{
+       DMAP_SHARE_GET_CLASS (share)->ctrl_int (share,
+                                               server,
+                                               message,
+                                               path, query, context);
 }
 
-static guint32
-_session_id_generate (void)
+static void
+_set_name (DmapShare * share, const char *name)
 {
-       guint32 id;
+       GError *error;
 
-       id = g_random_int ();
+       g_return_if_fail (share != NULL);
 
-       return id;
+       g_free (share->priv->name);
+       share->priv->name = g_strdup (name);
+
+       if (share->priv->published) {
+               error = NULL;
+               dmap_mdns_publisher_rename_at_port (share->priv->
+                                                   publisher,
+                                                   share->priv->port,
+                                                   name,
+                                                   &error);
+               if (error != NULL) {
+                       g_warning ("Unable to change MDNS service name: %s",
+                                  error->message);
+                       g_error_free (error);
+               }
+       }
 }
 
-guint32
-dmap_share_session_id_create (DmapShare * share, SoupClientContext * context)
+static void
+_published (DmapShare * share,
+            G_GNUC_UNUSED DmapMdnsPublisher * publisher,
+            const char *name)
 {
-       guint32 id;
-       const char *addr;
-       char *remote_address;
-
-       do {
-               /* create a unique session id */
-               id = _session_id_generate ();
-               g_debug ("Generated session id %u", id);
-
-               /* if already used, try again */
-               addr = g_hash_table_lookup (share->priv->session_ids,
-                                           GUINT_TO_POINTER (id));
-       } while (addr != NULL);
-
-       /* store session id and remote address */
-       /* FIXME, warning, this fails against libsoup-2.33.90-1.fc15.x86_64 with:
-        * (dmapd:12917): libsoup-CRITICAL **: soup_address_get_physical: assertion `SOUP_IS_ADDRESS (addr)' 
failed
-        * Is this a bug in libsoup or libdmapsharing?
-        */
-       remote_address = g_strdup (soup_client_context_get_host (context));
-       g_hash_table_insert (share->priv->session_ids, GUINT_TO_POINTER (id),
-                            remote_address);
+       if (share->priv->name == NULL || name == NULL) {
+               return;
+       }
 
-       return id;
+       if (strcmp (share->priv->name, name) == 0) {
+               g_debug ("mDNS publish successful");
+               share->priv->published = TRUE;
+       }
 }
 
-void
-dmap_share_session_id_remove (DmapShare * share,
-                              guint32 id)
+static void
+_published_adapter (DmapMdnsPublisher * publisher,
+                    const char *name,
+                    DmapShare * share)
 {
-       g_hash_table_remove (share->priv->session_ids, GUINT_TO_POINTER (id));
+       DMAP_SHARE_GET_CLASS (share)->published (share, publisher, name);
 }
 
-void
-dmap_share_message_set_from_dmap_structure (DmapShare * share,
-                                            SoupMessage * message,
-                                            GNode * structure)
+static void
+_name_collision (DmapShare * share,
+                 G_GNUC_UNUSED DmapMdnsPublisher * publisher,
+                 const char *name)
 {
-       gchar *resp;
-       guint length;
-
-       resp = dmap_structure_serialize (structure, &length);
-
-       if (resp == NULL) {
-               g_warning ("Serialize gave us null?");
-               return;
-       }
-
-       soup_message_set_response (message, "application/x-dmap-tagged",
-                                  SOUP_MEMORY_TAKE, resp, length);
+       g_assert(NULL != name);
+       g_assert(NULL != share->priv->name);
 
-       DMAP_SHARE_GET_CLASS (share)->message_add_standard_headers (share,
-                                                                   message);
+       g_warning ("Duplicate share name on mDNS; renaming share to %s", name);
 
-       soup_message_set_status (message, SOUP_STATUS_OK);
-}
+       _set_name (DMAP_SHARE (share), name);
 
-gboolean
-dmap_share_client_requested (DmapBits bits, gint field)
-{
-       return 0 != (bits & (((DmapBits) 1) << field));
+       return;
 }
 
-gboolean
-dmap_share_uri_is_local (const char *text_uri)
+static void
+_name_collision_adapter (DmapMdnsPublisher * publisher,
+                       const char *name, DmapShare * share)
 {
-       return g_str_has_prefix (text_uri, "file://");
+       DMAP_SHARE_GET_CLASS (share)->name_collision (share, publisher, name);
 }
 
-gboolean
-dmap_share_soup_auth_filter (G_GNUC_UNUSED SoupAuthDomain * auth_domain,
-                             SoupMessage * msg, G_GNUC_UNUSED gpointer user_data)
+static gboolean
+_soup_auth_filter (G_GNUC_UNUSED SoupAuthDomain * auth_domain,
+                   SoupMessage * msg, G_GNUC_UNUSED gpointer user_data)
 {
        gboolean ok = FALSE;
        const char *path;
@@ -996,1139 +1229,905 @@ done:
        return ok;
 }
 
-void
-dmap_share_content_codes (DmapShare * share,
-                          SoupMessage * message,
-                          const char *path,
-                          G_GNUC_UNUSED SoupClientContext * context)
+gboolean
+dmap_share_serve (DmapShare *share, GError **error)
 {
-/* MCCR content codes response
- *     MSTT status
- *     MDCL dictionary
- *             MCNM content codes number
- *             MCNA content codes name
- *             MCTY content codes type
- *     MDCL dictionary
- *     ...
- */
-       const DmapContentCodeDefinition *defs;
-       guint num_defs = 0;
-       guint i;
-       GNode *mccr;
-
-       g_debug ("Path is %s.", path);
-
-       defs = dmap_structure_content_codes (&num_defs);
+       guint desired_port = DMAP_SHARE_GET_CLASS (share)->get_desired_port (share);
+       gboolean password_required, ok = FALSE;
+       GSList *listening_uri_list;
+       SoupURI *listening_uri;
+       gboolean ret;
+       GError *error2 = NULL;
 
-       mccr = dmap_structure_add (NULL, DMAP_CC_MCCR);
-       dmap_structure_add (mccr, DMAP_CC_MSTT, (gint32) SOUP_STATUS_OK);
+       password_required = (share->priv->auth_method != DMAP_SHARE_AUTH_METHOD_NONE);
 
-       for (i = 0; i < num_defs; i++) {
-               GNode *mdcl;
+       if (password_required) {
+               SoupAuthDomain *auth_domain;
 
-               mdcl = dmap_structure_add (mccr, DMAP_CC_MDCL);
-               dmap_structure_add (mdcl, DMAP_CC_MCNM,
-                                   dmap_structure_cc_string_as_int32 (defs[i].string));
-               dmap_structure_add (mdcl, DMAP_CC_MCNA, defs[i].name);
-               dmap_structure_add (mdcl, DMAP_CC_MCTY,
-                                   (gint32) defs[i].type);
+               auth_domain =
+                       soup_auth_domain_basic_new (SOUP_AUTH_DOMAIN_REALM,
+                                                   "Music Sharing",
+                                                   SOUP_AUTH_DOMAIN_ADD_PATH,
+                                                   "/login",
+                                                   SOUP_AUTH_DOMAIN_ADD_PATH,
+                                                   "/update",
+                                                   SOUP_AUTH_DOMAIN_ADD_PATH,
+                                                   "/database",
+                                                   SOUP_AUTH_DOMAIN_FILTER,
+                                                   _soup_auth_filter,
+                                                   NULL);
+               soup_auth_domain_basic_set_auth_callback (auth_domain,
+                                                         (SoupAuthDomainBasicAuthCallback)
+                                                         _soup_auth_callback,
+                                                         g_object_ref
+                                                         (share),
+                                                         g_object_unref);
+               soup_server_add_auth_domain (share->priv->server, auth_domain);
        }
 
-       dmap_share_message_set_from_dmap_structure (share, message, mccr);
-       dmap_structure_destroy (mccr);
-}
-
-void
-dmap_share_login (DmapShare * share,
-                  SoupMessage * message,
-                  const char *path,
-                  G_GNUC_UNUSED GHashTable * query,
-                  SoupClientContext * context)
-{
-/* MLOG login response
- *     MSTT status
- *     MLID session id
- */
-       GNode *mlog;
-       guint32 session_id;
+       soup_server_add_handler (share->priv->server, "/server-info",
+                                (SoupServerCallback) _server_info_adapter,
+                                share, NULL);
+       soup_server_add_handler (share->priv->server, "/content-codes",
+                                (SoupServerCallback) _content_codes_adapter,
+                                share, NULL);
+       soup_server_add_handler (share->priv->server, "/login",
+                                (SoupServerCallback) _login_adapter,
+                                share, NULL);
+       soup_server_add_handler (share->priv->server, "/logout",
+                                (SoupServerCallback) _logout_adapter,
+                                share, NULL);
+       soup_server_add_handler (share->priv->server, "/update",
+                                (SoupServerCallback) _update_adapter,
+                                share, NULL);
+       soup_server_add_handler (share->priv->server, "/databases",
+                                (SoupServerCallback) _databases_adapter,
+                                share, NULL);
+       soup_server_add_handler (share->priv->server, "/ctrl-int",
+                                (SoupServerCallback) _ctrl_int_adapter,
+                                share, NULL);
 
-       g_debug ("Path is %s.", path);
+       ret = soup_server_listen_all (share->priv->server, desired_port, 0, &error2);
+       if (ret == FALSE) {
+               g_debug ("Unable to start music sharing server on port %d: %s. "
+                        "Trying any open IPv6 port", desired_port, error2->message);
+               g_error_free(error2);
 
-       session_id = dmap_share_session_id_create (share, context);
+               ret = soup_server_listen_all (share->priv->server, SOUP_ADDRESS_ANY_PORT,
+                                             0, error);
+       }
 
-       mlog = dmap_structure_add (NULL, DMAP_CC_MLOG);
-       dmap_structure_add (mlog, DMAP_CC_MSTT, (gint32) SOUP_STATUS_OK);
-       dmap_structure_add (mlog, DMAP_CC_MLID, session_id);
+       listening_uri_list = soup_server_get_uris (share->priv->server);
+       if (ret == FALSE || listening_uri_list == NULL) {
+               goto done;
+       }
 
-       dmap_share_message_set_from_dmap_structure (share, message, mlog);
-       dmap_structure_destroy (mlog);
-}
+       /* We can only expose one port, so no point checking more than one URI
+        * here. Maybe it somehow is listening on a different port for IPv4 vs.
+        * IPv6, but there's not much we can do.
+        */
+       listening_uri = listening_uri_list->data;
+       share->priv->port = soup_uri_get_port (listening_uri);
+       g_slist_free_full (listening_uri_list, (GDestroyNotify) soup_uri_free);
 
-void
-dmap_share_logout (DmapShare * share,
-                   SoupMessage * message,
-                   const char *path,
-                   GHashTable * query, SoupClientContext * context)
-{
-       int status;
-       guint32 id;
+       g_debug ("Started DMAP server on port %u", share->priv->port);
 
-       g_debug ("Path is %s.", path);
+       share->priv->server_active = TRUE;
 
-       if (dmap_share_session_id_validate
-           (share, context, query, &id)) {
-               dmap_share_session_id_remove (share, id);
+       ok = TRUE;
 
-               status = SOUP_STATUS_NO_CONTENT;
-       } else {
-               status = SOUP_STATUS_FORBIDDEN;
-       }
+done:
+       g_assert((!ok && (NULL == error || NULL != *error))
+             || ( ok && (NULL == error || NULL == *error)));
 
-       soup_message_set_status (message, status);
+       return ok;
 }
 
-void
-dmap_share_update (DmapShare * share,
-                   SoupServer * server,
-                   SoupMessage * message,
-                   const char *path,
-                   GHashTable * query)
+static gboolean
+_server_stop (DmapShare * share)
 {
-       guint revision_number;
-       gboolean res;
-
-       g_debug ("Path is %s.", path);
+       g_debug ("Stopping music sharing server on port %d",
+                share->priv->port);
 
-       res = dmap_share_get_revision_number_from_query (query,
-                                                         &revision_number);
+       if (share->priv->server) {
+               soup_server_disconnect (share->priv->server);
+       }
 
-       if (res && revision_number != dmap_share_get_revision_number (share)) {
-               /* MUPD update response
-                *      MSTT status
-                *      MUSR server revision
-                */
-               GNode *mupd;
+       if (share->priv->session_ids) {
+               g_hash_table_remove_all (share->priv->session_ids);
+       }
 
-               mupd = dmap_structure_add (NULL, DMAP_CC_MUPD);
-               dmap_structure_add (mupd, DMAP_CC_MSTT,
-                                   (gint32) SOUP_STATUS_OK);
-               dmap_structure_add (mupd, DMAP_CC_MUSR,
-                                   (gint32)
-                                   dmap_share_get_revision_number (share));
+       share->priv->server_active = FALSE;
 
-               dmap_share_message_set_from_dmap_structure (share, message,
-                                                            mupd);
-               dmap_structure_destroy (mupd);
-       } else {
-               /* FIXME: This seems like a bug. It just leaks the
-                * message (and socket) without ever replying.
-                */
-               g_object_ref (message);
-               soup_server_pause_message (server, message);
-       }
+       return TRUE;
 }
 
-DmapBits
-dmap_share_parse_meta_str (const char *attrs, struct DmapMetaDataMap *mdm)
+gboolean
+dmap_share_publish (DmapShare *share, GError **error)
 {
-       guint i;
-       DmapBits bits = 0;
-
-       /* iTunes 8 uses meta=all for /databases/1/items query: */
-       if (strcmp (attrs, "all") == 0) {
-               bits = ~0;
-       } else {
-               gchar **attrsv;
-
-               attrsv = g_strsplit (attrs, ",", -1);
+       gboolean ok;
+       gboolean password_required;
 
-               for (i = 0; attrsv[i]; i++) {
-                       guint j;
-                       gboolean found = FALSE;
+       password_required =
+               (share->priv->auth_method != DMAP_SHARE_AUTH_METHOD_NONE);
 
-                       for (j = 0; mdm[j].tag; j++) {
-                               if (strcmp (mdm[j].tag, attrsv[i]) == 0) {
-                                       bits |= (((DmapBits) 1) << mdm[j].md);
-                                       found = TRUE;
-                               }
-                       }
+       ok = dmap_mdns_publisher_publish (share->priv->publisher,
+                                         share->priv->name,
+                                         share->priv->port,
+                                         DMAP_SHARE_GET_CLASS (share)->
+                                         get_type_of_service (share),
+                                         password_required,
+                                         share->priv->txt_records, error);
 
-                       if (found == FALSE) {
-                               g_debug ("Unknown meta request: %s",
-                                        attrsv[i]);
-                       }
-               }
-               g_strfreev (attrsv);
+       if (ok == FALSE) {
+               goto done;
        }
 
-       return bits;
+       g_debug ("Published DMAP server information to mdns");
+
+done:
+       return ok;
 }
 
-DmapBits
-dmap_share_parse_meta (GHashTable * query, struct DmapMetaDataMap * mdm)
+static gboolean
+_publish_stop (DmapShare * share)
 {
-       DmapBits bits = 0;
-       const gchar *attrs;
+       GError *error;
+       gboolean ok = FALSE;
 
-       attrs = g_hash_table_lookup (query, "meta");
-       if (attrs == NULL) {
+       if (!share->priv->publisher) {
+               share->priv->published = FALSE;
                goto done;
        }
 
-       bits = dmap_share_parse_meta_str (attrs, mdm);
+       error = NULL;
+       ok = dmap_mdns_publisher_withdraw (share->priv->publisher,
+                                          share->priv->port,
+                                         &error);
+       if (error != NULL) {
+               g_warning
+                       ("Unable to withdraw music sharing service: %s",
+                        error->message);
+               g_error_free (error);
+       }
 
 done:
-       return bits;
+       return ok;
 }
 
-void
-dmap_share_add_playlist_to_mlcl (G_GNUC_UNUSED guint id,
-                                 DmapContainerRecord * record,
-                                 gpointer _mb)
+static void
+_restart (DmapShare * share)
 {
-       /* MLIT listing item
-        * MIID item id
-        * MPER persistent item id
-        * MINM item name
-        * MIMC item count
-        */
-       GNode *mlit;
-       guint num_songs;
-       gchar *name;
-       struct DmapMlclBits *mb = (struct DmapMlclBits *) _mb;
-
-       num_songs = dmap_container_record_get_entry_count (record);
-       g_object_get (record, "name", &name, NULL);
-
-       /* FIXME: ITEM_ID, etc. is defined in DmapAvShare, so I can't use
-        * with dmap_share_client_requested() here (see add_entry_to_mlcl())
-        */
-
-       mlit = dmap_structure_add (mb->mlcl, DMAP_CC_MLIT);
-       dmap_structure_add (mlit, DMAP_CC_MIID,
-                           dmap_container_record_get_id (record));
-       /* we don't have a persistant ID for playlists, unfortunately */
-       dmap_structure_add (mlit, DMAP_CC_MPER,
-                           (gint64) dmap_container_record_get_id (record));
-       dmap_structure_add (mlit, DMAP_CC_MINM, name);
-       dmap_structure_add (mlit, DMAP_CC_MIMC, (gint32) num_songs);
-
-       /* FIXME: Is this getting music-specific? */
-       dmap_structure_add (mlit, DMAP_CC_FQUESCH, 0);
-       dmap_structure_add (mlit, DMAP_CC_MPCO, 0);
-       dmap_structure_add (mlit, DMAP_CC_AESP, 0);
-       dmap_structure_add (mlit, DMAP_CC_AEPP, 0);
-       dmap_structure_add (mlit, DMAP_CC_AEPS, 0);
-       dmap_structure_add (mlit, DMAP_CC_AESG, 0);
+       GError *error = NULL;
+       gboolean res;
 
-       g_free (name);
+       _server_stop (share);
+       res = dmap_share_serve (share, &error);
+       if (NULL != error) {
+               g_signal_emit (share, _signals[ERROR], 0, error);
+               _publish_stop (share);
+       } else {
+               g_assert(FALSE != res);
 
-       return;
+               /* To update information just publish again */
+               dmap_share_publish (share, NULL);
+       }
 }
 
-GSList *
-dmap_share_build_filter (gchar * filterstr)
+static void
+_maybe_restart (DmapShare * share)
 {
-       /* 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')
-        * or (DACP):
-        * ('com.apple.itunes.mediakind:1','com.apple.itunes.mediakind:32') 'daap.songartist!:'
-        */
-
-       gchar *next_char;
-       GString *value;
-
-       //gboolean save_value;
-       gboolean is_key;
-       gboolean is_value;
-       gboolean new_group;
-       gboolean accept;
-       gboolean negate;
-       gint parentheses_count;
-       gint quotes_count;
-       DmapDbFilterDefinition *def;
-
-       GSList *list = NULL;
-       GSList *filter = NULL;
-
-       g_debug ("Filter string is %s.", filterstr);
+       if (share->priv->published) {
+               _restart (share);
+       }
+}
 
-       if (filterstr == NULL) {
+static void
+_set_password (DmapShare * share, const char *password)
+{
+       if (share->priv->password && password &&
+           0 == strcmp (password, share->priv->password)) {
                goto done;
        }
 
-       next_char = filterstr;
+       g_free (share->priv->password);
+       share->priv->password = g_strdup (password);
+       if (password != NULL) {
+               share->priv->auth_method = DMAP_SHARE_AUTH_METHOD_PASSWORD;
+       } else {
+               share->priv->auth_method = DMAP_SHARE_AUTH_METHOD_NONE;
+       }
 
-       parentheses_count = 0;
-       quotes_count = 0;
-       is_key = TRUE;
-       is_value = FALSE;
-       new_group = FALSE;
-       negate = FALSE;
-       value = NULL;
-       def = NULL;
-
-       // The query string is divided in groups of AND separated by a space,
-       // each group containing queries of OR separated by comma enclosed in
-       // parentheses. Queries are key, value pairs separated by ':' enclosed 
-       // in quotes or not.
-       // The result from this is a list of lists. Here is an example:
-       // String is ('com.apple.itunes.mediakind:1','com.apple.itunes.mediakind:32') 'daap.songartist!:'
-       // list:
-       // |-> filter1: -> com.apple.itunes.mediakind = 1
-       // |   |        -> com.apple.itunes.mediakind = 32
-       // |-> filter1: -> daap.songartist! = (null)
-       // This basically means that the query is (filter1 AND filter2), and
-       // filter1 is (com.apple.itunes.mediakind = 1) OR (com.apple.itunes.mediakind = 32)
-       while (TRUE) {
-               // We check each character to see if it should be included in
-               // the current value (the current value will become the query key
-               // or the query value). Anything that is not a special character
-               // handled below will be accepted (this means weird characters
-               // might appear in names).
-               // Handling of unicode characters is unknown, but as long it 
-               // doesn't appear as a character handled below, it will be
-               // included.
-               // This parser will result in unknown behaviour on bad-formatted
-               // queries (it does not check for syntax errors), but should not 
-               // crash. In this way it may accept much more syntaxes then
-               // other parsers.
-               accept = FALSE;
-               // A slash can escape characters such as ', so add the character
-               // after the slash.
-               if (*next_char == '\\') {
-                       accept = TRUE;
-                       next_char++;
-               } else {
-                       switch (*next_char) {
-                       case '(':
-                               if (is_value) {
-                                       accept = TRUE;
-                               } else {
-                                       parentheses_count++;
-                               }
-                               break;
-                       case ')':
-                               if (is_value) {
-                                       accept = TRUE;
-                               } else {
-                                       parentheses_count--;
-                               }
-                               break;
-                       case '\'':
-                               if (quotes_count > 0) {
-                                       quotes_count = 0;
-                               } else {
-                                       quotes_count = 1;
-                               }
-                               break;
-                       case ' ':
-                               if (is_value) {
-                                       accept = TRUE;
-                               } else {
-                                       new_group = TRUE;
-                               }
-                               break;
-                       case ':':
-                               // Inside values, they will be included in the 
-                               // query string, otherwise it indicates there
-                               // will be a value next.
-                               if (is_value) {
-                                       accept = TRUE;
-                               } else if (is_key) {
-                                       is_value = TRUE;
-                               }
-                               break;
-                       case '!':
-                               if (is_value) {
-                                       accept = TRUE;
-                               } else if (is_key && value) {
-                                       negate = TRUE;
-                               }
-                               break;
-                       case ',':
-                       case '+':
-                               // Accept these characters only if inside quotes
-                               if (is_value) {
-                                       accept = TRUE;
-                               }
-                               break;
-                       case '\0':
-                               // Never accept
-                               break;
-                       default:
-                               accept = TRUE;
-                               break;
-                       }
-               }
-               //g_debug ("Char: %c, Accept: %s", *next_char, accept?"TRUE":"FALSE");
-               //Is the next character to be accepted?
-               if (accept) {
-                       if (!value) {
-                               value = g_string_new ("");
-                       }
-                       g_string_append_c (value, *next_char);
-               } else if (value != NULL && *next_char != '!') {
-                       // If we won't accept this character, we are ending a 
-                       // query key or value, so we should save them in a new
-                       // DmapDbFilterDefinition. If is_value is TRUE, we will still
-                       // parse the query value, so we must keep our def around.
-                       // Otherwise, save it in the list of filters.
-                       if (!def) {
-                               def = g_new0 (DmapDbFilterDefinition, 1);
-                       }
-                       if (is_key) {
-                               def->key = value->str;
-                               g_string_free (value, FALSE);
-                               def->negate = negate;
-                               negate = FALSE;
-                               is_key = FALSE;
-                       } else {
-                               def->value = value->str;
-                               g_string_free (value, FALSE);
-                               is_value = FALSE;
-                               is_key = TRUE;
-                       }
-                       value = NULL;
-                       if (!is_value) {
-                               filter = g_slist_append (filter, def);
-                               def = NULL;
-                       }
-               }
-               if (new_group && filter) {
-                       list = g_slist_append (list, filter);
-                       filter = NULL;
-                       new_group = FALSE;
-               }
-               // Only handle \0 here so we can handle remaining values above.
-               if (*next_char == '\0') {
-                       break;
-               }
-               next_char++;
-       };
-
-       // Any remaining def or filter must still be handled here.
-       if (def) {
-               filter = g_slist_append (filter, def);
-       }
-
-       if (filter) {
-               list = g_slist_append (list, filter);
-       }
-
-       GSList *ptr1, *ptr2;
-
-       for (ptr1 = list; ptr1 != NULL; ptr1 = ptr1->next) {
-               for (ptr2 = ptr1->data; ptr2 != NULL; ptr2 = ptr2->next) {
-                       g_debug ("%s = %s",
-                                ((DmapDbFilterDefinition *) ptr2->data)->key,
-                                ((DmapDbFilterDefinition *) ptr2->data)->value);
-               }
-       }
+       _maybe_restart (share);
 
 done:
-       return list;
+       return;
 }
 
-void
-dmap_share_free_filter (GSList * filter)
+static void
+_set_property (GObject * object,
+                         guint prop_id,
+                         const GValue * value, GParamSpec * pspec)
 {
-       GSList *ptr1, *ptr2;
+       DmapShare *share = DMAP_SHARE (object);
 
-       for (ptr1 = filter; ptr1 != NULL; ptr1 = ptr1->next) {
-               for (ptr2 = ptr1->data; ptr2 != NULL; ptr2 = ptr2->next) {
-                       g_free (((DmapDbFilterDefinition *) ptr2->data)->value);
-                       g_free (ptr2->data);
+       switch (prop_id) {
+       case PROP_NAME:
+               _set_name (share, g_value_get_string (value));
+               break;
+       case PROP_PASSWORD:
+               _set_password (share, g_value_get_string (value));
+               break;
+       case PROP_DB:
+               if (share->priv->db) {
+                       g_object_unref(share->priv->db);
+               }
+               share->priv->db = g_value_dup_object (value);
+               break;
+       case PROP_CONTAINER_DB:
+               if (share->priv->container_db) {
+                       g_object_unref(share->priv->container_db);
                }
+               share->priv->container_db = g_value_dup_object (value);
+               break;
+       case PROP_TRANSCODE_MIMETYPE:
+               g_free(share->priv->transcode_mimetype);
+               share->priv->transcode_mimetype = g_value_dup_string (value);
+               break;
+       case PROP_TXT_RECORDS:
+               g_strfreev (share->priv->txt_records);
+               share->priv->txt_records = g_value_dup_boxed (value);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
        }
 }
 
-void
-dmap_share_emit_error(DmapShare *share, gint code, const gchar *format, ...)
+static void
+_get_property (GObject * object,
+                         guint prop_id, GValue * value, GParamSpec * pspec)
 {
-       va_list ap;
-       GError *error;
-
-       va_start(ap, format);
-       error = g_error_new_valist(DMAP_ERROR, code, format, ap);
-       g_signal_emit_by_name(share, "error", error);
+       DmapShare *share = DMAP_SHARE (object);
 
-       va_end(ap);
+       switch (prop_id) {
+       case PROP_SERVER:
+               g_value_set_object (value, share->priv->server);
+               return;
+       case PROP_NAME:
+               g_value_set_string (value, share->priv->name);
+               break;
+       case PROP_PASSWORD:
+               g_value_set_string (value, share->priv->password);
+               break;
+       case PROP_REVISION_NUMBER:
+               g_value_set_uint (value,
+                                 _get_revision_number
+                                 (DMAP_SHARE (object)));
+               break;
+       case PROP_AUTH_METHOD:
+               g_value_set_uint (value,
+                                 dmap_share_get_auth_method
+                                 (DMAP_SHARE (object)));
+               break;
+       case PROP_DB:
+               g_value_set_object (value, share->priv->db);
+               break;
+       case PROP_CONTAINER_DB:
+               g_value_set_object (value, share->priv->container_db);
+               break;
+       case PROP_TRANSCODE_MIMETYPE:
+               g_value_set_string (value, share->priv->transcode_mimetype);
+               break;
+       case PROP_TXT_RECORDS:
+               g_value_set_boxed (value, share->priv->txt_records);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
 }
 
-typedef struct {
-       gchar *name;
-       gint64 group_id;
-       gchar *artist;
-       int count;
-} GroupInfo;
-
 static void
-_group_items (G_GNUC_UNUSED gpointer key, DmapRecord * record, GHashTable * groups)
+_dispose (GObject * object)
 {
-       gchar *album, *artist;
-       GroupInfo *group_info;
-       gint64 group_id;
+       DmapShare *share = DMAP_SHARE (object);
 
-       g_object_get (record, "songartist", &artist, "songalbum", &album,
-                     "songalbumid", &group_id, NULL);
-       if (!album) {
-               g_free (artist);
-               return;
-       }
-       group_info = g_hash_table_lookup (groups, album);
-       if (!group_info) {
-               group_info = g_new0 (GroupInfo, 1);
-               g_hash_table_insert (groups, album, group_info);
-               // They will be freed when the hash table is freed.
-               group_info->name = album;
-               group_info->artist = artist;
-               group_info->group_id = group_id;
-       } else {
-               g_free (album);
-               g_free (artist);
+       if (share->priv->published) {
+               _publish_stop (share);
        }
-       (group_info->count)++;
-}
-
-static gint
-_group_info_cmp (gconstpointer group1, gconstpointer group2)
-{
-       return g_ascii_strcasecmp (((GroupInfo *) group1)->name,
-                                  ((GroupInfo *) group2)->name);
-}
-
-static void
-_debug_param (gpointer key, gpointer val, G_GNUC_UNUSED gpointer user_data)
-{
-       g_debug ("%s %s", (char *) key, (char *) val);
-}
 
-void
-dmap_share_ctrl_int (G_GNUC_UNUSED DmapShare * share,
-                     G_GNUC_UNUSED SoupServer * server,
-                     G_GNUC_UNUSED SoupMessage * message,
-                     const char *path,
-                     GHashTable * query,
-                     G_GNUC_UNUSED SoupClientContext * context)
-{
-       g_debug ("Path is %s.", path);
-       if (query) {
-               g_hash_table_foreach (query, _debug_param, NULL);
+       if (share->priv->server_active) {
+               _server_stop (share);
        }
 
-       g_debug ("ctrl-int not implemented");
+       g_clear_object (&share->priv->publisher);
+       g_clear_object (&share->priv->server);
+       g_clear_object (&share->priv->db);
+       g_clear_object (&share->priv->container_db);
+
+       G_OBJECT_CLASS (dmap_share_parent_class)->dispose (object);
 }
 
 static void
-_accumulate_mlcl_size_and_ids (guint id,
-                               DmapRecord * record,
-                               struct share_bitwise_t *share_bitwise)
+_finalize (GObject * object)
 {
-       share_bitwise->id_list = g_slist_append (share_bitwise->id_list, GUINT_TO_POINTER(id));
-
-       /* Make copy and set mlcl to NULL so real MLCL does not get changed */
-       struct DmapMlclBits mb_copy = share_bitwise->mb;
+       DmapShare *share = DMAP_SHARE (object);
 
-       mb_copy.mlcl = dmap_structure_add (NULL, DMAP_CC_MLCL);;
+       g_debug ("Finalizing DmapShare");
 
-       DMAP_SHARE_GET_CLASS (share_bitwise->mb.share)->add_entry_to_mlcl (id,
-                                                                          record,
-                                                                         &mb_copy);
-       share_bitwise->size += dmap_structure_get_size (mb_copy.mlcl);
+       g_hash_table_destroy (share->priv->session_ids);
+       share->priv->session_ids = NULL;
 
-       /* Minus eight because we do not want to add size of MLCL CC field + size field n times,
-        * where n == number of records.
-        */
-       share_bitwise->size -= 8;
+       g_free (share->priv->name);
+       g_free (share->priv->password);
+       g_free (share->priv->transcode_mimetype);
+       g_strfreev (share->priv->txt_records);
 
-       /* Destroy created structures as we go. */
-       dmap_structure_destroy (mb_copy.mlcl);
+       G_OBJECT_CLASS (dmap_share_parent_class)->finalize (object);
 }
 
 static void
-_accumulate_mlcl_size_and_ids_adapter (gpointer id,
-                                       DmapRecord * record,
-                                       struct share_bitwise_t *share_bitwise)
+dmap_share_class_init (DmapShareClass * klass)
 {
-       _accumulate_mlcl_size_and_ids(GPOINTER_TO_UINT(id),
-                                     record,
-                                     share_bitwise);
-}
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
-static void
-_write_dmap_preamble (SoupMessage * message, GNode * node)
-{
-       guint length;
-       gchar *data = dmap_structure_serialize (node, &length);
+       object_class->get_property = _get_property;
+       object_class->set_property = _set_property;
+       object_class->dispose = _dispose;
+       object_class->finalize = _finalize;
 
-       soup_message_body_append (message->response_body,
-                                 SOUP_MEMORY_TAKE, data, length);
-       dmap_structure_destroy (node);
-}
+       /* Pure virtual methods: */
+       klass->get_desired_port = NULL;
+       klass->get_type_of_service = NULL;
+       klass->message_add_standard_headers = NULL;
+       klass->get_meta_data_map = NULL;
+       klass->add_entry_to_mlcl = NULL;
+       klass->databases_browse_xxx = NULL;
+       klass->databases_items_xxx = NULL;
 
-static void
-_write_next_mlit (SoupMessage * message, struct share_bitwise_t *share_bitwise)
-{
-       if (share_bitwise->id_list == NULL) {
-               g_debug ("No more ID's, sending message complete.");
-               soup_message_body_complete (message->response_body);
-       } else {
-               gchar *data = NULL;
-               guint length;
-               DmapRecord *record;
-               struct DmapMlclBits mb = { NULL, 0, NULL };
+       /* Virtual methods: */
+       klass->content_codes = _content_codes;
+       klass->login = dmap_share_login;
+       klass->logout = _logout;
+       klass->update = _update;
+       klass->published = _published;
+       klass->name_collision = _name_collision;
+       klass->databases = _databases;
+       klass->ctrl_int = _ctrl_int;
 
-               record = share_bitwise->lookup_by_id (share_bitwise->db,
-                                                     GPOINTER_TO_UINT
-                                                     (share_bitwise->
-                                                      id_list->data));
+       g_object_class_install_property (object_class,
+                                        PROP_SERVER,
+                                        g_param_spec_object ("server",
+                                                             "Soup Server",
+                                                             "Soup server",
+                                                             SOUP_TYPE_SERVER,
+                                                             G_PARAM_READABLE));
 
-               mb.bits = share_bitwise->mb.bits;
-               mb.mlcl = dmap_structure_add (NULL, DMAP_CC_MLCL);
-               mb.share = share_bitwise->mb.share;
+       g_object_class_install_property (object_class,
+                                        PROP_NAME,
+                                        g_param_spec_string ("name",
+                                                             "Name",
+                                                             "Share Name",
+                                                             NULL,
+                                                             G_PARAM_READWRITE));
+       g_object_class_install_property (object_class,
+                                        PROP_PASSWORD,
+                                        g_param_spec_string ("password",
+                                                             "Authentication password",
+                                                             "Authentication password",
+                                                             NULL,
+                                                             G_PARAM_READWRITE));
 
-               DMAP_SHARE_GET_CLASS (share_bitwise->mb.share)->
-                       add_entry_to_mlcl (GPOINTER_TO_UINT(share_bitwise->id_list->data), record, &mb);
-               data = dmap_structure_serialize (g_node_first_child (mb.mlcl),
-                                                &length);
+       g_object_class_install_property (object_class,
+                                        PROP_REVISION_NUMBER,
+                                        g_param_spec_uint ("revision-number",
+                                                           "Revision number",
+                                                           "Revision number",
+                                                           0,
+                                                           G_MAXINT,
+                                                           0,
+                                                           G_PARAM_READWRITE));
 
-               soup_message_body_append (message->response_body,
-                                         SOUP_MEMORY_TAKE, data, length);
-               g_debug ("Sending ID %u.",
-                        GPOINTER_TO_UINT (share_bitwise->id_list->data));
-               dmap_structure_destroy (mb.mlcl);
+       g_object_class_install_property (object_class,
+                                        PROP_AUTH_METHOD,
+                                        g_param_spec_uint ("auth-method",
+                                                           "Authentication method",
+                                                           "Authentication method",
+                                                           DMAP_SHARE_AUTH_METHOD_NONE,
+                                                           DMAP_SHARE_AUTH_METHOD_PASSWORD,
+                                                           0,
+                                                           G_PARAM_READWRITE));
+       g_object_class_install_property (object_class,
+                                        PROP_DB,
+                                        g_param_spec_object ("db",
+                                                             "DB",
+                                                             "DB object",
+                                                              DMAP_TYPE_DB,
+                                                              G_PARAM_READWRITE |
+                                                              G_PARAM_CONSTRUCT_ONLY));
 
-               share_bitwise->id_list =
-                       g_slist_remove (share_bitwise->id_list,
-                                       share_bitwise->id_list->data);
+       g_object_class_install_property (object_class,
+                                        PROP_CONTAINER_DB,
+                                        g_param_spec_object ("container-db",
+                                                             "Container DB",
+                                                             "Container DB object",
+                                                              DMAP_TYPE_CONTAINER_DB,
+                                                              G_PARAM_READWRITE |
+                                                              G_PARAM_CONSTRUCT_ONLY));
 
-               g_object_unref (record);
-       }
+       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));
 
-       soup_server_unpause_message (share_bitwise->share->priv->server, message);
+       g_object_class_install_property (object_class,
+                                        PROP_TXT_RECORDS,
+                                        g_param_spec_boxed ("txt-records",
+                                                            "TXT-Records",
+                                                            "Set TXT-Records used for MDNS publishing",
+                                                            G_TYPE_STRV,
+                                                            G_PARAM_READWRITE));
+
+       _signals[ERROR] =
+               g_signal_new ("error",
+                              G_TYPE_FROM_CLASS (object_class),
+                              G_SIGNAL_RUN_FIRST,
+                              0, NULL, NULL,
+                              g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
+                              G_TYPE_POINTER);
 }
 
 static void
-_chunked_message_finished (G_GNUC_UNUSED SoupMessage * message,
-                           struct share_bitwise_t *share_bitwise)
+dmap_share_init (DmapShare * share)
 {
-       g_debug ("Finished sending chunked data.");
-       if (share_bitwise->destroy) {
-               share_bitwise->destroy (share_bitwise->db);
-       }
-       g_free (share_bitwise);
-}
+       share->priv = DMAP_SHARE_GET_PRIVATE (share);
 
-static DmapRecord *
-_lookup_adapter (GHashTable * ht, guint id)
-{
-       /* NOTE: each time this is called by _write_next_mlit(), the
-        * returned value will be unref'ed by _write_next_mlit(). We
-        * also need to destroy the GHashTable, so bump up the reference
-        * count so that both can happen. */
-       return g_object_ref (g_hash_table_lookup (ht, GUINT_TO_POINTER (id)));
-}
+       share->priv->revision_number = 5;
+       share->priv->auth_method = DMAP_SHARE_AUTH_METHOD_NONE;
+       share->priv->publisher = dmap_mdns_publisher_new ();
+       share->priv->server = soup_server_new (NULL, NULL);
 
-void
-dmap_share_databases (DmapShare * share,
-                      SoupServer * server,
-                      SoupMessage * message,
-                      const char *path,
-                      GHashTable * query, SoupClientContext * context)
-{
-       const char *rest_of_path;
+       /* using direct since there is no g_uint_hash or g_uint_equal */
+       share->priv->session_ids =
+               g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
+                                      g_free);
 
-       g_debug ("Path is %s.", path);
-       g_hash_table_foreach (query, _debug_param, NULL);
+       g_signal_connect_object (share->priv->publisher,
+                                "published",
+                                G_CALLBACK (_published_adapter), share, 0);
+       g_signal_connect_object (share->priv->publisher,
+                                "name-collision",
+                                G_CALLBACK (_name_collision_adapter),
+                                share, 0);
+}
 
-       if (!dmap_share_session_id_validate
-           (share, context, query, NULL)) {
-               soup_message_set_status (message, SOUP_STATUS_FORBIDDEN);
+guint
+dmap_share_get_auth_method (DmapShare * share)
+{
+       return share->priv->auth_method;
+}
+
+static gboolean
+_get_session_id (GHashTable * query, guint32 * id)
+{
+       gboolean ok = FALSE;
+       char *session_id_str;
+       guint32 session_id;
+
+       session_id_str = g_hash_table_lookup (query, "session-id");
+       if (session_id_str == NULL) {
+               g_warning ("Session id not found.");
                goto done;
        }
 
-       rest_of_path = strchr (path + 1, '/');
+       session_id = (guint32) strtoul (session_id_str, NULL, 10);
+       if (id != NULL) {
+               *id = session_id;
+       }
 
-       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
-                */
-               GNode *avdb;
-               GNode *mlcl;
-               GNode *mlit;
+       ok = TRUE;
 
-               avdb = dmap_structure_add (NULL, DMAP_CC_AVDB);
-               dmap_structure_add (avdb, DMAP_CC_MSTT,
-                                   (gint32) SOUP_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, share->priv->name);
-               dmap_structure_add (mlit, DMAP_CC_MIMC,
-                                   dmap_db_count (share->priv->db));
-               dmap_structure_add (mlit, DMAP_CC_MCTC, (gint32) 1);
+done:
+       return ok;
+}
 
-               dmap_share_message_set_from_dmap_structure (share, message,
-                                                            avdb);
-               dmap_structure_destroy (avdb);
-       } else if (g_ascii_strcasecmp ("/1/groups", rest_of_path) == 0) {
-               /* ADBS database songs
-                *      MSTT status
-                *      MUTY update type
-                *      MTCO specified total count
-                *      MRCO returned count
-                *      MLCL listing
-                *              MLIT
-                *                      attrs
-                *              MLIT
-                *              ...
-                */
+gboolean
+dmap_share_session_id_validate (DmapShare * share,
+                                SoupClientContext * context,
+                                GHashTable * query, guint32 * id)
+{
+       gboolean ok = FALSE;
+       guint32 session_id;
+       gboolean res;
+       const char *addr;
+       const char *remote_address;
 
-               GSList *filter_def;
-               gchar *record_query;
-               GHashTable *records = NULL;
-               GHashTable *groups;
-               GList *values;
-               GList *value;
-               gchar *sort_by;
-               GroupInfo *group_info;
-               GNode *agal;
-               GNode *mlcl;
-               GNode *mlit;
-               gint num;
+       if (id) {
+               *id = 0;
+       }
 
-               if (g_strcmp0
-                   (g_hash_table_lookup (query, "group-type"),
-                    "albums") != 0) {
-                       g_warning ("Unsupported grouping");
-                       soup_message_set_status (message,
-                                                SOUP_STATUS_INTERNAL_SERVER_ERROR);
-                       goto done;
-               }
+       res = _get_session_id (query, &session_id);
+       if (!res) {
+               g_warning ("Validation failed: Unable to parse session id");
+               goto done;
+       }
 
-               record_query = g_hash_table_lookup (query, "query");
-               filter_def = dmap_share_build_filter (record_query);
-               records =
-                       dmap_db_apply_filter (DMAP_DB (share->priv->db),
-                                             filter_def);
+       /* check hash for remote address */
+       addr = g_hash_table_lookup (share->priv->session_ids,
+                                   GUINT_TO_POINTER (session_id));
+       if (addr == NULL) {
+               g_warning
+                       ("Validation failed: Unable to lookup session id %u",
+                        session_id);
+               goto done;
+       }
 
-               groups = g_hash_table_new_full (g_str_hash, g_str_equal,
-                                               g_free, g_free);
-               g_hash_table_foreach (records, (GHFunc) _group_items, groups);
+       remote_address = soup_client_context_get_host (context);
+       g_debug ("Validating session id %u from %s matches %s",
+                session_id, remote_address, addr);
+       if (remote_address == NULL || strcmp (addr, remote_address) != 0) {
+               g_warning
+                       ("Validation failed: Remote address does not match stored address");
+               goto done;
+       }
 
-               agal = dmap_structure_add (NULL, DMAP_CC_AGAL);
-               dmap_structure_add (agal, DMAP_CC_MSTT,
-                                   (gint32) SOUP_STATUS_OK);
-               dmap_structure_add (agal, DMAP_CC_MUTY, 0);
+       if (id) {
+               *id = session_id;
+       }
 
-               num = g_hash_table_size (groups);
-               dmap_structure_add (agal, DMAP_CC_MTCO, (gint32) num);
-               dmap_structure_add (agal, DMAP_CC_MRCO, (gint32) num);
+       ok = TRUE;
 
-               mlcl = dmap_structure_add (agal, DMAP_CC_MLCL);
+done:
+       return ok;
+}
 
-               values = g_hash_table_get_values (groups);
-               if (g_hash_table_lookup (query, "include-sort-headers")) {
-                       sort_by = g_hash_table_lookup (query, "sort");
-                       if (g_strcmp0 (sort_by, "album") == 0) {
-                               values = g_list_sort (values, _group_info_cmp);
-                       } else {
-                               g_warning ("Unknown sort column: %s",
-                                          sort_by);
-                       }
-               }
+static guint32
+_session_id_generate (void)
+{
+       guint32 id;
 
-               for (value = values; value; value = g_list_next (value)) {
-                       group_info = (GroupInfo *) value->data;
-                       mlit = dmap_structure_add (mlcl, DMAP_CC_MLIT);
-                       dmap_structure_add (mlit, DMAP_CC_MIID,
-                                           (gint) group_info->group_id);
-                       dmap_structure_add (mlit, DMAP_CC_MPER,
-                                           group_info->group_id);
-                       dmap_structure_add (mlit, DMAP_CC_MINM,
-                                           group_info->name);
-                       dmap_structure_add (mlit, DMAP_CC_ASAA,
-                                           group_info->artist);
-                       dmap_structure_add (mlit, DMAP_CC_MIMC,
-                                           (gint32) group_info->count);
+       id = g_random_int ();
 
-                       // Free this now, since the hash free func won't.
-                       // Name will be freed when the hash table keys are freed.
-                       g_free (group_info->artist);
-               }
+       return id;
+}
 
-               g_list_free (values);
-               dmap_share_free_filter (filter_def);
+static guint32
+_session_id_create (DmapShare * share, SoupClientContext * context)
+{
+       guint32 id;
+       const char *addr;
+       char *remote_address;
 
-               dmap_share_message_set_from_dmap_structure (share, message,
-                                                            agal);
+       do {
+               /* create a unique session id */
+               id = _session_id_generate ();
+               g_debug ("Generated session id %u", id);
 
-               g_hash_table_destroy (records);
-               g_hash_table_destroy (groups);
-               dmap_structure_destroy (agal);
-       } 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 DmapMlclBits mb = { NULL, 0, NULL };
-               struct share_bitwise_t *share_bitwise;
+               /* if already used, try again */
+               addr = g_hash_table_lookup (share->priv->session_ids,
+                                           GUINT_TO_POINTER (id));
+       } while (addr != NULL);
 
-               record_query = g_hash_table_lookup (query, "query");
-               if (record_query) {
-                       GSList *filter_def;
+       /* store session id and remote address */
+       /* FIXME, warning, this fails against libsoup-2.33.90-1.fc15.x86_64 with:
+        * (dmapd:12917): libsoup-CRITICAL **: soup_address_get_physical: assertion `SOUP_IS_ADDRESS (addr)' 
failed
+        * Is this a bug in libsoup or libdmapsharing?
+        */
+       remote_address = g_strdup (soup_client_context_get_host (context));
+       g_hash_table_insert (share->priv->session_ids, GUINT_TO_POINTER (id),
+                            remote_address);
 
-                       filter_def = dmap_share_build_filter (record_query);
-                       records =
-                               dmap_db_apply_filter (DMAP_DB
-                                                     (share->priv->db),
-                                                     filter_def);
-                       num_songs = g_hash_table_size (records);
-                       g_debug ("Found %d records", num_songs);
-                       dmap_share_free_filter (filter_def);
-               } else {
-                       num_songs = dmap_db_count (share->priv->db);
-               }
+       return id;
+}
 
-               map = DMAP_SHARE_GET_CLASS (share)->get_meta_data_map (share);
-               mb.bits = dmap_share_parse_meta (query, map);
-               mb.share = share;
+void
+dmap_share_message_set_from_dmap_structure (DmapShare * share,
+                                            SoupMessage * message,
+                                            GNode * structure)
+{
+       gchar *resp;
+       guint length;
 
-               /* NOTE:
-                * We previously simply called foreach...add_entry_to_mlcl and later serialized the entire
-                * structure. This has the disadvantage that the entire response must be in memory before
-                * libsoup sends it to the client.
-                *
-                * Now, we go through the database in multiple passes (as an interim solution):
-                *
-                * 1. Accumulate the eventual size of the MLCL by creating and then free'ing each MLIT.
-                * 2. Generate the DAAP preamble ending with the MLCL (with size fudged for ADBS and MLCL).
-                * 3. Setup libsoup response headers, etc.
-                * 4. Setup callback to transmit DAAP preamble (_write_dmap_preamble)
-                * 5. Setup callback to transmit MLIT's (_write_next_mlit)
-                */
+       resp = dmap_structure_serialize (structure, &length);
 
-               /* 1: */
-               share_bitwise = g_new0 (struct share_bitwise_t, 1);
+       if (resp == NULL) {
+               g_warning ("Serialize gave us null?");
+               return;
+       }
 
-               share_bitwise->share = share;
-               share_bitwise->mb = mb;
-               share_bitwise->id_list = NULL;
-               share_bitwise->size = 0;
-               if (record_query) {
-                       share_bitwise->db = records;
-                       share_bitwise->lookup_by_id = (ShareBitwiseLookupByIdFunc)
-                               _lookup_adapter;
-                       share_bitwise->destroy = (ShareBitwiseDestroyFunc) g_hash_table_destroy;
-                       g_hash_table_foreach (records,
-                                            (GHFunc) _accumulate_mlcl_size_and_ids_adapter,
-                                             share_bitwise);
-               } else {
-                       share_bitwise->db = share->priv->db;
-                       share_bitwise->lookup_by_id = (ShareBitwiseLookupByIdFunc) dmap_db_lookup_by_id;
-                       share_bitwise->destroy = NULL;
-                       dmap_db_foreach (share->priv->db,
-                                       (DmapIdRecordFunc) _accumulate_mlcl_size_and_ids,
-                                        share_bitwise);
-               }
+       soup_message_set_response (message, "application/x-dmap-tagged",
+                                  SOUP_MEMORY_TAKE, resp, length);
 
-               /* 2: */
-               adbs = dmap_structure_add (NULL, DMAP_CC_ADBS);
-               dmap_structure_add (adbs, DMAP_CC_MSTT,
-                                   (gint32) SOUP_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);
-               dmap_structure_increase_by_predicted_size (adbs,
-                                                          share_bitwise->
-                                                          size);
-               dmap_structure_increase_by_predicted_size (mb.mlcl,
-                                                          share_bitwise->
-                                                          size);
+       DMAP_SHARE_GET_CLASS (share)->message_add_standard_headers (share,
+                                                                   message);
 
-               /* 3: */
-               /* Free memory after each chunk sent out over network. */
-               soup_message_body_set_accumulate (message->response_body,
-                                                 FALSE);
-               soup_message_headers_append (message->response_headers,
-                                            "Content-Type",
-                                            "application/x-dmap-tagged");
-               DMAP_SHARE_GET_CLASS (share)->
-                       message_add_standard_headers (share, message);
-               soup_message_headers_set_content_length (message->
-                                                        response_headers,
-                                                        dmap_structure_get_size
-                                                        (adbs));
-               soup_message_set_status (message, SOUP_STATUS_OK);
+       soup_message_set_status (message, SOUP_STATUS_OK);
+}
 
-               /* 4: */
-               g_signal_connect (message, "wrote_headers",
-                                 G_CALLBACK (_write_dmap_preamble), adbs);
+gboolean
+dmap_share_client_requested (DmapBits bits, gint field)
+{
+       return 0 != (bits & (((DmapBits) 1) << field));
+}
 
-               /* 5: */
-               g_signal_connect (message, "wrote_chunk",
-                                 G_CALLBACK (_write_next_mlit),
-                                 share_bitwise);
-               g_signal_connect (message, "finished",
-                                 G_CALLBACK (_chunked_message_finished),
-                                 share_bitwise);
+static gboolean
+_uri_is_local (const char *text_uri)
+{
+       return g_str_has_prefix (text_uri, "file://");
+}
 
-       } 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
-                *              ...
-                */
-               GNode *aply;
-               GNode *mlit;
-               struct DmapMetaDataMap *map;
-               struct DmapMlclBits mb = { NULL, 0, NULL };
+void
+dmap_share_login (DmapShare * share,
+                  SoupMessage * message,
+                  const char *path,
+                  G_GNUC_UNUSED GHashTable * query,
+                  SoupClientContext * context)
+{
+/* MLOG login response
+ *     MSTT status
+ *     MLID session id
+ */
+       GNode *mlog;
+       guint32 session_id;
 
-               map = DMAP_SHARE_GET_CLASS (share)->get_meta_data_map (share);
-               mb.bits = dmap_share_parse_meta (query, map);
-               mb.share = share;
+       g_debug ("Path is %s.", path);
 
-               aply = dmap_structure_add (NULL, DMAP_CC_APLY);
-               dmap_structure_add (aply, DMAP_CC_MSTT,
-                                   (gint32) SOUP_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);
-               mb.mlcl = dmap_structure_add (aply, DMAP_CC_MLCL);
+       session_id = _session_id_create (share, context);
 
-               /* Base playlist (playlist 1 contains all songs): */
-               mlit = dmap_structure_add (mb.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, share->priv->name);
-               dmap_structure_add (mlit, DMAP_CC_MIMC,
-                                   dmap_db_count (share->priv->db));
-               dmap_structure_add (mlit, DMAP_CC_FQUESCH, 0);
-               dmap_structure_add (mlit, DMAP_CC_MPCO, 0);
-               dmap_structure_add (mlit, DMAP_CC_AESP, 0);
-               dmap_structure_add (mlit, DMAP_CC_AEPP, 0);
-               dmap_structure_add (mlit, DMAP_CC_AEPS, 0);
-               dmap_structure_add (mlit, DMAP_CC_AESG, 0);
+       mlog = dmap_structure_add (NULL, DMAP_CC_MLOG);
+       dmap_structure_add (mlog, DMAP_CC_MSTT, (gint32) SOUP_STATUS_OK);
+       dmap_structure_add (mlog, DMAP_CC_MLID, session_id);
 
-               dmap_structure_add (mlit, DMAP_CC_ABPL, (gchar) 1);
+       dmap_share_message_set_from_dmap_structure (share, message, mlog);
+       dmap_structure_destroy (mlog);
+}
 
-               dmap_container_db_foreach (share->priv->container_db,
-                                          (DmapIdContainerRecordFunc)
-                                          dmap_share_add_playlist_to_mlcl,
-                                          &mb);
+GSList *
+dmap_share_build_filter (gchar * filterstr)
+{
+       /* Produces a list of lists, each being a filter definition that may
+        * be one or more filter criteria.
+        */
 
-               dmap_share_message_set_from_dmap_structure (share, message,
-                                                            aply);
-               dmap_structure_destroy (aply);
-       } 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 DmapMlclBits mb = { NULL, 0, NULL };
-               guint pl_id;
-               gchar *record_query;
-               GSList *filter_def;
-               GHashTable *records;
+       /* 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')
+        * or (DACP):
+        * ('com.apple.itunes.mediakind:1','com.apple.itunes.mediakind:32') 'daap.songartist!:'
+        */
 
-               map = DMAP_SHARE_GET_CLASS (share)->get_meta_data_map (share);
-               mb.bits = dmap_share_parse_meta (query, map);
-               mb.share = share;
+       gchar *next_char;
+       GString *value;
 
-               apso = dmap_structure_add (NULL, DMAP_CC_APSO);
-               dmap_structure_add (apso, DMAP_CC_MSTT,
-                                   (gint32) SOUP_STATUS_OK);
-               dmap_structure_add (apso, DMAP_CC_MUTY, 0);
+       //gboolean save_value;
+       gboolean is_key;
+       gboolean is_value;
+       gboolean new_group;
+       gboolean accept;
+       gboolean negate;
+       gint parentheses_count;
+       gint quotes_count;
+       DmapDbFilterDefinition *def;
 
-               if (g_ascii_strcasecmp ("/1/items", rest_of_path + 13) == 0) {
-                       GList *id;
-                       gchar *sort_by;
-                       GList *keys;
+       GSList *list = NULL;
+       GSList *filter = NULL;
 
-                       record_query = g_hash_table_lookup (query, "query");
-                       filter_def = dmap_share_build_filter (record_query);
-                       records =
-                               dmap_db_apply_filter (DMAP_DB
-                                                     (share->priv->db),
-                                                     filter_def);
-                       gint32 num_songs = g_hash_table_size (records);
+       g_debug ("Filter string is %s.", filterstr);
 
-                       g_debug ("Found %d records", num_songs);
-                       dmap_share_free_filter (filter_def);
-
-                       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);
+       if (filterstr == NULL) {
+               goto done;
+       }
 
-                       sort_by = g_hash_table_lookup (query, "sort");
-                       keys = g_hash_table_get_keys (records);
-                       if (g_strcmp0 (sort_by, "album") == 0) {
-                               keys = g_list_sort_with_data (keys,
-                                                             (GCompareDataFunc)
-                                                             dmap_av_record_cmp_by_album,
-                                                             share->priv->
-                                                             db);
-                       } else if (sort_by != NULL) {
-                               g_warning ("Unknown sort column: %s",
-                                          sort_by);
-                       }
+       next_char = filterstr;
 
-                       for (id = keys; id; id = id->next) {
-                               (*
-                                (DMAP_SHARE_GET_CLASS (share)->
-                                 add_entry_to_mlcl)) (GPOINTER_TO_UINT(id->data),
-                                                      g_hash_table_lookup
-                                                      (records, id->data),
-                                                      &mb);
-                       }
+       parentheses_count = 0;
+       quotes_count = 0;
+       is_key = TRUE;
+       is_value = FALSE;
+       new_group = FALSE;
+       negate = FALSE;
+       value = NULL;
+       def = NULL;
 
-                       g_list_free (keys);
-                       g_hash_table_destroy (records);
+       // The query string is divided in groups of AND separated by a space,
+       // each group containing queries of OR separated by comma enclosed in
+       // parentheses. Queries are key, value pairs separated by ':' enclosed 
+       // in quotes or not.
+       // The result from this is a list of lists. Here is an example:
+       // String is ('com.apple.itunes.mediakind:1','com.apple.itunes.mediakind:32') 'daap.songartist!:'
+       // list:
+       // |-> filter1: -> com.apple.itunes.mediakind = 1
+       // |   |        -> com.apple.itunes.mediakind = 32
+       // |-> filter1: -> daap.songartist! = (null)
+       // This basically means that the query is (filter1 AND filter2), and
+       // filter1 is (com.apple.itunes.mediakind = 1) OR (com.apple.itunes.mediakind = 32)
+       while (TRUE) {
+               // We check each character to see if it should be included in
+               // the current value (the current value will become the query key
+               // or the query value). Anything that is not a special character
+               // handled below will be accepted (this means weird characters
+               // might appear in names).
+               // Handling of unicode characters is unknown, but as long it 
+               // doesn't appear as a character handled below, it will be
+               // included.
+               // This parser will result in unknown behaviour on bad-formatted
+               // queries (it does not check for syntax errors), but should not 
+               // crash. In this way it may accept much more syntaxes then
+               // other parsers.
+               accept = FALSE;
+               // A slash can escape characters such as ', so add the character
+               // after the slash.
+               if (*next_char == '\\') {
+                       accept = TRUE;
+                       next_char++;
                } else {
-                       pl_id = strtoul (rest_of_path + 14, NULL, 10);
-                       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,
-                                                DMAP_SHARE_GET_CLASS
-                                                (share)->add_entry_to_mlcl,
-                                                &mb);
+                       switch (*next_char) {
+                       case '(':
+                               if (is_value) {
+                                       accept = TRUE;
+                               } else {
+                                       parentheses_count++;
+                               }
+                               break;
+                       case ')':
+                               if (is_value) {
+                                       accept = TRUE;
+                               } else {
+                                       parentheses_count--;
+                               }
+                               break;
+                       case '\'':
+                               if (quotes_count > 0) {
+                                       quotes_count = 0;
+                               } else {
+                                       quotes_count = 1;
+                               }
+                               break;
+                       case ' ':
+                               if (is_value) {
+                                       accept = TRUE;
+                               } else {
+                                       new_group = TRUE;
+                               }
+                               break;
+                       case ':':
+                               // Inside values, they will be included in the 
+                               // query string, otherwise it indicates there
+                               // will be a value next.
+                               if (is_value) {
+                                       accept = TRUE;
+                               } else if (is_key) {
+                                       is_value = TRUE;
+                               }
+                               break;
+                       case '!':
+                               if (is_value) {
+                                       accept = TRUE;
+                               } else if (is_key && value) {
+                                       negate = TRUE;
+                               }
+                               break;
+                       case ',':
+                       case '+':
+                               // Accept these characters only if inside quotes
+                               if (is_value) {
+                                       accept = TRUE;
+                               }
+                               break;
+                       case '\0':
+                               // Never accept
+                               break;
+                       default:
+                               accept = TRUE;
+                               break;
+                       }
+               }
+               //g_debug ("Char: %c, Accept: %s", *next_char, accept?"TRUE":"FALSE");
+               //Is the next character to be accepted?
+               if (accept) {
+                       if (!value) {
+                               value = g_string_new ("");
+                       }
+                       g_string_append_c (value, *next_char);
+               } else if (value != NULL && *next_char != '!') {
+                       // If we won't accept this character, we are ending a 
+                       // query key or value, so we should save them in a new
+                       // DmapDbFilterDefinition. If is_value is TRUE, we will still
+                       // parse the query value, so we must keep our def around.
+                       // Otherwise, save it in the list of filters.
+                       if (!def) {
+                               def = g_new0 (DmapDbFilterDefinition, 1);
+                       }
+                       if (is_key) {
+                               def->key = value->str;
+                               g_string_free (value, FALSE);
+                               def->negate = negate;
+                               negate = FALSE;
+                               is_key = FALSE;
                        } else {
-                               DmapContainerRecord *record;
-                               DmapDb *entries;
-                               guint num_songs;
+                               def->value = value->str;
+                               g_string_free (value, FALSE);
+                               is_value = FALSE;
+                               is_key = TRUE;
+                       }
+                       value = NULL;
+                       if (!is_value) {
+                               filter = g_slist_append (filter, def);
+                               def = NULL;
+                       }
+               }
+               if (new_group && filter) {
+                       list = g_slist_append (list, filter);
+                       filter = NULL;
+                       new_group = FALSE;
+               }
+               // Only handle \0 here so we can handle remaining values above.
+               if (*next_char == '\0') {
+                       break;
+               }
+               next_char++;
+       };
 
-                               record = dmap_container_db_lookup_by_id
-                                       (share->priv->container_db, pl_id);
-                               entries =
-                                       dmap_container_record_get_entries
-                                       (record);
-                               /* FIXME: what if entries is NULL (handled in dmapd but should be [also] 
handled here)? */
-                               num_songs = dmap_db_count (entries);
+       // Any remaining def or filter must still be handled here.
+       if (def) {
+               filter = g_slist_append (filter, def);
+       }
 
-                               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);
+       if (filter) {
+               list = g_slist_append (list, filter);
+       }
 
-                               dmap_db_foreach (entries,
-                                                DMAP_SHARE_GET_CLASS
-                                                (share)->add_entry_to_mlcl,
-                                                &mb);
+       GSList *ptr1, *ptr2;
 
-                               g_object_unref (entries);
-                               g_object_unref (record);
-                       }
+       for (ptr1 = list; ptr1 != NULL; ptr1 = ptr1->next) {
+               for (ptr2 = ptr1->data; ptr2 != NULL; ptr2 = ptr2->next) {
+                       g_debug ("%s = %s",
+                                ((DmapDbFilterDefinition *) ptr2->data)->key,
+                                ((DmapDbFilterDefinition *) ptr2->data)->value);
                }
-
-               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,
-                                                                   message,
-                                                                   path,
-                                                                   query);
-       } 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);
-       } else if (g_str_has_prefix (rest_of_path, "/1/groups/") &&
-                  g_str_has_suffix (rest_of_path, "/extra_data/artwork")) {
-               /* We don't yet implement cover requests here, say no cover */
-               g_debug ("Assuming no artwork for requested group/album");
-               soup_message_set_status (message, SOUP_STATUS_NOT_FOUND);
-       } else {
-               g_warning ("Unhandled: %s", path);
        }
 
 done:
-       return;
+       return list;
+}
+
+void
+dmap_share_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 (((DmapDbFilterDefinition *) ptr2->data)->value);
+                       g_free (ptr2->data);
+               }
+       }
+}
+
+void
+dmap_share_emit_error(DmapShare *share, gint code, const gchar *format, ...)
+{
+       va_list ap;
+       GError *error;
+
+       va_start(ap, format);
+       error = g_error_new_valist(DMAP_ERROR, code, format, ap);
+       g_signal_emit_by_name(share, "error", error);
+
+       va_end(ap);
 }


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