dpap-share.c 16.6 KB
Newer Older
1
/* Implmentation of DPAP (e.g., iPhoto Picture) sharing
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
 *
 * Copyright (C) 2005 Charles Schmidt <cschmidt2@emich.edu>
 *
 * Modifications Copyright (C) 2008 W. Michael Petullo <mike@flyn.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#include "config.h"

#include <time.h>
#include <string.h>
#include <stdlib.h>

#include <glib/gi18n.h>
30
#include <glib.h>
31 32 33 34 35 36 37 38 39 40 41 42

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <libsoup/soup.h>
#include <libsoup/soup-address.h>
#include <libsoup/soup-message.h>
#include <libsoup/soup-uri.h>
#include <libsoup/soup-server.h>

43
#include <libdmapsharing/dmap.h>
44
#include <libdmapsharing/dmap-private-utils.h>
45 46
#include <libdmapsharing/dmap-structure.h>

W. Michael Petullo's avatar
W. Michael Petullo committed
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
static void dpap_share_set_property (GObject * object,
				     guint prop_id,
				     const GValue * value,
				     GParamSpec * pspec);
static void dpap_share_get_property (GObject * object,
				     guint prop_id,
				     GValue * value, GParamSpec * pspec);
static void dpap_share_dispose (GObject * object);
guint dpap_share_get_desired_port (DMAPShare * share);
const char *dpap_share_get_type_of_service (DMAPShare * share);
void dpap_share_server_info (DMAPShare * share,
			     SoupServer * server,
			     SoupMessage * message,
			     const char *path,
			     GHashTable * query, SoupClientContext * context);
void dpap_share_message_add_standard_headers (DMAPShare * share,
					      SoupMessage * message);
static void databases_browse_xxx (DMAPShare * share,
				  SoupServer * server,
				  SoupMessage * msg,
67
				  const char *path,
W. Michael Petullo's avatar
W. Michael Petullo committed
68 69 70 71 72
				  GHashTable * query,
				  SoupClientContext * context);
static void databases_items_xxx (DMAPShare * share,
				 SoupServer * server,
				 SoupMessage * msg,
73
				 const char *path,
W. Michael Petullo's avatar
W. Michael Petullo committed
74 75 76 77
				 GHashTable * query,
				 SoupClientContext * context);
static struct DMAPMetaDataMap *get_meta_data_map (DMAPShare * share);
static void add_entry_to_mlcl (gpointer id, DMAPRecord * record, gpointer mb);
78 79 80 81

#define DPAP_TYPE_OF_SERVICE "_dpap._tcp"
#define DPAP_PORT 8770

W. Michael Petullo's avatar
W. Michael Petullo committed
82 83
struct DPAPSharePrivate
{
84
	gchar unused;
85 86 87 88 89 90 91
};

/* Mmap'ed full image file. Global so that it may be free'ed in a different
 * function call that the one that set it up.
 */
static GMappedFile *mapped_file = NULL;

92
#define DPAP_SHARE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DPAP_TYPE_SHARE, DPAPSharePrivate))
93

W. Michael Petullo's avatar
W. Michael Petullo committed
94 95
enum
{
96 97 98
	PROP_0,
};

99
G_DEFINE_TYPE (DPAPShare, dpap_share, DMAP_TYPE_SHARE);
100

101 102
static void
dpap_share_class_init (DPAPShareClass * klass)
103 104 105 106 107 108 109 110
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);
	DMAPShareClass *parent_class = DMAP_SHARE_CLASS (object_class);

	object_class->get_property = dpap_share_get_property;
	object_class->set_property = dpap_share_set_property;
	object_class->dispose = dpap_share_dispose;

W. Michael Petullo's avatar
W. Michael Petullo committed
111 112 113 114 115 116
	parent_class->get_desired_port = dpap_share_get_desired_port;
	parent_class->get_type_of_service = dpap_share_get_type_of_service;
	parent_class->message_add_standard_headers =
		dpap_share_message_add_standard_headers;
	parent_class->get_meta_data_map = get_meta_data_map;
	parent_class->add_entry_to_mlcl = add_entry_to_mlcl;
117
	parent_class->databases_browse_xxx = databases_browse_xxx;
W. Michael Petullo's avatar
W. Michael Petullo committed
118 119
	parent_class->databases_items_xxx = databases_items_xxx;
	parent_class->server_info = dpap_share_server_info;
120 121 122 123 124

	g_type_class_add_private (klass, sizeof (DPAPSharePrivate));
}

static void
W. Michael Petullo's avatar
W. Michael Petullo committed
125
dpap_share_init (DPAPShare * share)
126 127 128 129 130 131
{
	share->priv = DPAP_SHARE_GET_PRIVATE (share);
	/* FIXME: do I need to manually call parent _init? */
}

static void
W. Michael Petullo's avatar
W. Michael Petullo committed
132 133 134
dpap_share_set_property (GObject * object,
			 guint prop_id,
			 const GValue * value, GParamSpec * pspec)
135
{
136
	// DPAPShare *share = DPAP_SHARE (object);
137 138

	switch (prop_id) {
W. Michael Petullo's avatar
W. Michael Petullo committed
139
		/* FIXME: */
140 141 142 143 144 145 146
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}

static void
W. Michael Petullo's avatar
W. Michael Petullo committed
147 148
dpap_share_get_property (GObject * object,
			 guint prop_id, GValue * value, GParamSpec * pspec)
149
{
150
	// DPAPShare *share = DPAP_SHARE (object);
151 152 153 154 155 156 157 158 159

	switch (prop_id) {
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}

static void
W. Michael Petullo's avatar
W. Michael Petullo committed
160
dpap_share_dispose (GObject * object)
161 162 163 164 165 166 167 168 169 170 171
{
	/* FIXME: implement in parent */
}

/* FIXME: trancode_mimetype currently not used for DPAP, only DAAP. 
 *        Threrfore, it is not passed to g_object_new.
 */
DPAPShare *
dpap_share_new (const char *name,
		const char *password,
		gpointer db,
W. Michael Petullo's avatar
W. Michael Petullo committed
172
		gpointer container_db, gchar * transcode_mimetype)
173 174 175
{
	DPAPShare *share;

176
	share = DPAP_SHARE (g_object_new (DPAP_TYPE_SHARE,
W. Michael Petullo's avatar
W. Michael Petullo committed
177 178 179 180 181
					  "name", name,
					  "password", password,
					  "db", db,
					  "container-db", container_db,
					  NULL));
182

183 184
	_dmap_share_server_start (DMAP_SHARE (share));
	_dmap_share_publish_start (DMAP_SHARE (share));
185 186 187 188 189

	return share;
}

void
W. Michael Petullo's avatar
W. Michael Petullo committed
190 191
dpap_share_message_add_standard_headers (DMAPShare * share,
					 SoupMessage * message)
192
{
W. Michael Petullo's avatar
W. Michael Petullo committed
193
	soup_message_headers_append (message->response_headers, "DPAP-Server",
194
				     "libdmapsharing" VERSION);
195 196 197 198 199 200 201
}

#define DMAP_VERSION 2.0
#define DPAP_VERSION 1.1
#define DPAP_TIMEOUT 1800

guint
W. Michael Petullo's avatar
W. Michael Petullo committed
202
dpap_share_get_desired_port (DMAPShare * share)
203 204 205 206 207
{
	return DPAP_PORT;
}

const char *
W. Michael Petullo's avatar
W. Michael Petullo committed
208
dpap_share_get_type_of_service (DMAPShare * share)
209 210 211 212 213
{
	return DPAP_TYPE_OF_SERVICE;
}

void
W. Michael Petullo's avatar
W. Michael Petullo committed
214 215 216 217 218
dpap_share_server_info (DMAPShare * share,
			SoupServer * server,
			SoupMessage * message,
			const char *path,
			GHashTable * query, SoupClientContext * context)
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
{
/* 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);
246
	dmap_structure_add (msrv, DMAP_CC_MSTT, (gint32) DMAP_STATUS_OK);
247 248 249
	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);
W. Michael Petullo's avatar
W. Michael Petullo committed
250
	/*dmap_structure_add (msrv, DMAP_CC_MSAU, _dmap_share_get_auth_method (share)); */
251 252 253 254 255 256 257 258
	/* 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);
259
	/*dmap_structure_add (msrv, DMAP_CC_MSUP, (gchar) 1);
260 261 262 263 264 265 266 267
	 *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);

268
	_dmap_share_message_set_from_dmap_structure (share, message, msrv);
269 270 271 272 273
	dmap_structure_destroy (msrv);

	g_free (nameprop);
}

W. Michael Petullo's avatar
W. Michael Petullo committed
274 275
typedef enum
{
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
	ITEM_ID = 0,
	ITEM_NAME,
	ITEM_KIND,
	PERSISTENT_ID,
	CONTAINER_ITEM_ID,
	PHOTO_ASPECTRATIO,
	PHOTO_CREATIONDATE,
	PHOTO_IMAGEFILENAME,
	PHOTO_IMAGEFORMAT,
	PHOTO_IMAGEFILESIZE,
	PHOTO_IMAGELARGEFILESIZE,
	PHOTO_IMAGEPIXELHEIGHT,
	PHOTO_IMAGEPIXELWIDTH,
	PHOTO_IMAGERATING,
	PHOTO_HIRES,
	PHOTO_THUMB,
	PHOTO_FILEDATA,
	PHOTO_IMAGECOMMENTS
} DPAPMetaData;

static struct DMAPMetaDataMap meta_data_map[] = {
W. Michael Petullo's avatar
W. Michael Petullo committed
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
	{"dmap.itemid", ITEM_ID},
	{"dmap.itemname", ITEM_NAME},
	{"dmap.itemkind", ITEM_KIND},
	{"dmap.persistentid", PERSISTENT_ID},
	{"dmap.containeritemid", CONTAINER_ITEM_ID},
	{"dpap.aspectratio", PHOTO_ASPECTRATIO},
	{"dpap.creationdate", PHOTO_CREATIONDATE},
	{"dpap.imagefilename", PHOTO_IMAGEFILENAME},
	{"dpap.imageformat", PHOTO_IMAGEFORMAT},
	{"dpap.imagefilesize", PHOTO_IMAGEFILESIZE},
	{"dpap.imagelargefilesize", PHOTO_IMAGELARGEFILESIZE},
	{"dpap.imagepixelheight", PHOTO_IMAGEPIXELHEIGHT},
	{"dpap.imagepixelwidth", PHOTO_IMAGEPIXELWIDTH},
	{"dpap.imagerating", PHOTO_IMAGERATING},
	{"dpap.thumb", PHOTO_THUMB},
	{"dpap.hires", PHOTO_HIRES},
	{"dpap.filedata", PHOTO_FILEDATA},
	{"dpap.imagecomments", PHOTO_IMAGECOMMENTS},
	{NULL, 0}
};

#define DPAP_ITEM_KIND_PHOTO 3	/* This is the constant that dpap-sharp uses. */
319 320 321 322

static GMappedFile *
file_to_mmap (const char *location)
{
W. Michael Petullo's avatar
W. Michael Petullo committed
323 324 325 326
	GFile *file;
	GMappedFile *mapped_file = NULL;
	char *path;
	GError *error = NULL;
327

W. Michael Petullo's avatar
W. Michael Petullo committed
328
	file = g_file_new_for_uri (location);
329 330 331 332
	/* NOTE: this is broken if original filename contains "%20" etc. This
	 * is because g_file_get_path() will translate this to " ", etc. But
	 * the filename really may have used "%20" (not " ").
	 */
W. Michael Petullo's avatar
W. Michael Petullo committed
333 334 335 336 337 338 339 340 341 342 343 344 345 346
	path = g_file_get_path (file);
	if (path == NULL) {
		g_warning ("Couldn't mmap %s: couldn't get path", path);
		g_object_unref (file);
		return mapped_file;
	}
	g_object_unref (file);

	mapped_file = g_mapped_file_new (path, FALSE, &error);
	if (mapped_file == NULL) {
		g_warning ("Unable to map file %s: %s", path, error->message);
	}

	g_free (path);
347 348 349
	return mapped_file;
}

350
static void
W. Michael Petullo's avatar
W. Michael Petullo committed
351
add_entry_to_mlcl (gpointer id, DMAPRecord * record, gpointer _mb)
352 353
{
	GNode *mlit;
354
	struct MLCL_Bits *mb = (struct MLCL_Bits *) _mb;
W. Michael Petullo's avatar
W. Michael Petullo committed
355

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

358
	if (_dmap_share_client_requested (mb->bits, ITEM_KIND))
W. Michael Petullo's avatar
W. Michael Petullo committed
359 360
		dmap_structure_add (mlit, DMAP_CC_MIKD,
				    (gchar) DPAP_ITEM_KIND_PHOTO);
361
	if (_dmap_share_client_requested (mb->bits, ITEM_ID))
W. Michael Petullo's avatar
W. Michael Petullo committed
362 363
		dmap_structure_add (mlit, DMAP_CC_MIID,
				    GPOINTER_TO_UINT (id));
364
	if (_dmap_share_client_requested (mb->bits, ITEM_NAME)) {
365
		gchar *filename = NULL;
W. Michael Petullo's avatar
W. Michael Petullo committed
366

367
		g_object_get (record, "filename", &filename, NULL);
368 369 370 371
		if (filename) {
			dmap_structure_add (mlit, DMAP_CC_MINM, filename);
			g_free (filename);
		} else
372
			g_debug ("Filename requested but not available");
373
	}
374
	if (_dmap_share_client_requested (mb->bits, PERSISTENT_ID))
W. Michael Petullo's avatar
W. Michael Petullo committed
375 376
		dmap_structure_add (mlit, DMAP_CC_MPER,
				    GPOINTER_TO_UINT (id));
377 378 379
	if (TRUE) {
		/* dpap-sharp claims iPhoto '08 will not show thumbnails without PASP
		 * and this does seem to be the case when testing. */
380
		gchar *aspect_ratio = NULL;
W. Michael Petullo's avatar
W. Michael Petullo committed
381

382
		g_object_get (record, "aspect-ratio", &aspect_ratio, NULL);
383 384 385 386
		if (aspect_ratio) {
			dmap_structure_add (mlit, DMAP_CC_PASP, aspect_ratio);
			g_free (aspect_ratio);
		} else
387
			g_debug
W. Michael Petullo's avatar
W. Michael Petullo committed
388
				("Aspect ratio requested but not available");
389
	}
390
	if (_dmap_share_client_requested (mb->bits, PHOTO_CREATIONDATE)) {
391
		gint creation_date = 0;
W. Michael Petullo's avatar
W. Michael Petullo committed
392

393 394 395
		g_object_get (record, "creation-date", &creation_date, NULL);
		dmap_structure_add (mlit, DMAP_CC_PICD, creation_date);
	}
396
	if (_dmap_share_client_requested (mb->bits, PHOTO_IMAGEFILENAME)) {
397
		gchar *filename = NULL;
W. Michael Petullo's avatar
W. Michael Petullo committed
398

399
		g_object_get (record, "filename", &filename, NULL);
400 401 402 403
		if (filename) {
			dmap_structure_add (mlit, DMAP_CC_PIMF, filename);
			g_free (filename);
		} else
404
			g_debug ("Filename requested but not available");
405
	}
406
	if (_dmap_share_client_requested (mb->bits, PHOTO_IMAGEFORMAT)) {
407
		gchar *format = NULL;
W. Michael Petullo's avatar
W. Michael Petullo committed
408

409
		g_object_get (record, "format", &format, NULL);
410 411 412 413
		if (format) {
			dmap_structure_add (mlit, DMAP_CC_PFMT, format);
			g_free (format);
		} else
414
			g_debug ("Format requested but not available");
415
	}
416
	if (_dmap_share_client_requested (mb->bits, PHOTO_IMAGEFILESIZE)) {
417
		GByteArray *thumbnail = NULL;
W. Michael Petullo's avatar
W. Michael Petullo committed
418

419
		g_object_get (record, "thumbnail", &thumbnail, NULL);
W. Michael Petullo's avatar
W. Michael Petullo committed
420 421
		dmap_structure_add (mlit, DMAP_CC_PIFS,
				    thumbnail ? thumbnail->len : 0);
422
	}
423
	if (_dmap_share_client_requested (mb->bits, PHOTO_IMAGELARGEFILESIZE)) {
424
		gint large_filesize = 0;
W. Michael Petullo's avatar
W. Michael Petullo committed
425 426 427

		g_object_get (record, "large-filesize", &large_filesize,
			      NULL);
428 429
		dmap_structure_add (mlit, DMAP_CC_PLSZ, large_filesize);
	}
430
	if (_dmap_share_client_requested (mb->bits, PHOTO_IMAGEPIXELHEIGHT)) {
431
		gint pixel_height = 0;
W. Michael Petullo's avatar
W. Michael Petullo committed
432

433 434 435
		g_object_get (record, "pixel-height", &pixel_height, NULL);
		dmap_structure_add (mlit, DMAP_CC_PHGT, pixel_height);
	}
436
	if (_dmap_share_client_requested (mb->bits, PHOTO_IMAGEPIXELWIDTH)) {
437
		gint pixel_width = 0;
W. Michael Petullo's avatar
W. Michael Petullo committed
438

439 440 441
		g_object_get (record, "pixel-width", &pixel_width, NULL);
		dmap_structure_add (mlit, DMAP_CC_PWTH, pixel_width);
	}
442
	if (_dmap_share_client_requested (mb->bits, PHOTO_IMAGERATING)) {
443
		gint rating = 0;
W. Michael Petullo's avatar
W. Michael Petullo committed
444

445 446 447
		g_object_get (record, "rating", &rating, NULL);
		dmap_structure_add (mlit, DMAP_CC_PRAT, rating);
	}
448
	if (_dmap_share_client_requested (mb->bits, PHOTO_IMAGECOMMENTS)) {
449
		gchar *comments = NULL;
W. Michael Petullo's avatar
W. Michael Petullo committed
450

451
		g_object_get (record, "comments", &comments, NULL);
452 453 454 455
		if (comments) {
			dmap_structure_add (mlit, DMAP_CC_PCMT, comments);
			g_free (comments);
		} else
456
			g_debug ("Comments requested but not available");
457
	}
458
	if (_dmap_share_client_requested (mb->bits, PHOTO_FILEDATA)) {
459 460
		size_t size = 0;
		unsigned char *data = NULL;
461
		GByteArray *thumbnail = NULL;
W. Michael Petullo's avatar
W. Michael Petullo committed
462

463
		if (_dmap_share_client_requested (mb->bits, PHOTO_THUMB)) {
464 465 466 467 468
			g_object_get (record, "thumbnail", &thumbnail, NULL);
			if (thumbnail) {
				data = thumbnail->data;
				size = thumbnail->len;
			} else {
469 470
				data = NULL;
				size = 0;
471
			}
472 473
		} else {
			/* Should be PHOTO_HIRES */
474
			char *location = NULL;
W. Michael Petullo's avatar
W. Michael Petullo committed
475

476 477 478
			g_object_get (record, "location", &location, NULL);
			if (mapped_file) {
				/* Free any previously mapped image */
479
				g_mapped_file_unref (mapped_file);
480 481 482 483 484
				mapped_file = NULL;
			}

			mapped_file = file_to_mmap (location);
			if (mapped_file == NULL) {
485 486 487
				g_warning ("Error opening %s", location);
				data = NULL;
				size = 0;
488
			} else {
W. Michael Petullo's avatar
W. Michael Petullo committed
489 490 491
				data = (unsigned char *)
					g_mapped_file_get_contents
					(mapped_file);
492 493
				size = g_mapped_file_get_length (mapped_file);
			}
494
			g_free (location);
495 496 497 498 499
		}
		dmap_structure_add (mlit, DMAP_CC_PFDT, data, size);
	}
}

500
static void
W. Michael Petullo's avatar
W. Michael Petullo committed
501 502 503 504 505
databases_browse_xxx (DMAPShare * share,
		      SoupServer * server,
		      SoupMessage * msg,
		      const char *path,
		      GHashTable * query, SoupClientContext * context)
506
{
507 508
	g_warning ("Unhandled: %s\n", path);
}
509

510
static void
W. Michael Petullo's avatar
W. Michael Petullo committed
511 512
send_chunked_file (SoupServer * server, SoupMessage * message,
		   DPAPRecord * record, guint64 filesize)
513 514 515 516
{
	GInputStream *stream;
	const char *location;
	GError *error = NULL;
W. Michael Petullo's avatar
W. Michael Petullo committed
517
	ChunkData *cd = g_new0 (ChunkData, 1);
518 519 520 521 522 523 524 525 526 527

	g_object_get (record, "location", &location, NULL);

	cd->server = server;

	stream = G_INPUT_STREAM (dpap_record_read (record, &error));

	if (error != NULL) {
		g_warning ("Couldn't open %s: %s.", location, error->message);
		g_error_free (error);
W. Michael Petullo's avatar
W. Michael Petullo committed
528 529
		soup_message_set_status (message,
					 SOUP_STATUS_INTERNAL_SERVER_ERROR);
530 531 532 533 534 535 536 537 538 539 540 541
		g_free (cd);
		return;
	}

	cd->stream = stream;

	if (cd->stream == NULL) {
		g_warning ("Could not set up input stream");
		g_free (cd);
		return;
	}

W. Michael Petullo's avatar
W. Michael Petullo committed
542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558
	soup_message_headers_set_encoding (message->response_headers,
					   SOUP_ENCODING_CONTENT_LENGTH);
	soup_message_headers_set_content_length (message->response_headers,
						 filesize);

	soup_message_headers_append (message->response_headers, "Connection",
				     "Close");
	soup_message_headers_append (message->response_headers,
				     "Content-Type",
				     "application/x-dmap-tagged");

	g_signal_connect (message, "wrote_headers",
			  G_CALLBACK (dmap_write_next_chunk), cd);
	g_signal_connect (message, "wrote_chunk",
			  G_CALLBACK (dmap_write_next_chunk), cd);
	g_signal_connect (message, "finished",
			  G_CALLBACK (dmap_chunked_message_finished), cd);
559 560 561
	/* NOTE: cd g_free'd by chunked_message_finished(). */
}

562
static void
W. Michael Petullo's avatar
W. Michael Petullo committed
563 564 565 566 567
databases_items_xxx (DMAPShare * share,
		     SoupServer * server,
		     SoupMessage * msg,
		     const char *path,
		     GHashTable * query, SoupClientContext * context)
568
{
569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584
	DMAPDb *db;
	const gchar *rest_of_path;
	const gchar *id_str;
	guint id;
	guint64 filesize;
	DPAPRecord *record;

	rest_of_path = strchr (path + 1, '/');
	id_str = rest_of_path + 9;
	id = strtoul (id_str, NULL, 10);

	g_object_get (share, "db", &db, NULL);
	record = DPAP_RECORD (dmap_db_lookup_by_id (db, id));
	g_object_get (record, "large-filesize", &filesize, NULL);

	DMAP_SHARE_GET_CLASS (share)->message_add_standard_headers
W. Michael Petullo's avatar
W. Michael Petullo committed
585
		(share, msg);
586 587 588
	soup_message_set_status (msg, SOUP_STATUS_OK);

	send_chunked_file (server, msg, record, filesize);
W. Michael Petullo's avatar
W. Michael Petullo committed
589

590
	g_object_unref (record);
591
}
592

593
static struct DMAPMetaDataMap *
W. Michael Petullo's avatar
W. Michael Petullo committed
594
get_meta_data_map (DMAPShare * share)
595
{
W. Michael Petullo's avatar
W. Michael Petullo committed
596
	return meta_data_map;
597
}