void
dpap_share_server_info (DMAPShare * share,
			SoupServer * server,
			SoupMessage * message,
			const char *path,
			GHashTable * query, SoupClientContext * context)
{
/* MSRV	server info response
 * 	MSTT status
 * 	MPRO dpap version
 * 	PPRO dpap version
 * 	MINM name
 * 	MSAU authentication method
 * 	MSLR login required
 * 	MSTM timeout interval
 * 	MSAL supports auto logout
 * 	MSUP supports update
 * 	MSPI supports persistent ids
 * 	MSEX supports extensions
 * 	MSBR supports browse
 * 	MSQY supports query
 * 	MSIX supports index
 * 	MSRS supports resolve
 * 	MSDC databases count
 */
	gchar *nameprop;
	GNode *msrv;

	g_debug ("Path is %s.", path);

	g_object_get ((gpointer) share, "name", &nameprop, NULL);

	msrv = dmap_structure_add (NULL, DMAP_CC_MSRV);
	dmap_structure_add (msrv, DMAP_CC_MSTT, (gint32) DMAP_STATUS_OK);
	dmap_structure_add (msrv, DMAP_CC_MPRO, (gdouble) DMAP_VERSION);
	dmap_structure_add (msrv, DMAP_CC_PPRO, (gdouble) DPAP_VERSION);
	dmap_structure_add (msrv, DMAP_CC_MINM, nameprop);
	/*dmap_structure_add (msrv, DMAP_CC_MSAU, _dmap_share_get_auth_method (share)); */
	/* authentication method
	 * 0 is nothing
	 * 1 is name & password
	 * 2 is password only
	 */
	dmap_structure_add (msrv, DMAP_CC_MSLR, 0);
	dmap_structure_add (msrv, DMAP_CC_MSTM, (gint32) DPAP_TIMEOUT);
	dmap_structure_add (msrv, DMAP_CC_MSAL, (gchar) 0);
	/*dmap_structure_add (msrv, DMAP_CC_MSUP, (gchar) 1);
	 *dmap_structure_add (msrv, DMAP_CC_MSPI, (gchar) 0);
	 *dmap_structure_add (msrv, DMAP_CC_MSEX, (gchar) 0);
	 *dmap_structure_add (msrv, DMAP_CC_MSBR, (gchar) 0);
	 *dmap_structure_add (msrv, DMAP_CC_MSQY, (gchar) 0); */
	dmap_structure_add (msrv, DMAP_CC_MSIX, (gchar) 0);
	/* dmap_structure_add (msrv, DMAP_CC_MSRS, (gchar) 0); */
	dmap_structure_add (msrv, DMAP_CC_MSDC, (gint32) 1);

	_dmap_share_message_set_from_dmap_structure (share, message, msrv);
	dmap_structure_destroy (msrv);

	g_free (nameprop);
}
static void
add_to_category_listing (gpointer key, gpointer user_data)
{
	GNode *mlit;
	GNode *node = (GNode *) user_data;

	mlit = dmap_structure_add (node, DMAP_CC_MLIT);
	dmap_structure_add (mlit, DMAP_RAW, (char *) key);
}
void
dacp_share_ctrl_int (DMAPShare * share,
		     SoupServer * server,
		     SoupMessage * message,
		     const char *path,
		     GHashTable * query, SoupClientContext * context)
{
	const char *rest_of_path;

	DACPShare *dacp_share = DACP_SHARE (share);

	g_debug ("Path is %s.", path);
	if (query) {
		g_hash_table_foreach (query, debug_param, NULL);
	}

	rest_of_path = strchr (path + 1, '/');

	/* If calling /ctrl-int without args, the client doesnt need a 
	 * session-id, otherwise it does and it should be validated. */
	if ((rest_of_path != NULL)
	    &&
	    (!_dmap_share_session_id_validate
	     (share, context, message, query, NULL))) {
		soup_message_set_status (message, SOUP_STATUS_FORBIDDEN);
		return;
	}

	if (rest_of_path == NULL) {
		/* CACI control-int
		 *      MSTT status
		 *      MUTY update type
		 *      MTCO specified total count
		 *      MRCO returned count
		 *      MLCL listing
		 *              MLIT listing item
		 *                      MIID item id
		 *                      CMIK Unknown (TRUE)
		 *                      CMSP Unknown (TRUE)
		 *                      CMSV Unknown (TRUE)
		 *                      CASS Unknown (TRUE)
		 *                      CASU Unknown (TRUE)
		 *                      CASG Unknown (TRUE)
		 */

		GNode *caci;
		GNode *mlcl;
		GNode *mlit;

		// dacp.controlint
		caci = dmap_structure_add (NULL, DMAP_CC_CACI);
		// dmap.status
		dmap_structure_add (caci, DMAP_CC_MSTT,
				    (gint32) DMAP_STATUS_OK);
		// dmap.updatetype
		dmap_structure_add (caci, DMAP_CC_MUTY, 0);
		// dmap.specifiedtotalcount
		dmap_structure_add (caci, DMAP_CC_MTCO, (gint32) 1);
		// dmap.returnedcount
		dmap_structure_add (caci, DMAP_CC_MRCO, (gint32) 1);
		// dmap.listing
		mlcl = dmap_structure_add (caci, DMAP_CC_MLCL);
		// dmap.listingitem
		mlit = dmap_structure_add (mlcl, DMAP_CC_MLIT);
		// dmap.itemid
		dmap_structure_add (mlit, DMAP_CC_MIID, (gint32) 1);
		// Unknown (TRUE)
		dmap_structure_add (mlit, DMAP_CC_CMIK, (gint32) 1);
		// Unknown (TRUE)
		dmap_structure_add (mlit, DMAP_CC_CMSP, (gint32) 1);
		// Unknown (TRUE)
		dmap_structure_add (mlit, DMAP_CC_CMSV, (gint32) 1);
		// Unknown (TRUE)
		dmap_structure_add (mlit, DMAP_CC_CASS, (gint32) 1);
		// Unknown (TRUE)
		dmap_structure_add (mlit, DMAP_CC_CASU, (gint32) 1);
		// Unknown (TRUE)
		dmap_structure_add (mlit, DMAP_CC_CASG, (gint32) 1);

		_dmap_share_message_set_from_dmap_structure (share, message,
							     caci);
		dmap_structure_destroy (caci);
	} else if (g_ascii_strcasecmp ("/1/getproperty", rest_of_path) == 0) {
		gchar *properties_query, **properties, **property;
		GNode *cmgt;

		properties_query = g_hash_table_lookup (query, "properties");

		if (!properties_query) {
			g_warning ("No property specified");
			return;
		}

		cmgt = dmap_structure_add (NULL, DMAP_CC_CMGT);
		dmap_structure_add (cmgt, DMAP_CC_MSTT, DMAP_STATUS_OK);

		properties = g_strsplit (properties_query, ",", -1);
		for (property = properties; *property; property++) {
			if (g_ascii_strcasecmp (*property, "dmcp.volume") ==
			    0) {
				gulong volume;

				g_object_get (dacp_share->priv->player,
					      "volume", &volume, NULL);
				//g_debug ("Sending volume: %lu", volume);
				dmap_structure_add (cmgt, DMAP_CC_CMVO,
						    volume);
			} else {
				g_warning ("Unhandled property %s",
					   *property);
			}
		}

		g_strfreev (properties);

		_dmap_share_message_set_from_dmap_structure (share, message,
							     cmgt);
		dmap_structure_destroy (cmgt);
	} else if (g_ascii_strcasecmp ("/1/setproperty", rest_of_path) == 0) {
		if (g_hash_table_lookup (query, "dmcp.volume")) {
			gdouble volume =
				strtod (g_hash_table_lookup
					(query, "dmcp.volume"), NULL);
			g_object_set (dacp_share->priv->player, "volume",
				      (gulong) volume, NULL);
		}
		soup_message_set_status (message, SOUP_STATUS_NO_CONTENT);
	} else if (g_ascii_strcasecmp ("/1/getspeakers", rest_of_path) == 0) {
		GNode *casp;

		casp = dmap_structure_add (NULL, DMAP_CC_CASP);
		dmap_structure_add (casp, DMAP_CC_MSTT,
				    (gint32) DMAP_STATUS_OK);
		dmap_structure_add (casp, DMAP_CC_MDCL);

		dmap_structure_add (casp, DMAP_CC_CAIA, TRUE);
		dmap_structure_add (casp, DMAP_CC_MINM, "Computer");
		dmap_structure_add (casp, DMAP_CC_MSMA, (gint32) 0);

		_dmap_share_message_set_from_dmap_structure (share, message,
							     casp);
		dmap_structure_destroy (casp);
	} else if (g_ascii_strcasecmp ("/1/playstatusupdate", rest_of_path) ==
		   0) {
		gchar *revision =
			g_hash_table_lookup (query, "revision-number");
		gint revision_number = atoi (revision);

		if (revision_number >= dacp_share->priv->current_revision) {
			g_object_ref (message);
			dacp_share->priv->update_queue =
				g_slist_prepend (dacp_share->
						 priv->update_queue, message);
			g_signal_connect_object (message, "finished",
						 G_CALLBACK
						 (status_update_message_finished),
						 dacp_share, 0);
			soup_server_pause_message (server, message);
		} else {
			dacp_share_fill_playstatusupdate (dacp_share,
							  message);
		}
	} else if (g_ascii_strcasecmp ("/1/playpause", rest_of_path) == 0) {
		dacp_player_play_pause (dacp_share->priv->player);
		soup_message_set_status (message, SOUP_STATUS_NO_CONTENT);
	} else if (g_ascii_strcasecmp ("/1/pause", rest_of_path) == 0) {
		dacp_player_pause (dacp_share->priv->player);
		soup_message_set_status (message, SOUP_STATUS_NO_CONTENT);
	} else if (g_ascii_strcasecmp ("/1/nextitem", rest_of_path) == 0) {
		dacp_player_next_item (dacp_share->priv->player);
		soup_message_set_status (message, SOUP_STATUS_NO_CONTENT);
	} else if (g_ascii_strcasecmp ("/1/previtem", rest_of_path) == 0) {
		dacp_player_prev_item (dacp_share->priv->player);
		soup_message_set_status (message, SOUP_STATUS_NO_CONTENT);
	} else if (g_ascii_strcasecmp ("/1/nowplayingartwork", rest_of_path)
		   == 0) {
		guint width = 320;
		guint height = 320;
		gchar *artwork_filename;
		gchar *buffer;
		gsize buffer_len;

		if (g_hash_table_lookup (query, "mw"))
			width = atoi (g_hash_table_lookup (query, "mw"));
		if (g_hash_table_lookup (query, "mh"))
			height = atoi (g_hash_table_lookup (query, "mh"));
		artwork_filename =
			dacp_player_now_playing_artwork (dacp_share->
							 priv->player, width,
							 height);
		if (!artwork_filename) {
			g_debug ("No artwork for currently playing song");
			soup_message_set_status (message,
						 SOUP_STATUS_NOT_FOUND);
			return;
		}
#ifdef HAVE_GDKPIXBUF
		GdkPixbuf *artwork =
			gdk_pixbuf_new_from_file_at_scale (artwork_filename,
							   width, height,
							   TRUE, NULL);

		if (!artwork) {
			g_debug ("Error loading image file");
			g_free (artwork_filename);
			soup_message_set_status (message,
						 SOUP_STATUS_INTERNAL_SERVER_ERROR);
			return;
		}
		if (!gdk_pixbuf_save_to_buffer
		    (artwork, &buffer, &buffer_len, "png", NULL, NULL)) {
			g_debug ("Error saving artwork to PNG");
			g_object_unref (artwork);
			g_free (artwork_filename);
			soup_message_set_status (message,
						 SOUP_STATUS_INTERNAL_SERVER_ERROR);
			return;
		}
		g_object_unref (artwork);
#else
		if (!g_file_get_contents
		    (artwork_filename, &buffer, &buffer_len, NULL)) {
			g_debug ("Error getting artwork data");
			g_free (artwork_filename);
			soup_message_set_status (message,
						 SOUP_STATUS_INTERNAL_SERVER_ERROR);
			return;
		}
#endif
		g_free (artwork_filename);
		soup_message_set_status (message, SOUP_STATUS_OK);
		soup_message_set_response (message, "image/png",
					   SOUP_MEMORY_TAKE, buffer,
					   buffer_len);
	} else if (g_ascii_strcasecmp ("/1/cue", rest_of_path) == 0) {
		gchar *command;

		command = g_hash_table_lookup (query, "command");

		if (!command) {
			g_debug ("No CUE command specified");
			soup_message_set_status (message,
						 SOUP_STATUS_NO_CONTENT);
			return;
		} else if (g_ascii_strcasecmp ("clear", command) == 0) {
			dacp_player_cue_clear (dacp_share->priv->player);
			soup_message_set_status (message,
						 SOUP_STATUS_NO_CONTENT);
		} else if (g_ascii_strcasecmp ("play", command) == 0) {
			GNode *cacr;
			gchar *record_query;
			gchar *sort_by;
			GHashTable *records;
			GList *sorted_records;
			GSList *filter_def;
			DMAPDb *db;
			gint index =
				atoi (g_hash_table_lookup (query, "index"));

			g_object_get (share, "db", &db, NULL);
			record_query = g_hash_table_lookup (query, "query");
			filter_def = _dmap_share_build_filter (record_query);
			records = dmap_db_apply_filter (db, filter_def);
			sorted_records = g_hash_table_get_values (records);
			sort_by = g_hash_table_lookup (query, "sort");
			if (g_strcmp0 (sort_by, "album") == 0) {
				sorted_records =
					g_list_sort_with_data (sorted_records,
							       (GCompareDataFunc)
							       daap_record_cmp_by_album,
							       db);
			} else if (sort_by != NULL) {
				g_warning ("Unknown sort column: %s",
					   sort_by);
			}

			dacp_player_cue_play (dacp_share->priv->player,
					      sorted_records, index);

			g_list_free (sorted_records);
			g_hash_table_unref (records);
			dmap_share_free_filter (filter_def);

			cacr = dmap_structure_add (NULL, DMAP_CC_CACR);
			dmap_structure_add (cacr, DMAP_CC_MSTT,
					    DMAP_STATUS_OK);
			dmap_structure_add (cacr, DMAP_CC_MIID, index);

			_dmap_share_message_set_from_dmap_structure (share,
								     message,
								     cacr);
			dmap_structure_destroy (cacr);
		} else {
			g_warning ("Unhandled cue command: %s", command);
			soup_message_set_status (message,
						 SOUP_STATUS_NO_CONTENT);
			return;
		}
	} else {
		g_warning ("Unhandled ctrl-int command: %s", rest_of_path);
		soup_message_set_status (message, SOUP_STATUS_BAD_REQUEST);
	}
}
static void
dacp_share_fill_playstatusupdate (DACPShare * share, SoupMessage * message)
{
	GNode *cmst;
	DAAPRecord *record;
	DACPPlayState play_state;
	DACPRepeatState repeat_state;
	gboolean shuffle_state;
	guint playing_time;

	g_object_get (share->priv->player,
		      "play-state", &play_state,
		      "repeat-state", &repeat_state,
		      "shuffle-state", &shuffle_state,
		      "playing-time", &playing_time, NULL);

	record = dacp_player_now_playing_record (share->priv->player);

	cmst = dmap_structure_add (NULL, DMAP_CC_CMST);
	dmap_structure_add (cmst, DMAP_CC_MSTT, (gint32) DMAP_STATUS_OK);
	dmap_structure_add (cmst, DMAP_CC_CMSR,
			    share->priv->current_revision);
	dmap_structure_add (cmst, DMAP_CC_CAPS, (gint32) play_state);
	dmap_structure_add (cmst, DMAP_CC_CASH, shuffle_state ? 1 : 0);
	dmap_structure_add (cmst, DMAP_CC_CARP, (gint32) repeat_state);
	if (record) {
		gchar *title;
		gchar *artist;
		gchar *album;
		gint duration;
		guint track_time;

		g_object_get (record,
			      "title", &title,
			      "songartist", &artist,
			      "songalbum", &album,
			      "duration", &duration, NULL);
		track_time = duration * 1000;
		//dmap_structure_add (cmst, DMAP_CC_CAVC, 1);
		dmap_structure_add (cmst, DMAP_CC_CAAS, 2);
		dmap_structure_add (cmst, DMAP_CC_CAAR, 6);
		dmap_structure_add (cmst, DMAP_CC_CANP, (gint64) 0);
		if (title)
			dmap_structure_add (cmst, DMAP_CC_CANN, title);
		if (artist)
			dmap_structure_add (cmst, DMAP_CC_CANA, artist);
		if (album)
			dmap_structure_add (cmst, DMAP_CC_CANL, album);
		dmap_structure_add (cmst, DMAP_CC_CANG, "");
		dmap_structure_add (cmst, DMAP_CC_ASAI, 0);
		//dmap_structure_add (cmst, DMAP_CC_AEMK, 1);
		g_debug ("Playing time: %u, Track time: %u", playing_time,
			 track_time);
		dmap_structure_add (cmst, DMAP_CC_CANT,
				    track_time - playing_time);
		dmap_structure_add (cmst, DMAP_CC_CAST, track_time);

		g_free (title);
		g_free (artist);
		g_free (album);

		g_object_unref (record);
	}

	_dmap_share_message_set_from_dmap_structure (DMAP_SHARE (share),
						     message, cmst);
	dmap_structure_destroy (cmst);
}
static void
add_entry_to_mlcl (gpointer id, DMAPRecord * record, gpointer _mb)
{
	GNode *mlit;
	struct MLCL_Bits *mb = (struct MLCL_Bits *) _mb;

	mlit = dmap_structure_add (mb->mlcl, DMAP_CC_MLIT);

	if (_dmap_share_client_requested (mb->bits, ITEM_KIND))
		dmap_structure_add (mlit, DMAP_CC_MIKD,
				    (gchar) DPAP_ITEM_KIND_PHOTO);
	if (_dmap_share_client_requested (mb->bits, ITEM_ID))
		dmap_structure_add (mlit, DMAP_CC_MIID,
				    GPOINTER_TO_UINT (id));
	if (_dmap_share_client_requested (mb->bits, ITEM_NAME)) {
		gchar *filename = NULL;

		g_object_get (record, "filename", &filename, NULL);
		if (filename) {
			dmap_structure_add (mlit, DMAP_CC_MINM, filename);
			g_free (filename);
		} else
			g_debug ("Filename requested but not available");
	}
	if (_dmap_share_client_requested (mb->bits, PERSISTENT_ID))
		dmap_structure_add (mlit, DMAP_CC_MPER,
				    GPOINTER_TO_UINT (id));
	if (TRUE) {
		/* dpap-sharp claims iPhoto '08 will not show thumbnails without PASP
		 * and this does seem to be the case when testing. */
		gchar *aspect_ratio = NULL;

		g_object_get (record, "aspect-ratio", &aspect_ratio, NULL);
		if (aspect_ratio) {
			dmap_structure_add (mlit, DMAP_CC_PASP, aspect_ratio);
			g_free (aspect_ratio);
		} else
			g_debug
				("Aspect ratio requested but not available");
	}
	if (_dmap_share_client_requested (mb->bits, PHOTO_CREATIONDATE)) {
		gint creation_date = 0;

		g_object_get (record, "creation-date", &creation_date, NULL);
		dmap_structure_add (mlit, DMAP_CC_PICD, creation_date);
	}
	if (_dmap_share_client_requested (mb->bits, PHOTO_IMAGEFILENAME)) {
		gchar *filename = NULL;

		g_object_get (record, "filename", &filename, NULL);
		if (filename) {
			dmap_structure_add (mlit, DMAP_CC_PIMF, filename);
			g_free (filename);
		} else
			g_debug ("Filename requested but not available");
	}
	if (_dmap_share_client_requested (mb->bits, PHOTO_IMAGEFORMAT)) {
		gchar *format = NULL;

		g_object_get (record, "format", &format, NULL);
		if (format) {
			dmap_structure_add (mlit, DMAP_CC_PFMT, format);
			g_free (format);
		} else
			g_debug ("Format requested but not available");
	}
	if (_dmap_share_client_requested (mb->bits, PHOTO_IMAGEFILESIZE)) {
		GByteArray *thumbnail = NULL;

		g_object_get (record, "thumbnail", &thumbnail, NULL);
		dmap_structure_add (mlit, DMAP_CC_PIFS,
				    thumbnail ? thumbnail->len : 0);
	}
	if (_dmap_share_client_requested (mb->bits, PHOTO_IMAGELARGEFILESIZE)) {
		gint large_filesize = 0;

		g_object_get (record, "large-filesize", &large_filesize,
			      NULL);
		dmap_structure_add (mlit, DMAP_CC_PLSZ, large_filesize);
	}
	if (_dmap_share_client_requested (mb->bits, PHOTO_IMAGEPIXELHEIGHT)) {
		gint pixel_height = 0;

		g_object_get (record, "pixel-height", &pixel_height, NULL);
		dmap_structure_add (mlit, DMAP_CC_PHGT, pixel_height);
	}
	if (_dmap_share_client_requested (mb->bits, PHOTO_IMAGEPIXELWIDTH)) {
		gint pixel_width = 0;

		g_object_get (record, "pixel-width", &pixel_width, NULL);
		dmap_structure_add (mlit, DMAP_CC_PWTH, pixel_width);
	}
	if (_dmap_share_client_requested (mb->bits, PHOTO_IMAGERATING)) {
		gint rating = 0;

		g_object_get (record, "rating", &rating, NULL);
		dmap_structure_add (mlit, DMAP_CC_PRAT, rating);
	}
	if (_dmap_share_client_requested (mb->bits, PHOTO_IMAGECOMMENTS)) {
		gchar *comments = NULL;

		g_object_get (record, "comments", &comments, NULL);
		if (comments) {
			dmap_structure_add (mlit, DMAP_CC_PCMT, comments);
			g_free (comments);
		} else
			g_debug ("Comments requested but not available");
	}
	if (_dmap_share_client_requested (mb->bits, PHOTO_FILEDATA)) {
		size_t size = 0;
		unsigned char *data = NULL;
		GByteArray *thumbnail = NULL;

		if (_dmap_share_client_requested (mb->bits, PHOTO_THUMB)) {
			g_object_get (record, "thumbnail", &thumbnail, NULL);
			if (thumbnail) {
				data = thumbnail->data;
				size = thumbnail->len;
			} else {
				data = NULL;
				size = 0;
			}
		} else {
			/* Should be PHOTO_HIRES */
			char *location = NULL;

			g_object_get (record, "location", &location, NULL);
			if (mapped_file) {
				/* Free any previously mapped image */
				g_mapped_file_unref (mapped_file);
				mapped_file = NULL;
			}

			mapped_file = file_to_mmap (location);
			if (mapped_file == NULL) {
				g_warning ("Error opening %s", location);
				data = NULL;
				size = 0;
			} else {
				data = (unsigned char *)
					g_mapped_file_get_contents
					(mapped_file);
				size = g_mapped_file_get_length (mapped_file);
			}
			g_free (location);
		}
		dmap_structure_add (mlit, DMAP_CC_PFDT, data, size);
	}
}
static void
databases_browse_xxx (DMAPShare * share,
		      SoupServer * server,
		      SoupMessage * msg,
		      const char *path,
		      GHashTable * query, SoupClientContext * context)
{
	/* ABRO database browse
	 *      MSTT status
	 *      MUTY update type
	 *      MTCO specified total count
	 *      MRCO returned count
	 *      ABGN genre listing
	 *              MLIT listing item
	 *              ...
	 */
	DMAPDb *db;
	const gchar *rest_of_path;
	GNode *abro, *node;
	gchar *filter;
	GSList *filter_def;
	GHashTable *filtered;
	guint num_genre;
	const gchar *browse_category;
	GHashTable *category_items;
	DMAPContentCode category_cc;
	GList *values;

	rest_of_path = strchr (path + 1, '/');
	browse_category = rest_of_path + 10;
	category_items = g_hash_table_new (g_str_hash, g_str_equal);

	filter = g_hash_table_lookup (query, "filter");
	filter_def = _dmap_share_build_filter (filter);
	g_object_get (share, "db", &db, NULL);
	filtered = dmap_db_apply_filter (db, filter_def);

	if (g_ascii_strcasecmp (browse_category, "genres") == 0) {
		g_hash_table_foreach (filtered, (GHFunc) genre_tabulator,
				      category_items);
		category_cc = DMAP_CC_ABGN;
	} else if (g_ascii_strcasecmp (browse_category, "artists") == 0) {
		g_hash_table_foreach (filtered, (GHFunc) artist_tabulator,
				      category_items);
		category_cc = DMAP_CC_ABAR;
	} else if (g_ascii_strcasecmp (browse_category, "albums") == 0) {
		g_hash_table_foreach (filtered, (GHFunc) album_tabulator,
				      category_items);
		category_cc = DMAP_CC_ABAL;
	} else {
		g_warning ("Unsupported browse category: %s",
			   browse_category);
		goto _bad_category;
	}

	abro = dmap_structure_add (NULL, DMAP_CC_ABRO);
	dmap_structure_add (abro, DMAP_CC_MSTT, (gint32) DMAP_STATUS_OK);
	dmap_structure_add (abro, DMAP_CC_MUTY, 0);

	num_genre = g_hash_table_size (category_items);
	dmap_structure_add (abro, DMAP_CC_MTCO, (gint32) num_genre);
	dmap_structure_add (abro, DMAP_CC_MRCO, (gint32) num_genre);

	node = dmap_structure_add (abro, category_cc);

	values = g_hash_table_get_keys (category_items);
	if (values && g_hash_table_lookup (query, "include-sort-headers")) {
		g_debug ("Sorting...");
		values = g_list_sort (values,
				      (GCompareFunc) g_ascii_strcasecmp);
	}

	g_list_foreach (values, add_to_category_listing, node);

	g_list_free (values);

	_dmap_share_message_set_from_dmap_structure (share, msg, abro);
	dmap_structure_destroy (abro);
      _bad_category:
	dmap_share_free_filter (filter_def);
	/* Free's hash table but not data (points into real DB): */
	g_hash_table_destroy (filtered);
	g_hash_table_destroy (category_items);
}
static void
add_entry_to_mlcl (gpointer id, DMAPRecord * record, gpointer _mb)
{
	GNode *mlit;
	gboolean has_video = 0;
	struct MLCL_Bits *mb = (struct MLCL_Bits *) _mb;

	mlit = dmap_structure_add (mb->mlcl, DMAP_CC_MLIT);
	g_object_get (record, "has-video", &has_video, NULL);

	if (_dmap_share_client_requested (mb->bits, ITEM_KIND))
		dmap_structure_add (mlit, DMAP_CC_MIKD,
				    (gchar) DAAP_ITEM_KIND_AUDIO);
	if (_dmap_share_client_requested (mb->bits, ITEM_ID))
		dmap_structure_add (mlit, DMAP_CC_MIID,
				    GPOINTER_TO_UINT (id));
	if (_dmap_share_client_requested (mb->bits, ITEM_NAME)) {
		gchar *title = NULL;

		g_object_get (record, "title", &title, NULL);
		if (title) {
			dmap_structure_add (mlit, DMAP_CC_MINM, title);
			g_free (title);
		} else
			g_debug ("Title requested but not available");
	}
	if (_dmap_share_client_requested (mb->bits, PERSISTENT_ID))
		dmap_structure_add (mlit, DMAP_CC_MPER,
				    GPOINTER_TO_UINT (id));
	if (_dmap_share_client_requested (mb->bits, CONTAINER_ITEM_ID))
		dmap_structure_add (mlit, DMAP_CC_MCTI,
				    GPOINTER_TO_UINT (id));
	if (_dmap_share_client_requested (mb->bits, SONG_DATA_KIND))
		dmap_structure_add (mlit, DMAP_CC_ASDK,
				    (gchar) DAAP_SONG_DATA_KIND_NONE);
	/* FIXME: Any use for this?
	 * if (_dmap_share_client_requested (mb->bits, SONG_DATA_URL))
	 * dmap_structure_add (mlit, DMAP_CC_ASUL, "daap://192.168.0.100:%u/databases/1/items/%d.%s?session-id=%s", data->port, *id, daap_record_get_format (DAAP_RECORD (record)), data->session_id);
	 */
	if (_dmap_share_client_requested (mb->bits, SONG_ALBUM)) {
		gchar *album = NULL;

		g_object_get (record, "songalbum", &album, NULL);
		if (album) {
			dmap_structure_add (mlit, DMAP_CC_ASAL, album);
			g_free (album);
		} else
			g_debug ("Album requested but not available");
	}
	if (_dmap_share_client_requested (mb->bits, SONG_GROUPING))
		dmap_structure_add (mlit, DMAP_CC_AGRP, "");
	if (_dmap_share_client_requested (mb->bits, SONG_ARTIST)) {
		gchar *artist = NULL;

		g_object_get (record, "songartist", &artist, NULL);
		if (artist) {
			dmap_structure_add (mlit, DMAP_CC_ASAR, artist);
			g_free (artist);
		} else
			g_debug ("Artist requested but not available");
	}
	if (_dmap_share_client_requested (mb->bits, SONG_BITRATE)) {
		gint32 bitrate = 0;

		g_object_get (record, "bitrate", &bitrate, NULL);
		if (bitrate != 0)
			dmap_structure_add (mlit, DMAP_CC_ASBR,
					    (gint32) bitrate);
	}
	if (_dmap_share_client_requested (mb->bits, SONG_BPM))
		dmap_structure_add (mlit, DMAP_CC_ASBT, (gint32) 0);
	if (_dmap_share_client_requested (mb->bits, SONG_COMMENT))
		dmap_structure_add (mlit, DMAP_CC_ASCM, "");
	if (_dmap_share_client_requested (mb->bits, SONG_COMPILATION))
		dmap_structure_add (mlit, DMAP_CC_ASCO, (gchar) FALSE);
	if (_dmap_share_client_requested (mb->bits, SONG_COMPOSER))
		dmap_structure_add (mlit, DMAP_CC_ASCP, "");
	if (_dmap_share_client_requested (mb->bits, SONG_DATE_ADDED)) {
		gint32 firstseen = 0;

		g_object_get (record, "firstseen", &firstseen, NULL);
		dmap_structure_add (mlit, DMAP_CC_ASDA, firstseen);
	}
	if (_dmap_share_client_requested (mb->bits, SONG_DATE_MODIFIED)) {
		gint32 mtime = 0;

		g_object_get (record, "mtime", &mtime, NULL);
		dmap_structure_add (mlit, DMAP_CC_ASDM, mtime);
	}
	if (_dmap_share_client_requested (mb->bits, SONG_DISC_COUNT))
		dmap_structure_add (mlit, DMAP_CC_ASDC, (gint32) 0);
	if (_dmap_share_client_requested (mb->bits, SONG_DISC_NUMBER)) {
		gint32 disc = 0;

		g_object_get (record, "disc", &disc, NULL);
		dmap_structure_add (mlit, DMAP_CC_ASDN, disc);
	}
	if (_dmap_share_client_requested (mb->bits, SONG_DISABLED))
		dmap_structure_add (mlit, DMAP_CC_ASDB, (gchar) FALSE);
	if (_dmap_share_client_requested (mb->bits, SONG_EQ_PRESET))
		dmap_structure_add (mlit, DMAP_CC_ASEQ, "");
	if (_dmap_share_client_requested (mb->bits, SONG_FORMAT)) {
		gchar *format = NULL;
		gchar *transcode_mimetype = NULL;

		g_object_get (mb->share, "transcode-mimetype",
			      &transcode_mimetype, NULL);
		// Not presently transcoding videos (see also same comments elsewhere).
		if (! has_video && transcode_mimetype) {
			format = g_strdup (dmap_mime_to_format
					   (transcode_mimetype));
			g_free (transcode_mimetype);
		} else {
			g_object_get (record, "format", &format, NULL);
		}
		if (format) {
			dmap_structure_add (mlit, DMAP_CC_ASFM, format);
			g_free (format);
		} else
			g_debug ("Format requested but not available");
	}
	if (_dmap_share_client_requested (mb->bits, SONG_GENRE)) {
		gchar *genre = NULL;

		g_object_get (record, "songgenre", &genre, NULL);
		if (genre) {
			dmap_structure_add (mlit, DMAP_CC_ASGN, genre);
			g_free (genre);
		} else
			g_debug ("Genre requested but not available");
	}
	if (_dmap_share_client_requested (mb->bits, SONG_DESCRIPTION))
		dmap_structure_add (mlit, DMAP_CC_ASDT, "");	/* FIXME: e.g., wav audio file */
	if (_dmap_share_client_requested (mb->bits, SONG_RELATIVE_VOLUME))
		dmap_structure_add (mlit, DMAP_CC_ASRV, 0);
	if (_dmap_share_client_requested (mb->bits, SONG_SAMPLE_RATE))
		dmap_structure_add (mlit, DMAP_CC_ASSR, 0);
	if (_dmap_share_client_requested (mb->bits, SONG_SIZE)) {
		guint64 filesize = 0;

		g_object_get (record, "filesize", &filesize, NULL);
		dmap_structure_add (mlit, DMAP_CC_ASSZ, (gint32) filesize);
	}
	if (_dmap_share_client_requested (mb->bits, SONG_START_TIME))
		dmap_structure_add (mlit, DMAP_CC_ASST, 0);
	if (_dmap_share_client_requested (mb->bits, SONG_STOP_TIME))
		dmap_structure_add (mlit, DMAP_CC_ASSP, 0);
	if (_dmap_share_client_requested (mb->bits, SONG_TIME)) {
		gint32 duration;

		g_object_get (record, "duration", &duration, NULL);
		dmap_structure_add (mlit, DMAP_CC_ASTM, (1000 * duration));
	}
	if (_dmap_share_client_requested (mb->bits, SONG_TRACK_COUNT))
		dmap_structure_add (mlit, DMAP_CC_ASTC, 0);
	if (_dmap_share_client_requested (mb->bits, SONG_TRACK_NUMBER)) {
		gint32 track = 0;

		g_object_get (record, "track", &track, NULL);
		dmap_structure_add (mlit, DMAP_CC_ASTN, track);
	}
	if (_dmap_share_client_requested (mb->bits, SONG_USER_RATING)) {
		gint32 rating = 0;

		g_object_get (record, "rating", &rating, NULL);
		dmap_structure_add (mlit, DMAP_CC_ASUR, rating);
	}
	if (_dmap_share_client_requested (mb->bits, SONG_YEAR)) {
		gint32 year = 0;

		g_object_get (record, "year", &year, NULL);
		dmap_structure_add (mlit, DMAP_CC_ASYR, year);
	}
	if (_dmap_share_client_requested (mb->bits, SONG_HAS_VIDEO)) {
		dmap_structure_add (mlit, DMAP_CC_AEHV, has_video);
	}
	if (_dmap_share_client_requested (mb->bits, SONG_SORT_ARTIST)) {
		gchar *sort_artist = NULL;

		g_object_get (record, "sort-artist", &sort_artist, NULL);
		if (sort_artist) {
			dmap_structure_add (mlit, DMAP_CC_ASSA, sort_artist);
			g_free (sort_artist);
		} else {
			g_debug ("Sort artist requested but not available");
		}
	}
	if (_dmap_share_client_requested (mb->bits, SONG_SORT_ALBUM)) {
		gchar *sort_album = NULL;

		g_object_get (record, "sort-album", &sort_album, NULL);
		if (sort_album) {
			dmap_structure_add (mlit, DMAP_CC_ASSU, sort_album);
			g_free (sort_album);
		} else {
			g_debug ("Sort album requested but not available");
		}
	}
	if (_dmap_share_client_requested (mb->bits, SONG_MEDIAKIND)) {
		gint mediakind = 0;

		g_object_get (record, "mediakind", &mediakind, NULL);
		dmap_structure_add (mlit, DMAP_CC_AEMK, mediakind);
	}
}