diff src/daap/xmms2-daap/cc_handlers.c @ 1407:776dd8fc2b38

DAAP plugin (work in progress)
author Cristi Magherusan <majeru@atheme-project.org>
date Sun, 05 Aug 2007 00:26:21 +0300
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/daap/xmms2-daap/cc_handlers.c	Sun Aug 05 00:26:21 2007 +0300
@@ -0,0 +1,892 @@
+/** @file cc_handlers.c
+ *  Functions for parsing DAAP content code data.
+ *
+ *  Copyright (C) 2006-2007 XMMS2 Team
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ */
+
+#define _GNU_SOURCE
+#include <string.h>
+
+#include <stdlib.h>
+
+#include <glib.h>
+#include <glib/gprintf.h>
+
+#include "cc_handlers.h"
+#include "daap_conn.h"
+
+#define DMAP_BYTES_REMAINING ((gint) (data_end - current_data))
+
+static void
+endian_swap_int16 (gint16 *i)
+{
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+	gint16 tmp;
+	tmp = (gint16) (((*i >>  8) & 0x00FF) |
+	                ((*i <<  8) & 0xFF00));
+	*i = tmp;
+#endif
+}
+
+static void
+endian_swap_int32 (gint32 *i)
+{
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+	gint32 tmp;
+	tmp = (gint32) (((*i >> 24) & 0x000000FF) |
+	                ((*i >>  8) & 0x0000FF00) |
+	                ((*i <<  8) & 0x00FF0000) |
+	                ((*i << 24) & 0xFF000000));
+	*i = tmp;
+#endif
+}
+
+static void
+endian_swap_int64 (gint64 *i)
+{
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+	gint64 tmp;
+	tmp = (gint64) (                 ((*i >> 40) & 0x00000000000000FF) |
+	                                 ((*i >> 32) & 0x000000000000FF00) |
+	                                 ((*i >> 24) & 0x0000000000FF0000) |
+	                                 ((*i >>  8) & 0x00000000FF000000) |
+	               G_GINT64_CONSTANT ((*i <<  8) & 0x000000FF00000000) |
+	               G_GINT64_CONSTANT ((*i << 24) & 0x0000FF0000000000) |
+	               G_GINT64_CONSTANT ((*i << 32) & 0x00FF000000000000) |
+	               G_GINT64_CONSTANT ((*i << 40) & 0xFF00000000000000));
+	*i = tmp;
+#endif
+}
+
+static gint
+grab_data_string (gchar **container, gchar *data, gint str_len)
+{
+	gint offset = 0;
+
+	if (0 != str_len) {
+		*container = (gchar *) malloc (sizeof (gchar) * (str_len+1));
+
+		memcpy (*container, data + DMAP_CC_SZ + DMAP_INT_SZ, str_len);
+		(*container)[str_len] = '\0';
+
+		offset += str_len;
+	}
+
+	return offset;
+}
+
+static gint
+grab_data_version (gint16 *cont_upper, gint16 *cont_lower, gchar *data)
+{
+	gint offset = DMAP_CC_SZ;
+
+	memcpy (cont_lower, data + offset, DMAP_INT_SZ);
+	endian_swap_int16 (cont_lower);
+	offset += DMAP_INT_SZ;
+
+	memcpy (cont_upper, data + offset, DMAP_INT_SZ);
+	endian_swap_int16 (cont_upper);
+	offset += DMAP_INT_SZ;
+
+	return offset;
+}
+
+static gint
+grab_data (void *container, gchar *data, content_type ct)
+{
+	gint offset;
+	gint data_size;
+
+	offset = DMAP_CC_SZ;
+	memcpy (&data_size, data + offset, DMAP_INT_SZ);
+	endian_swap_int32 ((gint32 *)&data_size);
+	offset += DMAP_INT_SZ;
+
+	switch (ct) {
+		case DMAP_CTYPE_BYTE:
+		case DMAP_CTYPE_UNSIGNEDBYTE:
+			memcpy (container, data + offset, DMAP_BYTE_SZ);
+			offset += DMAP_BYTE_SZ;
+			break;
+		case DMAP_CTYPE_SHORT:
+		case DMAP_CTYPE_UNSIGNEDSHORT:
+			memcpy (container, data + offset, DMAP_SHORT_SZ);
+			endian_swap_int16 (container);
+			offset += DMAP_SHORT_SZ;
+			break;
+		case DMAP_CTYPE_INT:
+		case DMAP_CTYPE_UNSIGNEDINT:
+			memcpy (container, data + offset, DMAP_INT_SZ);
+			endian_swap_int32 (container);
+			offset += DMAP_INT_SZ;
+			break;
+		case DMAP_CTYPE_LONG:
+		case DMAP_CTYPE_UNSIGNEDLONG:
+			memcpy (container, data + offset, DMAP_LONG_SZ);
+			endian_swap_int64 (container);
+			offset += DMAP_LONG_SZ;
+			break;
+		case DMAP_CTYPE_STRING:
+			offset += grab_data_string ((gchar **) container, data, data_size);
+			break;
+		case DMAP_CTYPE_DATE:
+			memcpy (container, data + offset, DMAP_INT_SZ);
+			endian_swap_int32 (container);
+			offset += DMAP_INT_SZ;
+			break;
+		default:
+			g_print ("Warning: Unrecognized content type (%d).\n", ct);
+			break;
+	}
+
+	return offset;
+}
+
+static gint
+cc_handler_mtco (cc_data_t *fields, gchar *current_data)
+{
+	gint offset = grab_data (&(fields->n_rec_matches), current_data, DMAP_CTYPE_INT);
+	return offset;
+}
+
+static gint
+cc_handler_mrco (cc_data_t *fields, gchar *current_data)
+{
+	gint offset = grab_data (&(fields->n_ret_items), current_data, DMAP_CTYPE_INT);
+	return offset;
+}
+
+static gint
+cc_handler_muty (cc_data_t *fields, gchar *current_data)
+{
+	gint offset = grab_data (&(fields->updt_type), current_data, DMAP_CTYPE_BYTE);
+	return offset;
+}
+
+static gint
+cc_handler_mstt (cc_data_t *fields, gchar *current_data)
+{
+	gint offset = grab_data (&(fields->status), current_data, DMAP_CTYPE_INT);
+	return offset;
+}
+
+static gint
+cc_handler_mlit (cc_data_t *fields, gchar *data, gint data_len)
+{
+	gint offset = 0;
+	gboolean do_break = FALSE;
+	gchar *current_data, *data_end;
+	cc_item_record_t *item_fields;
+
+	current_data = data + 8;
+	data_end = data + data_len;
+
+	item_fields = g_malloc0 (sizeof (cc_item_record_t));
+
+	while (current_data < data_end && !do_break) {
+		switch (CC_TO_INT (current_data[0], current_data[1],
+		                   current_data[2], current_data[3])) {
+			case CC_TO_INT ('m','i','i','d'):
+				offset += grab_data (&(item_fields->dbid), current_data,
+				                    DMAP_CTYPE_INT);
+				break;
+			case CC_TO_INT ('m','p','e','r'):
+				offset += grab_data (&(item_fields->persistent_id), current_data,
+				                    DMAP_CTYPE_LONG);
+				break;
+			case CC_TO_INT ('m','i','n','m'):
+				offset += grab_data (&(item_fields->iname), current_data,
+				                    DMAP_CTYPE_STRING);
+				break;
+
+			/* song list specific */
+			case CC_TO_INT ('m','i','k','d'):
+				offset += grab_data (&(item_fields->item_kind), current_data,
+				                    DMAP_CTYPE_BYTE);
+				break;
+			case CC_TO_INT ('a','s','d','k'):
+				offset += grab_data (&(item_fields->song_data_kind), current_data,
+				                    DMAP_CTYPE_BYTE);
+				break;
+			case CC_TO_INT ('a','s','u','l'):
+				offset += grab_data (&(item_fields->song_data_url), current_data,
+				                    DMAP_CTYPE_STRING);
+				break;
+			case CC_TO_INT ('a','s','a','l'):
+				offset += grab_data (&(item_fields->song_data_album),
+				                    current_data,
+				                    DMAP_CTYPE_STRING);
+				break;
+			case CC_TO_INT ('a','s','a','r'):
+				offset += grab_data (&(item_fields->song_data_artist),
+				                    current_data,
+				                    DMAP_CTYPE_STRING);
+				break;
+			case CC_TO_INT ('a','s','b','r'):
+				offset += grab_data (&(item_fields->song_bitrate), current_data,
+				                    DMAP_CTYPE_SHORT);
+				break;
+			case CC_TO_INT ('a','s','c','m'):
+				offset += grab_data (&(item_fields->song_comment), current_data,
+				                    DMAP_CTYPE_STRING);
+				break;
+			case CC_TO_INT ('a','s','d','a'):
+				offset += grab_data (&(item_fields->song_date), current_data,
+				                    DMAP_CTYPE_DATE);
+				break;
+			case CC_TO_INT ('a','s','d','m'):
+				offset += grab_data (&(item_fields->song_date_mod), current_data,
+				                    DMAP_CTYPE_DATE);
+				break;
+			case CC_TO_INT ('a','s','g','n'):
+				offset += grab_data (&(item_fields->song_genre), current_data,
+				                    DMAP_CTYPE_STRING);
+				break;
+			case CC_TO_INT ('a','s','f','m'):
+				offset += grab_data (&(item_fields->song_format), current_data,
+				                    DMAP_CTYPE_STRING);
+				break;
+			case CC_TO_INT ('a','s','d','t'):
+				offset += grab_data (&(item_fields->song_description), current_data,
+				                    DMAP_CTYPE_STRING);
+				break;
+			case CC_TO_INT ('a','s','s','r'):
+				offset += grab_data (&(item_fields->sample_rate), current_data,
+				                    DMAP_CTYPE_INT);
+				break;
+			case CC_TO_INT ('a','s','s','z'):
+				offset += grab_data (&(item_fields->song_size), current_data,
+				                    DMAP_CTYPE_INT);
+				break;
+			case CC_TO_INT ('a','s','s','t'):
+				offset += grab_data (&(item_fields->song_start_time), current_data,
+				                    DMAP_CTYPE_INT);
+				break;
+			case CC_TO_INT ('a','s','s','p'):
+				offset += grab_data (&(item_fields->song_stop_time), current_data,
+				                    DMAP_CTYPE_INT);
+				break;
+			case CC_TO_INT ('a','s','t','m'):
+				offset += grab_data (&(item_fields->song_total_time), current_data,
+				                    DMAP_CTYPE_INT);
+				break;
+			case CC_TO_INT ('a','s','y','r'):
+				offset += grab_data (&(item_fields->song_year), current_data,
+				                    DMAP_CTYPE_SHORT);
+				break;
+			case CC_TO_INT ('a','s','t','n'):
+				offset += grab_data (&(item_fields->song_track_no), current_data,
+				                    DMAP_CTYPE_SHORT);
+				break;
+			case CC_TO_INT ('a','s','c','p'):
+				offset += grab_data (&(item_fields->song_composer), current_data,
+				                    DMAP_CTYPE_STRING);
+				break;
+			case CC_TO_INT ('a','s','t','c'):
+				offset += grab_data (&(item_fields->song_track_count), current_data,
+				                    DMAP_CTYPE_SHORT);
+				break;
+			case CC_TO_INT ('a','s','d','c'):
+				offset += grab_data (&(item_fields->song_disc_count), current_data,
+				                    DMAP_CTYPE_SHORT);
+				break;
+			case CC_TO_INT ('a','s','d','n'):
+				offset += grab_data (&(item_fields->song_disc_no), current_data,
+				                    DMAP_CTYPE_SHORT);
+				break;
+			case CC_TO_INT ('a','s','c','o'):
+				offset += grab_data (&(item_fields->song_compilation), current_data,
+				                    DMAP_CTYPE_BYTE);
+				break;
+			case CC_TO_INT ('a','s','b','t'):
+				offset += grab_data (&(item_fields->song_bpm), current_data,
+				                    DMAP_CTYPE_SHORT);
+				break;
+			case CC_TO_INT ('a','g','r','p'):
+				offset += grab_data (&(item_fields->song_grouping), current_data,
+				                    DMAP_CTYPE_STRING);
+				break;
+			case CC_TO_INT ('m','c','t','i'):
+				offset += grab_data (&(item_fields->container_id), current_data,
+				                    DMAP_CTYPE_INT);
+				break;
+			case CC_TO_INT ('m','u','d','l'):
+				offset += grab_data (&(item_fields->deleted_id), current_data,
+				                    DMAP_CTYPE_INT);
+				break;
+
+			/* db list specific */
+			case CC_TO_INT ('m','i','m','c'):
+				offset += grab_data (&(item_fields->db_n_items), current_data,
+				                    DMAP_CTYPE_INT);
+				break;
+			case CC_TO_INT ('m','c','t','c'):
+				offset += grab_data (&(item_fields->db_n_playlist), current_data,
+				                    DMAP_CTYPE_INT);
+				break;
+			case CC_TO_INT ('a','e','S','P'):
+				offset += grab_data (&(item_fields->is_smart_pl), current_data,
+				                    DMAP_CTYPE_BYTE);
+				break;
+			case CC_TO_INT ('a','b','p','l'):
+				offset += grab_data (&(item_fields->is_base_pl), current_data,
+				                    DMAP_CTYPE_BYTE);
+				break;
+
+			/* exit conditions */
+			case CC_TO_INT ('m','l','i','t'):
+				do_break = TRUE;
+				break;
+			default:
+				g_print ("Warning: Unrecognized content code "
+				          "or end of data: %s\n", current_data);
+				do_break = TRUE;
+				break;
+		}
+
+		current_data += offset;
+		offset = 0;
+	}
+
+	fields->record_list = g_slist_prepend (fields->record_list, item_fields);
+
+	return (gint) (current_data - data);
+}
+
+static gint
+cc_handler_mlcl (cc_data_t *fields, gchar *data, gint data_len)
+{
+	gint offset = 0;
+	gboolean do_break = FALSE;
+	gchar *current_data, *data_end;
+	current_data = data + 8;
+	data_end = data + data_len;
+
+	while (current_data < data_end && !do_break) {
+		if (CC_TO_INT (current_data[0], current_data[1],
+		               current_data[2], current_data[3]) ==
+		    CC_TO_INT ('m','l','i','t')) {
+
+			offset += cc_handler_mlit (fields, current_data,
+			                           DMAP_BYTES_REMAINING);
+		} else {
+			break;
+		}
+
+		current_data += offset;
+		offset = 0;
+	}
+
+	return (gint)(current_data - data);
+}
+
+static cc_data_t *
+cc_handler_adbs (gchar *data, gint data_len)
+{
+	gint offset = 0;
+	gboolean do_break = FALSE;
+	gchar *current_data, *data_end;
+	cc_data_t *fields;
+
+	current_data = data + 8;
+	data_end = data + data_len;
+
+	fields = cc_data_new ();
+
+	while ((current_data < data_end) && !do_break) {
+		switch (CC_TO_INT (current_data[0], current_data[1],
+		                   current_data[2], current_data[3])) {
+			case CC_TO_INT ('m','s','t','t'):
+				offset += cc_handler_mstt (fields, current_data);
+				break;
+			case CC_TO_INT ('m','u','t','y'):
+				offset += cc_handler_muty (fields, current_data);
+				break;
+			case CC_TO_INT ('m','t','c','o'):
+				offset += cc_handler_mtco (fields, current_data);
+				break;
+			case CC_TO_INT ('m','r','c','o'):
+				offset += cc_handler_mrco (fields, current_data);
+				break;
+			case CC_TO_INT ('m','l','c','l'):
+				offset += cc_handler_mlcl (fields, current_data,
+				                           DMAP_BYTES_REMAINING);
+				break;
+			default:
+				do_break = TRUE;
+				break;
+		}
+
+		current_data += offset;
+		offset = 0;
+	}
+
+	return fields;
+}
+
+static cc_data_t *
+cc_handler_msrv (gchar *data, gint data_len)
+{
+	gint offset = 0;
+	gboolean do_break = FALSE;
+	gchar *current_data, *data_end;
+	cc_data_t *fields;
+	current_data = data + 8;
+	data_end = data + data_len;
+
+	fields = cc_data_new ();
+
+	while ((current_data < data_end) && !do_break) {
+		switch (CC_TO_INT (current_data[0], current_data[1],
+		                   current_data[2], current_data[3])) {
+			case CC_TO_INT ('m','s','t','t'):
+				offset += cc_handler_mstt (fields, current_data);
+				break;
+			case CC_TO_INT ('a','p','r','o'):
+				offset += grab_data_version (&(fields->daap_proto_major),
+				                            &(fields->daap_proto_minor),
+				                            current_data);
+				break;
+			case CC_TO_INT ('m','p','r','o'):
+				offset += grab_data_version (&(fields->dmap_proto_major),
+				                            &(fields->dmap_proto_minor),
+				                            current_data);
+				break;
+			case CC_TO_INT ('m','s','t','m'):
+				offset += grab_data (&(fields->timeout_interval), current_data,
+				                    DMAP_CTYPE_INT);
+				break;
+			case CC_TO_INT ('m','s','i','x'):
+				offset += grab_data (&(fields->has_indexing), current_data,
+				                    DMAP_CTYPE_BYTE);
+				break;
+			case CC_TO_INT ('m','s','e','x'):
+				offset += grab_data (&(fields->has_extensions), current_data,
+				                    DMAP_CTYPE_BYTE);
+				break;
+			case CC_TO_INT ('m','s','u','p'):
+				offset += grab_data (&(fields->has_update), current_data,
+				                    DMAP_CTYPE_BYTE);
+				break;
+			case CC_TO_INT ('m','s','a','l'):
+				offset += grab_data (&(fields->has_autologout), current_data,
+				                    DMAP_CTYPE_BYTE);
+				break;
+			case CC_TO_INT ('m','s','l','r'):
+				offset += grab_data (&(fields->login_required), current_data,
+				                    DMAP_CTYPE_BYTE);
+				break;
+			case CC_TO_INT ('m','s','q','y'):
+				offset += grab_data (&(fields->has_queries), current_data,
+				                    DMAP_CTYPE_BYTE);
+				break;
+			case CC_TO_INT ('m','s','r','s'):
+				offset += grab_data (&(fields->has_resolve), current_data,
+				                    DMAP_CTYPE_BYTE);
+				break;
+			case CC_TO_INT ('m','s','b','r'):
+				offset += grab_data (&(fields->has_browsing), current_data,
+				                    DMAP_CTYPE_BYTE);
+				break;
+			case CC_TO_INT ('m','s','p','i'):
+				offset += grab_data (&(fields->has_persistent), current_data,
+				                    DMAP_CTYPE_BYTE);
+				break;
+			case CC_TO_INT ('m','s','a','s'):
+				offset += grab_data (&(fields->auth_type), current_data,
+				                    DMAP_CTYPE_BYTE);
+				break;
+			case CC_TO_INT ('m','s','a','u'):
+				offset += grab_data (&(fields->auth_method), current_data,
+				                    DMAP_CTYPE_BYTE);
+				break;
+			case CC_TO_INT ('a','e','S','V'):
+				offset += grab_data (&(fields->sharing_version), current_data,
+				                    DMAP_CTYPE_INT);
+				break;
+			case CC_TO_INT ('m','s','d','c'):
+				offset += grab_data (&(fields->db_count), current_data,
+				                    DMAP_CTYPE_INT);
+				break;
+			case CC_TO_INT ('m','i','n','m'):
+				offset += grab_data (&(fields->server_name), current_data,
+				                    DMAP_CTYPE_STRING);
+				break;
+			default:
+				g_print ("Warning: Unrecognized content code "
+				          "or end of data: %s\n",
+				          current_data);
+				do_break = TRUE;
+				break;
+		}
+
+		current_data += offset;
+		offset = 0;
+	}
+
+	return fields;
+}
+
+static cc_data_t *
+cc_handler_mccr (gchar *data, gint data_len)
+{
+	/* not implemented */
+	return NULL;
+}
+
+static cc_data_t *
+cc_handler_mlog (gchar *data, gint data_len)
+{
+	gint offset = 0;
+	gboolean do_break = FALSE;
+	gchar *current_data, *data_end;
+	cc_data_t *fields;
+
+	current_data = data + 8;
+	data_end = data + data_len;
+
+	fields = cc_data_new ();
+
+	while ((current_data < data_end) && !do_break) {
+		switch (CC_TO_INT (current_data[0], current_data[1],
+		                   current_data[2], current_data[3])) {
+			case CC_TO_INT ('m','s','t','t'):
+				offset += cc_handler_mstt (fields, current_data);
+				break;
+			case CC_TO_INT ('m','l','i','d'):
+				offset += grab_data (&(fields->session_id), current_data, DMAP_CTYPE_INT);
+				break;
+			default:
+				g_print ("Unrecognized content code or end of data: %s\n",
+				          current_data);
+				do_break = TRUE;
+				break;
+		}
+
+		current_data += offset;
+		offset = 0;
+	}
+
+	return fields;
+}
+
+static cc_data_t *
+cc_handler_mupd (gchar *data, gint data_len)
+{
+	gint offset = 0;
+	gboolean do_break = FALSE;
+	gchar *current_data, *data_end;
+	cc_data_t *fields;
+
+	current_data = data + 8;
+	data_end = data + data_len;
+
+	fields = cc_data_new ();
+
+	while ((current_data < data_end) && !do_break) {
+		switch (CC_TO_INT (current_data[0], current_data[1],
+		                   current_data[2], current_data[3])) {
+			case CC_TO_INT ('m','u','s','r'):
+				offset += grab_data (&(fields->revision_id), current_data,
+				                    DMAP_CTYPE_INT);
+				break;
+			case CC_TO_INT ('m','s','t','t'):
+				offset += cc_handler_mstt (fields, current_data);
+				break;
+			default:
+				g_print ("Unrecognized content code or end of data: %s\n",
+				          current_data);
+				do_break = TRUE;
+				break;
+		}
+
+		current_data += offset;
+		offset = 0;
+	}
+
+	return fields;
+}
+
+static cc_data_t *
+cc_handler_avdb (gchar *data, gint data_len)
+{
+	gint offset = 0;
+	gboolean do_break = FALSE;
+	gchar *current_data, *data_end;
+	cc_data_t *fields;
+
+	current_data = data + 8;
+	data_end = data + data_len;
+
+	fields = cc_data_new ();
+
+	while ((current_data < data_end) && !do_break) {
+		switch (CC_TO_INT (current_data[0], current_data[1],
+		                   current_data[2], current_data[3])) {
+			case CC_TO_INT ('m','s','t','t'):
+				offset += cc_handler_mstt (fields, current_data);
+				break;
+			case CC_TO_INT ('m','u','t','y'):
+				offset += cc_handler_muty (fields, current_data);
+				break;
+			case CC_TO_INT ('m','t','c','o'):
+				offset += cc_handler_mtco (fields, current_data);
+				break;
+			case CC_TO_INT ('m','r','c','o'):
+				offset += cc_handler_mrco (fields, current_data);
+				break;
+			case CC_TO_INT ('m','l','c','l'):
+				offset += cc_handler_mlcl (fields, current_data,
+				                           DMAP_BYTES_REMAINING);
+				break;
+			default:
+				do_break = TRUE;
+				break;
+		}
+
+		current_data += offset;
+		offset = 0;
+	}
+
+	return fields;
+}
+
+static cc_data_t *
+cc_handler_apso (gchar *data, gint data_len)
+{
+	gint offset = 0;
+	gboolean do_break = FALSE;
+	gchar *current_data, *data_end;
+	cc_data_t *fields;
+
+	current_data = data + 8;
+	data_end = data + data_len;
+
+	fields = cc_data_new ();
+
+	while ((current_data < data_end) && !do_break) {
+		switch (CC_TO_INT (current_data[0], current_data[1],
+		                   current_data[2], current_data[3])) {
+			case CC_TO_INT ('m','s','t','t'):
+				offset += cc_handler_mstt (fields, current_data);
+				break;
+			case CC_TO_INT ('m','u','t','y'):
+				offset += cc_handler_muty (fields, current_data);
+				break;
+			case CC_TO_INT ('m','t','c','o'):
+				offset += cc_handler_mtco (fields, current_data);
+				break;
+			case CC_TO_INT ('m','r','c','o'):
+				offset += cc_handler_mrco (fields, current_data);
+				break;
+			case CC_TO_INT ('m','l','c','l'):
+				offset += cc_handler_mlcl (fields, current_data,
+				                           DMAP_BYTES_REMAINING);
+				break;
+			default:
+				do_break = TRUE;
+				break;
+		}
+
+		current_data += offset;
+		offset = 0;
+	}
+
+	return fields;
+}
+
+static cc_data_t *
+cc_handler_aply (gchar *data, gint data_len)
+{
+	gint offset = 0;
+	gboolean do_break = FALSE;
+	gchar *current_data, *data_end;
+	cc_data_t *fields;
+
+	current_data = data + 8;
+	data_end = data + data_len;
+
+	fields = cc_data_new ();
+
+	while ((current_data < data_end) && !do_break) {
+		switch (CC_TO_INT (current_data[0], current_data[1],
+		                   current_data[2], current_data[3])) {
+			case CC_TO_INT ('m','s','t','t'):
+				offset += cc_handler_mstt (fields, current_data);
+				break;
+			case CC_TO_INT ('m','u','t','y'):
+				offset += cc_handler_muty (fields, current_data);
+				break;
+			case CC_TO_INT ('m','t','c','o'):
+				offset += cc_handler_mtco (fields, current_data);
+				break;
+			case CC_TO_INT ('m','r','c','o'):
+				offset += cc_handler_mrco (fields, current_data);
+				break;
+			case CC_TO_INT ('m','l','c','l'):
+				offset += cc_handler_mlcl (fields, current_data,
+				                           DMAP_BYTES_REMAINING);
+				break;
+			default:
+				do_break = TRUE;
+				break;
+		}
+
+		current_data += offset;
+		offset = 0;
+	}
+
+	return fields;
+}
+
+cc_data_t *
+cc_data_new ()
+{
+	cc_data_t *retval;
+
+	retval = g_malloc0 (sizeof (cc_data_t));
+	retval->record_list = NULL;
+
+	return retval;
+}
+
+void
+cc_data_free (cc_data_t *fields)
+{
+	if (NULL != fields->server_name) g_free (fields->server_name);
+
+	g_slist_foreach (fields->record_list,
+	                 (GFunc) cc_item_record_free,
+	                 NULL);
+	g_slist_free (fields->record_list);
+
+	g_free (fields);
+}
+
+void
+cc_item_record_free (cc_item_record_t *item)
+{
+	if (NULL != item->iname)            g_free (item->iname);
+	if (NULL != item->song_data_url)    g_free (item->song_data_url);
+	if (NULL != item->song_data_album)  g_free (item->song_data_album);
+	if (NULL != item->song_data_artist) g_free (item->song_data_artist);
+	if (NULL != item->song_comment)     g_free (item->song_comment);
+	if (NULL != item->song_description) g_free (item->song_description);
+	if (NULL != item->song_genre)       g_free (item->song_genre);
+	if (NULL != item->song_format)      g_free (item->song_format);
+	if (NULL != item->song_composer)    g_free (item->song_composer);
+	if (NULL != item->song_grouping)    g_free (item->song_grouping);
+
+	g_free (item);
+}
+
+GSList *
+cc_record_list_deep_copy (GSList *record_list) {
+	GSList *retval = NULL;
+	cc_item_record_t *record, *data;
+
+	for (; record_list; record_list = g_slist_next (record_list)) {
+		data = record_list->data;
+		record = g_malloc0 (sizeof (cc_item_record_t));
+		if (!record) {
+			g_print ("memory allocation failed for cc_record_list_deep_copy\n");
+			return NULL;
+		}
+
+		record->item_kind        = data->item_kind;
+		record->song_data_kind   = data->song_data_kind;
+		record->song_compilation = data->song_compilation;
+		record->is_smart_pl      = data->is_smart_pl;
+		record->is_base_pl       = data->is_base_pl;
+
+		record->song_bitrate     = data->song_bitrate;
+		record->song_year        = data->song_year;
+		record->song_track_no    = data->song_track_no;
+		record->song_track_count = data->song_track_count;
+		record->song_disc_count  = data->song_disc_count;
+		record->song_disc_no     = data->song_disc_no;
+		record->song_bpm         = data->song_bpm;
+
+		record->dbid             = data->dbid;
+		record->sample_rate      = data->sample_rate;
+		record->song_size        = data->song_size;
+		record->song_start_time  = data->song_start_time;
+		record->song_stop_time   = data->song_stop_time;
+		record->song_total_time  = data->song_total_time;
+		record->song_date        = data->song_date;
+		record->song_date_mod    = data->song_date_mod;
+		record->container_id     = data->container_id;
+
+		record->deleted_id       = data->deleted_id;
+
+		record->persistent_id    = data->persistent_id;
+
+		record->iname            = g_strdup (data->iname);
+		record->song_data_url    = g_strdup (data->song_data_url);
+		record->song_data_album  = g_strdup (data->song_data_album);
+		record->song_data_artist = g_strdup (data->song_data_artist);
+		record->song_comment     = g_strdup (data->song_comment);
+		record->song_description = g_strdup (data->song_description);
+		record->song_genre       = g_strdup (data->song_genre);
+		record->song_format      = g_strdup (data->song_format);
+		record->song_composer    = g_strdup (data->song_composer);
+		record->song_grouping    = g_strdup (data->song_grouping);
+
+		/* db list specific */
+		record->db_n_items       = data->db_n_items;
+		record->db_n_playlist    = data->db_n_playlist;
+
+		retval = g_slist_prepend (retval, record);
+	}
+
+	return retval;
+}
+
+cc_data_t *
+cc_handler (gchar *data, gint data_len)
+{
+	cc_data_t *retval;
+
+	switch (CC_TO_INT (data[0],data[1],data[2],data[3])) {
+		case CC_TO_INT ('a','d','b','s'):
+			retval = cc_handler_adbs (data, data_len);
+			break;
+		case CC_TO_INT ('m','s','r','v'):
+			retval = cc_handler_msrv (data, data_len);
+			break;
+		case CC_TO_INT ('m','c','c','r'):
+			retval = cc_handler_mccr (data, data_len);
+			break;
+		case CC_TO_INT ('m','l','o','g'):
+			retval = cc_handler_mlog (data, data_len);
+			break;
+		case CC_TO_INT ('m','u','p','d'):
+			retval = cc_handler_mupd (data, data_len);
+			break;
+		case CC_TO_INT ('a','v','d','b'):
+			retval = cc_handler_avdb (data, data_len);
+			break;
+		case CC_TO_INT ('a','p','s','o'):
+			retval = cc_handler_apso (data, data_len);
+			break;
+		case CC_TO_INT ('a','p','l','y'):
+			retval = cc_handler_aply (data, data_len);
+			break;
+		default:
+			retval = NULL;
+			break;
+	}
+
+	return retval;
+}
+