aboutsummaryrefslogtreecommitdiff
path: root/lib/libXDAAP
diff options
context:
space:
mode:
authortheuni <theuni-nospam-@xbmc.org>2011-01-24 16:05:21 -0500
committertheuni <theuni-nospam-@xbmc.org>2011-01-24 16:05:21 -0500
commitc51b1189e3d5353e842991f5859ddcea0f73e426 (patch)
treeef2cb8a6184699aa614f3655dca4ce661cdc108e /lib/libXDAAP
parentbe61ebdc9e897fe40c6f371111724de79ddee8d5 (diff)
Merged cptspiff's code-reshuffle branch.
Squashed commit due to build breakage during code-reshuffle history. Conflicts: xbmc/Util.cpp xbmc/cdrip/CDDARipper.cpp xbmc/filesystem/Directory.cpp xbmc/filesystem/File.cpp
Diffstat (limited to 'lib/libXDAAP')
-rw-r--r--lib/libXDAAP/Authentication/hasher.c212
-rw-r--r--lib/libXDAAP/Authentication/hasher.h36
-rw-r--r--lib/libXDAAP/Authentication/md5.c296
-rw-r--r--lib/libXDAAP/Authentication/md5.h17
-rw-r--r--lib/libXDAAP/Makefile.in6
-rw-r--r--lib/libXDAAP/ReadMe.txt1
-rw-r--r--lib/libXDAAP/client.h301
-rw-r--r--lib/libXDAAP/compat.h90
-rw-r--r--lib/libXDAAP/daap.c583
-rw-r--r--lib/libXDAAP/daap.h183
-rw-r--r--lib/libXDAAP/daap_contentcodes.h97
-rw-r--r--lib/libXDAAP/daap_readtypes.h147
-rw-r--r--lib/libXDAAP/debug.c183
-rw-r--r--lib/libXDAAP/debug.h114
-rw-r--r--lib/libXDAAP/dmap_generics.c280
-rw-r--r--lib/libXDAAP/dmap_generics.h103
-rw-r--r--lib/libXDAAP/endian_swap.h84
-rw-r--r--lib/libXDAAP/global.c97
-rw-r--r--lib/libXDAAP/httpClient.c1114
-rw-r--r--lib/libXDAAP/httpClient.h105
-rw-r--r--lib/libXDAAP/ioloop.c339
-rw-r--r--lib/libXDAAP/ioloop.h83
-rw-r--r--lib/libXDAAP/libXDAAP.c1860
-rw-r--r--lib/libXDAAP/libXDAAP.h25
-rw-r--r--lib/libXDAAP/libXDAAP_win32/libXDAAP_win32.vcproj260
-rw-r--r--lib/libXDAAP/libXDAAP_win32/libXDAAP_win32.vcxproj115
-rw-r--r--lib/libXDAAP/libXDAAP_win32/libXDAAP_win32.vcxproj.filters86
-rw-r--r--lib/libXDAAP/portability.h91
-rw-r--r--lib/libXDAAP/private.h204
-rw-r--r--lib/libXDAAP/thread.h104
-rw-r--r--lib/libXDAAP/threadlib.h52
-rw-r--r--lib/libXDAAP/threadpool.c368
-rw-r--r--lib/libXDAAP/threadpool.h66
-rw-r--r--lib/libXDAAP/types.h54
34 files changed, 7756 insertions, 0 deletions
diff --git a/lib/libXDAAP/Authentication/hasher.c b/lib/libXDAAP/Authentication/hasher.c
new file mode 100644
index 0000000000..b937c6e100
--- /dev/null
+++ b/lib/libXDAAP/Authentication/hasher.c
@@ -0,0 +1,212 @@
+// Place the code and data below here into the LIBXDAAP section.
+#ifndef __GNUC__
+#pragma code_seg( "LIBXDAAP_TEXT" )
+#pragma data_seg( "LIBXDAAP_DATA" )
+#pragma bss_seg( "LIBXDAAP_BSS" )
+#pragma const_seg( "LIBXDAAP_RD" )
+#endif
+
+/* Copyright (c) 2004 David Hammerton
+ * david@crazney.net
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "md5.h"
+
+static int staticHashDone = 0;
+static char staticHash_42[256*65] = {0};
+static char staticHash_45[256*65] = {0};
+
+static const char hexchars[] = "0123456789ABCDEF";
+static const char appleCopyright[] = "Copyright 2003 Apple Computer, Inc.";
+
+static void DigestToString(const unsigned char *digest, char *string)
+{
+ int i;
+ for (i = 0; i < 16; i++)
+ {
+ unsigned char tmp = digest[i];
+ string[i*2+1] = hexchars[tmp & 0x0f];
+ string[i*2] = hexchars[(tmp >> 4) & 0x0f];
+ }
+}
+
+static void GenerateStatic_42()
+{
+ MD5_CTX ctx;
+ unsigned char *p = (unsigned char *) staticHash_42;
+ int i;
+ unsigned char buf[16];
+
+ for (i = 0; i < 256; i++)
+ {
+ OpenDaap_MD5Init(&ctx, 0);
+
+#define MD5_STRUPDATE(str) OpenDaap_MD5Update(&ctx, (const unsigned char *)str, strlen(str))
+
+ if ((i & 0x80) != 0)
+ MD5_STRUPDATE("Accept-Language");
+ else
+ MD5_STRUPDATE("user-agent");
+
+ if ((i & 0x40) != 0)
+ MD5_STRUPDATE("max-age");
+ else
+ MD5_STRUPDATE("Authorization");
+
+ if ((i & 0x20) != 0)
+ MD5_STRUPDATE("Client-DAAP-Version");
+ else
+ MD5_STRUPDATE("Accept-Encoding");
+
+ if ((i & 0x10) != 0)
+ MD5_STRUPDATE("daap.protocolversion");
+ else
+ MD5_STRUPDATE("daap.songartist");
+
+ if ((i & 0x08) != 0)
+ MD5_STRUPDATE("daap.songcomposer");
+ else
+ MD5_STRUPDATE("daap.songdatemodified");
+
+ if ((i & 0x04) != 0)
+ MD5_STRUPDATE("daap.songdiscnumber");
+ else
+ MD5_STRUPDATE("daap.songdisabled");
+
+ if ((i & 0x02) != 0)
+ MD5_STRUPDATE("playlist-item-spec");
+ else
+ MD5_STRUPDATE("revision-number");
+
+ if ((i & 0x01) != 0)
+ MD5_STRUPDATE("session-id");
+ else
+ MD5_STRUPDATE("content-codes");
+#undef MD5_STRUPDATE
+
+ OpenDaap_MD5Final(&ctx, buf);
+ DigestToString((unsigned char *) buf, (char *) p);
+ p += 65;
+ }
+}
+
+static void GenerateStatic_45()
+{
+ MD5_CTX ctx;
+ unsigned char *p = (unsigned char *) staticHash_45;
+ int i;
+ unsigned char buf[16];
+
+ for (i = 0; i < 256; i++)
+ {
+ OpenDaap_MD5Init(&ctx, 1);
+
+#define MD5_STRUPDATE(str) OpenDaap_MD5Update(&ctx, (const unsigned char *)str, strlen(str))
+
+ if ((i & 0x40) != 0)
+ MD5_STRUPDATE("eqwsdxcqwesdc");
+ else
+ MD5_STRUPDATE("op[;lm,piojkmn");
+
+ if ((i & 0x20) != 0)
+ MD5_STRUPDATE("876trfvb 34rtgbvc");
+ else
+ MD5_STRUPDATE("=-0ol.,m3ewrdfv");
+
+ if ((i & 0x10) != 0)
+ MD5_STRUPDATE("87654323e4rgbv ");
+ else
+ MD5_STRUPDATE("1535753690868867974342659792");
+
+ if ((i & 0x08) != 0)
+ MD5_STRUPDATE("Song Name");
+ else
+ MD5_STRUPDATE("DAAP-CLIENT-ID:");
+
+ if ((i & 0x04) != 0)
+ MD5_STRUPDATE("111222333444555");
+ else
+ MD5_STRUPDATE("4089961010");
+
+ if ((i & 0x02) != 0)
+ MD5_STRUPDATE("playlist-item-spec");
+ else
+ MD5_STRUPDATE("revision-number");
+
+ if ((i & 0x01) != 0)
+ MD5_STRUPDATE("session-id");
+ else
+ MD5_STRUPDATE("content-codes");
+
+ if ((i & 0x80) != 0)
+ MD5_STRUPDATE("IUYHGFDCXWEDFGHN");
+ else
+ MD5_STRUPDATE("iuytgfdxwerfghjm");
+
+#undef MD5_STRUPDATE
+
+ OpenDaap_MD5Final(&ctx, buf);
+ DigestToString((unsigned char *) buf, (char *) p);
+ p += 65;
+ }
+}
+
+void GenerateHash(short version_major,
+ const char *url, char hashSelect,
+ char *outhash,
+ int request_id)
+{
+ unsigned char buf[16];
+ MD5_CTX ctx;
+
+ char *hashTable = (version_major == 3) ?
+ staticHash_45 : staticHash_42;
+
+ if (!staticHashDone)
+ {
+ GenerateStatic_42();
+ GenerateStatic_45();
+ staticHashDone = 1;
+ }
+
+ OpenDaap_MD5Init(&ctx, (version_major == 3) ? 1 : 0);
+
+ OpenDaap_MD5Update(&ctx, (const unsigned char *) url, strlen((char *) url));
+ OpenDaap_MD5Update(&ctx, (const unsigned char *) appleCopyright, strlen(appleCopyright));
+
+ OpenDaap_MD5Update(&ctx, (const unsigned char *) &hashTable[hashSelect * 65], 32);
+
+ if (request_id && version_major == 3)
+ {
+ char scribble[20];
+ sprintf(scribble, "%u", request_id);
+ OpenDaap_MD5Update(&ctx, (const unsigned char *) scribble, strlen(scribble));
+ }
+
+ OpenDaap_MD5Final(&ctx, buf);
+ DigestToString((const unsigned char *) buf, (char *) outhash);
+}
+
diff --git a/lib/libXDAAP/Authentication/hasher.h b/lib/libXDAAP/Authentication/hasher.h
new file mode 100644
index 0000000000..1540456cfa
--- /dev/null
+++ b/lib/libXDAAP/Authentication/hasher.h
@@ -0,0 +1,36 @@
+/* Copyright (c) 2004 David Hammerton
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+
+#ifndef _HASHER_H
+#define _HASHER_H
+
+void GenerateHash(short version_major,
+ const char *url, char hashSelect,
+ char *outhash,
+ int request_id);
+
+
+#endif /* _HASHER_H */
+
diff --git a/lib/libXDAAP/Authentication/md5.c b/lib/libXDAAP/Authentication/md5.c
new file mode 100644
index 0000000000..cd860dfaaf
--- /dev/null
+++ b/lib/libXDAAP/Authentication/md5.c
@@ -0,0 +1,296 @@
+// Place the code and data below here into the LIBXDAAP section.
+#ifndef __GNUC__
+#pragma code_seg( "LIBXDAAP_TEXT" )
+#pragma data_seg( "LIBXDAAP_DATA" )
+#pragma bss_seg( "LIBXDAAP_BSS" )
+#pragma const_seg( "LIBXDAAP_RD" )
+#endif
+
+/* Parts Copyright 2004 David Hammerton <crazney@crazney.net>
+ * Parts found in the public domain with no copyright claim.
+ *
+ * see rfc1321
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include <string.h>
+#include "md5.h"
+
+/*
+* This code implements the MD5 message-digest algorithm.
+* The algorithm is due to Ron Rivest. This code was
+* written by Colin Plumb in 1993, no copyright is claimed.
+* This code is in the public domain; do with it what you wish.
+*
+* Equivalent code is available from RSA Data Security, Inc.
+* This code has been tested against that, and is equivalent,
+* except that you don't need to include two pages of legalese
+* with every copy.
+*
+* To compute the message digest of a chunk of bytes, declare an MD5Context
+* structure, pass it to OpenDaap_MD5Init, call OpenDaap_MD5Update as needed
+* on buffers full of bytes, and then call OpenDaap_MD5Final, which will fill
+* a supplied 16-byte array with the digest.
+*/
+static void MD5Transform(u_int32_t buf[4], u_int32_t const in[16], int apple_ver);
+/* for some reason we still have to reverse bytes on bigendian machines
+ * I don't really know why... but otherwise it fails..
+ * Any MD5 gurus out there know why???
+ */
+#if 0 //ndef WORDS_BIGENDIAN /* was: HIGHFIRST */
+#define byteReverse(buf, len) /* Nothing */
+#else
+static void byteReverse(unsigned char *buf, unsigned longs);
+
+#ifndef ASM_MD5
+/*
+* Note: this code is harmless on little-endian machines.
+*/
+static void byteReverse(unsigned char *buf, unsigned longs)
+{
+ u_int32_t t;
+ do {
+ t = (u_int32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+ ((unsigned) buf[1] << 8 | buf[0]);
+ *(u_int32_t *) buf = t;
+ buf += 4;
+ } while (--longs);
+}
+#endif
+#endif
+
+
+void OpenDaap_MD5Init(MD5_CTX *ctx, int apple_ver)
+{
+ memset(ctx, 0, sizeof(MD5_CTX));
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+
+ ctx->apple_ver = apple_ver;
+}
+
+void OpenDaap_MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned int len)
+{
+ u_int32_t t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((u_int32_t) len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if (t) {
+ unsigned char *p = (unsigned char *) ctx->in + t;
+
+ t = 64 - t;
+ if (len < t) {
+ memcpy(p, buf, len);
+ return;
+ }
+ memcpy(p, buf, t);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (u_int32_t *) ctx->in, ctx->apple_ver);
+ buf += t;
+ len -= t;
+ }
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (u_int32_t *) ctx->in, ctx->apple_ver);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ memcpy(ctx->in, buf, len);
+
+}
+
+void OpenDaap_MD5Final(MD5_CTX *ctx, unsigned char digest[16])
+{
+ unsigned count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset(p, 0, count);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (u_int32_t *) ctx->in, ctx->apple_ver);
+
+ /* Now fill the next block with 56 bytes */
+ memset(ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(p, 0, count - 8);
+ }
+ byteReverse(ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ((u_int32_t *) ctx->in)[14] = ctx->bits[0];
+ ((u_int32_t *) ctx->in)[15] = ctx->bits[1];
+
+ MD5Transform(ctx->buf, (u_int32_t *) ctx->in, ctx->apple_ver);
+ byteReverse((unsigned char *) ctx->buf, 4);
+ memcpy(digest, ctx->buf, 16);
+ memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+* The core of the MD5 algorithm, this alters an existing MD5 hash to reflect
+* the addition of 16 longwords of new data. OpenDaap_MD5Update blocks the
+* data and converts bytes into longwords for this routine.
+*/
+static void MD5Transform(u_int32_t buf[4], u_int32_t const in[16], int apple_ver)
+{
+ u_int32_t a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+
+ if (apple_ver == 1)
+ {
+ MD5STEP(F2, b, c, d, a, in[8] + 0x445a14ed, 20);
+ }
+ else
+ {
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ }
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+#endif
+
+
+
diff --git a/lib/libXDAAP/Authentication/md5.h b/lib/libXDAAP/Authentication/md5.h
new file mode 100644
index 0000000000..b174ebef14
--- /dev/null
+++ b/lib/libXDAAP/Authentication/md5.h
@@ -0,0 +1,17 @@
+#ifndef __MD5_H__
+#define __MD5_H__
+
+#include "../portability.h"
+
+typedef struct {
+ u_int32_t buf[4];
+ u_int32_t bits[2];
+ unsigned char in[64];
+ int apple_ver;
+} MD5_CTX;
+
+void OpenDaap_MD5Init(MD5_CTX *, int apple_ver);
+void OpenDaap_MD5Update(MD5_CTX *, const unsigned char *, unsigned int);
+void OpenDaap_MD5Final(MD5_CTX *, unsigned char digest[16]);
+
+#endif /* __WINE_MD5_H__ */
diff --git a/lib/libXDAAP/Makefile.in b/lib/libXDAAP/Makefile.in
new file mode 100644
index 0000000000..aa1b8a45f5
--- /dev/null
+++ b/lib/libXDAAP/Makefile.in
@@ -0,0 +1,6 @@
+SRCS= daap.c debug.c dmap_generics.c global.c httpClient.c ioloop.c libXDAAP.c threadpool.c Authentication/hasher.c Authentication/md5.c
+
+LIB=libxdaap-@ARCH@.a
+
+include ../../Makefile.include
+
diff --git a/lib/libXDAAP/ReadMe.txt b/lib/libXDAAP/ReadMe.txt
new file mode 100644
index 0000000000..222c2ec362
--- /dev/null
+++ b/lib/libXDAAP/ReadMe.txt
@@ -0,0 +1 @@
+This library is based mainly on the libOpenDAAP library by David Hammerton (http://craz.net/programs/itunes/libopendaap.html) \ No newline at end of file
diff --git a/lib/libXDAAP/client.h b/lib/libXDAAP/client.h
new file mode 100644
index 0000000000..0a87281ec7
--- /dev/null
+++ b/lib/libXDAAP/client.h
@@ -0,0 +1,301 @@
+/* client class
+ *
+ * Copyright (c) 2004 David Hammerton
+ * crazney@crazney.net
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/* PUBLIC */
+
+#ifndef _CLIENT_H
+#define _CLIENT_H
+
+#include <stdio.h>
+#include "portability.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* type defintions */
+typedef struct DAAP_SClientTAG DAAP_SClient;
+typedef struct DAAP_SClientHostTAG DAAP_SClientHost;
+
+/* the callback will call with a status code.
+ * The updating and downloading status codes also call back
+ * with an integer value, between 1 and 1000, representing
+ * how much of the operation is complete.
+ * Further information on the downloading status can be
+ * discovered from the download streaming APIs
+ * When you recieve the dying status, you have released the last
+ * reference to the SClient object and should think about
+ * releasing all of your SClientHost objects (although its not
+ * mandatory, they won't be much good anymore).
+ */
+
+/* constants */
+typedef enum
+{
+ DAAP_STATUS_error = -2,
+ DAAP_STATUS_dying = -1,
+ DAAP_STATUS_idle = 0,
+ DAAP_STATUS_connecting,
+ DAAP_STATUS_negotiating,
+ DAAP_STATUS_updating,
+ DAAP_STATUS_downloading,
+ DAAP_STATUS_hostschanged
+} DAAP_Status;
+
+/* function pointer definitions */
+typedef void (*DAAP_fnClientStatus)(DAAP_SClient *, DAAP_Status, int, void*);
+
+/* Hosts are discoverd via mDNS (rendezvous) automatically by the Client
+ * interface (FIXME: only in multi-threaded environments for now).
+ * Known hosts can be enumerated with the DAAP_Client_EnumerateHosts function.
+ * libopendaap holds a lock on the host list during the entire enumeration
+ * process, so it is recommended that your DAAP_fnClientEnumerateHosts callback
+ * is rather fast. Perhaps store the information you want to a list and process
+ * it afterwards.
+ * return 0 if you wish to interupt the enumeration (it will release the lock
+ * and return immediatly) or any other number to continue.
+ * DO NOT call any other DAAP_Client API functions during the enumeration.
+ * You MAY call DAAP_ClientHost API functions during the enumeration. However,
+ * be sure to call DAAP_Client_AddRef before doing so
+ */
+typedef int (*DAAP_fnClientEnumerateHosts)(DAAP_SClient *, DAAP_SClientHost *host,
+ void *);
+
+/* Async write callback, flag=1 for header, flag=2 for data, flag<=0 for finished */
+typedef int (*DAAP_fnHttpWrite)(const char *buffer, int size, int flag, int contentlength, void* context);
+
+
+/* Interface - Client */
+DAAP_SClient *DAAP_Client_Create(DAAP_fnClientStatus pfnCallback,
+ void *pvCallbackContext);
+int DAAP_Client_SetDebug(DAAP_SClient *pCThis, const char *const debug);
+unsigned int DAAP_Client_AddRef(DAAP_SClient *pCThis);
+unsigned int DAAP_Client_Release(DAAP_SClient *pCThis);
+unsigned int DAAP_Client_EnumerateHosts(DAAP_SClient *pCThis,
+ DAAP_fnClientEnumerateHosts pfnCallback,
+ void *context);
+/* hack - don't use externally unless you have to. */
+DAAP_SClientHost *DAAP_Client_AddHost(DAAP_SClient *pCThis, char *host,
+ char *sharename, char *sharename_friendly);
+
+unsigned int DAAP_Client_GetDatabases(DAAP_SClientHost *pCHThis);
+
+int GetStreamThreadStatus(void);
+
+/* Interface - ClientHost */
+
+/* databases structure, get from DAAP_ClientHost_GetDatabases */
+typedef struct
+{
+ int id;
+ char *name;
+} DAAP_ClientHost_Database;
+
+/* item structure, get from DAAP_ClientHost_GetDatabaseItems */
+typedef struct
+{
+ int id;
+ char *itemname;
+ char *songalbum;
+ char *songartist;
+ short songbeatsperminute;
+ short songbitrate;
+ short songdisccount;
+ short songdiscnumber;
+ char *songgenre;
+ int songsamplerate;
+ int songsize;
+ int songtime;
+ short songtrackcount;
+ short songtracknumber;
+ char songuserrating;
+ short songyear;
+ char *songformat;
+} DAAP_ClientHost_DatabaseItem;
+
+typedef struct
+{
+ int songid;
+} DAAP_ClientHost_DatabasePlaylistItem;
+
+typedef struct
+{
+ int id;
+ int count;
+ DAAP_ClientHost_DatabasePlaylistItem *items;
+ char *itemname;
+} DAAP_ClientHost_DatabasePlaylist;
+
+typedef struct
+{
+ int size;
+ int streamlen;
+ void *data;
+} DAAP_ClientHost_Song;
+
+typedef struct albumTAG albumPTR;
+struct albumTAG
+{
+ char *album;
+ albumPTR *next;
+};
+
+typedef struct artistTAG artistPTR;
+struct artistTAG
+{
+ char *artist;
+ albumPTR *albumhead;
+ artistPTR *next;
+};
+
+
+
+/* ClientHost classes must be created through the Client APIs,
+ * these functions are intended to retrieve static information
+ * from a SClientHost. Appart from AddRef and Release, no
+ * state will be changed with these functions. It is recommended
+ * you keep a reference to them after the enumeration, however. */
+unsigned int DAAP_ClientHost_AddRef(DAAP_SClientHost *pCHThis);
+unsigned int DAAP_ClientHost_Release(DAAP_SClientHost *pCHThis);
+
+/* call this to get the name of the host (user friendly name in utf8)
+ * returns 0 on success, otherwise the size of the buffer required.
+ */
+unsigned int DAAP_ClientHost_GetSharename(DAAP_SClientHost *pCHThis,
+ char *buf,
+ int bufsize);
+
+/* if the client is password protected, Connect will fail
+ * with -401. Then call this with the password and
+ * the next call to connect will use this password */
+
+void DAAP_ClientHost_SetPassword(DAAP_SClientHost *pCHThis,
+ char *password);
+/* you must connect before using any of the things below.
+ * disconnect when you no longer want to use it.
+ */
+unsigned int DAAP_ClientHost_Connect(DAAP_SClientHost *pCHThis);
+unsigned int DAAP_ClientHost_Disconnect(DAAP_SClientHost *pCHThis);
+
+/* returns 0 on successful copy, otherwise returns length
+ * of required buffer (buffer is an array of size 'n' -
+ * string allocated at end of buffer)
+ */
+unsigned int DAAP_ClientHost_GetDatabases(DAAP_SClientHost *pCHThis,
+ DAAP_ClientHost_Database *buffer,
+ int *n, int bufsize);
+
+/* returns 0 on successful copy, -1 on error, otherwise
+ * length of required buffer
+ */
+int DAAP_ClientHost_GetDatabaseItems(DAAP_SClientHost *pCHThis,
+ int databaseid,
+ DAAP_ClientHost_DatabaseItem *buffer,
+ int *n, int bufsize);
+
+/* returns 0 on successful copy, otherwise returns length
+ * of required buffer
+ * FIXME: for now databaseid is ignored, presumes single database
+ */
+unsigned int DAAP_ClientHost_GetPlaylists(DAAP_SClientHost *pCHThis,
+ int databaseid,
+ DAAP_ClientHost_DatabasePlaylist *buffer,
+ int *n, int bufsize);
+
+/* returns 0 on successful copy, -1 on error, otherwise
+ * length of required buffer
+ */
+unsigned int DAAP_ClientHost_GetPlaylistItems(DAAP_SClientHost *pCHThis,
+ int databaseid, int playlistid,
+ DAAP_ClientHost_DatabasePlaylistItem *buffer,
+ int *n, int bufsize);
+
+/* returns 0 on success, -1 on error. be sure to free the file
+ * with DAAP_ClientHost_FreeAudioFile */
+int DAAP_ClientHost_GetAudioFile(DAAP_SClientHost *pCHThis,
+ int databaseid, int songid,
+ const char *songformat,
+ DAAP_ClientHost_Song *song);
+int DAAP_ClientHost_FreeAudioFile(DAAP_SClientHost *pCHThis,
+ DAAP_ClientHost_Song *song);
+/* async get of an audio file. writes it as it gets it to
+ * the fd specified. also sends callback if possible.
+ * returns immediatly.
+ * I suggest you use a pipe as the fd.
+ */
+int DAAP_ClientHost_AsyncGetAudioFile(DAAP_SClientHost *pCHThis,
+ int databaseid, int songid,
+ const char *songformat,
+#if defined(SYSTEM_POSIX)
+ int fd);
+#elif defined(SYSTEM_WIN32)
+ HANDLE fd);
+#else
+ FILE *fd);
+#endif
+
+int DAAP_ClientHost_AsyncGetAudioFileCallback(DAAP_SClientHost *pCHThis,
+ int databaseid, int songid,
+ const char *songformat,
+ int startbyte,
+ DAAP_fnHttpWrite callback,
+ void* context);
+
+/* call this to make the async get abort asap */
+int DAAP_ClientHost_AsyncStop(DAAP_SClientHost *pCHThis);
+
+/* [incomplete]
+ * database update stuff. call this to make libopendaap open a request
+ * to iTunes asking for database changes.
+ * It will most likely run in a different thread and return status through
+ * your status callback.
+ * It will keep pushing new update requests until AsyncStopUpdate is called.
+ */
+int DAAP_ClientHost_AsyncWaitUpdate(DAAP_SClientHost *pCHThis);
+/* [unimplemented]
+ * stop the async wait update
+ */
+int DAAP_ClientHost_AsyncStopUpdate(DAAP_SClientHost *pCHThis);
+
+// XBMC Async ops
+int DAAP_ClientHost_GetAudioFileAsync(DAAP_SClientHost *pCHThis,
+ int databaseid, int songid, char *songformat,
+ DAAP_ClientHost_Song *song);
+
+void DAAP_ClientHost_StopAudioFileAsync(DAAP_SClientHost *pCHThis);
+
+//unsigned int Priv_DAAP_ClientHost_GetDatabasePlaylistItems(DAAP_SClientHost *pCHThis,
+ //int databaseid,
+ //int playlistid,
+ //DAAP_ClientHost_DatabasePlaylistItem *items);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CLIENT_H */
diff --git a/lib/libXDAAP/compat.h b/lib/libXDAAP/compat.h
new file mode 100644
index 0000000000..e69fc4efec
--- /dev/null
+++ b/lib/libXDAAP/compat.h
@@ -0,0 +1,90 @@
+#ifndef __COMPAT_H__
+#define __COMPAT_H__
+
+#if WIN32
+#include <direct.h>
+#include <windows.h>
+#include <process.h>
+#else
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <semaphore.h>
+#include <pthread.h>
+#endif
+
+//
+// This file handles all portablity issues with streamripper
+//
+
+// File Routines
+//////////////////////////////////////////
+
+#if WIN32
+#define FHANDLE HANDLE
+#define OpenFile(_filename_) CreateFile(_filename_, GENERIC_READ, \
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, \
+ FILE_ATTRIBUTE_NORMAL, NULL)
+#define CloseFile(_fhandle_) CloseHandle(_fhandle_)
+//#define MoveFile(_oldfile_, _newfile_) MoveFile(_oldfile_, _newfile_)
+//#define CloseFile(_file_) CloseHandle(file)
+#define INVALID_FHANDLE INVALID_HANDLE_VALUE
+#elif __UNIX__
+
+#define FHANDLE int
+#define OpenFile(_filename_) open(_filename_, O_RDWR | O_CREAT, S_IRWXU | S_IRGRP | S_IROTH)
+#define CloseFile(_fhandle_) close(_fhandle_)
+#define MoveFile(_oldfile_, _newfile_) rename(_oldfile_, _newfile_)
+#define DeleteFile(_file_) (!unlink(_file_))
+#define INVALID_FHANDLE -1
+
+#endif
+
+// Thread Routines
+//////////////////////////////////////////
+//#if WIN32
+#if WIN32
+#define THANDLE HANDLE
+#define BeginThread(_thandle_, callback) \
+ {_thandle_ = (THANDLE)_beginthread((void *)callback, 0, (void *)NULL);}
+#define WaitForThread(_thandle_) WaitForSingleObject(_thandle_, INFINITE);
+#define DestroyThread(_thandle_) CloseHandle(_thandle_)
+
+#define HSEM HANDLE
+#define SemInit(_s_) {_s_ = CreateEvent(NULL, TRUE, FALSE, NULL);}
+#define SemWait(_s_) {WaitForSingleObject(_s_, INFINITE); ResetEvent(_s_);}
+#define SemPost(_s_) SetEvent(_s_)
+#define SemDestroy(_s_) CloseHandle(_s_)
+
+
+#elif __UNIX__
+
+#define THANDLE pthread_t
+#define BeginThread(_thandle_, callback) pthread_create(&_thandle_, NULL, \
+ (void *)callback, (void *)NULL)
+#define WaitForThread(_thandle_) pthread_join(_thandle_, NULL)
+#define DestroyThread(_thandle_) // is there one for unix?
+#define HSEM sem_t
+#define SemInit(_s_) sem_init(&(_s_), 0, 0)
+#define SemWait(_s_) sem_wait(&(_s_))
+#define SemPost(_s_) sem_post(&(_s_))
+#define SemDestroy(_s_) sem_destroy(&(_s_))
+#define Sleep(x) usleep(x)
+#define SOCKET socket_t
+
+#endif
+
+// Socket Routines
+//////////////////////////////////////////
+
+#if WIN32
+//#define EAGAIN WSAEWOULDBLOCK
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#elif __UNIX__
+#define closesocket close
+#define SOCKET_ERROR -1
+#define WSACleanup()
+#endif
+
+#endif // __COMPAT_H__
diff --git a/lib/libXDAAP/daap.c b/lib/libXDAAP/daap.c
new file mode 100644
index 0000000000..530abbda3c
--- /dev/null
+++ b/lib/libXDAAP/daap.c
@@ -0,0 +1,583 @@
+// Place the code and data below here into the LIBXDAAP section.
+#ifndef __GNUC__
+#pragma code_seg( "LIBXDAAP_TEXT" )
+#pragma data_seg( "LIBXDAAP_DATA" )
+#pragma bss_seg( "LIBXDAAP_BSS" )
+#pragma const_seg( "LIBXDAAP_RD" )
+#endif
+
+/* daap
+ *
+ * core protocol
+ *
+ * Copyright (c) 2004 David Hammerton
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "portability.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libXDAAP.h"
+
+#include "debug.h"
+#include "daap.h"
+#include "daap_contentcodes.h"
+#include "dmap_generics.h"
+
+#define DEFAULT_DEBUG_CHANNEL "daap"
+
+#include "daap_readtypes.h"
+
+
+#define UNHANDLED_CONTENT_CODE \
+ do { ERR("unhandled content code [%c%c%c%c]\n", \
+ SPLITFOURCC(code) \
+ ); } while(0)
+
+static int dmap_initilized = 0;
+
+#define IMPLEMENT_GENERICTOTYPE(_type, umember) \
+dmap_DataTypes dmap_genericTo##_type(dmap_GenericType in, DMAP_##_type *out) \
+{ \
+ if (in.type != DMAP_DATATYPE_##_type) return in.type; \
+ *out = in.u.umember; \
+ return in.type; \
+}
+IMPLEMENT_GENERICTOTYPE(INT8, int8)
+IMPLEMENT_GENERICTOTYPE(UINT8, uint8)
+IMPLEMENT_GENERICTOTYPE(INT16, int16)
+IMPLEMENT_GENERICTOTYPE(UINT16, uint16)
+IMPLEMENT_GENERICTOTYPE(INT32, int32)
+IMPLEMENT_GENERICTOTYPE(UINT32, uint32)
+IMPLEMENT_GENERICTOTYPE(INT64, int64)
+IMPLEMENT_GENERICTOTYPE(UINT64, uint64)
+IMPLEMENT_GENERICTOTYPE(STRING, string)
+IMPLEMENT_GENERICTOTYPE(TIME, time)
+IMPLEMENT_GENERICTOTYPE(VERSION, version)
+#undef IMPLEMENT_GENERICTOTYPE
+
+
+/* FIXME - replace with dictionary implementation */
+dmap_ContentCode_table dmap_table =
+ {
+ "dmap",
+ NULL
+ };
+
+dmap_ContentCode_table daap_table =
+ {
+ "daap",
+ NULL
+ };
+
+dmap_ContentCode_table com_table =
+ {
+ "com",
+ NULL
+ };
+
+
+const dmap_ContentCode *dmap_lookupCode(const dmap_ContentCode_table *table,
+ const char *name)
+{
+ dmap_ContentCodeContainer *cur = table->codes;
+ while (cur)
+ {
+ if (strcmp(cur->c.cc_name, name) == 0)
+ return &cur->c;
+ cur = cur->next;
+ }
+ return NULL;
+}
+
+const dmap_ContentCode *dmap_lookupCodeFromFOURCC(const dmap_ContentCode_table *table,
+ const dmap_contentCodeFOURCC fourcc)
+{
+ dmap_ContentCodeContainer *cur = table->codes;
+ while (cur)
+ {
+ if (cur->c.cc_number == fourcc)
+ return &cur->c;
+ cur = cur->next;
+ }
+ return NULL;
+}
+
+void dmap_addCode(dmap_ContentCode_table *table, const char *name,
+ const dmap_contentCodeFOURCC code, const dmap_DataTypes type)
+{
+ dmap_ContentCodeContainer *newItem;
+ const dmap_ContentCode *existing = dmap_lookupCode(table, name);
+
+ if (existing)
+ {
+ if (existing->cc_number != code)
+ ERR("code for existing content code differs [%s] [%c%c%c%c vs %c%c%c%c]\n",
+ name, SPLITFOURCC(existing->cc_number), SPLITFOURCC(code));
+ if (existing->cc_type != type)
+ ERR("type for existing content code differs [%s] [%i vs %i]\n",
+ name, existing->cc_type, type);
+ return;
+ }
+
+ newItem = (dmap_ContentCodeContainer *) malloc(sizeof(dmap_ContentCodeContainer) + strlen(name) + 1);
+ newItem->c.cc_number = code;
+ newItem->c.cc_name = (char*)newItem + sizeof(dmap_ContentCodeContainer);
+ strcpy(newItem->c.cc_name, name);
+ newItem->c.cc_type = type;
+
+ newItem->next = table->codes;
+ table->codes = newItem;
+}
+
+static const char *getTypeString(dmap_DataTypes type)
+{
+ switch (type)
+ {
+ case DMAP_DATATYPE_INVALID:
+ return "DMAP_DATATYPE_INVALID\n";
+
+ case DMAP_DATATYPE_INT8:
+ return "DMAP_DATATYPE_INT8";
+ case DMAP_DATATYPE_UINT8:
+ return "DMAP_DATATYPE_UINT8";
+
+ case DMAP_DATATYPE_INT16:
+ return "DMAP_DATATYPE_INT16";
+ case DMAP_DATATYPE_UINT16:
+ return "DMAP_DATATYPE_UINT16";
+
+ case DMAP_DATATYPE_INT32:
+ return "DMAP_DATATYPE_INT32";
+ case DMAP_DATATYPE_UINT32:
+ return "DMAP_DATATYPE_UINT32";
+
+ case DMAP_DATATYPE_UINT64:
+ return "DMAP_DATATYPE_UINT64";
+ case DMAP_DATATYPE_INT64:
+ return "DMAP_DATATYPE_INT64";
+
+ case DMAP_DATATYPE_STRING:
+ return "DMAP_DATATYPE_STRING";
+ case DMAP_DATATYPE_TIME:
+ return "DMAP_DATATYPE_TIME";
+ case DMAP_DATATYPE_VERSION:
+ return "DMAP_DATATYPE_VERSION";
+ case DMAP_DATATYPE_CONTAINER:
+ return "DMAP_DATATYPE_CONTAINER";
+ }
+ return "UNKNOWN_TYPE!\n";
+}
+
+static void dumpContentCodes(dmap_ContentCode_table *table)
+{
+ dmap_ContentCodeContainer *cur = table->codes;
+ if (!TRACE_ON) return;
+ while (cur)
+ {
+ DPRINTF("/* %c%c%c%c */\n", SPLITFOURCC(cur->c.cc_number));
+ DPRINTF("%s_add(\"%s\", ", table->prefix, cur->c.cc_name);
+ DPRINTF("MAKEFOURCC('%c','%c','%c','%c'),\n", SPLITFOURCC(cur->c.cc_number));
+ DPRINTF(" %s);\n", getTypeString(cur->c.cc_type));
+ DPRINTF("\n");
+ cur = cur->next;
+ }
+}
+
+
+/* END FIXME */
+
+dmap_DataTypes dmap_isCC(const dmap_contentCodeFOURCC fourcc,
+ const dmap_ContentCode *code)
+{
+ if (!code)
+ {
+ ERR("unknown / unsupported content code\n");
+ return DMAP_DATATYPE_INVALID;
+ }
+ if (code->cc_number == fourcc)
+ return code->cc_type;
+ return DMAP_DATATYPE_INVALID;
+}
+
+/* utility functions */
+
+int dmap_parseContainer(containerHandlerFunc pfnHandler,
+ const int size, const char *buffer,
+ void *scopeData)
+{
+ int n = 0;
+ while (n < size)
+ {
+ dmap_contentCodeFOURCC code;
+ int codesize;
+
+ /* we expect a content code first */
+ code = read_fourcc(&buffer[n], sizeof(code));
+ n+=4;
+
+ /* then a code size */
+ codesize = readBigEndian_INT32(&buffer[n], sizeof(codesize));
+ n+=4;
+
+ /* send it off to the container parser */
+ pfnHandler(code, codesize, &buffer[n], scopeData);
+ n += codesize;
+ }
+ return 1;
+}
+
+/* container parsers */
+
+/* content codes */
+static void contentCodesDictionary(dmap_contentCodeFOURCC code,
+ const int size, const char *buffer,
+ void *scopeData)
+{
+ scopeContentCodesDictionary *cd = (scopeContentCodesDictionary*)scopeData;
+ if (dmap_isCC(code, dmap_l("contentcodesnumber")) == DMAP_DATATYPE_INT32)
+ {
+ dmap_contentCodeFOURCC fourcc = read_fourcc(buffer, size);
+ cd->c.cc_number = fourcc;
+ }
+ else if (dmap_isCC(code, dmap_l("contentcodesname")) == DMAP_DATATYPE_STRING)
+ {
+ cd->c.cc_name = read_string_withalloc(buffer, size); /* freed by parent */
+ }
+ else if (dmap_isCC(code, dmap_l("contentcodestype")) == DMAP_DATATYPE_INT16)
+ {
+ DMAP_INT16 type = readBigEndian_INT16(buffer, size);
+ cd->c.cc_type = type;
+ }
+ else
+ UNHANDLED_CONTENT_CODE;
+}
+
+static void contentCodesResponse(dmap_contentCodeFOURCC code,
+ const int size, const char *buffer,
+ void *scopeData)
+{
+ if (dmap_isCC(code, dmap_l("status")) == DMAP_DATATYPE_INT32)
+ {
+ DMAP_INT32 status = readBigEndian_INT32(buffer, size);
+ if (status != 200)
+ FIXME("unknown status code %i\n", status);
+ }
+ else if (dmap_isCC(code, dmap_l("dictionary")) == DMAP_DATATYPE_CONTAINER)
+ {
+ scopeContentCodesDictionary sd;
+ memset(&sd, 0, sizeof(sd));
+ dmap_parseContainer(contentCodesDictionary, size, buffer, (void*)&sd);
+ if (sd.c.cc_name)
+ {
+ if (strncmp("dmap.", sd.c.cc_name, 5) == 0)
+ {
+ const char *name = sd.c.cc_name + 5;
+ dmap_add(name, sd.c.cc_number, sd.c.cc_type);
+ }
+ else if (strncmp("daap.", sd.c.cc_name, 5) == 0)
+ {
+ const char *name = sd.c.cc_name + 5;
+ daap_add(name, sd.c.cc_number, sd.c.cc_type);
+ }
+ else if (strncmp("com.", sd.c.cc_name, 4) == 0)
+ {
+ const char *name = sd.c.cc_name + 4;
+ com_add(name, sd.c.cc_number, sd.c.cc_type);
+ }
+ else
+ ERR("unknown class for content code: %s\n", sd.c.cc_name);
+ free(sd.c.cc_name);
+ }
+ }
+ else
+ UNHANDLED_CONTENT_CODE;
+}
+
+/* server info container */
+static void serverInfoResponse(dmap_contentCodeFOURCC code,
+ const int size, const char *buffer,
+ void *scopeData)
+{
+ protoParseResult_serverinfo *sd = (protoParseResult_serverinfo*)scopeData;
+ if (dmap_isCC(code, dmap_l("status")) == DMAP_DATATYPE_INT32)
+ {
+ DMAP_INT32 status = readBigEndian_INT32(buffer, size);
+ if (status != 200)
+ FIXME("unknown status code %i\n", status);
+ }
+ else if (dmap_isCC(code, dmap_l("protocolversion")) == DMAP_DATATYPE_VERSION)
+ {
+ if (sd)
+ sd->dmap_version = read_version(buffer, size);
+ }
+ else if (dmap_isCC(code, daap_l("protocolversion")) == DMAP_DATATYPE_VERSION)
+ {
+ if (sd)
+ sd->daap_version = read_version(buffer, size);
+ }
+ else if (dmap_isCC(code, dmap_l("itemname")) == DMAP_DATATYPE_STRING)
+ {
+ if (sd)
+ sd->hostname = read_string_withalloc(buffer, size);
+ }
+ else if (dmap_isCC(code, dmap_l("authenticationmethod")) == DMAP_DATATYPE_INT8)
+ {
+ if (readBigEndian_INT8(buffer, size))
+ TRACE("requires a login\n");
+ }
+ else if (dmap_isCC(code, dmap_l("loginrequired")) == DMAP_DATATYPE_INT8)
+ {
+ if (readBigEndian_INT8(buffer, size))
+ TRACE("requires a login\n");
+ }
+ else if (dmap_isCC(code, dmap_l("timeoutinterval")) == DMAP_DATATYPE_INT32)
+ {
+ TRACE("timeout interval: %i\n", readBigEndian_INT32(buffer, size));
+ }
+ else if (dmap_isCC(code, dmap_l("supportsautologout")) == DMAP_DATATYPE_INT8)
+ {
+ }
+ else if (dmap_isCC(code, dmap_l("supportsupdate")) == DMAP_DATATYPE_INT8)
+ {
+ }
+ else if (dmap_isCC(code, dmap_l("supportspersistentids")) == DMAP_DATATYPE_INT8)
+ {
+ }
+ else if (dmap_isCC(code, dmap_l("supportsextensions")) == DMAP_DATATYPE_INT8)
+ {
+ }
+ else if (dmap_isCC(code, dmap_l("supportsbrowse")) == DMAP_DATATYPE_INT8)
+ {
+ }
+ else if (dmap_isCC(code, dmap_l("supportsquery")) == DMAP_DATATYPE_INT8)
+ {
+ }
+ else if (dmap_isCC(code, dmap_l("supportsindex")) == DMAP_DATATYPE_INT8)
+ {
+ }
+ else if (dmap_isCC(code, dmap_l("supportsresolve")) == DMAP_DATATYPE_INT8)
+ {
+ }
+ else if (dmap_isCC(code, dmap_l("databasescount")) == DMAP_DATATYPE_INT32)
+ {
+ if (sd)
+ sd->databasescount = readBigEndian_INT32(buffer, size);
+ }
+ else
+ UNHANDLED_CONTENT_CODE;
+}
+
+/* login container */
+static void loginResponse(dmap_contentCodeFOURCC code,
+ const int size, const char *buffer,
+ void *scopeData)
+{
+ protoParseResult_login *sd = (protoParseResult_login*)scopeData;
+ if (dmap_isCC(code, dmap_l("status")) == DMAP_DATATYPE_INT32)
+ {
+ DMAP_INT32 status = readBigEndian_INT32(buffer, size);
+ if (status != 200)
+ FIXME("unknown status code %i\n", status);
+ }
+ else if (dmap_isCC(code, dmap_l("sessionid")) == DMAP_DATATYPE_INT32)
+ {
+ sd->sessionid = readBigEndian_INT32(buffer, size);
+ }
+ else
+ UNHANDLED_CONTENT_CODE;
+}
+
+/* update container */
+static void updateResponse(dmap_contentCodeFOURCC code,
+ const int size, const char *buffer,
+ void *scopeData)
+{
+ protoParseResult_update *sd = (protoParseResult_update*)scopeData;
+ if (dmap_isCC(code, dmap_l("status")) == DMAP_DATATYPE_INT32)
+ {
+ DMAP_INT32 status = readBigEndian_INT32(buffer, size);
+ if (status != 200)
+ FIXME("unknown status code %i\n", status);
+ }
+ else if (dmap_isCC(code, dmap_l("serverrevision")) == DMAP_DATATYPE_INT32)
+ {
+ sd->serverrevision = readBigEndian_INT32(buffer, size);
+ }
+ else
+ UNHANDLED_CONTENT_CODE;
+}
+
+/* main container */
+static void toplevelResponse(dmap_contentCodeFOURCC code,
+ const int size, const char *buffer,
+ void *scopeData)
+{
+ protoParseResult *sd = (protoParseResult*)scopeData;
+ if (dmap_isCC(code, dmap_l("serverinforesponse")) == DMAP_DATATYPE_CONTAINER)
+ {
+ if (sd && sd->expecting == QUERY_SERVERINFORESPONSE)
+ dmap_parseContainer(serverInfoResponse, size, buffer, sd);
+ }
+ else if (dmap_isCC(code, dmap_l("contentcodesresponse")) == DMAP_DATATYPE_CONTAINER)
+ {
+ dmap_parseContainer(contentCodesResponse, size, buffer, NULL);
+#if 1
+ dumpContentCodes(&dmap_table);
+ dumpContentCodes(&daap_table);
+ dumpContentCodes(&com_table);
+#endif
+ }
+ else if (dmap_isCC(code, dmap_l("loginresponse")) == DMAP_DATATYPE_CONTAINER)
+ {
+ if (sd && sd->expecting == QUERY_LOGINRESPONSE)
+ dmap_parseContainer(loginResponse, size, buffer, sd);
+ }
+ else if (dmap_isCC(code, dmap_l("updateresponse")) == DMAP_DATATYPE_CONTAINER)
+ {
+ if (sd && sd->expecting == QUERY_UPDATERESPONSE)
+ dmap_parseContainer(updateResponse, size, buffer, sd);
+ }
+ else if (dmap_isCC(code, daap_l("serverdatabases")) == DMAP_DATATYPE_CONTAINER)
+ {
+ if (sd && sd->expecting == QUERY_GENERICLISTING)
+ dmap_parseContainer(preListingContainer, size, buffer, sd);
+ }
+ else if (dmap_isCC(code, daap_l("databasesongs")) == DMAP_DATATYPE_CONTAINER)
+ {
+ if (sd && sd->expecting == QUERY_GENERICLISTING)
+ dmap_parseContainer(preListingContainer, size, buffer, sd);
+ }
+ else if (dmap_isCC(code, daap_l("databaseplaylists")) == DMAP_DATATYPE_CONTAINER)
+ {
+ if (sd && sd->expecting == QUERY_GENERICLISTING)
+ dmap_parseContainer(preListingContainer, size, buffer, sd);
+ }
+ else if (dmap_isCC(code, daap_l("playlistsongs")) == DMAP_DATATYPE_CONTAINER)
+ {
+ if (sd && sd->expecting == QUERY_GENERICLISTING)
+ dmap_parseContainer(preListingContainer, size, buffer, sd);
+ }
+ else
+ UNHANDLED_CONTENT_CODE;
+}
+
+/* we load most of the content codes using the /content-codes
+ * request. But to get there we must first be able to
+ * pass the /server-info response and the /content-codes response
+ * so this is the 'boot strapper' for that
+ */
+static void dmap_ContentCodesBootstrap()
+{
+ /* common */
+ dmap_add("status", MAKEFOURCC('m','s','t','t'),
+ DMAP_DATATYPE_INT32);
+ dmap_add("dictionary", MAKEFOURCC('m','d','c','l'),
+ DMAP_DATATYPE_CONTAINER);
+
+ /* server info */
+ dmap_add("serverinforesponse", MAKEFOURCC('m','s','r','v'),
+ DMAP_DATATYPE_CONTAINER);
+ dmap_add("protocolversion", MAKEFOURCC('m','p','r','o'),
+ DMAP_DATATYPE_VERSION);
+ daap_add("protocolversion", MAKEFOURCC('a','p','r','o'),
+ DMAP_DATATYPE_VERSION);
+ dmap_add("itemname", MAKEFOURCC('m','i','n','m'),
+ DMAP_DATATYPE_STRING);
+ dmap_add("authenticationmethod", MAKEFOURCC('m','s','a','u'),
+ DMAP_DATATYPE_INT8);
+ dmap_add("loginrequired", MAKEFOURCC('m','s','l','r'),
+ DMAP_DATATYPE_INT8);
+ dmap_add("timeoutinterval", MAKEFOURCC('m','s','t','m'),
+ DMAP_DATATYPE_INT32);
+ dmap_add("supportsautologout", MAKEFOURCC('m','s','a','l'),
+ DMAP_DATATYPE_INT8);
+ dmap_add("supportsupdate", MAKEFOURCC('m','s','u','p'),
+ DMAP_DATATYPE_INT8);
+ dmap_add("supportspersistentids", MAKEFOURCC('m','s','p','i'),
+ DMAP_DATATYPE_INT8);
+ dmap_add("supportsextensions", MAKEFOURCC('m','s','e','x'),
+ DMAP_DATATYPE_INT8);
+ dmap_add("supportsbrowse", MAKEFOURCC('m','s','b','r'),
+ DMAP_DATATYPE_INT8);
+ dmap_add("supportsquery", MAKEFOURCC('m','s','q','y'),
+ DMAP_DATATYPE_INT8);
+ dmap_add("supportsindex", MAKEFOURCC('m','s','i','x'),
+ DMAP_DATATYPE_INT8);
+ dmap_add("supportsresolve", MAKEFOURCC('m','s','r','s'),
+ DMAP_DATATYPE_INT8);
+ dmap_add("databasescount", MAKEFOURCC('m','s','d','c'),
+ DMAP_DATATYPE_INT32);
+
+
+ dmap_add("contentcodesresponse", MAKEFOURCC('m','c','c','r'),
+ DMAP_DATATYPE_CONTAINER);
+ dmap_add("contentcodesnumber", MAKEFOURCC('m','c','n','m'),
+ DMAP_DATATYPE_INT32);
+ dmap_add("contentcodesname", MAKEFOURCC('m','c','n','a'),
+ DMAP_DATATYPE_STRING);
+ dmap_add("contentcodestype", MAKEFOURCC('m','c','t','y'),
+ DMAP_DATATYPE_INT16);
+}
+
+/* main function called to parse the thing */
+
+void dmap_init()
+{
+ if (dmap_initilized) return;
+ dmap_ContentCodesBootstrap();
+ dmap_initilized = 1;
+}
+
+void dmap_deinit()
+{
+ if (dmap_initilized)
+ {
+ dmap_ContentCodeContainer *newItem;
+
+ while(dmap_table.codes)
+ {
+ newItem = dmap_table.codes->next;
+ free(dmap_table.codes);
+ dmap_table.codes = newItem;
+ }
+ dmap_initilized = 0;
+ }
+
+ return;
+}
+
+void dmap_parseProtocolData(const int size, const char *buffer, protoParseResult *res)
+{
+ if (!dmap_initilized)
+ {
+ ERR("dmap_init must be called first!\n");
+ return;
+ }
+ dmap_parseContainer(toplevelResponse, size, buffer, res);
+}
+
+
diff --git a/lib/libXDAAP/daap.h b/lib/libXDAAP/daap.h
new file mode 100644
index 0000000000..dc1960d01a
--- /dev/null
+++ b/lib/libXDAAP/daap.h
@@ -0,0 +1,183 @@
+/* daap
+ *
+ * core protocol definitions
+ *
+ * Copyright (c) 2004 David Hammerton
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/* DMAP is probable a parent protocol which DAAP
+ * is implemented over.
+ * For now I'm not splitting them up, but all
+ * DMAP_ things seem to be generic to this parent
+ * protocol
+ */
+
+#ifndef _DAAP_H
+#define _DAAP_H
+
+#include "portability.h"
+
+typedef int32_t dmap_contentCodeFOURCC;
+#ifndef MAKEFOURCC
+#define MAKEFOURCC(ch0, ch1, ch2, ch3) \
+ ( (int32_t)(char)(ch0) | ( (int32_t)(char)(ch1) << 8 ) | \
+ ( (int32_t)(char)(ch2) << 16 ) | \
+ ( (int32_t)(char)(ch3) << 24 ) )
+#endif
+
+#ifndef SLPITFOURCC
+/* splits it into ch0, ch1, ch2, ch3 - use for printf's */
+#define SPLITFOURCC(code) \
+ (char)code, (char)((int32_t)code >> 8), \
+ (char)((int32_t)code >> 16), \
+ (char)((int32_t)code >> 24)
+
+
+#endif
+
+/* I'm not sure if these ints are signed or not,
+ * however I would hazard a guess that they are signed,
+ * with types 2,4,6 and 8 being unsigned.
+ */
+
+typedef enum
+{
+ DMAP_DATATYPE_INVALID = -1,
+
+ DMAP_DATATYPE_INT8 = 1,
+ DMAP_DATATYPE_UINT8 = 2,
+ DMAP_DATATYPE_INT16 = 3,
+ DMAP_DATATYPE_UINT16 = 4,
+ DMAP_DATATYPE_INT32 = 5,
+ DMAP_DATATYPE_UINT32 = 6,
+ DMAP_DATATYPE_INT64 = 7,
+ DMAP_DATATYPE_UINT64 = 8,
+
+ DMAP_DATATYPE_STRING = 9,
+ DMAP_DATATYPE_TIME = 10,
+ DMAP_DATATYPE_VERSION = 11,
+ DMAP_DATATYPE_CONTAINER = 12
+} dmap_DataTypes;
+
+typedef int8_t DMAP_INT8;
+typedef u_int8_t DMAP_UINT8;
+
+typedef int16_t DMAP_INT16;
+typedef u_int16_t DMAP_UINT16;
+
+typedef int32_t DMAP_INT32;
+typedef u_int32_t DMAP_UINT32;
+
+typedef int64_t DMAP_INT64;
+typedef u_int64_t DMAP_UINT64;
+
+typedef char* DMAP_STRING;
+typedef time_t DMAP_TIME;
+typedef struct { int16_t v1, v2; } DMAP_VERSION;
+
+typedef struct
+{
+ union
+ {
+ DMAP_INT8 int8;
+ DMAP_UINT8 uint8;
+ DMAP_INT16 int16;
+ DMAP_UINT16 uint16;
+ DMAP_INT32 int32;
+ DMAP_UINT32 uint32;
+ DMAP_INT64 int64;
+ DMAP_UINT64 uint64;
+ DMAP_STRING string;
+ DMAP_TIME time;
+ DMAP_VERSION version;
+ } u;
+ dmap_DataTypes type;
+} dmap_GenericType;
+
+typedef struct
+{
+ dmap_contentCodeFOURCC cc_number;
+ char * cc_name;
+ dmap_DataTypes cc_type;
+} dmap_ContentCode;
+
+/* to query the protocol parser engine */
+typedef enum
+{
+ QUERY_SERVERINFORESPONSE = 0,
+ QUERY_LOGINRESPONSE,
+ QUERY_UPDATERESPONSE,
+ QUERY_GENERICLISTING
+} PROTO_PARSE_EXPECTING;
+
+typedef struct
+{
+ PROTO_PARSE_EXPECTING expecting;
+} protoParseResult;
+
+typedef struct
+{
+ protoParseResult h;
+ DMAP_VERSION dmap_version;
+ DMAP_VERSION daap_version;
+ DMAP_INT32 databasescount;
+ DMAP_STRING hostname;
+} protoParseResult_serverinfo;
+
+typedef struct
+{
+ protoParseResult h;
+ DMAP_INT32 sessionid;
+} protoParseResult_login;
+
+typedef struct
+{
+ protoParseResult h;
+ DMAP_INT32 serverrevision;
+} protoParseResult_update;
+
+/* functions */
+/* dmap_init MUST be called during process attach. */
+void dmap_init();
+void dmap_deinit();
+void dmap_parseProtocolData(const int size, const char *buffer,
+ protoParseResult *res);
+
+/* generic type */
+dmap_DataTypes dmap_genericToINT8(dmap_GenericType in, DMAP_INT8 *out);
+dmap_DataTypes dmap_genericToUINT8(dmap_GenericType in, DMAP_UINT8 *out);
+dmap_DataTypes dmap_genericToINT16(dmap_GenericType in, DMAP_INT16 *out);
+dmap_DataTypes dmap_genericToUINT16(dmap_GenericType in, DMAP_UINT16 *out);
+dmap_DataTypes dmap_genericToINT32(dmap_GenericType in, DMAP_INT32 *out);
+dmap_DataTypes dmap_genericToUINT32(dmap_GenericType in, DMAP_UINT32 *out);
+dmap_DataTypes dmap_genericToINT64(dmap_GenericType in, DMAP_INT64 *out);
+dmap_DataTypes dmap_genericToUINT64(dmap_GenericType in, DMAP_UINT64 *out);
+
+dmap_DataTypes dmap_genericToSTRING(dmap_GenericType in, DMAP_STRING *out);
+dmap_DataTypes dmap_genericToTIME(dmap_GenericType in, DMAP_TIME *out);
+dmap_DataTypes dmap_genericToVERSION(dmap_GenericType in, DMAP_VERSION *out);
+
+
+#endif /* _DAAP_H */
+
diff --git a/lib/libXDAAP/daap_contentcodes.h b/lib/libXDAAP/daap_contentcodes.h
new file mode 100644
index 0000000000..fb4b388448
--- /dev/null
+++ b/lib/libXDAAP/daap_contentcodes.h
@@ -0,0 +1,97 @@
+/* list of known contentcodes
+ *
+ * Copyright (c) David Hammerton 2003
+ *
+ * FIXME: needs to be made per connection.. as some hosts may have
+ * different content codes.
+ *
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef _DAAP_CONTENTCODES_H
+#define _DAAP_CONTENTCODES_H
+
+#include "daap.h"
+
+/* FIXME: replace this with a hashed dictionary, rather
+ * than this linked list. its slow
+ */
+
+typedef struct dmap_ContentCodeContainerTAG dmap_ContentCodeContainer;
+struct dmap_ContentCodeContainerTAG
+{
+ dmap_ContentCode c;
+ dmap_ContentCodeContainer *next;
+};
+
+typedef struct
+{
+ char *prefix;
+ dmap_ContentCodeContainer *codes;
+} dmap_ContentCode_table;
+
+const dmap_ContentCode *dmap_lookupCode(const dmap_ContentCode_table *table,
+ const char *name);
+const dmap_ContentCode *dmap_lookupCodeFromFOURCC(const dmap_ContentCode_table *table,
+ const dmap_contentCodeFOURCC fourcc);
+/* add actually performs a duplicate test, also.
+ * if the duplicate test matches, add will print an ERR if the records arn't
+ * the same
+ */
+void dmap_addCode(dmap_ContentCode_table *table, const char *name,
+ const dmap_contentCodeFOURCC code, const dmap_DataTypes type);
+
+
+extern dmap_ContentCode_table dmap_table;
+extern dmap_ContentCode_table daap_table;
+extern dmap_ContentCode_table com_table;
+
+#define dmap_l(str) dmap_lookupCode(&dmap_table, str)
+#define dmap_add(str, code, type) dmap_addCode(&dmap_table, str, code, type)
+
+#define daap_l(str) dmap_lookupCode(&daap_table, str)
+#define daap_add(str, code, type) dmap_addCode(&daap_table, str, code, type)
+
+#define com_l(str) dmap_lookupCode(&com_table, str)
+#define com_add(str, code, type) dmap_addCode(&com_table, str, code, type)
+
+dmap_DataTypes dmap_isCC(const dmap_contentCodeFOURCC fourcc,
+ const dmap_ContentCode *code);
+
+
+typedef void (*containerHandlerFunc)(dmap_contentCodeFOURCC code,
+ const int size,
+ const char *buffer,
+ void *scopeData);
+int dmap_parseContainer(containerHandlerFunc pfnHandler,
+ const int size, const char *buffer,
+ void *scopeData);
+
+/* private structures, get passed around various container parses in daap.c */
+typedef struct
+{
+ dmap_ContentCode c;
+} scopeContentCodesDictionary;
+
+#endif /* _DAAP_CONTENTCODES_H */
+
diff --git a/lib/libXDAAP/daap_readtypes.h b/lib/libXDAAP/daap_readtypes.h
new file mode 100644
index 0000000000..522788ee64
--- /dev/null
+++ b/lib/libXDAAP/daap_readtypes.h
@@ -0,0 +1,147 @@
+/* endian reads
+ *
+ * Copyright (c) 2004 David Hammerton
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+
+#ifndef DAAP_READTYPES
+#define DAAP_READTYPES
+
+#include "endian_swap.h"
+
+/* Solaris (at least) requires integers to be
+ * properly aligned. This stuff will copy the required data of
+ * the buffer into a properly aligned temporary buffer, if the compiler
+ * supports alignment, and the buffer isn't properly aligned
+ */
+#ifdef HAVE___ALIGNOF__
+# define FIXUP_ALIGNMENT(type) \
+ type abuf; \
+ if (size != sizeof(type)) { \
+ FIXME("funny sized\n"); \
+ } \
+ if (((int)buf % __alignof__(type)) != 0) { \
+ memcpy(&abuf, buf, \
+ size <= sizeof(type) ? size : sizeof(type)); \
+ buf = (const void*)&abuf; \
+ }
+#else
+# define FIXUP_ALIGNMENT(type) \
+ if (size != sizeof(type)) { \
+ FIXME("funny sized\n"); \
+ }
+#endif
+
+static DMAP_INT8 readBigEndian_INT8(const void *buf, size_t size)
+{
+ /* really int8 will never need alignment, but for constancy.. */
+ FIXUP_ALIGNMENT(DMAP_INT8);
+
+ return *(DMAP_INT8*)buf;
+}
+
+static DMAP_UINT8 readBigEndian_UINT8(const void *buf, size_t size)
+{
+ FIXUP_ALIGNMENT(DMAP_UINT8);
+
+ return *(DMAP_UINT8*)buf;
+}
+
+static DMAP_INT16 readBigEndian_INT16(const void *buf, size_t size)
+{
+ FIXUP_ALIGNMENT(DMAP_INT16);
+
+ return __Swap16(*(DMAP_INT16*)buf);
+}
+
+static DMAP_UINT16 readBigEndian_UINT16(const void *buf, size_t size)
+{
+ FIXUP_ALIGNMENT(DMAP_UINT16);
+
+ return __Swap16(*(DMAP_UINT16*)buf);
+}
+
+static DMAP_INT32 readBigEndian_INT32(const void *buf, size_t size)
+{
+ FIXUP_ALIGNMENT(DMAP_INT32);
+
+ return __Swap32(*(DMAP_INT32*)buf);
+}
+
+static DMAP_UINT32 readBigEndian_UINT32(const void *buf, size_t size)
+{
+ FIXUP_ALIGNMENT(DMAP_UINT32);
+
+ return __Swap32(*(DMAP_UINT32*)buf);
+}
+
+static DMAP_INT64 readBigEndian_INT64(const void *buf, size_t size)
+{
+ DMAP_INT64 val;
+ FIXUP_ALIGNMENT(DMAP_INT64);
+
+ val = *(DMAP_INT64*)buf;
+ __SwapPtr64(&val);
+ return val;
+}
+
+static DMAP_UINT64 readBigEndian_UINT64(const void *buf, size_t size)
+{
+ DMAP_INT64 val;
+ FIXUP_ALIGNMENT(DMAP_UINT64);
+
+ val = *(DMAP_INT64*)buf;
+ __SwapPtr64(&val);
+ return val;
+}
+
+static dmap_contentCodeFOURCC read_fourcc(const void *buf, size_t size)
+{
+ const char *c = (char*)buf;
+ if (size != sizeof(dmap_contentCodeFOURCC))
+ FIXME("funny sized\n");
+ return MAKEFOURCC(c[0], c[1], c[2], c[3]);
+}
+
+static DMAP_VERSION read_version(const void *buf, size_t size)
+{
+ DMAP_VERSION v;
+ if (size != sizeof(DMAP_VERSION))
+ FIXME("funny sized\n");
+ v.v1 = readBigEndian_UINT16(buf, 2);
+ v.v2 = readBigEndian_UINT16((char*)buf+2, 2);
+
+ return v;
+}
+
+static char* read_string_withalloc(const void *buf, size_t size)
+{
+ char *str = (char*)malloc(size + 1);
+ strncpy(str, (char*)buf, size);
+ str[size] = 0;
+ return str;
+}
+
+#endif /* DAAP_READTYPES */
+
diff --git a/lib/libXDAAP/debug.c b/lib/libXDAAP/debug.c
new file mode 100644
index 0000000000..6e5dd4f3ae
--- /dev/null
+++ b/lib/libXDAAP/debug.c
@@ -0,0 +1,183 @@
+/* Copyright (c) David Hammerton 2003
+ *
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <memory.h>
+
+#include "debug.h"
+
+#define DEFAULT_DEBUG_CHANNEL "debug"
+
+static int tracesEnabled = 0;
+static int errEnabled = 1;
+static int fixmeEnabled = 1;
+
+/* returns -1 on failure, 0 on success */
+int daap_debug_init(const char *const debug_init_string)
+{
+ const char *p = debug_init_string;
+
+ while (*p)
+ {
+ char *class_or_channel;
+ int class_or_channel_size;
+ const char *end;
+ int the_switch;
+
+ switch (*p)
+ {
+ case '+': the_switch = 1;
+ break;
+ case '-': the_switch = 0;
+ break;
+ default:
+ goto err;
+ }
+
+ p++;
+ if (!p) goto err;
+ end = strchr(p, ',');
+ if (end) end--;
+ else end = p + strlen(p);
+
+ class_or_channel_size = end - p + 1;
+
+ class_or_channel = (char*)malloc(class_or_channel_size + 1);
+ strncpy(class_or_channel, p, class_or_channel_size);
+ class_or_channel[class_or_channel_size] = 0;
+
+ /* now check if it is a class */
+ if (strcmp(class_or_channel, "err") == 0)
+ errEnabled = the_switch;
+ else if (strcmp(class_or_channel, "fixme") == 0)
+ fixmeEnabled = the_switch;
+ else if (strcmp(class_or_channel, "trace") == 0)
+ tracesEnabled = the_switch;
+ else /* check channels */
+ {
+ FIXME("sorry, but currently you can only toggle debug classes. Not switching '%s'.\n",
+ class_or_channel);
+ }
+
+ p = end + 1;
+ if (*p == ',') p++;
+ };
+ return 0;
+err:
+ return -1;
+}
+
+static char *get_debug_class_str(enum __DEBUG_CLASS debug_class)
+{
+ switch (debug_class)
+ {
+ case __DEBUG_TRACE: return "trace";
+ case __DEBUG_ERR: return "err";
+ case __DEBUG_FIXME: return "fixme";
+ default: return "";
+ }
+}
+
+/* FIXME: don't care about channel for now */
+int debug_get_debugging(enum __DEBUG_CLASS debug_class, const char *debug_channel)
+{
+ switch (debug_class)
+ {
+ case __DEBUG_TRACE: return tracesEnabled;
+ case __DEBUG_ERR: return errEnabled;
+ case __DEBUG_FIXME: return fixmeEnabled;
+ default: return 0;
+ }
+}
+
+int debug_log(enum __DEBUG_CLASS debug_class, const char *module,
+ const char *function,
+ const int line, const char *format, ...)
+{
+ va_list valist;
+ int ret = 0;
+ char *debug_class_str = get_debug_class_str(debug_class);
+
+ va_start(valist, format);
+
+ ret += fprintf(stderr, "%s:%s:", debug_class_str, module);
+ ret += fprintf(stderr, "%s:%i ", function, line);
+
+ ret += vfprintf(stderr, format, valist);
+
+ va_end(valist);
+
+ return ret;
+}
+
+int debug_printf(const char *format, ...)
+{
+ va_list valist;
+ int ret = 0;
+
+ va_start(valist, format);
+
+ ret += vfprintf(stderr, format, valist);
+
+ va_end(valist);
+
+ return ret;
+}
+
+void debug_hexdump(void *data, unsigned long len)
+{
+ unsigned int i;
+ char *cdata = (char *)data;
+ char textout[16];
+
+ for (i = 0; i < len; i++)
+ {
+ if ((i % 8) == 0 && i) fprintf(stderr, " ");
+ if ((i % 16) == 0 && i) fprintf(stderr, " '%.8s' '%.8s'\n", &textout[0], &textout[8]);
+ textout[i % 16] = (cdata[i] && isprint(cdata[i])) ? cdata[i] : '.';
+ fprintf(stderr, "%02hhx ", cdata[i]);
+ }
+ if (i % 16)
+ {
+ unsigned int j;
+ for (j = 0; j < (16 - (i % 16)); j++)
+ {
+ if (((16 - (i % 16)) - j) == 8) fprintf(stderr, " ");
+ fprintf(stderr, ".. ");
+ }
+ fprintf(stderr, " '");
+ for (j = 0; j < (i % 16); j++)
+ {
+ fprintf(stderr, "%c", textout[j]);
+ if (j == 8) fprintf(stderr, "' '");
+ }
+ fprintf(stderr, "'\n");
+ }
+ fprintf(stderr, "\n");
+}
+
diff --git a/lib/libXDAAP/debug.h b/lib/libXDAAP/debug.h
new file mode 100644
index 0000000000..653b209cab
--- /dev/null
+++ b/lib/libXDAAP/debug.h
@@ -0,0 +1,114 @@
+/* Copyright (c) David Hammerton, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef DEBUG_H
+#define DEBUG_H
+
+#include "portability.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum __DEBUG_CLASS
+{
+ __DEBUG_TRACE = 0,
+ __DEBUG_ERR,
+ __DEBUG_FIXME
+};
+
+/* exported from debug.c */
+int daap_debug_init(const char *const debug_init_string);
+
+int debug_log(enum __DEBUG_CLASS, const char *module,
+ const char *function,
+ const int line, const char *format, ...);
+int debug_printf(const char *format, ...);
+void debug_hexdump(void *data, unsigned long len);
+int debug_get_debugging(enum __DEBUG_CLASS debug_class, const char *debug_channel);
+
+#ifdef NO_DEBUG
+# define __GET_DEBUGGING(debug_class, debug_channel) 0
+#else
+# define __GET_DEBUGGING(debug_class, debug_channel) \
+ debug_get_debugging(__DEBUG##debug_class, debug_channel)
+#endif
+
+#ifdef SYSTEM_POSIX
+
+#define __DEBUG_LOG(debug_class, debug_channel) \
+ do { if (__GET_DEBUGGING(debug_class, debug_channel)) { \
+ const char * const __channel = debug_channel; \
+ const enum __DEBUG_CLASS __class = __DEBUG##debug_class; \
+ __DEBUG_LOG_2
+
+#define __DEBUG_LOG_2(args...) \
+ debug_log(__class, __channel, __FUNCTION__, __LINE__, args); } } while(0)
+
+#define DPRINTF(args...) \
+ debug_printf(args)
+
+#elif defined(SYSTEM_WIN32)
+/* does not support variadic macros (visual c++) */
+
+#define __DEBUG_LOG(debug_class, debug_channel) \
+ (!__GET_DEBUGGING(debug_class, debug_channel) || \
+ (debug_log(__DEBUG##debug_class, debug_channel, __FUNCTION__, __LINE__, "")) ? \
+ (void)0 : (void)debug_printf
+
+#define DPRINTF debug_printf
+
+#endif
+
+#define TRACE __DEBUG_LOG(_TRACE, DEFAULT_DEBUG_CHANNEL)
+#define TRACE_ON __GET_DEBUGGING(_TRACE, DEFAULT_DEBUG_CHANNEL)
+
+#define ERR __DEBUG_LOG(_ERR, DEFAULT_DEBUG_CHANNEL)
+#define ERR_ON __GET_DEBUGGING(_ERR, DEFAULT_DEBUG_CHANNEL)
+
+#define FIXME __DEBUG_LOG(_FIXME, DEFAULT_DEBUG_CHANNEL)
+#define FIXME_ON __GET_DEBUGGING(_FIXME, DEFAULT_DEBUG_CHANNEL)
+
+
+#if defined(WIN32)
+
+#undef TRACE
+#define TRACE printf
+
+#undef ERR
+#define ERR printf
+
+#undef FIXME
+#define FIXME printf
+
+#undef DPRINTF
+#define DPRINTF printf
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DEBUG_H */
diff --git a/lib/libXDAAP/dmap_generics.c b/lib/libXDAAP/dmap_generics.c
new file mode 100644
index 0000000000..ecb5d8b5e4
--- /dev/null
+++ b/lib/libXDAAP/dmap_generics.c
@@ -0,0 +1,280 @@
+// Place the code and data below here into the LIBXDAAP section.
+#ifndef __GNUC__
+#pragma code_seg( "LIBXDAAP_TEXT" )
+#pragma data_seg( "LIBXDAAP_DATA" )
+#pragma bss_seg( "LIBXDAAP_BSS" )
+#pragma const_seg( "LIBXDAAP_RD" )
+#endif
+
+/* dmap generic defintions
+ *
+ * Copyright (c) 2004 David Hammerton
+ *
+ * This file implements some generic protocol containers
+ * and utility functions for them.
+ * See comments on the individual segments for more details.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "portability.h"
+
+#include "debug.h"
+#include "daap.h"
+#include "daap_contentcodes.h"
+#include "dmap_generics.h"
+
+#define DEFAULT_DEBUG_CHANNEL "daap"
+
+#include "daap_readtypes.h"
+
+#define UNHANDLED_CONTENT_CODE \
+ do { ERR("unhandled content code [%c%c%c%c]\n", \
+ SPLITFOURCC(code) \
+ ); } while(0)
+
+
+/* util functions for accessing dmapGenericContainer */
+dmap_GenericType dmapGeneric_LookupContainerItem(dmapGenericContainer *c,
+ const dmap_ContentCode *code)
+{
+ dmap_GenericType invalidGeneric = { {0}, DMAP_DATATYPE_INVALID };
+ dmapGenericItem *cur = c->head;
+ while (cur)
+ {
+ if (code->cc_number == cur->cc)
+ return cur->data;
+ cur = cur->next;
+ }
+ return invalidGeneric;
+}
+
+void dmapGeneric_DumpContainerCCs(dmapGenericContainer *c)
+{
+ dmapGenericItem *cur = c->head;
+ while (cur)
+ {
+ TRACE("cc: %c%c%c%c\n", SPLITFOURCC(cur->cc));
+ cur = cur->next;
+ }
+}
+
+#define IMPLEMENT_LOOKUPCONTAINERITEM_TYPE(type) \
+dmap_DataTypes dmapGeneric_LookupContainerItem_##type( \
+ dmapGenericContainer *c, const dmap_ContentCode *code, \
+ DMAP_##type *out) \
+{ \
+ dmap_GenericType r; \
+ r = dmapGeneric_LookupContainerItem(c, code); \
+ return dmap_genericTo##type(r, out); \
+}
+IMPLEMENT_LOOKUPCONTAINERITEM_TYPE(INT8)
+IMPLEMENT_LOOKUPCONTAINERITEM_TYPE(UINT8)
+IMPLEMENT_LOOKUPCONTAINERITEM_TYPE(INT16)
+IMPLEMENT_LOOKUPCONTAINERITEM_TYPE(UINT16)
+IMPLEMENT_LOOKUPCONTAINERITEM_TYPE(INT32)
+IMPLEMENT_LOOKUPCONTAINERITEM_TYPE(UINT32)
+IMPLEMENT_LOOKUPCONTAINERITEM_TYPE(INT64)
+IMPLEMENT_LOOKUPCONTAINERITEM_TYPE(UINT64)
+IMPLEMENT_LOOKUPCONTAINERITEM_TYPE(STRING)
+IMPLEMENT_LOOKUPCONTAINERITEM_TYPE(TIME)
+IMPLEMENT_LOOKUPCONTAINERITEM_TYPE(VERSION)
+#undef IMPLEMENT_LOOKUPCONTAINERITEM_TYPE
+
+/* CONTAINER GENERIC
+ * This is used by (at least) daap.serverdatabases and
+ * daap.databasesongs - both from /databases urls.
+ *
+ * This is basically any container that contains the following:
+ * dmap.status
+ * dmap.updatetype
+ * dmap.specifiedtotalcount
+ * dmap.returnedcount
+ * dmap.listing
+ * This one is then a bit more complex, it is a container
+ * that contains a whole bunch of dmap.listingitems (yet another
+ * container).
+ * Now this is where things get odd - dmap.listingitems can
+ * contain (highly) variable sub items.
+ */
+
+void freeGenericContainer(dmapGenericContainer *gc)
+{
+ dmapGenericItem *item = gc->head;
+ while (item)
+ {
+ dmapGenericItem *next = item->next;
+ if (item->data.type == DMAP_DATATYPE_STRING)
+ free(item->data.u.string);
+ free(item);
+ item = next;
+ }
+}
+
+void freeGenericPreListing(protoParseResult_genericPreListing *prelist)
+{
+ int i;
+ for (i = 0; i < prelist->returnedcount; i++)
+ {
+ dmapGenericContainer *item = &(prelist->listitems[i]);
+ freeGenericContainer(item);
+ }
+ free(prelist->listitems);
+}
+
+static void listitemGenericContainer(dmap_contentCodeFOURCC code,
+ const int size, const char *buffer,
+ void *scopeData)
+{
+ dmapGenericContainer *gc = (dmapGenericContainer*)scopeData;
+ const dmap_ContentCode *cc;
+ dmap_DataTypes type = DMAP_DATATYPE_INVALID;
+ dmapGenericItem *newItem;
+
+ if ((cc = dmap_lookupCodeFromFOURCC(&dmap_table, code)))
+ {
+ type = cc->cc_type;
+ }
+ else if ((cc = dmap_lookupCodeFromFOURCC(&daap_table, code)))
+ {
+ type = cc->cc_type;
+ }
+ else if ((cc = dmap_lookupCodeFromFOURCC(&com_table, code)))
+ {
+ type = cc->cc_type;
+ }
+ if (type == DMAP_DATATYPE_INVALID || type == DMAP_DATATYPE_CONTAINER)
+ {
+ UNHANDLED_CONTENT_CODE;
+ return;
+ }
+// dmapGenericItem *newItem = (dmapGenericItem *) malloc(sizeof(dmapGenericItem));
+ newItem = malloc(sizeof(dmapGenericItem));
+ newItem->cc = code;
+ newItem->data.type = type;
+ switch(type)
+ {
+ case DMAP_DATATYPE_INT8:
+ newItem->data.u.int8 = readBigEndian_INT8(buffer, size);
+ break;
+ case DMAP_DATATYPE_UINT8:
+ newItem->data.u.uint8 = readBigEndian_UINT8(buffer, size);
+ break;
+ case DMAP_DATATYPE_INT16:
+ newItem->data.u.int16 = readBigEndian_INT16(buffer, size);
+ break;
+ case DMAP_DATATYPE_UINT16:
+ newItem->data.u.uint16 = readBigEndian_UINT16(buffer, size);
+ break;
+ case DMAP_DATATYPE_INT32:
+ newItem->data.u.int32 = readBigEndian_INT32(buffer, size);
+ break;
+ case DMAP_DATATYPE_UINT32:
+ newItem->data.u.uint32 = readBigEndian_UINT32(buffer, size);
+ break;
+ case DMAP_DATATYPE_INT64:
+ newItem->data.u.int64 = readBigEndian_INT64(buffer, size);
+ break;
+ case DMAP_DATATYPE_UINT64:
+ newItem->data.u.uint64 = readBigEndian_UINT64(buffer, size);
+ break;
+ case DMAP_DATATYPE_STRING:
+ newItem->data.u.string = read_string_withalloc(buffer, size);
+ break;
+ case DMAP_DATATYPE_VERSION:
+ newItem->data.u.version = read_version(buffer, size);
+ break;
+ case DMAP_DATATYPE_TIME:
+ FIXME("read time\n");
+ case DMAP_DATATYPE_INVALID:
+ case DMAP_DATATYPE_CONTAINER:
+ TRACE("can't handle this type\n");
+ free(newItem);
+ return;
+ }
+ newItem->next = gc->head;
+ gc->head = newItem;
+}
+
+static void listingContainer(dmap_contentCodeFOURCC code,
+ const int size, const char *buffer,
+ void *scopeData)
+{
+ protoParseResult_genericPreListing* sd =
+ (protoParseResult_genericPreListing*)scopeData;
+ if (dmap_isCC(code, dmap_l("listingitem")) == DMAP_DATATYPE_CONTAINER)
+ {
+ dmap_parseContainer(listitemGenericContainer, size, buffer,
+ &(sd->listitems[sd->curIndex]));
+ sd->curIndex++;
+ }
+ else
+ UNHANDLED_CONTENT_CODE;
+}
+
+void preListingContainer(dmap_contentCodeFOURCC code,
+ const int size, const char *buffer,
+ void *scopeData)
+{
+ protoParseResult_genericPreListing* sd =
+ (protoParseResult_genericPreListing*)scopeData;
+ if (dmap_isCC(code, dmap_l("status")) == DMAP_DATATYPE_INT32)
+ {
+ DMAP_INT32 status = readBigEndian_INT32(buffer, size);
+ if (status != 200)
+ FIXME("unknown status code %i\n", status);
+ }
+ else if (dmap_isCC(code, dmap_l("updatetype")) == DMAP_DATATYPE_INT8)
+ {
+ DMAP_INT8 updatetype = readBigEndian_INT8(buffer, size);
+ if (updatetype != 0)
+ FIXME("unknown updatetype %i\n", updatetype);
+ }
+ else if (dmap_isCC(code, dmap_l("specifiedtotalcount")) == DMAP_DATATYPE_INT32)
+ {
+ sd->totalcount = readBigEndian_INT32(buffer, size);
+ }
+ else if (dmap_isCC(code, dmap_l("returnedcount")) == DMAP_DATATYPE_INT32)
+ {
+ sd->returnedcount = readBigEndian_INT32(buffer, size);
+ }
+ else if (dmap_isCC(code, dmap_l("listing")) == DMAP_DATATYPE_CONTAINER)
+ {
+ /* FIXME: need a way to ensure sd->returnedcount has been set */
+ sd->curIndex = 0;
+ if (sd->returnedcount) /* eh? mt-daapd sets to 0 */
+ {
+ /* needs to be inited to 0 - linked list uses head for next */
+ sd->listitems = (dmapGenericContainer*)calloc(sd->returnedcount, sizeof(dmapGenericContainer));
+ dmap_parseContainer(listingContainer, size, buffer, (void*)sd);
+ }
+ else sd->listitems = NULL;
+ }
+ else
+ UNHANDLED_CONTENT_CODE;
+
+}
+
diff --git a/lib/libXDAAP/dmap_generics.h b/lib/libXDAAP/dmap_generics.h
new file mode 100644
index 0000000000..bbde022d19
--- /dev/null
+++ b/lib/libXDAAP/dmap_generics.h
@@ -0,0 +1,103 @@
+/* dmap
+ *
+ * generic definitions
+ *
+ * Copyright (c) 2004 David Hammerton
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "daap.h"
+
+/* anonymous container */
+typedef struct _dmapGenericItem dmapGenericItem;
+
+struct _dmapGenericItem
+{
+ dmap_contentCodeFOURCC cc;
+ dmap_GenericType data;
+ dmapGenericItem *next;
+};
+
+typedef struct
+{
+ struct _dmapGenericItem *head;
+} dmapGenericContainer;
+
+void freeGenericContainer(dmapGenericContainer *gc);
+
+void dmapGeneric_DumpContainerCCs(dmapGenericContainer *c);
+
+dmap_GenericType dmapGeneric_LookupContainerItem(dmapGenericContainer *c,
+ const dmap_ContentCode *code);
+
+dmap_DataTypes dmapGeneric_LookupContainerItem_INT8(
+ dmapGenericContainer *c, const dmap_ContentCode *code,
+ DMAP_INT8 *out);
+dmap_DataTypes dmapGeneric_LookupContainerItem_UINT8(
+ dmapGenericContainer *c, const dmap_ContentCode *code,
+ DMAP_UINT8 *out);
+dmap_DataTypes dmapGeneric_LookupContainerItem_INT16(
+ dmapGenericContainer *c, const dmap_ContentCode *code,
+ DMAP_INT16 *out);
+dmap_DataTypes dmapGeneric_LookupContainerItem_UINT16(
+ dmapGenericContainer *c, const dmap_ContentCode *code,
+ DMAP_UINT16 *out);
+dmap_DataTypes dmapGeneric_LookupContainerItem_INT32(
+ dmapGenericContainer *c, const dmap_ContentCode *code,
+ DMAP_INT32 *out);
+dmap_DataTypes dmapGeneric_LookupContainerItem_UINT32(
+ dmapGenericContainer *c, const dmap_ContentCode *code,
+ DMAP_UINT32 *out);
+dmap_DataTypes dmapGeneric_LookupContainerItem_INT64(
+ dmapGenericContainer *c, const dmap_ContentCode *code,
+ DMAP_INT64 *out);
+dmap_DataTypes dmapGeneric_LookupContainerItem_UINT64(
+ dmapGenericContainer *c, const dmap_ContentCode *code,
+ DMAP_UINT64 *out);
+dmap_DataTypes dmapGeneric_LookupContainerItem_STRING(
+ dmapGenericContainer *c, const dmap_ContentCode *code,
+ DMAP_STRING *out);
+dmap_DataTypes dmapGeneric_LookupContainerItem_TIME(
+ dmapGenericContainer *c, const dmap_ContentCode *code,
+ DMAP_TIME *out);
+dmap_DataTypes dmapGeneric_LookupContainerItem_VERSION(
+ dmapGenericContainer *c, const dmap_ContentCode *code,
+ DMAP_VERSION *out);
+
+/* listing container */
+
+typedef struct
+{
+ protoParseResult h;
+ DMAP_INT32 totalcount;
+ DMAP_INT32 returnedcount;
+ dmapGenericContainer *listitems;
+ int curIndex; /* used for building */
+} protoParseResult_genericPreListing;
+
+void preListingContainer(dmap_contentCodeFOURCC code,
+ const int size, const char *buffer,
+ void *scopeData);
+
+void freeGenericPreListing(protoParseResult_genericPreListing *prelist);
+
diff --git a/lib/libXDAAP/endian_swap.h b/lib/libXDAAP/endian_swap.h
new file mode 100644
index 0000000000..90fd773c99
--- /dev/null
+++ b/lib/libXDAAP/endian_swap.h
@@ -0,0 +1,84 @@
+/* endian swaps
+ *
+ * Copyright (c) 2004 David Hammerton
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef ENDIAN_SWAP_H_
+#define ENDIAN_SWAP_H_
+
+#include "portability.h"
+
+#ifdef WORDS_BIGENDIAN
+
+#define __Swap16(v) (v)
+#define __Swap32(v) (v)
+#define __Swap64(v) (v)
+#define __SwapPtr64(v)
+
+#else /* WORDS_BIGENDIAN */
+
+#define __Swap16(v) ((((v) & 0x00FF) << 0x08) | \
+ (((v) & 0xFF00) >> 0x08))
+#define __Swap32(v) ((((v) & 0x000000FF) << 0x18) | \
+ (((v) & 0x0000FF00) << 0x08) | \
+ (((v) & 0x00FF0000) >> 0x08) | \
+ (((v) & 0xFF000000) >> 0x18))
+/* Microsft Visual C++ doesn't like this */
+/*
+#define __Swap64(v) ((((v) & 0x00000000000000FFLL) >> 0x38) | \
+ (((v) & 0x000000000000FF00LL) >> 0x28) | \
+ (((v) & 0x0000000000FF0000LL) >> 0x18) | \
+ (((v) & 0x00000000FF000000LL) >> 0x08) | \
+ (((v) & 0x000000FF00000000LL) << 0x08) | \
+ (((v) & 0x0000FF0000000000LL) << 0x18) | \
+ (((v) & 0x00FF000000000000LL) << 0x28) | \
+ (((v) & 0xFF00000000000000LL) << 0x38))*/
+static void __SwapPtr64(int64_t *v)
+{
+ unsigned char b, *bv;
+
+ bv = (unsigned char*)v;
+
+ b = bv[0];
+ bv[0] = bv[7];
+ bv[7] = b;
+
+ b = bv[1];
+ bv[1] = bv[6];
+ bv[6] = b;
+
+ b = bv[2];
+ bv[2] = bv[5];
+ bv[5] = b;
+
+ b = bv[3];
+ bv[3] = bv[4];
+ bv[4] = b;
+}
+
+#endif /* WORDS_BIGENDIAN */
+
+
+#endif /* ENDIAN_SWAP_H */
+
diff --git a/lib/libXDAAP/global.c b/lib/libXDAAP/global.c
new file mode 100644
index 0000000000..fef8f3fada
--- /dev/null
+++ b/lib/libXDAAP/global.c
@@ -0,0 +1,97 @@
+// Place the code and data below here into the LIBXDAAP section.
+#ifndef __GNUC__
+#pragma code_seg( "LIBXDAAP_TEXT" )
+#pragma data_seg( "LIBXDAAP_DATA" )
+#pragma bss_seg( "LIBXDAAP_BSS" )
+#pragma const_seg( "LIBXDAAP_RD" )
+#endif
+
+/*
+ *
+ * Copyright (c) 2003 David Hammerton
+ * crazney@crazney.net
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "portability.h"
+
+#ifdef SYSTEM_POSIX
+#include <sys/time.h>
+#include <time.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+/* PRIVATE */
+
+/* globals, tools, etc. */
+
+/* returns time since its first call in milliseconds */
+unsigned int CP_GetTickCount()
+{
+#ifdef SYSTEM_POSIX
+
+ static int startSeconds = -1;
+ struct timeval tv;
+
+ unsigned int ticks = 0;
+
+ gettimeofday(&tv, NULL);
+ if (startSeconds < 0)
+ startSeconds = tv.tv_sec;
+ ticks += ((tv.tv_sec - startSeconds) * 1000);
+ ticks += tv.tv_usec / 1000;
+ return ticks;
+
+#elif defined(SYSTEM_WIN32)
+ return (unsigned int)GetTickCount();
+#else
+#error IMPLEMENT ME
+#endif
+}
+
+
+/* we use this a fair bit */
+
+char *safe_sprintf(const char *format, ...)
+{
+ va_list valist;
+ char *str;
+ int len;
+
+ va_start(valist, format);
+ len = vsnprintf(NULL, 0, format, valist);
+ va_end(valist);
+
+ str = malloc(len + 1);
+
+ va_start(valist, format);
+ vsnprintf(str, len + 1, format, valist);
+ va_end(valist);
+
+ return str;
+}
+
+
diff --git a/lib/libXDAAP/httpClient.c b/lib/libXDAAP/httpClient.c
new file mode 100644
index 0000000000..7d6399a63a
--- /dev/null
+++ b/lib/libXDAAP/httpClient.c
@@ -0,0 +1,1114 @@
+// Place the code and data below here into the LIBXDAAP section.
+#ifndef __GNUC__
+#pragma code_seg( "LIBXDAAP_TEXT" )
+#pragma data_seg( "LIBXDAAP_DATA" )
+#pragma bss_seg( "LIBXDAAP_BSS" )
+#pragma const_seg( "LIBXDAAP_RD" )
+#endif
+
+/* HTTP client requests
+ * (mostly GET)
+ *
+ * Copyright (c) 2004 David Hammerton
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/*
+ * Port to XBMC by Forza of the XBMC Media Center development team
+ *
+ * 3rd July 2004
+ *
+ */
+
+#include "libXDAAP.h"
+
+
+#include "portability.h"
+#include "thread.h"
+
+#ifndef WIN32
+#include "ioloop.h"
+#endif
+
+#include "httpClient.h"
+#include "debug.h"
+
+#define DEFAULT_DEBUG_CHANNEL "http_client"
+
+#ifdef _LINUX
+typedef int SOCKET;
+#include <netdb.h>
+#include <netinet/in.h>
+#endif
+
+struct HTTP_ConnectionTAG
+{
+ char *host;
+ char *password;
+ SOCKET sockfd;
+};
+
+typedef struct HTTP_HeaderTAG HTTP_Header;
+struct HTTP_HeaderTAG
+{
+ char *field_name;
+ char *field_value;
+ HTTP_Header *next;
+};
+
+/* when we get a song with iTunes, we have to create a new
+ * HTTP connection. It seems to expect the HTTP connections
+ * source ports to be seperated by 2. I'm guessing this is
+ * because if the source port is X then it expects some
+ * audiocast thing to be at X-1. (which we don't support).
+ */
+static void bind_socket(int sockfd)
+{
+ static int port = 1050; /* start here */
+
+ struct sockaddr_in saddrBindingSocket;
+
+ int bound = 0;
+ int times = 0;
+
+ saddrBindingSocket.sin_family = AF_INET;
+ saddrBindingSocket.sin_addr.s_addr = INADDR_ANY;
+
+ do
+ {
+ int res;
+
+ saddrBindingSocket.sin_port = htons(port);
+ res = bind(sockfd, (struct sockaddr *)&saddrBindingSocket,
+ sizeof(saddrBindingSocket));
+ if (res == 0) bound = 1;
+ port++;
+ } while (!bound && (times++ < 20));
+}
+
+
+static SOCKET HTTP_Connect(const char *host, const char *port)
+{
+#ifdef WIN32
+ SOCKET sockfd;
+ struct sockaddr_in sa;
+
+ memset(&sa, 0, sizeof (sa));
+ sa.sin_port = htons(atoi(port));
+ sa.sin_family = AF_INET;
+ if (inet_addr(host) == INADDR_NONE)
+ {
+ return -1;
+ }
+ else
+ {
+ sa.sin_addr.s_addr = inet_addr(host);
+ }
+
+ sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (sockfd < 0) return -1;
+
+ if (connect(sockfd, (struct sockaddr *)&sa, sizeof (sa)) != 0)
+ {
+ DAAP_SOCKET_CLOSE(sockfd);
+ return -1;
+ }
+
+ return sockfd;
+#else
+ int sockfd, ret;
+ struct addrinfo hints, *res, *ressave;
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ ret = getaddrinfo(host, port, &hints, &res);
+
+ if (ret)
+ {
+ ERR("getaddrinfo error: [%s] (%s)\n",
+ gai_strerror(ret), host);
+ return -1;
+ }
+
+ ressave = res;
+ sockfd = -1;
+
+ while (res)
+ {
+ sockfd = socket(res->ai_family,
+ res->ai_socktype,
+ res->ai_protocol);
+ if (sockfd >= 0)
+ {
+ bind_socket(sockfd);
+
+ if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0)
+ break;
+
+#ifdef SYSTEM_WIN32
+ closesocket(sockfd);
+#else
+ close(sockfd);
+#endif
+ sockfd = -1;
+ }
+ res = res->ai_next;
+ }
+
+ freeaddrinfo(ressave);
+
+ return sockfd;
+#endif
+}
+
+static void HTTP_Close(SOCKET sockfd)
+{
+ DAAP_SOCKET_CLOSE(sockfd);
+ //close(sockfd);
+}
+
+HTTP_Connection *HTTP_Client_Open(const char *host, const char *password)
+{
+ HTTP_Connection *new = NULL;
+ int sockfd;
+ char *port_str;
+ char *host_cpy = NULL;
+
+ port_str = strchr(host, ':');
+ if (port_str)
+ {
+ char *c;
+ host_cpy = strdup(host);
+ c = strchr(host_cpy, ':');
+ c[0] = 0;
+
+ port_str++;
+ }
+ if (port_str && !port_str[0])
+ port_str = NULL;
+ if (!port_str)
+ port_str = "3689";
+
+ sockfd = HTTP_Connect(host_cpy ? host_cpy : host, port_str);
+
+ if (sockfd == -1) goto end;
+
+ new = malloc(sizeof(HTTP_Connection));
+ new->sockfd = sockfd;
+ new->host = malloc(strlen(host)+1);
+ strcpy(new->host, host_cpy ? host_cpy : host);
+
+ if (password)
+ new->password = strdup(password);
+ else
+ new->password = NULL;
+
+end:
+ if (host_cpy) free(host_cpy);
+ return new;
+}
+
+static void HTTP_Client_Reset(HTTP_Connection *connection)
+{
+ HTTP_Close(connection->sockfd);
+ connection->sockfd = HTTP_Connect(connection->host, "3689");
+ if (connection->sockfd == -1) ERR("unhandled error\n");
+}
+
+void HTTP_Client_Close(HTTP_Connection *connection)
+{
+ HTTP_Close(connection->sockfd);
+ if (connection->password) free(connection->password);
+ free(connection->host);
+ free(connection);
+}
+
+#define IS_CRLF(p) ((*p) == '\n' || (*p) == '\r')
+
+static int HTTP_ProcessHeaders(char *curin, int curlen,
+ char **curout,
+ int *requiredExtraLen,
+ char **contentout, int *contentoutlen,
+ HTTP_Header **headers)
+{
+ char *curHeaderStart = curin;
+ int curHeaderLen = 0;
+ int n = 0;
+
+ *requiredExtraLen = 0;
+ *contentout = NULL;
+
+ while (n < curlen)
+ {
+ HTTP_Header *header;
+ int i;
+ /* FIXME: there is a bug here. if the previous header ends on a single
+ * \r, then the next header this will trigger with the \n and we'll
+ * quit early :(
+ */
+ if (IS_CRLF(curHeaderStart))
+ { /* end of header */
+ char *content = curHeaderStart + 1;
+
+ if (n < curlen-1)
+ {
+ if (IS_CRLF(content))
+ {
+ content++;
+ n++;
+ }
+ *contentout = content;
+ *contentoutlen = curlen - n - 1;
+ }
+ return 0;
+ }
+ while (!IS_CRLF(&curHeaderStart[curHeaderLen]))
+ {
+ curHeaderLen++;
+ n++;
+ if (n >= curlen)
+ {
+ if (curHeaderStart == curin)
+ *requiredExtraLen = 1;
+ *curout = curHeaderStart;
+ return 1;
+ }
+ }
+ n++;
+ curHeaderLen++;
+ if (IS_CRLF(&curin[n]))
+ n++;
+
+ /* sizeof(HTTP_Header) + curHeaderLen + \0 + \0 - ':' - ' '
+ * + (possibly a \0 if its not a real header) = 1 */
+ header = malloc(sizeof(HTTP_Header) + curHeaderLen + 1);
+
+ i = 0;
+ header->field_name = (char*)header + sizeof(HTTP_Header);
+ while (curHeaderStart[i] != ':' && i < curHeaderLen)
+ {
+ header->field_name[i] = curHeaderStart[i];
+ i++;
+ }
+
+ if (curHeaderStart[i] == ':')
+ {
+ header->field_name[i] = 0;
+ header->field_value = (char*)header + sizeof(HTTP_Header) +
+ strlen(header->field_name) + 1;
+ strncpy(header->field_value, &curHeaderStart[i+2],
+ curHeaderLen - i - 2);
+ header->field_value[curHeaderLen - i - 3] = 0;
+ }
+ else
+ {
+ header->field_name[curHeaderLen-1] = 0;
+ header->field_value = NULL;
+ }
+ header->next = NULL;
+
+ /* we want to store it at the tail, but we are given the head */
+ if (!(*headers))
+ {
+ *headers = header;
+ }
+ else
+ {
+ HTTP_Header *curIter = *headers;
+ while (curIter->next)
+ {
+ curIter = curIter->next;
+ }
+ curIter->next = header;
+ }
+
+ curHeaderStart = &curin[n];
+ curHeaderLen = 0;
+ }
+ *curout = NULL;
+ return 1;
+}
+
+int HTTP_PassStandardHeaders(HTTP_Header *headersList,
+ int *httpContentLength)
+{
+ int httpStatusCode;
+ HTTP_Header *cur;
+
+ *httpContentLength = 0;
+
+ /* first get the first header, this will be status code */
+ if (headersList->field_value == NULL &&
+ strncmp(headersList->field_name, "HTTP/1.1 ", 8) == 0)
+ {
+ TRACE("http status line: '%s'\n", headersList->field_name);
+ httpStatusCode = atoi(headersList->field_name + 8);
+ }
+ else
+ {
+ ERR("HTTP status code wan't first\n");
+ return 0;
+ }
+
+ /* now find content length (then return) */
+ cur = headersList;
+ while (cur)
+ {
+ if (strcmp(cur->field_name, "Content-Length") == 0)
+ {
+ *httpContentLength = atoi(cur->field_value);
+ return httpStatusCode;
+ }
+ cur = cur->next;
+ }
+
+ return httpStatusCode;
+}
+
+#define METHOD_GET "GET "
+#define HTTP_VERSION " HTTP/1.1\r\n"
+#define HEADER_HOST "Host: "
+#define DAAP_VERSION "\r\nClient-DAAP-Version: 3.0\r\n"
+#define HEADER_USERAGENT "User-Agent: iTunes/4.6 (Windows; N)\r\n"
+#define HEADER_ACCEPT "Accept-Language: en-us, en;q=5.0\r\n"
+#define DAAP_VALIDATE "Client-DAAP-Validation: "
+#define DAAP_ACCESS_INDEX "Client-DAAP-Access-Index: 2\r\n"
+#define ACCEPT_ENCODING "Accept-Encoding: gzip\r\n"
+#define CONNECTION_CLOSE "Connection: close\r\n"
+#define HEADER_PASSWD "Authorization: Basic "
+#define END_REQUEST "\r\n"
+
+static int HTTP_Client_RequestGet(const HTTP_Connection *connection,
+ const char *path, const char *hash,
+ const char *extra_header,
+ int send_close)
+{
+ int buffer_size = 0;
+ char *buffer = NULL;
+ char *buffersend = NULL;
+ int tosend;
+ int ret, res = 1;
+
+ buffer_size += strlen(METHOD_GET);
+ buffer_size += strlen(path);
+ buffer_size += strlen(HTTP_VERSION);
+ buffer_size += strlen(HEADER_HOST);
+ buffer_size += strlen(connection->host);
+ buffer_size += strlen(DAAP_VERSION);
+ buffer_size += strlen(HEADER_USERAGENT);
+ buffer_size += strlen(HEADER_ACCEPT);
+ if (send_close)
+ buffer_size += strlen(CONNECTION_CLOSE);
+ buffer_size += strlen(DAAP_ACCESS_INDEX);
+ if (hash)
+ {
+ buffer_size += strlen(DAAP_VALIDATE);
+ buffer_size += strlen(hash);
+ buffer_size += strlen(END_REQUEST);
+ }
+ if (connection->password)
+ {
+ buffer_size += strlen(HEADER_PASSWD);
+ buffer_size += strlen(connection->password);
+ buffer_size += strlen(END_REQUEST);
+ }
+// buffer_size += strlen(ACCEPT_ENCODING);
+ buffer_size += strlen(END_REQUEST);
+ if (extra_header) buffer_size += strlen(extra_header);
+ buffer_size += 1;
+
+ buffer = malloc(buffer_size);
+ buffer[0] = 0;
+ strcat(buffer, METHOD_GET);
+ strcat(buffer, path);
+ strcat(buffer, HTTP_VERSION);
+ strcat(buffer, HEADER_HOST);
+ strcat(buffer, connection->host);
+ strcat(buffer, DAAP_VERSION);
+ strcat(buffer, HEADER_USERAGENT);
+ strcat(buffer, HEADER_ACCEPT);
+ strcat(buffer, DAAP_ACCESS_INDEX);
+ if (hash)
+ {
+ strcat(buffer, DAAP_VALIDATE);
+ strcat(buffer, hash);
+ strcat(buffer, END_REQUEST);
+ }
+// strcat(buffer, ACCEPT_ENCODING);
+ if (extra_header) strcat(buffer, extra_header);
+ if (send_close)
+ strcat(buffer, CONNECTION_CLOSE);
+ if (connection->password)
+ {
+ strcat(buffer, HEADER_PASSWD);
+ strcat(buffer, connection->password);
+ strcat(buffer, END_REQUEST);
+ }
+ /* just for printing. we shouldn't send this; */
+ strcat(buffer, END_REQUEST);
+ buffer[buffer_size-1] = 0;
+
+ TRACE("about to send something of size %i\n", buffer_size);
+ if (TRACE_ON) DPRINTF("%s\n\n", buffer);
+#if 0
+ debug_hexdump(buffer, buffer_size);
+#endif
+
+ buffersend = buffer;
+ tosend = buffer_size - 1;
+ while (tosend)
+ {
+ ret = DAAP_SOCKET_WRITE(connection->sockfd, buffersend, tosend);
+ if (ret == -1)
+ {
+/* ERR("send error: [%s]\n", strerror(errno));*/
+ res = 0;
+ goto end;
+ }
+ tosend -= ret;
+ buffersend -= ret;
+ }
+
+end:
+ free(buffer);
+ return res;
+}
+
+static char *HTTP_Client_ReadHeaders(const HTTP_Connection *connection,
+ HTTP_Header **headersList,
+ char **contentFromHeaders,
+ int *contentLenFromHeaders)
+{
+ char *buffer = NULL;
+ int buffer_size;
+ int ret;
+ char *headers;
+ int headers_needmore = 0;
+
+ buffer_size = 1000;
+ buffer = malloc(buffer_size);
+ headers = NULL;
+ do
+ {
+ int read_max = buffer_size;
+ if (headers)
+ {
+ char *new;
+ int required_copy;
+
+ required_copy = buffer_size - (headers - buffer);
+ if (headers_needmore)
+ buffer_size *= 2;
+ new = malloc(buffer_size);
+ memcpy(new, headers, required_copy);
+
+ free(buffer);
+ buffer = new;
+
+ headers = buffer + required_copy;
+ read_max = buffer_size - required_copy;
+ }
+ else headers = buffer;
+ ret = DAAP_SOCKET_READ(connection->sockfd, headers, read_max);
+ if (ret == -1)
+ {
+ ERR("an error occured on recv!\n");
+ free(buffer);
+ goto end;
+ }
+ //debug_hexdump(buffer, ret);
+ }
+ while (HTTP_ProcessHeaders(buffer, ret,
+ &headers,
+ &headers_needmore,
+ contentFromHeaders, contentLenFromHeaders,
+ headersList));
+
+ if (!*headersList)
+ goto end;
+
+ return buffer;
+end:
+ free(buffer);
+ return NULL;
+
+}
+
+HTTP_GetResult *HTTP_Client_Get(HTTP_Connection *connection,
+ const char *path,
+ const char *hash,
+ const char *extra_header,
+ int reset_send_close)
+{
+ char *databuffer = NULL;
+ int torecv;
+ int ret;
+ char *headerbuffer = NULL;
+
+ char *contentFromHeaders;
+ int contentLenFromHeaders;
+
+ HTTP_Header *headersList = NULL;
+ HTTP_GetResult *result = NULL;
+
+ int httpStatusCode;
+ int httpContentLength;
+
+ if (!HTTP_Client_RequestGet(connection, path, hash,
+ extra_header, reset_send_close))
+ goto end;
+
+
+ if (!(headerbuffer = HTTP_Client_ReadHeaders(connection, &headersList,
+ &contentFromHeaders, &contentLenFromHeaders)))
+ {
+ ERR("failed to recieve any headers\n");
+ goto end;
+ }
+
+ httpStatusCode = HTTP_PassStandardHeaders(headersList,
+ &httpContentLength);
+
+ result = malloc(sizeof(HTTP_GetResult) + httpContentLength);
+ result->httpStatusCode = httpStatusCode;
+ result->data = NULL;
+ result->contentlen = 0;
+
+ if (httpStatusCode == 401) /* unauthorized */
+ {
+ goto end;
+ }
+ if (httpStatusCode != 200 && httpStatusCode != 206)
+ {
+ ERR("invalid status code [%i]\n", httpStatusCode);
+ goto end;
+ }
+ if (httpContentLength == 0)
+ {
+ ERR("no content length\n");
+ goto end;
+ }
+
+ result->data = (char*)result + sizeof(HTTP_GetResult);
+ result->contentlen = httpContentLength;
+
+ torecv = httpContentLength;
+ databuffer = (char *) result->data;
+ if (contentFromHeaders)
+ {
+ memcpy(result->data, contentFromHeaders, contentLenFromHeaders);
+ torecv -= contentLenFromHeaders;
+ databuffer += contentLenFromHeaders;
+ }
+ free(headerbuffer);
+ while (torecv)
+ {
+ ret = DAAP_SOCKET_READ(connection->sockfd, databuffer, torecv);
+ if (ret == -1)
+ {
+ ERR("an error occured on recv\n");
+ goto end;
+ }
+ torecv -= ret;
+ databuffer += ret;
+ }
+
+ if (headersList)
+ {
+ HTTP_Header *header = NULL;
+
+ while(headersList)
+ {
+ header = headersList->next;
+ free(headersList);
+ headersList = header;
+ }
+ }
+
+ if (reset_send_close)
+ HTTP_Client_Reset(connection);
+
+ return result;
+end:
+ if (headerbuffer) free(headerbuffer);
+
+ if (reset_send_close)
+ HTTP_Client_Reset(connection);
+ ERR("returning with error\n");
+
+ return NULL;
+}
+
+int HTTP_Client_Get_Callback(HTTP_Connection *connection,
+ const char *path,
+ const char *hash,
+ const char *extra_header,
+ int reset_send_close,
+ HTTP_fnHttpWrite callback,
+ void* context)
+{
+ int chunksize = 1024;
+ char *databuffer = malloc(chunksize);
+ char *headerbuffer = NULL;
+
+ int torecv;
+ int ret;
+ int result = -1;
+
+ char *contentFromHeaders;
+ int contentLenFromHeaders;
+ HTTP_Header *headersList = NULL;
+
+ int httpStatusCode;
+ int httpContentLength;
+
+ if (!HTTP_Client_RequestGet(connection, path, hash,
+ extra_header, reset_send_close))
+ goto end;
+
+
+ if (!(headerbuffer = HTTP_Client_ReadHeaders(connection, &headersList,
+ &contentFromHeaders, &contentLenFromHeaders)))
+ {
+ ERR("failed to recieve any headers\n");
+ goto end;
+ }
+
+ httpStatusCode = HTTP_PassStandardHeaders(headersList,
+ &httpContentLength);
+
+ if (httpStatusCode == 401) /* unauthorized */
+ {
+ ERR("unauthorized");
+ goto end;
+ }
+ if (httpStatusCode != 200 && httpStatusCode != 206)
+ {
+ ERR("invalid status code [%i]\n", httpStatusCode);
+ goto end;
+ }
+ if (httpContentLength == 0)
+ {
+ ERR("no content length\n");
+ goto end;
+ }
+
+ torecv = httpContentLength;
+
+ if (contentFromHeaders)
+ {
+ ret = callback( contentFromHeaders, contentLenFromHeaders, 1, httpContentLength, context );
+ if( ret < 0 )
+ {
+ TRACE("callback aborted");
+ goto end;
+ }
+
+ torecv -= contentLenFromHeaders;
+ }
+
+ while (torecv)
+ {
+ ret = DAAP_SOCKET_READ(connection->sockfd, databuffer, chunksize);
+ if (ret == -1)
+ {
+ ERR("an error occured on recv\n");
+ goto end;
+ }
+ torecv -= ret;
+
+ ret = callback( databuffer, ret, 2, httpContentLength, context );
+ if( ret < 0 )
+ {
+ TRACE("callback aborted");
+ goto end;
+ }
+ }
+
+
+ result = 0;
+end:
+ if (headersList)
+ {
+ HTTP_Header *header = NULL;
+
+ while(headersList)
+ {
+ header = headersList->next;
+ free(headersList);
+ headersList = header;
+ }
+ }
+
+ if (headerbuffer) free(headerbuffer);
+ if (databuffer) free(databuffer);
+
+ if (reset_send_close)
+ HTTP_Client_Reset(connection);
+
+
+ return result;
+}
+
+
+/* used for async io */
+int HTTP_Client_Get_ToFile(HTTP_Connection *connection,
+ const char *path,
+ const char *hash,
+ const char *extra_header,
+#ifdef SYSTEM_POSIX
+ int filed,
+#elif defined(SYSTEM_WIN32)
+ HANDLE filed,
+#else
+ FILE *filed,
+#endif
+ int (*pfnStatus)(void*,int),
+ void *userdata,
+ int reset_send_close)
+{
+ char *headerbuffer;
+ char *contentFromHeaders;
+ int contentLenFromHeaders;
+ HTTP_Header *headersList = NULL;
+
+ int httpStatusCode;
+ int httpContentLength;
+
+ char readbuffer[1024];
+ int torecv;
+
+ int last_micropercent = -1;
+ int micropercent;
+
+
+ if (!HTTP_Client_RequestGet(connection, path, hash,
+ extra_header, reset_send_close))
+ goto end;
+
+ if (!(headerbuffer = HTTP_Client_ReadHeaders(connection, &headersList,
+ &contentFromHeaders, &contentLenFromHeaders)))
+ {
+ ERR("failed to recieve any headers\n");
+ goto end;
+ }
+
+ httpStatusCode = HTTP_PassStandardHeaders(headersList,
+ &httpContentLength);
+
+ if (httpStatusCode != 200)
+ {
+ ERR("invalid status code [%i]\n", httpStatusCode);
+ goto end;
+ }
+ if (httpContentLength == 0)
+ {
+ ERR("no content length\n");
+ goto end;
+ }
+
+ torecv = httpContentLength;
+ /* FIXME: write in portions */
+ if (contentFromHeaders)
+ {
+#if defined(SYSTEM_POSIX)
+ write(filed, contentFromHeaders, contentLenFromHeaders);
+#elif defined(SYSTEM_WIN32)
+ DWORD total_written = 0;
+ do
+ {
+ DWORD written = 0;
+ WriteFile(filed, contentFromHeaders + total_written,
+ contentLenFromHeaders - total_written,
+ &written, NULL);
+ total_written += written;
+ } while (total_written < (unsigned)contentLenFromHeaders);
+#else
+ int total_written = 0;
+ do
+ {
+ int written = 0;
+ written = fwrite(contentFromHeaders + total_written, 1,
+ contentLenFromHeaders - total_written,
+ filed);
+ total_written += written;
+ } while (total_written < contentLenFromHeaders);
+#endif
+ torecv -= contentLenFromHeaders;
+ }
+ free(headerbuffer);
+
+ micropercent = (int)(((float)(httpContentLength - torecv) /
+ (float)httpContentLength) * 1000.0);
+ if (micropercent > last_micropercent)
+ {
+ if (pfnStatus(userdata, micropercent))
+ {
+ HTTP_Client_Reset(connection);
+ goto end;
+ }
+ last_micropercent = micropercent;
+ }
+
+
+ while (torecv)
+ {
+ int ret;
+ int readsize = (int)sizeof(readbuffer) < torecv ?
+ (int)sizeof(readbuffer) : torecv;
+ ret = DAAP_SOCKET_READ(connection->sockfd, readbuffer, readsize);
+ if (ret == -1)
+ {
+ ERR("an error occured on recv\n");
+ goto end;
+ }
+ torecv -= ret;
+#if defined(SYSTEM_POSIX)
+ write(filed, readbuffer, ret);
+#elif defined(SYSTEM_WIN32)
+ {
+ DWORD total_written = 0;
+ do
+ {
+ DWORD written = 0;
+ WriteFile(filed, readbuffer + total_written,
+ ret - total_written,
+ &written, NULL);
+ total_written += written;
+ } while (total_written < (unsigned)ret);
+ }
+#else
+ {
+ int total_written = 0;
+ do
+ {
+ int written = 0;
+ written = fwrite(readbuffer + total_written, 1,
+ ret - total_written,
+ filed);
+ total_written += written;
+ } while (total_written < ret);
+ }
+#endif
+ micropercent = (int)(((float)(httpContentLength - torecv) /
+ (float)httpContentLength) * 1000.0);
+ if (micropercent > last_micropercent)
+ {
+ if (pfnStatus(userdata, micropercent))
+ {
+ HTTP_Client_Reset(connection);
+ goto end;
+ }
+ last_micropercent = micropercent;
+ }
+ }
+
+ if (reset_send_close)
+ HTTP_Client_Reset(connection);
+ return 1;
+end:
+ if (reset_send_close)
+ HTTP_Client_Reset(connection);
+ return 0;
+}
+
+void HTTP_Client_FreeResult(HTTP_GetResult *res)
+{
+ free(res);
+}
+#ifdef WIN32
+HTTP_ConnectionWatch *HTTP_Client_WatchQueue_New()
+{
+ return NULL;
+}
+void HTTP_Client_WatchQueue_RunLoop(HTTP_ConnectionWatch *watch)
+{
+
+}
+void HTTP_Client_WatchQueue_Destroy(HTTP_ConnectionWatch *watch)
+{
+
+}
+
+void HTTP_Client_WatchQueue_AddUpdateWatch(
+ HTTP_ConnectionWatch *watch,
+ const HTTP_Connection *connection,
+ const char *path,
+ const char *hash,
+ void (*response_callback)(void *ctx), /* FIXME result? */
+ void *callback_ctx)
+{
+
+}
+
+void HTTP_Client_WatchQueue_RemoveUpdateWatch(
+ HTTP_ConnectionWatch *watch,
+ const HTTP_Connection *connection)
+{
+}
+#else
+
+/*************** For the ConnectionWatch stuff ************/
+typedef struct watch_listTAG watch_list;
+struct watch_listTAG
+{
+ const HTTP_Connection *connection;
+ void (*callback)(void *ctx);
+ void *cb_context;
+ watch_list *next;
+ HTTP_ConnectionWatch *parent;
+};
+
+
+struct HTTP_ConnectionWatchTAG
+{
+ ioloop *main_loop;
+ watch_list *watch_list_head;
+ ts_mutex mutex;
+};
+
+
+HTTP_ConnectionWatch *HTTP_Client_WatchQueue_New()
+{
+ HTTP_ConnectionWatch *new_watch = malloc(sizeof(HTTP_ConnectionWatch));
+ if (!new_watch) goto err;
+
+ new_watch->main_loop = NULL;
+ new_watch->watch_list_head = NULL;
+
+ new_watch->main_loop = ioloop_create();
+ if (!new_watch->main_loop)
+ goto err;
+
+ ts_mutex_create(new_watch->mutex);
+
+ return new_watch;
+
+err:
+ if (new_watch)
+ {
+ if (new_watch->main_loop) ioloop_destroy(new_watch->main_loop);
+ free(new_watch);
+ }
+ return NULL;
+}
+
+void HTTP_Client_WatchQueue_RunLoop(HTTP_ConnectionWatch *watch)
+{
+ ioloop_runloop(watch->main_loop);
+}
+
+void HTTP_Client_WatchQueue_Destroy(HTTP_ConnectionWatch *watch)
+{
+ watch_list *cur;
+ ts_mutex_lock(watch->mutex);
+ ioloop_destroy(watch->main_loop);
+
+ cur = watch->watch_list_head;
+ while (cur)
+ {
+ free(cur);
+ cur = cur->next;
+ }
+
+ ts_mutex_destroy(watch->mutex);
+}
+
+void httpwatch_callback(int fd, void *ctx)
+{
+ watch_list *watch_item = (watch_list*)ctx;
+ void (*cb)(void *ctx);
+ void *cb_ctx;
+
+ ts_mutex_lock(watch_item->parent->mutex);
+ cb = watch_item->callback;
+ cb_ctx = watch_item->cb_context;
+ ts_mutex_unlock(watch_item->parent->mutex);
+
+ if (cb) cb(cb_ctx);
+}
+
+void HTTP_Client_WatchQueue_AddUpdateWatch(
+ HTTP_ConnectionWatch *watch,
+ const HTTP_Connection *connection,
+ const char *path,
+ const char *hash,
+ void (*response_callback)(void *ctx), /* FIXME result? */
+ void *callback_ctx)
+{
+ watch_list *new_item = NULL;
+ if (!HTTP_Client_RequestGet(connection, path, hash, NULL, 0))
+ {
+ TRACE("get failed\n");
+ return; /* FIXME send failure */
+ }
+
+ ts_mutex_lock(watch->mutex);
+
+ new_item = malloc(sizeof(watch_list));
+
+ new_item->connection = connection;
+ new_item->callback = response_callback;
+ new_item->cb_context = callback_ctx;
+ new_item->next = watch->watch_list_head;
+ watch->watch_list_head = new_item;
+ new_item->parent = watch;
+
+ ioloop_add_select_item(watch->main_loop, connection->sockfd,
+ httpwatch_callback, new_item);
+
+ ts_mutex_unlock(watch->mutex);
+}
+
+void HTTP_Client_WatchQueue_RemoveUpdateWatch(
+ HTTP_ConnectionWatch *watch,
+ const HTTP_Connection *connection)
+{
+ watch_list *cur;
+ watch_list *prev = NULL;
+ ts_mutex_lock(watch->mutex);
+
+ cur = watch->watch_list_head;
+ while (cur)
+ {
+ if (cur->connection == connection)
+ {
+ ioloop_delete_select_item(watch->main_loop, connection->sockfd);
+ if (prev) prev->next = cur->next;
+ else watch->watch_list_head = cur->next;
+ cur->callback = NULL;
+ free(cur);
+ ts_mutex_unlock(watch->mutex);
+ return;
+ }
+ prev = cur;
+ cur = cur->next;
+ }
+ ERR("connection not being watched?\n");
+
+ ts_mutex_unlock(watch->mutex);
+}
+
+#endif
diff --git a/lib/libXDAAP/httpClient.h b/lib/libXDAAP/httpClient.h
new file mode 100644
index 0000000000..5e28c5881c
--- /dev/null
+++ b/lib/libXDAAP/httpClient.h
@@ -0,0 +1,105 @@
+/* HTTP client requests
+ * (mostly GET)
+ *
+ * Copyright (c) 2004 David Hammerton
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/* PRIVATE */
+
+/* all requests are handled syncrhonously, they _will_ block */
+
+#ifndef _HTTP_CLIENT_H
+#define _HTTP_CLIENT_H
+
+typedef struct HTTP_ConnectionTAG HTTP_Connection;
+
+struct HTTP_GetResultTAG
+{
+ int httpStatusCode;
+ int contentlen;
+ void *data;
+};
+typedef struct HTTP_GetResultTAG HTTP_GetResult;
+
+/* callback function for http, flag=1 for header, flag=2 for data*/
+typedef int (*HTTP_fnHttpWrite)(const char *buffer, int size, int flag, int contentlength, void* context);
+
+HTTP_Connection *HTTP_Client_Open(const char *host, const char *password);
+void HTTP_Client_Close(HTTP_Connection *connection);
+HTTP_GetResult *HTTP_Client_Get(HTTP_Connection *connection,
+ const char *path,
+ const char *hash,
+ const char *extra_header,
+ int reset_send_close);
+
+int HTTP_Client_Get_Callback(HTTP_Connection *connection,
+ const char *path,
+ const char *hash,
+ const char *extra_header,
+ int reset_send_close,
+ HTTP_fnHttpWrite callback, void *context);
+
+
+int HTTP_Client_Get_ToFile(HTTP_Connection *connection,
+ const char *path,
+ const char *hash,
+ const char *extra_header,
+#ifdef SYSTEM_POSIX
+ int filed,
+#elif defined(SYSTEM_WIN32)
+ HANDLE filed,
+#else
+ FILE *filed,
+#endif
+ int (*pfnStatus)(void*,int),
+ void *userdata,
+ int reset_send_close);
+
+void HTTP_Client_FreeResult(HTTP_GetResult *res);
+
+typedef struct HTTP_ConnectionWatchTAG HTTP_ConnectionWatch;
+
+HTTP_ConnectionWatch *HTTP_Client_WatchQueue_New();
+void HTTP_Client_WatchQueue_RunLoop(HTTP_ConnectionWatch *watch);
+
+/* sends the GET with the given path / hash (generally update)
+ * and adds the socket to the recv watch.
+ */
+void HTTP_Client_WatchQueue_AddUpdateWatch(
+ HTTP_ConnectionWatch *watch,
+ const HTTP_Connection *connection,
+ const char *path,
+ const char *hash,
+ void (*response_callback)(void *ctx), /* FIXME result? */
+ void *callback_ctx);
+
+void HTTP_Client_WatchQueue_RemoveUpdateWatch(
+ HTTP_ConnectionWatch *watch,
+ const HTTP_Connection *connection);
+
+void HTTP_Client_WatchQueue_Destroy(HTTP_ConnectionWatch *watch);
+
+
+#endif /* _HTTP_CLIENT_H */
+
diff --git a/lib/libXDAAP/ioloop.c b/lib/libXDAAP/ioloop.c
new file mode 100644
index 0000000000..74975a4c00
--- /dev/null
+++ b/lib/libXDAAP/ioloop.c
@@ -0,0 +1,339 @@
+// Place the code and data below here into the LIBXDAAP section.
+#ifndef __GNUC__
+#pragma code_seg( "LIBXDAAP_TEXT" )
+#pragma data_seg( "LIBXDAAP_DATA" )
+#pragma bss_seg( "LIBXDAAP_BSS" )
+#pragma const_seg( "LIBXDAAP_RD" )
+#endif
+
+/* ioloop stuff
+ * Can add IO watches, event watches
+ *
+ * Copyright (c) 2004 David Hammerton
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/* PRIVATE */
+
+#define WIN32_LEAN_AND_MEAN
+
+#include "portability.h"
+
+#ifdef SYSTEM_POSIX
+#include <pthread.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <netdb.h>
+#include <fcntl.h>
+#else
+#error implement me
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "thread.h"
+#include "ioloop.h"
+#include "debug.h"
+
+#define DEFAULT_DEBUG_CHANNEL "ioloop"
+
+
+typedef struct select_itemTAG select_item;
+
+struct ioloopTAG
+{
+ select_item *select_head;
+ /* these two events could probably be combined into one */
+ fd_event *destroy_event;
+ fd_event *modify_select_event;
+ int destroying, loop_running;
+ ts_mutex mutex;
+
+ /* debug */
+ int entry_level;
+};
+
+/******* abstraction layer for events ********/
+struct fd_eventTAG
+{
+ int pipe[2];
+ int signalled;
+ int manual_reset;
+ fd_event_callback callback;
+ void *cb_context;
+};
+
+fd_event *fd_event_create(int manual_reset,
+ fd_event_callback callback,
+ void *cb_context)
+{
+ fd_event *new_event = malloc(sizeof(fd_event));
+
+ new_event->signalled = 0;
+ new_event->manual_reset = manual_reset;
+ new_event->callback = callback;
+ new_event->cb_context = cb_context;
+
+ if (pipe(new_event->pipe))
+ {
+ free(new_event);
+ return NULL;
+ }
+
+ return new_event;
+}
+
+void fd_event_destroy(fd_event *event)
+{
+ close(event->pipe[0]);
+ close(event->pipe[1]);
+}
+
+void fd_event_signal(fd_event *event)
+{
+ const char buf[1] = {0};
+ event->signalled = 1;
+ write(event->pipe[1], buf, 1);
+}
+
+void fd_event_reset(fd_event *event)
+{
+ event->signalled = 0;
+ char buf[1] = {0};
+ int flags;
+
+ /* set non-blocking on read pipe */
+ flags = fcntl(event->pipe[0], F_GETFL, 0);
+ if (flags == -1) flags = 0;
+ fcntl(event->pipe[0], F_SETFL, flags | O_NONBLOCK);
+
+ /* read until empty - pipe is non-blocking as above */
+ while (read(event->pipe[0], buf, 1) == 1)
+ {
+ }
+
+ /* set blocking */
+ fcntl(event->pipe[0], F_SETFL, flags);
+}
+
+static int fd_event_getfd(fd_event *event)
+{
+ return event->pipe[0];
+}
+
+static void fd_event_handle(int fd, void *ctx)
+{
+ fd_event *event = (fd_event*)ctx;
+ if (!event->manual_reset)
+ fd_event_reset(event);
+
+ if (event->callback)
+ event->callback(event, event->cb_context);
+}
+
+/************* main event loop for connection watch *********/
+/******* select abstraction *******/
+struct select_itemTAG
+{
+ int fd;
+ select_fd_datacb callback;
+ void *cb_context;
+ select_item *next;
+};
+
+void ioloop_add_select_item(
+ ioloop *ioloop,
+ int fd,
+ select_fd_datacb callback,
+ void *cb_context)
+{
+ select_item *new_select = malloc(sizeof(select_item));
+
+ if (ioloop->entry_level) FIXME("reentering ioloop, could be a problem\n");
+
+ ts_mutex_lock(ioloop->mutex);
+
+ new_select->fd = fd;
+ new_select->callback = callback;
+ new_select->cb_context = cb_context;
+ new_select->next = ioloop->select_head;
+ ioloop->select_head = new_select;
+
+ fd_event_signal(ioloop->modify_select_event);
+ ts_mutex_unlock(ioloop->mutex);
+}
+
+void ioloop_delete_select_item(
+ ioloop *ioloop,
+ int fd)
+{
+ select_item *cur;
+ select_item *prev = NULL;
+
+ if (ioloop->entry_level) FIXME("reentering ioloop, could be a problem\n");
+
+ ts_mutex_lock(ioloop->mutex);
+ cur = ioloop->select_head;
+ while (cur)
+ {
+ if (cur->fd == fd)
+ {
+ if (prev) prev->next = cur->next;
+ else ioloop->select_head = cur->next;
+ free(cur);
+ ts_mutex_unlock(ioloop->mutex);
+ return;
+ }
+
+ prev = cur;
+ cur = cur->next;
+ }
+ fd_event_signal(ioloop->modify_select_event);
+ ts_mutex_unlock(ioloop->mutex);
+}
+
+void ioloop_add_select_event(
+ ioloop *ioloop,
+ fd_event *event)
+{
+ ioloop_add_select_item(ioloop, fd_event_getfd(event),
+ fd_event_handle, (void*)event);
+}
+
+void ioloop_delete_select_event(
+ ioloop *ioloop,
+ fd_event *event)
+{
+ ioloop_delete_select_item(ioloop, fd_event_getfd(event));
+}
+
+static void ioloop_realdestroy(ioloop *ioloop)
+{
+ select_item *cur = ioloop->select_head;
+ TRACE("(%p)\n", ioloop);
+ while (cur)
+ {
+ select_item *next = cur->next;
+ free(cur);
+ cur = next;
+ }
+ fd_event_destroy(ioloop->destroy_event);
+ fd_event_destroy(ioloop->modify_select_event);
+ ts_mutex_destroy(ioloop->mutex);
+}
+
+void ioloop_runloop(ioloop *ioloop)
+{
+ ioloop->loop_running = 1;
+ do
+ {
+ fd_set fdset;
+ int retval;
+ int n = 0, i = 0;
+ select_item *cur = NULL;
+
+ /* setup select */
+ FD_ZERO(&fdset);
+
+ ts_mutex_lock(ioloop->mutex);
+ cur = ioloop->select_head;
+ while (cur)
+ {
+ FD_SET(cur->fd, &fdset);
+ if (cur->fd > n) n = cur->fd;
+
+ cur = cur->next;
+ i++;
+ }
+ ts_mutex_unlock(ioloop->mutex);
+
+ retval = select(n, &fdset, NULL, NULL, NULL);
+
+ if (retval <= 0)
+ {
+ ERR("select failed\n");
+ continue;
+ }
+
+ ts_mutex_lock(ioloop->mutex);
+ n = 0;
+ cur = ioloop->select_head;
+ while (cur && retval > n)
+ {
+ if (FD_ISSET(cur->fd, &fdset))
+ {
+ ioloop->entry_level++;
+ cur->callback(cur->fd, cur->cb_context);
+ ioloop->entry_level--;
+ n++;
+ }
+
+ cur = cur->next;
+ }
+ ts_mutex_unlock(ioloop->mutex);
+
+ } while (ioloop->destroying == 0);
+ ioloop_realdestroy(ioloop);
+}
+
+ioloop* ioloop_create()
+{
+ ioloop *newIOLoop = malloc(sizeof(ioloop));
+ newIOLoop->select_head = NULL;
+ ts_mutex_create_recursive(newIOLoop->mutex);
+
+ newIOLoop->entry_level = 0;
+ newIOLoop->loop_running = 0;
+ newIOLoop->destroying = 0;
+
+ /* can probably combine this into one */
+ newIOLoop->modify_select_event = fd_event_create(0, NULL, NULL);
+ newIOLoop->destroy_event = fd_event_create(0, NULL, NULL);
+ ioloop_add_select_event(newIOLoop, newIOLoop->modify_select_event);
+ ioloop_add_select_event(newIOLoop, newIOLoop->destroy_event);
+
+ return newIOLoop;
+}
+
+void ioloop_destroy(ioloop *ioloop)
+{
+ if (ioloop->entry_level) TRACE("reentering ioloop, could be a problem\n");
+
+ ts_mutex_lock(ioloop->mutex);
+ if (ioloop->loop_running)
+ {
+ ioloop->destroying = 1;
+ fd_event_signal(ioloop->destroy_event);
+ }
+ else
+ {
+ ioloop_realdestroy(ioloop);
+ }
+ /* mutex may be destroyed by now */
+ /*ts_mutex_unlock(ioloop->mutex);*/
+}
+
diff --git a/lib/libXDAAP/ioloop.h b/lib/libXDAAP/ioloop.h
new file mode 100644
index 0000000000..1e9db9d07d
--- /dev/null
+++ b/lib/libXDAAP/ioloop.h
@@ -0,0 +1,83 @@
+/* ioloop stuff
+ * Can add IO watches, event watches
+ *
+ * Copyright (c) 2004 David Hammerton
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/* PRIVATE */
+
+#ifndef _IOLOOP_H
+#define _IOLOOP_H
+
+typedef struct fd_eventTAG fd_event;
+
+/* ioloop */
+typedef void (*select_fd_datacb)(int fd, void *ctx);
+typedef struct ioloopTAG ioloop;
+
+void ioloop_add_select_item(
+ ioloop *ioloop,
+ int fd,
+ select_fd_datacb callback,
+ void *cb_context);
+
+void ioloop_delete_select_item(
+ ioloop *ioloop,
+ int fd);
+
+void ioloop_add_select_event(
+ ioloop *ioloop,
+ fd_event *event);
+
+void ioloop_delete_select_event(
+ ioloop *ioloop,
+ fd_event *event);
+
+ioloop* ioloop_create();
+
+void ioloop_destroy(ioloop *ioloop);
+
+/* doesn't return until it's destroyed.
+ * event and select item callbacks will get
+ * dispatched from within this context
+ */
+void ioloop_runloop(ioloop *ioloop);
+
+
+/* events */
+typedef void (*fd_event_callback)(fd_event *event, void *context);
+
+fd_event *fd_event_create(int manual_reset,
+ fd_event_callback callback,
+ void *cb_context);
+
+void fd_event_destroy(fd_event *event);
+
+void fd_event_signal(fd_event *event);
+
+void fd_event_reset(fd_event *event);
+
+
+#endif /* _IOLOOP_H */
+
diff --git a/lib/libXDAAP/libXDAAP.c b/lib/libXDAAP/libXDAAP.c
new file mode 100644
index 0000000000..ddec6e5889
--- /dev/null
+++ b/lib/libXDAAP/libXDAAP.c
@@ -0,0 +1,1860 @@
+// Place the code and data below here into the LIBXDAAP section.
+#ifndef __GNUC__
+#pragma code_seg( "LIBXDAAP_TEXT" )
+#pragma data_seg( "LIBXDAAP_DATA" )
+#pragma bss_seg( "LIBXDAAP_BSS" )
+#pragma const_seg( "LIBXDAAP_RD" )
+
+#pragma comment(linker, "/merge:LIBXDAAP_TEXT=LIBXDAAP")
+#pragma comment(linker, "/merge:LIBXDAAP_DATA=LIBXDAAP")
+#pragma comment(linker, "/merge:LIBXDAAP_BSS=LIBXDAAP")
+#pragma comment(linker, "/merge:LIBXDAAP_RD=LIBXDAAP")
+#pragma comment(linker, "/section:LIBXDAAP,RWE")
+#endif
+
+/* client class
+ *
+ * Copyright (c) 2004 David Hammerton
+ * crazney@crazney.net
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * Port to XBMC by Forza @ XBMC Media Center Developers
+ *
+ * XBox modifications (c) 2004 Forza (Chris Barnett)
+ *
+ */
+
+/*
+
+NOTE: The discovery services have been disabled due to the XBox lacking multicast packets
+
+*/
+
+#include "portability.h"
+#include "thread.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#if defined(SYSTEM_POSIX)
+# include "threadpool.h"
+#endif
+
+#include "libXDAAP.h"
+#include "client.h"
+//#include "discover.h"
+#include "httpClient.h"
+#include "private.h"
+
+#include "daap.h"
+#include "daap_contentcodes.h"
+#include "dmap_generics.h"
+
+#include "Authentication/hasher.h"
+
+#include "debug.h"
+
+#define DEFAULT_DEBUG_CHANNEL "client"
+
+
+static DAAP_SClientHost *DAAP_ClientHost_Create(DAAP_SClient *parent, char *host,
+ char *sharename);
+static int Priv_DAAP_ClientHost_GetDatabaseItems(DAAP_SClientHost *pCHThis,
+ int databaseid);
+static int Priv_DAAP_ClientHost_GetDatabasePlaylists(DAAP_SClientHost *pCHThis,
+ int databaseid);
+
+struct DAAP_ClientHost_FakeTAG
+{
+ char sharename[1005];
+ char sharename_friendly[1005];
+ unsigned char ip[4];
+ unsigned short port;
+ DAAP_ClientHost_Fake *next;
+
+ int marked;
+};
+
+#if !defined(WIN32) && !defined(_LINUX)
+static int ClientHasHost_AndMark(DAAP_SClient *pClient, char *sharename)
+{
+ DAAP_ClientHost_Fake *cur_fake = pClient->fakehosts;
+ DAAP_SClientHost *cur_real = pClient->hosts;
+ while (cur_fake)
+ {
+ if (strcmp(cur_fake->sharename, sharename) == 0)
+ {
+ cur_fake->marked = 1;
+ return 1;
+ }
+ cur_fake = cur_fake->next;
+ }
+ while (cur_real)
+ {
+ if (strcmp(cur_real->sharename, sharename) == 0)
+ {
+ cur_real->marked = 1;
+ return 1;
+ }
+ cur_real = cur_real->next;
+ }
+ return 0;
+}
+
+static void DiscoverCB(SDiscover *disc, void *pv_pClient)
+{
+ DAAP_SClient *pClient = (DAAP_SClient*)pv_pClient;
+ int added = 0;
+ int deleted = 0;
+
+ DAAP_ClientHost_Fake *cur_fake, *prev_fake;
+ DAAP_SClientHost *cur_real;
+ SDiscover_HostList *hosts;
+
+ ts_mutex_lock(pClient->mtObjectLock);
+
+ cur_fake = pClient->fakehosts;
+ cur_real = pClient->hosts;
+ while (cur_fake)
+ {
+ cur_fake->marked = 0;
+ cur_fake = cur_fake->next;
+ }
+ while (cur_real)
+ {
+ cur_real->marked = 0;
+ cur_real = cur_real->next;
+ }
+
+ Discover_GetHosts(disc, &hosts);
+ while (hosts)
+ {
+ if (!ClientHasHost_AndMark(pClient, hosts->sharename))
+ {
+ char *hostname_buf = safe_sprintf(
+ "%hhu.%hhu.%hhu.%hhu:%hu",
+ hosts->ip[0], hosts->ip[1],
+ hosts->ip[2], hosts->ip[3],
+ hosts->port);
+ if (!DAAP_Client_AddHost(pClient, hostname_buf,
+ hosts->sharename, hosts->sharename_friendly))
+ {
+ /* if we failed to add the host, add it to the fake
+ * so we don't try and add it again... */
+ DAAP_ClientHost_Fake *new_fake = malloc(sizeof(DAAP_ClientHost_Fake));
+ strcpy(new_fake->sharename, hosts->sharename);
+ strcpy(new_fake->sharename_friendly, hosts->sharename_friendly);
+ new_fake->ip[0] = hosts->ip[0];
+ new_fake->ip[1] = hosts->ip[1];
+ new_fake->ip[2] = hosts->ip[2];
+ new_fake->ip[3] = hosts->ip[3];
+ new_fake->port = hosts->port;
+ new_fake->next = pClient->fakehosts;
+ new_fake->marked = 1;
+ pClient->fakehosts = new_fake;
+ }
+ else
+ {
+ added++;
+ }
+ free(hostname_buf);
+ }
+ hosts = hosts->next;
+ }
+
+ /* delete any that arn't there... */
+ prev_fake = NULL;
+ cur_fake = pClient->fakehosts;
+ cur_real = pClient->hosts;
+ while (cur_fake)
+ {
+ DAAP_ClientHost_Fake *next_fake = cur_fake->next;
+ if (!cur_fake->marked)
+ {
+ if (prev_fake) prev_fake->next = cur_fake->next;
+ else pClient->fakehosts = cur_fake->next;
+ free(cur_fake);
+ deleted++;
+ }
+ cur_fake = next_fake;
+ }
+ while (cur_real)
+ {
+ DAAP_SClientHost *next_real = cur_real->next;
+ if (!cur_real->marked)
+ {
+ DAAP_SClientHost *prev = cur_real->prev;
+ if (DAAP_ClientHost_Release(cur_real) != 0)
+ TRACE("app still holds reference to deleted host\n");
+
+ if (prev) prev->next = next_real;
+ else pClient->hosts = next_real;
+ if (next_real) next_real->prev = prev;
+
+ deleted++;
+ }
+ cur_real = next_real;
+ }
+
+ ts_mutex_unlock(pClient->mtObjectLock);
+
+ if (added || deleted)
+ {
+ TRACE("%i added, %i deleted\n", added, deleted);
+ pClient->pfnCallbackStatus(pClient, DAAP_STATUS_hostschanged,
+ 0, pClient->pvCallbackStatusContext);
+ }
+}
+#endif //WIN32
+
+DAAP_SClient *DAAP_Client_Create(DAAP_fnClientStatus pfnCallback,
+ void *pvCallbackContext)
+{
+ DAAP_SClient *pClientNew = malloc(sizeof(DAAP_SClient));
+ memset(pClientNew, 0, sizeof(DAAP_SClient));
+
+ dmap_init();
+
+ pClientNew->uiRef = 1;
+
+ pClientNew->pfnCallbackStatus = pfnCallback;
+ pClientNew->pvCallbackStatusContext = pvCallbackContext;
+
+#if defined(SYSTEM_POSIX) && !defined(_LINUX)
+ pClientNew->tp = CP_ThreadPool_Create(4);
+#endif
+#if !defined(WIN32) && !defined(_LINUX)
+ pClientNew->discover = Discover_Create(
+#if defined(SYSTEM_POSIX)
+ pClientNew->tp,
+#endif
+ DiscoverCB, (void*)pClientNew);
+#endif
+ pClientNew->update_watch = NULL;
+
+ ts_mutex_create(pClientNew->mtObjectLock);
+
+ return pClientNew;
+}
+
+int DAAP_Client_SetDebug(DAAP_SClient *pCThis, const char *const debug)
+{
+ return daap_debug_init(debug);
+}
+
+unsigned int DAAP_Client_AddRef(DAAP_SClient *pCThis)
+{
+ return ++pCThis->uiRef;
+}
+
+unsigned int DAAP_Client_Release(DAAP_SClient *pCThis)
+{
+ if (--pCThis->uiRef)
+ return pCThis->uiRef;
+
+ while (pCThis->hosts)
+ {
+ DAAP_SClientHost *cur = pCThis->hosts;
+ pCThis->hosts = cur->next;
+ if (pCThis->hosts) pCThis->hosts->prev = NULL;
+ cur->next = NULL;
+ DAAP_ClientHost_Release(cur);
+ }
+
+ if (pCThis->update_watch)
+ {
+ HTTP_Client_WatchQueue_Destroy(pCThis->update_watch);
+ }
+#if !defined(WIN32) && !defined(_LINUX)
+ Discover_Release(pCThis->discover);
+#endif
+#if defined(SYSTEM_POSIX) && !defined(_LINUX)
+ CP_ThreadPool_Release(pCThis->tp);
+#endif
+ free(pCThis);
+ dmap_deinit();
+ return 0;
+}
+
+unsigned int DAAP_Client_EnumerateHosts(DAAP_SClient *pCThis,
+ DAAP_fnClientEnumerateHosts pfnCallback,
+ void *context)
+{
+ int count = 0;
+ DAAP_SClientHost *cur = pCThis->hosts;
+
+ ts_mutex_lock(pCThis->mtObjectLock);
+
+ while(cur)
+ {
+ pfnCallback(pCThis, cur, context);
+ cur = cur->next;
+ count++;
+ }
+ ts_mutex_unlock(pCThis->mtObjectLock);
+ return count;
+}
+
+/* hack */
+DAAP_SClientHost *DAAP_Client_AddHost(DAAP_SClient *pCThis, char *host,
+ char *sharename, char *sharename_friendly)
+{
+ DAAP_SClientHost *pClientHost = DAAP_ClientHost_Create(pCThis, host,
+ sharename_friendly);
+ if (!pClientHost) return NULL;
+ if (sharename) strcpy(pClientHost->sharename, sharename);
+ if (pCThis->hosts)
+ pCThis->hosts->prev = pClientHost;
+ pClientHost->next = pCThis->hosts;
+ pCThis->hosts = pClientHost;
+ pClientHost->marked = 1;
+ return pClientHost;
+}
+
+/* ClientHost */
+
+/* this macro is used on returned http results. It will return a failure
+ * if the http result is NULL (returns 1).
+ * If the http status code is not 200 (OK) it will return the status code.
+ * It will also free the http result if it returns.
+ */
+#define HTTP_RETURN_IF_FAILED(h) \
+ if (h == NULL) return 1; \
+ if (h->httpStatusCode != 200) { \
+ int ret = h->httpStatusCode; \
+ HTTP_Client_FreeResult(h); \
+ return ret; \
+ }
+
+/* private initial stuff */
+static int Priv_DAAP_ClientHost_InitialTransaction(DAAP_SClientHost *pCHThis)
+{
+ HTTP_GetResult *httpRes;
+
+ protoParseResult_serverinfo serverinfo;
+ protoParseResult_login logininfo;
+ protoParseResult_update updateinfo;
+
+ char hash[33] = {0};
+ char updateUrl[] = "/update?session-id=%i&revision-number=1";
+ char *buf;
+
+ /* get server-info */
+ httpRes = HTTP_Client_Get(pCHThis->connection, "/server-info", NULL, NULL, 0);
+ HTTP_RETURN_IF_FAILED(httpRes);
+
+ serverinfo.h.expecting = QUERY_SERVERINFORESPONSE;
+ dmap_parseProtocolData(httpRes->contentlen, httpRes->data,
+ (protoParseResult *)&serverinfo);
+
+ HTTP_Client_FreeResult(httpRes);
+
+ pCHThis->version_major = serverinfo.daap_version.v1;
+ pCHThis->version_minor = serverinfo.daap_version.v2;
+
+ /* validate server info */
+ if (serverinfo.dmap_version.v1 != 2 && serverinfo.dmap_version.v2 != 0)
+ {
+ FIXME("unknown version\n");
+ return 1;
+ }
+
+ /* hack */
+ free(serverinfo.hostname);
+
+ /* get content codes */
+ httpRes = HTTP_Client_Get(pCHThis->connection, "/content-codes", NULL, NULL, 0);
+ HTTP_RETURN_IF_FAILED(httpRes);
+
+#if 0
+ {
+ FILE *f = fopen("/tmp/content-codes", "w");
+ int i;
+ for (i = 0; i < httpRes->contentlen; i++)
+ {
+ fputc(((unsigned char*)httpRes->data)[i], f);
+ }
+ fclose(f);
+ }
+#endif
+
+ /* send content codes to the dmap parser. don't require any result */
+ dmap_parseProtocolData(httpRes->contentlen, httpRes->data, NULL);
+
+ HTTP_Client_FreeResult(httpRes);
+
+ /* send login */
+ httpRes = HTTP_Client_Get(pCHThis->connection, "/login", NULL, NULL, 0);
+ HTTP_RETURN_IF_FAILED(httpRes);
+
+ logininfo.h.expecting = QUERY_LOGINRESPONSE;
+ dmap_parseProtocolData(httpRes->contentlen, httpRes->data,
+ (protoParseResult*)&logininfo);
+
+ HTTP_Client_FreeResult(httpRes);
+
+ pCHThis->sessionid = logininfo.sessionid;
+
+ /* get the current revision id */
+ buf = safe_sprintf(updateUrl, pCHThis->sessionid);
+
+ GenerateHash(pCHThis->version_major, buf, 2, hash, 0);
+
+ httpRes = HTTP_Client_Get(pCHThis->connection, buf, hash, NULL, 0);
+
+ free(buf);
+
+ HTTP_RETURN_IF_FAILED(httpRes);
+
+ updateinfo.h.expecting = QUERY_UPDATERESPONSE;
+ dmap_parseProtocolData(httpRes->contentlen, httpRes->data,
+ (protoParseResult*)&updateinfo);
+
+ HTTP_Client_FreeResult(httpRes);
+
+ pCHThis->revision_number = updateinfo.serverrevision;
+
+ return 0;
+}
+
+static int Priv_DAAP_ClientHost_GetDatabases(DAAP_SClientHost *pCHThis)
+{
+ char hash[33] = {0};
+ char databasesUrl[] = "/databases?session-id=%i&revision-number=%i";
+ char *buf;
+
+ protoParseResult_genericPreListing databases;
+
+ HTTP_GetResult *httpRes;
+
+ int i, j;
+ char *strpos;
+ int sizereq;
+
+ buf = safe_sprintf(databasesUrl, pCHThis->sessionid, pCHThis->revision_number);
+
+ GenerateHash(pCHThis->version_major, buf, 2, hash, 0);
+
+ httpRes = HTTP_Client_Get(pCHThis->connection, buf, hash, NULL, 0);
+
+ free(buf);
+
+ HTTP_RETURN_IF_FAILED(httpRes);
+
+ databases.h.expecting = QUERY_GENERICLISTING;
+ dmap_parseProtocolData(httpRes->contentlen, httpRes->data,
+ (protoParseResult*)&databases);
+
+ HTTP_Client_FreeResult(httpRes);
+
+ if (databases.totalcount != databases.returnedcount)
+ FIXME("didn't return all db's, need to handle split\n");
+
+ j = 0;
+ sizereq = sizeof(DAAP_ClientHost_Database) * databases.returnedcount;
+ for (i = 0; i < databases.returnedcount; i++)
+ {
+ dmapGenericContainer *item = &(databases.listitems[i]);
+ DMAP_INT32 itemid;
+ DMAP_STRING itemname;
+
+ if (dmapGeneric_LookupContainerItem_INT32(item, dmap_l("itemid"), &itemid) !=
+ DMAP_DATATYPE_INT32)
+ continue;
+
+ if (dmapGeneric_LookupContainerItem_STRING(item, dmap_l("itemname"), &itemname) !=
+ DMAP_DATATYPE_STRING)
+ continue;
+
+ sizereq += strlen(itemname) + 1;
+
+ j++;
+ }
+
+ if (pCHThis->databases) free(pCHThis->databases);
+
+ pCHThis->databases_size = sizereq;
+ pCHThis->databases = malloc(sizereq);
+
+ if (pCHThis->dbitems)
+ {
+ int i;
+ for (i = 0; i < pCHThis->nDatabases; i++)
+ {
+ free(&(pCHThis->dbitems->items[i]));
+ }
+ free(pCHThis->dbitems);
+ }
+
+ if (pCHThis->dbplaylists) free(pCHThis->dbplaylists);
+
+ pCHThis->dbitems = malloc(sizeof(DatabaseItemContainer) * j);
+ memset(pCHThis->dbitems, 0, sizeof(DatabaseItemContainer) * j);
+ pCHThis->dbplaylists = malloc(sizeof(DatabasePlaylistContainer) * j);
+ memset(pCHThis->dbplaylists, 0, sizeof(DatabasePlaylistContainer) * j);
+
+ pCHThis->nDatabases = j;
+
+ strpos = (char*)(((char*)pCHThis->databases) +
+ (sizeof(DAAP_ClientHost_Database) * databases.returnedcount));
+
+ j = 0;
+ for (i = 0; i < databases.returnedcount; i++)
+ {
+ dmapGenericContainer *item = &(databases.listitems[i]);
+ DAAP_ClientHost_Database *db = &(pCHThis->databases[j]);
+ DMAP_INT32 itemid;
+ DMAP_STRING itemname;
+
+ if (dmapGeneric_LookupContainerItem_INT32(item, dmap_l("itemid"), &itemid) !=
+ DMAP_DATATYPE_INT32)
+ continue;
+
+ if (dmapGeneric_LookupContainerItem_STRING(item, dmap_l("itemname"), &itemname) !=
+ DMAP_DATATYPE_STRING)
+ continue;
+
+ db->id = itemid;
+ strcpy(strpos, itemname);
+ db->name = strpos;
+ strpos += strlen(itemname) + 1;
+
+ pCHThis->dbitems[j].id = itemid;
+ pCHThis->dbplaylists[j].id = itemid;
+ Priv_DAAP_ClientHost_GetDatabaseItems(pCHThis, itemid);
+ Priv_DAAP_ClientHost_GetDatabasePlaylists(pCHThis, itemid);
+
+ j++;
+ }
+
+ freeGenericPreListing(&databases);
+
+ return 0;
+}
+
+static int Priv_DAAP_ClientHost_GetDatabaseItems(DAAP_SClientHost *pCHThis,
+ int databaseid)
+{
+ char hash[33] = {0};
+ char itemsUrl[] = "/databases/%i/items?session-id=%i&revision-number=%i&meta="
+ "dmap.itemid,dmap.itemname,daap.songalbum,daap.songartist,"
+ "daap.songbeatsperminute,daap.songbitrate,daap.songdisccount,"
+ "daap.songdiscnumber,daap.songgenre,daap.songsamplerate,"
+ "daap.songsize,daap.songtime,daap.songtrackcount,"
+ "daap.songtracknumber,daap.songuserrating,"
+ "daap.songyear,daap.songformat";
+ char *buf;
+
+ protoParseResult_genericPreListing items;
+
+ DatabaseItemContainer *dbcontainer = NULL;
+
+ HTTP_GetResult *httpRes;
+
+ int i, j;
+ char *strpos;
+ int sizereq;
+
+ for (i = 0; i < pCHThis->nDatabases; i++)
+ {
+ DatabaseItemContainer *container = &(pCHThis->dbitems[i]);
+ if (container->id == databaseid)
+ {
+ dbcontainer = container;
+ break;
+ }
+ }
+
+ if (!dbcontainer)
+ {
+ ERR("container not found, returning\n");
+ freeGenericPreListing(&items);
+ return 0;
+ }
+
+
+ buf = safe_sprintf(itemsUrl, databaseid, pCHThis->sessionid, pCHThis->revision_number);
+
+ GenerateHash(pCHThis->version_major, buf, 2, hash, 0);
+
+ httpRes = HTTP_Client_Get(pCHThis->connection, buf, hash, NULL, 0);
+
+ free(buf);
+
+ HTTP_RETURN_IF_FAILED(httpRes);
+
+ items.h.expecting = QUERY_GENERICLISTING;
+ dmap_parseProtocolData(httpRes->contentlen, httpRes->data,
+ (protoParseResult*)&items);
+
+ HTTP_Client_FreeResult(httpRes);
+
+ if (items.totalcount != items.returnedcount)
+ FIXME("didn't return all db's, need to handle split (%i vs %i)\n",
+ items.totalcount, items.returnedcount);
+
+ TRACE("returnedcount: %i\n", items.returnedcount);
+ sizereq = sizeof(DAAP_ClientHost_DatabaseItem) * items.returnedcount;
+ for (i = 0; i < items.returnedcount; i++)
+ {
+ dmapGenericContainer *item = &(items.listitems[i]);
+ DMAP_INT32 buf32;
+ /*DMAP_INT16 buf16;
+ DMAP_INT8 buf8;*/
+ DMAP_STRING buf;
+
+ if (dmapGeneric_LookupContainerItem_INT32(item, dmap_l("itemid"), &buf32) !=
+ DMAP_DATATYPE_INT32)
+ continue;
+
+ if (dmapGeneric_LookupContainerItem_STRING(item, dmap_l("itemname"), &buf) !=
+ DMAP_DATATYPE_STRING)
+ continue;
+ sizereq += strlen(buf) + 1;
+
+ if (dmapGeneric_LookupContainerItem_STRING(item, daap_l("songalbum"), &buf) ==
+ DMAP_DATATYPE_STRING)
+ sizereq += strlen(buf) + 1;
+
+ if (dmapGeneric_LookupContainerItem_STRING(item, daap_l("songartist"), &buf) ==
+ DMAP_DATATYPE_STRING)
+ sizereq += strlen(buf) + 1;
+
+ /*
+ * OPTIONAL - had to comment this out because mt-daapd doesn't provide all of these
+ * all of the time.
+ *
+ if (dmapGeneric_LookupContainerItem_INT16(item, daap_l("songbeatsperminute"), &buf16) !=
+ DMAP_DATATYPE_INT16)
+ continue;
+
+ if (dmapGeneric_LookupContainerItem_INT16(item, daap_l("songbitrate"), &buf16) !=
+ DMAP_DATATYPE_INT16)
+ continue;
+
+ if (dmapGeneric_LookupContainerItem_INT16(item, daap_l("songdisccount"), &buf16) !=
+ DMAP_DATATYPE_INT16)
+ continue;
+
+ if (dmapGeneric_LookupContainerItem_INT16(item, daap_l("songdiscnumber"), &buf16) !=
+ DMAP_DATATYPE_INT16)
+ continue;
+ */
+
+ if (dmapGeneric_LookupContainerItem_STRING(item, daap_l("songgenre"), &buf) ==
+ DMAP_DATATYPE_STRING)
+ sizereq += strlen(buf) + 1;
+
+ if (dmapGeneric_LookupContainerItem_INT32(item, daap_l("songsamplerate"), &buf32) !=
+ DMAP_DATATYPE_INT32)
+ continue;
+
+ if (dmapGeneric_LookupContainerItem_INT32(item, daap_l("songsize"), &buf32) !=
+ DMAP_DATATYPE_INT32)
+ continue;
+
+ if (dmapGeneric_LookupContainerItem_INT32(item, daap_l("songtime"), &buf32) !=
+ DMAP_DATATYPE_INT32)
+ continue;
+
+ /* optional, see above re mt-daapd
+ if (dmapGeneric_LookupContainerItem_INT16(item, daap_l("songtrackcount"), &buf16) !=
+ DMAP_DATATYPE_INT16)
+ continue;
+
+ if (dmapGeneric_LookupContainerItem_INT16(item, daap_l("songtracknumber"), &buf16) !=
+ DMAP_DATATYPE_INT16)
+ continue;
+
+ if (dmapGeneric_LookupContainerItem_INT8(item, daap_l("songuserrating"), &buf8) !=
+ DMAP_DATATYPE_INT8)
+ continue;
+
+ if (dmapGeneric_LookupContainerItem_INT16(item, daap_l("songyear"), &buf16) !=
+ DMAP_DATATYPE_INT16)
+ continue;
+ */
+
+ if (dmapGeneric_LookupContainerItem_STRING(item, daap_l("songformat"), &buf) !=
+ DMAP_DATATYPE_STRING)
+ continue;
+ sizereq += strlen(buf) + 1;
+ }
+
+ if (dbcontainer->items) free(dbcontainer->items);
+
+ dbcontainer->items_size = sizereq;
+ dbcontainer->items = malloc(sizereq);
+
+ strpos = (char*)(((char*)dbcontainer->items) +
+ (sizeof(DAAP_ClientHost_DatabaseItem) * items.returnedcount));
+
+ j = 0;
+ for (i = 0; i < items.returnedcount; i++)
+ {
+ dmapGenericContainer *item = &(items.listitems[i]);
+ DAAP_ClientHost_DatabaseItem *dbitem = &(dbcontainer->items[j]);
+ DMAP_INT32 buf32;
+ DMAP_INT16 buf16;
+ DMAP_INT8 buf8;
+ DMAP_STRING itemname, songalbum, songartist;
+ DMAP_STRING songgenre, songformat;
+
+ if (dmapGeneric_LookupContainerItem_INT32(item, dmap_l("itemid"), &buf32) !=
+ DMAP_DATATYPE_INT32)
+ continue;
+ dbitem->id = buf32;
+
+ if (dmapGeneric_LookupContainerItem_STRING(item, daap_l("songformat"), &songformat) !=
+ DMAP_DATATYPE_STRING)
+ continue;
+
+ if (dmapGeneric_LookupContainerItem_STRING(item, dmap_l("itemname"), &itemname) !=
+ DMAP_DATATYPE_STRING)
+ continue;
+
+ if (dmapGeneric_LookupContainerItem_STRING(item, daap_l("songalbum"), &songalbum) !=
+ DMAP_DATATYPE_STRING)
+ songalbum = NULL;
+
+ if (dmapGeneric_LookupContainerItem_STRING(item, daap_l("songartist"), &songartist) !=
+ DMAP_DATATYPE_STRING)
+ songartist = NULL;
+
+ if (dmapGeneric_LookupContainerItem_INT16(item, daap_l("songbeatsperminute"), &buf16) ==
+ DMAP_DATATYPE_INT16)
+ dbitem->songbeatsperminute = buf16;
+ else
+ dbitem->songbeatsperminute = 0;
+
+ if (dmapGeneric_LookupContainerItem_INT16(item, daap_l("songbitrate"), &buf16) ==
+ DMAP_DATATYPE_INT16)
+ dbitem->songbitrate = buf16;
+ else
+ dbitem->songbitrate = 0;
+
+ if (dmapGeneric_LookupContainerItem_INT16(item, daap_l("songdisccount"), &buf16) ==
+ DMAP_DATATYPE_INT16)
+ dbitem->songdisccount = buf16;
+ else
+ dbitem->songdisccount = 0;
+
+ if (dmapGeneric_LookupContainerItem_INT16(item, daap_l("songdiscnumber"), &buf16) ==
+ DMAP_DATATYPE_INT16)
+ dbitem->songdiscnumber = buf16;
+ else
+ dbitem->songdiscnumber = buf16;
+
+ if (dmapGeneric_LookupContainerItem_STRING(item, daap_l("songgenre"), &songgenre) !=
+ DMAP_DATATYPE_STRING)
+ songgenre = NULL;
+
+ if (dmapGeneric_LookupContainerItem_INT32(item, daap_l("songsamplerate"), &buf32) !=
+ DMAP_DATATYPE_INT32)
+ continue;
+ dbitem->songsamplerate = buf32;
+
+ if (dmapGeneric_LookupContainerItem_INT32(item, daap_l("songsize"), &buf32) !=
+ DMAP_DATATYPE_INT32)
+ continue;
+ dbitem->songsize = buf32;
+
+ if (dmapGeneric_LookupContainerItem_INT32(item, daap_l("songtime"), &buf32) !=
+ DMAP_DATATYPE_INT32)
+ continue;
+ dbitem->songtime = buf32;
+
+ if (dmapGeneric_LookupContainerItem_INT16(item, daap_l("songtrackcount"), &buf16) ==
+ DMAP_DATATYPE_INT16)
+ dbitem->songtrackcount = buf16;
+ else
+ dbitem->songtrackcount = 0;
+
+ if (dmapGeneric_LookupContainerItem_INT16(item, daap_l("songtracknumber"), &buf16) ==
+ DMAP_DATATYPE_INT16)
+ dbitem->songtracknumber = buf16;
+ else
+ dbitem->songtracknumber = 0;
+
+ if (dmapGeneric_LookupContainerItem_INT8(item, daap_l("songuserrating"), &buf8) ==
+ DMAP_DATATYPE_INT8)
+ dbitem->songuserrating = buf8;
+ else
+ dbitem->songuserrating = 0;
+
+ if (dmapGeneric_LookupContainerItem_INT16(item, daap_l("songyear"), &buf16) ==
+ DMAP_DATATYPE_INT16)
+ dbitem->songyear = buf16;
+ else
+ dbitem->songyear = 0;
+
+ strcpy(strpos, itemname);
+ dbitem->itemname = strpos;
+ strpos += strlen(strpos)+1;
+
+ if (songalbum)
+ {
+ strcpy(strpos, songalbum);
+ dbitem->songalbum = strpos;
+ strpos += strlen(strpos)+1;
+ }
+ else dbitem->songalbum = NULL;
+
+ if (songartist)
+ {
+ strcpy(strpos, songartist);
+ dbitem->songartist = strpos;
+ strpos += strlen(strpos)+1;
+ }
+ else dbitem->songartist = NULL;
+
+ if (songgenre)
+ {
+ strcpy(strpos, songgenre);
+ dbitem->songgenre = strpos;
+ strpos += strlen(strpos)+1;
+ }
+ else dbitem->songgenre = NULL;
+
+ strcpy(strpos, songformat);
+ dbitem->songformat = strpos;
+ strpos += strlen(strpos)+1;
+
+ j++;
+ }
+
+ dbcontainer->nItems = j;
+ TRACE("items: %i\n", j);
+
+ freeGenericPreListing(&items);
+
+ return 0;
+}
+
+static int Priv_DAAP_ClientHost_GetDatabasePlaylistItems(DAAP_SClientHost *pCHThis,
+ int databaseid,
+ int playlistid)
+{
+ char hash[33] = {0};
+ char playlistUrl[] = "/databases/%i/containers/%i/items?session-id=%i&revision-number=%i";
+ char *buf;
+
+ protoParseResult_genericPreListing playlist;
+
+ DatabasePlaylistContainer *dbcontainer = NULL;
+ DAAP_ClientHost_DatabasePlaylist *dbplaylist;
+
+ HTTP_GetResult *httpRes;
+
+ int i;
+ //char *strpos;
+ int sizereq;
+
+ for (i = 0; i < pCHThis->nDatabases; i++)
+ {
+ DatabasePlaylistContainer *container = &(pCHThis->dbplaylists[i]);
+ if (container->id == databaseid)
+ {
+ dbcontainer = container;
+ break;
+ }
+ }
+
+ if (!dbcontainer)
+ {
+ ERR("container not found, returning\n");
+ freeGenericPreListing(&playlist);
+ return 1;
+ }
+
+ dbplaylist = NULL;
+ for (i = 0; i < dbcontainer->nPlaylists; i++)
+ {
+ if (dbcontainer->playlists[i].id == playlistid)
+ dbplaylist = &(dbcontainer->playlists[i]);
+ }
+ if (!dbplaylist)
+ {
+ ERR("playlist (%i) not found, returning\n", playlistid);
+ freeGenericPreListing(&playlist);
+ return 1;
+ }
+
+ buf = safe_sprintf(playlistUrl, databaseid, playlistid, pCHThis->sessionid, pCHThis->revision_number);
+
+ GenerateHash(pCHThis->version_major, buf, 2, hash, 0);
+
+ httpRes = HTTP_Client_Get(pCHThis->connection, buf, hash, NULL, 0);
+
+ free(buf);
+
+ HTTP_RETURN_IF_FAILED(httpRes);
+
+ playlist.h.expecting = QUERY_GENERICLISTING;
+
+ dmap_parseProtocolData(httpRes->contentlen, httpRes->data,
+ (protoParseResult*)&playlist);
+
+ HTTP_Client_FreeResult(httpRes);
+
+ if (playlist.totalcount != playlist.returnedcount)
+ FIXME("didn't return all playlists's, need to handle split\n");
+
+ TRACE("returnedcount: %i\n", playlist.returnedcount);
+
+ sizereq = sizeof(DAAP_ClientHost_DatabasePlaylistItem) * playlist.returnedcount;
+
+ for (i = 0; i < playlist.returnedcount; i++)
+ {
+ dmapGenericContainer *item = &(playlist.listitems[i]);
+
+ DMAP_INT32 buf32;
+ //DMAP_STRING buf;
+ if (dmapGeneric_LookupContainerItem_INT32(item, dmap_l("itemid"), &buf32) !=
+ DMAP_DATATYPE_INT32)
+ continue;
+ }
+
+ dbplaylist->items = malloc(sizereq);
+ //items = malloc(sizereq);
+ for (i = 0; i < playlist.returnedcount; i++)
+ {
+ dmapGenericContainer *item = &(playlist.listitems[i]);
+ DAAP_ClientHost_DatabasePlaylistItem *playlistitem = &(dbplaylist->items[i]);
+ DMAP_INT32 buf32;
+
+ if (dmapGeneric_LookupContainerItem_INT32(item, dmap_l("itemid"), &buf32) !=
+ DMAP_DATATYPE_INT32)
+ continue;
+ playlistitem->songid = buf32;
+ }
+
+ freeGenericPreListing(&playlist);
+ return 0;
+}
+
+static int Priv_DAAP_ClientHost_GetDatabasePlaylists(DAAP_SClientHost *pCHThis,
+ int databaseid)
+{
+ char hash[33] = {0};
+ char playlistUrl[] = "/databases/%i/containers?session-id=%i&revision-number=%i";
+ char *buf;
+
+ protoParseResult_genericPreListing playlists;
+
+ DatabasePlaylistContainer *dbcontainer = NULL;
+
+ HTTP_GetResult *httpRes;
+
+ int i, j;
+ char *strpos;
+ int sizereq;
+
+ for (i = 0; i < pCHThis->nDatabases; i++)
+ {
+ DatabasePlaylistContainer *container = &(pCHThis->dbplaylists[i]);
+ if (container->id == databaseid)
+ {
+ dbcontainer = container;
+ break;
+ }
+ }
+
+ if (!dbcontainer)
+ {
+ ERR("container not found, returning\n");
+ freeGenericPreListing(&playlists);
+ }
+
+ buf = safe_sprintf(playlistUrl, databaseid, pCHThis->sessionid, pCHThis->revision_number);
+
+ GenerateHash(pCHThis->version_major, buf, 2, hash, 0);
+
+ httpRes = HTTP_Client_Get(pCHThis->connection, buf, hash, NULL, 0);
+
+ free(buf);
+
+ HTTP_RETURN_IF_FAILED(httpRes);
+
+ playlists.h.expecting = QUERY_GENERICLISTING;
+
+ dmap_parseProtocolData(httpRes->contentlen, httpRes->data,
+ (protoParseResult*)&playlists);
+
+ HTTP_Client_FreeResult(httpRes);
+
+ if (playlists.totalcount != playlists.returnedcount)
+ FIXME("didn't return all playlists's, need to handle split\n");
+
+ TRACE("returnedcount: %i\n", playlists.returnedcount);
+
+ sizereq = sizeof(DAAP_ClientHost_DatabasePlaylist) * playlists.returnedcount;
+
+ for (i = 0; i < playlists.returnedcount; i++)
+ {
+ dmapGenericContainer *playlist = &(playlists.listitems[i]);
+ DMAP_INT32 buf32;
+ DMAP_STRING buf;
+
+ if (dmapGeneric_LookupContainerItem_INT32(playlist, dmap_l("itemcount"), &buf32) !=
+ DMAP_DATATYPE_INT32)
+ continue;
+
+ if (dmapGeneric_LookupContainerItem_INT32(playlist, dmap_l("itemid"), &buf32) !=
+ DMAP_DATATYPE_INT32)
+ continue;
+
+ if (dmapGeneric_LookupContainerItem_STRING(playlist, dmap_l("itemname"), &buf) !=
+ DMAP_DATATYPE_STRING)
+ continue;
+ sizereq += strlen(buf) + 1;
+ }
+
+ if (dbcontainer->playlists) free(dbcontainer->playlists);
+
+ dbcontainer->playlists_size = sizereq;
+ dbcontainer->playlists = malloc(sizereq);
+
+ strpos = (char*)(((char*)dbcontainer->playlists) +
+ (sizeof(DAAP_ClientHost_DatabasePlaylist) * playlists.returnedcount));
+
+ j = 0;
+ for (i = 0; i < playlists.returnedcount; i++)
+ {
+ dmapGenericContainer *playlist = &(playlists.listitems[i]);
+ DAAP_ClientHost_DatabasePlaylist *dbplaylist = &(dbcontainer->playlists[j]);
+ DMAP_INT32 buf32;
+ DMAP_STRING itemname;
+
+ if (dmapGeneric_LookupContainerItem_INT32(playlist, dmap_l("itemcount"), &buf32) !=
+ DMAP_DATATYPE_INT32)
+ continue;
+ dbplaylist->count = buf32;
+
+ if (dmapGeneric_LookupContainerItem_INT32(playlist, dmap_l("itemid"), &buf32) !=
+ DMAP_DATATYPE_INT32)
+ continue;
+ dbplaylist->id = buf32;
+
+ if (dmapGeneric_LookupContainerItem_STRING(playlist, dmap_l("itemname"), &itemname) !=
+ DMAP_DATATYPE_STRING)
+ continue;
+
+ strcpy(strpos, itemname);
+ dbplaylist->itemname = strpos;
+ strpos += strlen(strpos)+1;
+
+ TRACE("(%i) got a playlist '%s', item count: %i\n",
+ dbplaylist->id, dbplaylist->itemname, dbplaylist->count);
+
+ j++;
+ dbcontainer->nPlaylists = j; /* need to update on the go for the following to work: */
+
+ Priv_DAAP_ClientHost_GetDatabasePlaylistItems(pCHThis, databaseid, dbplaylist->id);
+ }
+
+ TRACE("playlists: %i\n", dbcontainer->nPlaylists);
+
+ freeGenericPreListing(&playlists);
+
+ return 0;
+}
+
+
+/* private create */
+static DAAP_SClientHost *DAAP_ClientHost_Create(DAAP_SClient *parent, char *host,
+ char *sharename)
+{
+ DAAP_SClientHost *pClientHostNew = malloc(sizeof(DAAP_SClientHost));
+ memset(pClientHostNew, 0, sizeof(DAAP_SClientHost));
+
+ /* HACK */ pClientHostNew->interrupt = 66;
+
+ pClientHostNew->uiRef = 1;
+
+ /* we don't hold a reference to this.. as it will always exist
+ * while this object exists.
+ */
+ pClientHostNew->parent = parent;
+
+ strncpy(pClientHostNew->sharename_friendly, sharename,
+ sizeof(pClientHostNew->sharename_friendly) - 1);
+
+ pClientHostNew->host = malloc(strlen(host) + 1);
+ strcpy(pClientHostNew->host, host);
+
+ pClientHostNew->password = NULL;
+
+ pClientHostNew->prev = NULL;
+ pClientHostNew->next = NULL;
+
+ return pClientHostNew;
+
+}
+
+/* public */
+unsigned int DAAP_ClientHost_AddRef(DAAP_SClientHost *pCHThis)
+{
+ return ++pCHThis->uiRef;
+}
+
+unsigned int DAAP_ClientHost_Release(DAAP_SClientHost *pCHThis)
+{
+ if (--pCHThis->uiRef)
+ return pCHThis->uiRef;
+
+ ERR("freeing (ref %i)\n", pCHThis->uiRef);
+
+ if (pCHThis->connection) HTTP_Client_Close(pCHThis->connection);
+ if (pCHThis->databases) free(pCHThis->databases);
+ if (pCHThis->dbitems)
+ {
+ int i;
+ for (i = 0; i < pCHThis->nDatabases; i++)
+ {
+ free(&(pCHThis->dbitems->items[i]));
+ }
+ free(pCHThis->dbitems);
+ }
+
+ if (pCHThis->password) free(pCHThis->password);
+
+ free(pCHThis->host);
+ free(pCHThis);
+
+ return 0;
+}
+
+unsigned int DAAP_ClientHost_GetSharename(DAAP_SClientHost *pCHThis,
+ char *buf,
+ int bufsize)
+{
+ int reqsize;
+
+ reqsize = strlen(pCHThis->sharename_friendly) + 1;
+
+ if (bufsize < reqsize)
+ return reqsize;
+
+ strcpy(buf, pCHThis->sharename_friendly);
+ return 0;
+}
+
+static char *encode_base64(char *string)
+{
+ static const char base64chars[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ int out_index = 0;
+
+ int outlen = ((strlen(string) * 4) / 3) + 4;
+ char *out = malloc(outlen+1);
+ memset(out, 0, outlen);
+
+ while (string[0])
+ {
+ int index;
+
+ /* first 6 bits from string[0] */
+ index = (string[0] & 0xFC) >> 2;
+ out[out_index++] = base64chars[index];
+
+ /* last 2 bits from string[0] and 6 bits from string[1] */
+ index = (string[0] & 0x3) << 4;
+ index |= (string[1] & 0xF0) >> 4;
+ out[out_index++] = base64chars[index];
+
+ /* but if string[1] was 0, it's the final char. fill the rest with pad */
+ if (!string[1])
+ {
+ out[out_index++] = '=';
+ out[out_index++] = '=';
+ break;
+ }
+
+ /* last 4 bits from string[1] and 2 bits from string[2] */
+ index = (string[1] & 0x0F) << 2;
+ index |= (string[2] & 0xC0) >> 6;
+ out[out_index++] = base64chars[index];
+
+ /* but if string[2] was 0, it was the final char. */
+ if (!string[2])
+ {
+ out[out_index++] = '=';
+ break;
+ }
+
+ /* finally, last 6 bits of string[2] */
+ index = (string[2] & 0x3F);
+ out[out_index++] = base64chars[index];
+
+ string += 3;
+ }
+ out[out_index++] = 0;
+
+ return out;
+}
+
+void DAAP_ClientHost_SetPassword(DAAP_SClientHost *pCHThis,
+ char *password)
+{
+ char *tmppass;
+
+ if (pCHThis->password)
+ free(pCHThis->password);
+
+ tmppass = malloc(strlen(password) + 2);
+ tmppass[0] = ':';
+ strcpy(tmppass+1, password);
+
+ pCHThis->password = encode_base64(tmppass);
+
+ free(tmppass);
+}
+
+unsigned int DAAP_ClientHost_Connect(DAAP_SClientHost *pCHThis)
+{
+ int retcode = 1;
+
+ if (pCHThis->connection)
+ {
+ ERR("already connected? %i\n", pCHThis->interrupt);
+ goto err;
+ }
+
+ TRACE("connecting to %s\n", pCHThis->host);
+ pCHThis->connection = HTTP_Client_Open(pCHThis->host, pCHThis->password);
+ if (!pCHThis->connection)
+ {
+ ERR("couldn't open connection to host\n");
+ goto err;
+ }
+ TRACE("connected\n");
+
+ /* do initial login etc */
+ if ((retcode = Priv_DAAP_ClientHost_InitialTransaction(pCHThis)))
+ {
+ ERR("couldn't finish initial transaction with server. [%i]\n", retcode);
+ goto err;
+ }
+ if ((retcode = Priv_DAAP_ClientHost_GetDatabases(pCHThis)))
+ {
+ ERR("couldn't get database list [%i]\n", retcode);
+ goto err;
+ }
+
+ return 0;
+err:
+ if (pCHThis->connection)
+ {
+ HTTP_Client_Close(pCHThis->connection);
+ pCHThis->connection = NULL;
+ }
+ return -1 * retcode;
+}
+
+unsigned int DAAP_ClientHost_Disconnect(DAAP_SClientHost *pCHThis)
+{
+ if (!pCHThis->connection)
+ return -1;
+
+ HTTP_Client_Close(pCHThis->connection);
+ pCHThis->connection = NULL;
+ return 0;
+}
+
+unsigned int DAAP_ClientHost_GetPlaylists(DAAP_SClientHost *pCHThis,
+ int databaseid,
+ DAAP_ClientHost_DatabasePlaylist *buffer,
+ int *n, int bufsize)
+{
+ if (!pCHThis->connection)
+ return -1;
+
+ /* FIXME: ignoring databaseid */
+
+ if (bufsize < pCHThis->dbplaylists->playlists_size)
+ return pCHThis->dbplaylists->playlists_size;
+
+ memcpy(buffer, pCHThis->dbplaylists->playlists,
+ pCHThis->dbplaylists->playlists_size);
+ *n = pCHThis->dbplaylists->nPlaylists;
+
+ return 0;
+}
+
+unsigned int DAAP_ClientHost_GetPlaylistItems(DAAP_SClientHost *pCHThis,
+ int databaseid, int playlistid,
+ DAAP_ClientHost_DatabasePlaylistItem *buffer,
+ int *n, int bufsize)
+{
+ int i;
+
+ /* FIXME: compile time assert.
+ * basically we need to store the playlist items in a private
+ * container structure so we know the size of the list.
+ * for the moment we are just presuming they are a list of ints.
+ */
+ char assert[(sizeof(DAAP_ClientHost_DatabasePlaylistItem) == sizeof(int)) ? 1 : -1];
+
+ if (!pCHThis->connection)
+ return -1;
+
+ /* FIXME: ignoring databaseid */
+
+ for (i = 0; i < pCHThis->dbplaylists->nPlaylists; i++)
+ {
+ if (pCHThis->dbplaylists->playlists[i].id == playlistid)
+ {
+ if (bufsize < (pCHThis->dbplaylists->playlists[i].count *
+ (int)sizeof(DAAP_ClientHost_DatabasePlaylistItem)))
+ return (pCHThis->dbplaylists->playlists[i].count *
+ sizeof(DAAP_ClientHost_DatabasePlaylistItem));
+
+ if (pCHThis->dbplaylists->playlists[i].count == 0)
+ return 0;
+
+ memcpy(buffer, pCHThis->dbplaylists->playlists[i].items,
+ (pCHThis->dbplaylists->playlists[i].count *
+ sizeof(DAAP_ClientHost_DatabasePlaylistItem)));
+
+ *n = pCHThis->dbplaylists->playlists[i].count;
+
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+unsigned int DAAP_Client_GetDatabases(DAAP_SClientHost *pCHThis)
+{
+ if (!Priv_DAAP_ClientHost_GetDatabases(pCHThis))
+ {
+ ERR("couldn't get database list\n");
+ return -1;
+ }
+ return 0;
+}
+
+unsigned int DAAP_ClientHost_GetDatabases(DAAP_SClientHost *pCHThis,
+ DAAP_ClientHost_Database *buffer,
+ int *n, int bufsize)
+{
+ if (!pCHThis->connection)
+ return -1;
+
+ if (bufsize < pCHThis->databases_size)
+ return pCHThis->databases_size;
+
+ memcpy(buffer, pCHThis->databases, pCHThis->databases_size);
+ *n = pCHThis->nDatabases;
+
+ return 0;
+}
+
+int DAAP_ClientHost_GetDatabaseItems(DAAP_SClientHost *pCHThis,
+ int databaseid,
+ DAAP_ClientHost_DatabaseItem *buffer,
+ int *n, int bufsize)
+{
+ int i;
+
+ if (!pCHThis->connection)
+ return -1;
+
+ for (i = 0; i < pCHThis->nDatabases; i++)
+ {
+ if (pCHThis->dbitems[i].id == databaseid)
+ {
+ if (bufsize < pCHThis->dbitems[i].items_size)
+ return pCHThis->dbitems[i].items_size;
+
+ memcpy(buffer, pCHThis->dbitems[i].items,
+ pCHThis->dbitems[i].items_size);
+ *n = pCHThis->dbitems[i].nItems;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+int DAAP_ClientHost_GetAudioFile(DAAP_SClientHost *pCHThis,
+ int databaseid, int songid,
+ const char *songformat,
+ DAAP_ClientHost_Song *song)
+{
+ char hash[33] = {0};
+ char *hashUrl;
+
+ char songUrl_42[] = "/databases/%i/items/%i.%s?session-id=%i&revision-id=%i";
+ char songUrl_45[] = "daap://%s/databases/%i/items/%i.%s?session-id=%i";
+ char *buf;
+
+ char requestid_45[] = "Client-DAAP-Request-ID: %u\r\n";
+ char *buf_45extra = NULL;
+ int requestid = 0;
+
+ HTTP_Connection *http_connection;
+ HTTP_GetResult *httpRes;
+
+ if (strlen(songformat) > 4) return -1;
+
+ if (pCHThis->version_major != 3)
+ {
+ buf = safe_sprintf(songUrl_42, databaseid, songid, songformat,
+ pCHThis->sessionid, pCHThis->revision_number);
+ }
+ else
+ {
+ buf = safe_sprintf(songUrl_45, pCHThis->host, databaseid, songid,
+ songformat, pCHThis->sessionid);
+ requestid = ++pCHThis->request_id;
+ buf_45extra = safe_sprintf(requestid_45, requestid);
+ }
+
+ /* dodgy hack as the hash for 4.5 needs to not include daap:// */
+ if (!strstr(buf, "daap://"))
+ hashUrl = buf;
+ else
+ hashUrl = strstr(buf, "/databases");
+
+ GenerateHash(pCHThis->version_major, hashUrl, 2, hash, requestid);
+
+ /* use a seperate connection */
+ http_connection = HTTP_Client_Open(pCHThis->host, pCHThis->password);
+
+ /* 1 = Connection: Close*/
+ TRACE("untested\n");
+ httpRes = HTTP_Client_Get(http_connection, buf, hash,
+ requestid ? buf_45extra : NULL,
+ 1);
+
+ free(buf);
+ free(buf_45extra);
+
+ HTTP_Client_Close(http_connection);
+
+ /* custom */
+ if (!httpRes) return -1;
+ if (httpRes->httpStatusCode != 200)
+ {
+ int ret = -1 * httpRes->httpStatusCode;
+ free(httpRes);
+ return ret;
+ }
+
+ song->size = httpRes->contentlen;
+ song->data = malloc(httpRes->contentlen);
+ memcpy(song->data, httpRes->data, httpRes->contentlen);
+
+ HTTP_Client_FreeResult(httpRes);
+
+ return 0;
+}
+
+int DAAP_ClientHost_FreeAudioFile(DAAP_SClientHost *pCHThis,
+ DAAP_ClientHost_Song *song)
+{
+ free(song->data);
+ return 0;
+}
+
+typedef struct
+{
+ char *url;
+ char *extra_header;
+ int requestid;
+#if defined(SYSTEM_POSIX)
+ int fileid;
+#elif defined(SYSTEM_WIN32)
+ HANDLE fileid;
+#else
+ FILE *fileid;
+#endif
+
+#if defined(WIN32) || defined(_LINUX)
+ DAAP_fnHttpWrite callback;
+ void *context;
+#endif
+
+ int interrupt;
+} GetFile;
+
+static int httpCallback(void *pv_pCHThis, int pos)
+{
+ DAAP_SClientHost *pCHThis = (DAAP_SClientHost*)pv_pCHThis;
+ if (pCHThis->interrupt)
+ {
+ return 1;
+ }
+ if (pCHThis->parent->pfnCallbackStatus)
+ pCHThis->parent->pfnCallbackStatus(pCHThis->parent,
+ DAAP_STATUS_downloading,
+ pos,
+ pCHThis->parent->pvCallbackStatusContext);
+ return 0;
+}
+
+static void AsyncGetFile(void *pv_pCHThis, void *pv_pGetFile)
+{
+ GetFile *pGetFile = (GetFile*)pv_pGetFile;
+ DAAP_SClientHost *pCHThis = (DAAP_SClientHost*)pv_pCHThis;
+ HTTP_Connection *http_connection;
+ int ret;
+
+ char hash[33] = {0};
+ char *hashUrl;
+
+ /* dodgy hack as the hash for 4.5 needs to not include daap:// */
+ if (!strstr(pGetFile->url, "daap://"))
+ hashUrl = pGetFile->url;
+ else
+ hashUrl = strstr(pGetFile->url, "/databases");
+
+ pCHThis->interrupt = 0;
+
+ GenerateHash(pCHThis->version_major, hashUrl, 2, hash, pGetFile->requestid);
+
+ if (pCHThis->parent->pfnCallbackStatus)
+ pCHThis->parent->pfnCallbackStatus(pCHThis->parent,
+ DAAP_STATUS_negotiating,
+ 0,
+ pCHThis->parent->pvCallbackStatusContext);
+
+ /* use a seperate connection */
+ http_connection = HTTP_Client_Open(pCHThis->host, pCHThis->password);
+ if (!http_connection) goto err;
+
+ if( pGetFile->callback )
+ {
+ ret = HTTP_Client_Get_Callback(http_connection, pGetFile->url,
+ hash, pGetFile->extra_header,
+ 1 /* 1 = Connection: close */,
+ pGetFile->callback,
+ pGetFile->context);
+ }
+ else
+ {
+ ret = HTTP_Client_Get_ToFile(http_connection, pGetFile->url,
+ hash, pGetFile->extra_header, pGetFile->fileid,
+ /* 1 = Connection: close */
+ httpCallback, pv_pCHThis, 1);
+
+ }
+
+ /* finished prematurely, and not interrupted */
+ if (!ret && !pCHThis->interrupt) goto err;
+
+
+ if( pGetFile->callback )
+ {
+ /*Send null data to show we are finished*/
+ pGetFile->callback(NULL, 0, 0, 0, pGetFile->context);
+ }
+
+ HTTP_Client_Close(http_connection);
+ http_connection = NULL;
+
+ pCHThis->interrupt = 0;
+
+ if (pCHThis->parent->pfnCallbackStatus)
+ pCHThis->parent->pfnCallbackStatus(pCHThis->parent,
+ DAAP_STATUS_idle,
+ 0,
+ pCHThis->parent->pvCallbackStatusContext);
+
+ free(pGetFile->url);
+ if (pGetFile->extra_header)
+ free(pGetFile->extra_header);
+ free(pGetFile);
+ DAAP_ClientHost_Release(pCHThis);
+
+ return;
+
+err:
+ if( pGetFile->callback )
+ {
+ /*Send null data with error*/
+ pGetFile->callback(NULL, 0, -1, 0, pGetFile->context);
+ }
+
+ if (http_connection)
+ HTTP_Client_Close(http_connection);
+
+ free(pGetFile);
+ DAAP_ClientHost_Release(pCHThis);
+
+ if (pCHThis->parent->pfnCallbackStatus)
+ pCHThis->parent->pfnCallbackStatus(pCHThis->parent,
+ DAAP_STATUS_error,
+ 0,
+ pCHThis->parent->pvCallbackStatusContext);
+}
+
+#if !defined(SYSTEM_POSIX)
+typedef struct
+{
+ void *arg1, *arg2;
+} tsApiWrap_data;
+
+ts_thread_cb(tsApiWrap_AsyncGetFile)
+{
+ tsApiWrap_data *data = arg;
+
+ AsyncGetFile(data->arg1, data->arg2);
+
+ free(data);
+
+ return ts_thread_defaultret;
+}
+#endif
+
+int DAAP_ClientHost_AsyncGetAudioFile(DAAP_SClientHost *pCHThis,
+ int databaseid, int songid,
+ const char *songformat,
+#if defined(SYSTEM_POSIX)
+ int fd)
+#elif defined(SYSTEM_WIN32)
+ HANDLE fd)
+#else
+ FILE *fd)
+#endif
+{
+ /* FIXME: aac?? */
+ char songUrl_42[] = "/databases/%i/items/%i.%s?session-id=%i&revision-id=%i";
+ char songUrl_45[] = "daap://%s/databases/%i/items/%i.%s?session-id=%i";
+
+ char requestid_45[] = "Client-DAAP-Request-ID: %u\r\n";
+
+ GetFile *pGetFile = malloc(sizeof(GetFile));
+
+ pGetFile->fileid = fd;
+ pGetFile->url = NULL;
+ pGetFile->extra_header = NULL;
+
+ if (pCHThis->version_major != 3)
+ {
+ pGetFile->url = safe_sprintf(songUrl_42, databaseid, songid,
+ songformat, pCHThis->sessionid, pCHThis->revision_number);
+ }
+ else
+ {
+ pGetFile->url = safe_sprintf(songUrl_45, pCHThis->host, databaseid, songid,
+ songformat, pCHThis->sessionid);
+ pGetFile->requestid = ++pCHThis->request_id;
+ pGetFile->extra_header = safe_sprintf(requestid_45, pGetFile->requestid);
+ }
+
+ DAAP_ClientHost_AddRef(pCHThis);
+
+#if defined(SYSTEM_POSIX)
+ CP_ThreadPool_QueueWorkItem(pCHThis->parent->tp, AsyncGetFile,
+ (void*)pCHThis, (void*)pGetFile);
+#else /*if defined(SYSTEM_WIN32) */
+ {
+ ts_thread thread;
+ tsApiWrap_data *data = malloc(sizeof(tsApiWrap_data));
+ data->arg1 = (void*)pCHThis;
+ data->arg2 = (void*)pGetFile;
+ ts_thread_create(thread, tsApiWrap_AsyncGetFile, data);
+#ifndef WIN32 //don't close this here since we need this handle later
+ ts_thread_close(thread);
+#endif
+ }
+#endif
+
+ return 0;
+}
+
+#ifdef WIN32
+int DAAP_ClientHost_AsyncGetAudioFileCallback(DAAP_SClientHost *pCHThis,
+ int databaseid, int songid,
+ const char *songformat,
+ int startbyte,
+ DAAP_fnHttpWrite callback,
+ void* context)
+{
+ /* FIXME: aac?? */
+ char songUrl_42[] = "/databases/%i/items/%i.%s?session-id=%i&revision-id=%i";
+ char songUrl_45[] = "daap://%s/databases/%i/items/%i.%s?session-id=%i";
+
+ char requestid_45[] = "Client-DAAP-Request-ID: %u\r\n";
+
+ char requestresume[] = "Range: bytes=%d-\r\n";
+
+ char *requestid = NULL;
+ char *start = NULL;
+
+ GetFile *pGetFile = malloc(sizeof(GetFile));
+
+ pGetFile->fileid = NULL;
+ pGetFile->url = NULL;
+ pGetFile->extra_header = NULL;
+
+ pGetFile->callback = callback;
+ pGetFile->context = context;
+
+
+ if (pCHThis->version_major != 3)
+ {
+ pGetFile->url = safe_sprintf(songUrl_42, databaseid, songid,
+ songformat, pCHThis->sessionid, pCHThis->revision_number);
+ }
+ else
+ {
+ pGetFile->url = safe_sprintf(songUrl_45, pCHThis->host, databaseid, songid,
+ songformat, pCHThis->sessionid);
+ pGetFile->requestid = ++pCHThis->request_id;
+ requestid = safe_sprintf(requestid_45, pGetFile->requestid);
+ }
+
+
+ if( startbyte )
+ {
+ start = safe_sprintf(requestresume, startbyte);
+ }
+
+ pGetFile->extra_header = safe_sprintf("%s%s", requestid,start );
+ if( start ) free(start);
+ if( requestid ) free(requestid);
+
+ DAAP_ClientHost_AddRef(pCHThis);
+
+#if defined(SYSTEM_POSIX)
+ CP_ThreadPool_QueueWorkItem(pCHThis->parent->tp, AsyncGetFile,
+ (void*)pCHThis, (void*)pGetFile);
+#else /*if defined(SYSTEM_WIN32) */
+ {
+ ts_thread thread;
+ tsApiWrap_data *data = malloc(sizeof(tsApiWrap_data));
+ data->arg1 = (void*)pCHThis;
+ data->arg2 = (void*)pGetFile;
+ ts_thread_create(thread, tsApiWrap_AsyncGetFile, data);
+ ts_thread_close(thread);
+ }
+#endif
+
+ return 0;
+
+}
+
+#endif
+
+int DAAP_ClientHost_AsyncStop(DAAP_SClientHost *pCHThis)
+{
+ pCHThis->interrupt = 1;
+ return 0;
+}
+
+/********** update watcher ***********/
+
+static void update_watch_cb(void *pv_pCHThis)
+{
+ DAAP_SClientHost *pCHThis = (DAAP_SClientHost*)pv_pCHThis;
+ FIXME("got an update from host %p (%s). Expect brokenness!\n",
+ pCHThis, pCHThis->sharename_friendly);
+ /* FIXME: since we don't handle updates just yet, we'll remove
+ * ourselves from the update watch.
+ * This will result in 505s as the iTunes times out waiting for us.
+ */
+ HTTP_Client_WatchQueue_RemoveUpdateWatch(pCHThis->parent->update_watch,
+ pCHThis->connection);
+}
+
+static void AsyncWaitUpdate(void *pv_pCHThis, void *unused)
+{
+ DAAP_SClientHost *pCHThis = (DAAP_SClientHost*)pv_pCHThis;
+ char hash[33] = {0};
+ char updateUrl[] = "/update?session-id=%i&revision-number=%i&delta=%i";
+ char *buf;
+ TRACE("()\n");
+
+ buf = safe_sprintf(updateUrl, pCHThis->sessionid, pCHThis->revision_number,
+ pCHThis->revision_number);
+ GenerateHash(pCHThis->version_major, buf, 2, hash, 0);
+
+ /* should return pretty quickly */
+ HTTP_Client_WatchQueue_AddUpdateWatch(pCHThis->parent->update_watch,
+ pCHThis->connection, buf, hash,
+ update_watch_cb,
+ pv_pCHThis);
+
+ free(buf);
+}
+
+static void update_watch_runloop(void *pv_pUpdateWatch, void *unused)
+{
+ HTTP_ConnectionWatch *watch = (HTTP_ConnectionWatch*)pv_pUpdateWatch;
+ HTTP_Client_WatchQueue_RunLoop(watch);
+}
+
+#ifndef WIN32
+int DAAP_ClientHost_AsyncWaitUpdate(DAAP_SClientHost *pCHThis)
+{
+ /* lazy create update_watch */
+ ts_mutex_lock(pCHThis->parent->mtObjectLock);
+ if (!pCHThis->parent->update_watch)
+ {
+ pCHThis->parent->update_watch = HTTP_Client_WatchQueue_New();
+ if (!pCHThis->parent->update_watch)
+ {
+ ERR("couldn't create update watch\n");
+ return 1;
+ }
+ /* and now run it in a new thread */
+#if defined(SYSTEM_POSIX)
+ CP_ThreadPool_QueueWorkItem(pCHThis->parent->tp, update_watch_runloop,
+ (void*)pCHThis->parent->update_watch, NULL);
+#else
+#error implement me
+#endif
+ }
+ ts_mutex_unlock(pCHThis->parent->mtObjectLock);
+ TRACE("about to call async wait update\n");
+#if defined(SYSTEM_POSIX)
+ /* doesn't _really_ need to be done in a different thread, but
+ * the HTTP GET could take a little while, and then AddUpdateWatch will return
+ */
+ TRACE("calling\n");
+ CP_ThreadPool_QueueWorkItem(pCHThis->parent->tp, AsyncWaitUpdate,
+ (void*)pCHThis, NULL);
+#else
+#error please implement
+#endif
+ return 0;
+}
+#endif
+
+int DAAP_ClientHost_AsyncStopUpdate(DAAP_SClientHost *pCHThis)
+{
+ /* that's naughty, the app called this without installing a watch */
+ if (!pCHThis->parent->update_watch) return 0;
+
+ HTTP_Client_WatchQueue_RemoveUpdateWatch(pCHThis->parent->update_watch,
+ pCHThis->connection);
+ return 0;
+}
diff --git a/lib/libXDAAP/libXDAAP.h b/lib/libXDAAP/libXDAAP.h
new file mode 100644
index 0000000000..9c10a3bf9c
--- /dev/null
+++ b/lib/libXDAAP/libXDAAP.h
@@ -0,0 +1,25 @@
+#ifdef WIN32
+#include <windows.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "client.h"
+#include "httpClient.h"
+/*
+#include "daap.h"
+#include "daap_contentcodes.h"
+#include "dmap_generics.h"
+#include "Authentication/hasher.h"
+*/
+
+#define DAAP_SOCKET_FD_TYPE SOCKET
+#ifdef _LINUX
+#define DAAP_SOCKET_CLOSE close
+#else
+#define DAAP_SOCKET_CLOSE closesocket
+#endif
+#define DAAP_SOCKET_WRITE(s, b, l) send((s), (b), (l), 0)
+#define DAAP_SOCKET_READ(s, b, l) recv((s), (b), (l), 0)
+
diff --git a/lib/libXDAAP/libXDAAP_win32/libXDAAP_win32.vcproj b/lib/libXDAAP/libXDAAP_win32/libXDAAP_win32.vcproj
new file mode 100644
index 0000000000..6c5548bad7
--- /dev/null
+++ b/lib/libXDAAP/libXDAAP_win32/libXDAAP_win32.vcproj
@@ -0,0 +1,260 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9,00"
+ Name="libXDAAP"
+ ProjectGUID="{19B16CD0-3B47-47B7-AB0E-81EF2BF1B187}"
+ RootNamespace="libXDAAP"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="131072"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)libs\$(ProjectName)\$(ConfigurationName)\"
+ IntermediateDirectory="$(SolutionDir)objs\$(ProjectName)\$(ConfigurationName)\"
+ ConfigurationType="4"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions="WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="4"
+ CompileAs="1"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="$(OutDir)\$(ProjectName)d.lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)libs\$(ProjectName)\$(ConfigurationName)\"
+ IntermediateDirectory="$(SolutionDir)objs\$(ProjectName)\$(ConfigurationName)\"
+ ConfigurationType="4"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE"
+ StringPooling="true"
+ RuntimeLibrary="0"
+ BufferSecurityCheck="false"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="0"
+ CompileAs="1"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="$(OutDir)\$(ProjectName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Quelldateien"
+ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath="..\daap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\debug.c"
+ >
+ </File>
+ <File
+ RelativePath="..\dmap_generics.c"
+ >
+ </File>
+ <File
+ RelativePath="..\global.c"
+ >
+ </File>
+ <File
+ RelativePath="..\Authentication\hasher.c"
+ >
+ </File>
+ <File
+ RelativePath="..\httpClient.c"
+ >
+ </File>
+ <File
+ RelativePath="..\libXDAAP.c"
+ >
+ </File>
+ <File
+ RelativePath="..\Authentication\md5.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Headerdateien"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath="..\client.h"
+ >
+ </File>
+ <File
+ RelativePath="..\compat.h"
+ >
+ </File>
+ <File
+ RelativePath="..\daap.h"
+ >
+ </File>
+ <File
+ RelativePath="..\daap_contentcodes.h"
+ >
+ </File>
+ <File
+ RelativePath="..\daap_readtypes.h"
+ >
+ </File>
+ <File
+ RelativePath="..\debug.h"
+ >
+ </File>
+ <File
+ RelativePath="..\dmap_generics.h"
+ >
+ </File>
+ <File
+ RelativePath="..\endian_swap.h"
+ >
+ </File>
+ <File
+ RelativePath="..\httpClient.h"
+ >
+ </File>
+ <File
+ RelativePath="..\libXDAAP.h"
+ >
+ </File>
+ <File
+ RelativePath="..\Authentication\md5.h"
+ >
+ </File>
+ <File
+ RelativePath="..\portability.h"
+ >
+ </File>
+ <File
+ RelativePath="..\private.h"
+ >
+ </File>
+ <File
+ RelativePath="..\thread.h"
+ >
+ </File>
+ <File
+ RelativePath="..\types.h"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/lib/libXDAAP/libXDAAP_win32/libXDAAP_win32.vcxproj b/lib/libXDAAP/libXDAAP_win32/libXDAAP_win32.vcxproj
new file mode 100644
index 0000000000..d0ea54f360
--- /dev/null
+++ b/lib/libXDAAP/libXDAAP_win32/libXDAAP_win32.vcxproj
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectName>libXDAAP</ProjectName>
+ <ProjectGuid>{19B16CD0-3B47-47B7-AB0E-81EF2BF1B187}</ProjectGuid>
+ <RootNamespace>libXDAAP</RootNamespace>
+ <Keyword>Win32Proj</Keyword>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
+ <Import Project="..\..\..\project\VS2010Express\XBMC for Windows.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
+ <Import Project="..\..\..\project\VS2010Express\XBMC for Windows.props" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)libs\$(TargetName)\$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)objs\$(TargetName)\$(Configuration)\</IntDir>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)libs\$(TargetName)\$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)objs\$(TargetName)\$(Configuration)\</IntDir>
+ <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectName)d</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>true</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(OutDir)$(TargetFileName)</OutputFile>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>
+ </DebugInformationFormat>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(OutDir)$(TargetFileName)</OutputFile>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\daap.c" />
+ <ClCompile Include="..\debug.c" />
+ <ClCompile Include="..\dmap_generics.c" />
+ <ClCompile Include="..\global.c" />
+ <ClCompile Include="..\Authentication\hasher.c" />
+ <ClCompile Include="..\httpClient.c" />
+ <ClCompile Include="..\libXDAAP.c" />
+ <ClCompile Include="..\Authentication\md5.c" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\client.h" />
+ <ClInclude Include="..\compat.h" />
+ <ClInclude Include="..\daap.h" />
+ <ClInclude Include="..\daap_contentcodes.h" />
+ <ClInclude Include="..\daap_readtypes.h" />
+ <ClInclude Include="..\debug.h" />
+ <ClInclude Include="..\dmap_generics.h" />
+ <ClInclude Include="..\endian_swap.h" />
+ <ClInclude Include="..\httpClient.h" />
+ <ClInclude Include="..\libXDAAP.h" />
+ <ClInclude Include="..\Authentication\md5.h" />
+ <ClInclude Include="..\portability.h" />
+ <ClInclude Include="..\private.h" />
+ <ClInclude Include="..\thread.h" />
+ <ClInclude Include="..\types.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/lib/libXDAAP/libXDAAP_win32/libXDAAP_win32.vcxproj.filters b/lib/libXDAAP/libXDAAP_win32/libXDAAP_win32.vcxproj.filters
new file mode 100644
index 0000000000..bf018e4d86
--- /dev/null
+++ b/lib/libXDAAP/libXDAAP_win32/libXDAAP_win32.vcxproj.filters
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Quelldateien">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Headerdateien">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\daap.c">
+ <Filter>Quelldateien</Filter>
+ </ClCompile>
+ <ClCompile Include="..\debug.c">
+ <Filter>Quelldateien</Filter>
+ </ClCompile>
+ <ClCompile Include="..\dmap_generics.c">
+ <Filter>Quelldateien</Filter>
+ </ClCompile>
+ <ClCompile Include="..\global.c">
+ <Filter>Quelldateien</Filter>
+ </ClCompile>
+ <ClCompile Include="..\Authentication\hasher.c">
+ <Filter>Quelldateien</Filter>
+ </ClCompile>
+ <ClCompile Include="..\httpClient.c">
+ <Filter>Quelldateien</Filter>
+ </ClCompile>
+ <ClCompile Include="..\libXDAAP.c">
+ <Filter>Quelldateien</Filter>
+ </ClCompile>
+ <ClCompile Include="..\Authentication\md5.c">
+ <Filter>Quelldateien</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\client.h">
+ <Filter>Headerdateien</Filter>
+ </ClInclude>
+ <ClInclude Include="..\compat.h">
+ <Filter>Headerdateien</Filter>
+ </ClInclude>
+ <ClInclude Include="..\daap.h">
+ <Filter>Headerdateien</Filter>
+ </ClInclude>
+ <ClInclude Include="..\daap_contentcodes.h">
+ <Filter>Headerdateien</Filter>
+ </ClInclude>
+ <ClInclude Include="..\daap_readtypes.h">
+ <Filter>Headerdateien</Filter>
+ </ClInclude>
+ <ClInclude Include="..\debug.h">
+ <Filter>Headerdateien</Filter>
+ </ClInclude>
+ <ClInclude Include="..\dmap_generics.h">
+ <Filter>Headerdateien</Filter>
+ </ClInclude>
+ <ClInclude Include="..\endian_swap.h">
+ <Filter>Headerdateien</Filter>
+ </ClInclude>
+ <ClInclude Include="..\httpClient.h">
+ <Filter>Headerdateien</Filter>
+ </ClInclude>
+ <ClInclude Include="..\libXDAAP.h">
+ <Filter>Headerdateien</Filter>
+ </ClInclude>
+ <ClInclude Include="..\Authentication\md5.h">
+ <Filter>Headerdateien</Filter>
+ </ClInclude>
+ <ClInclude Include="..\portability.h">
+ <Filter>Headerdateien</Filter>
+ </ClInclude>
+ <ClInclude Include="..\private.h">
+ <Filter>Headerdateien</Filter>
+ </ClInclude>
+ <ClInclude Include="..\thread.h">
+ <Filter>Headerdateien</Filter>
+ </ClInclude>
+ <ClInclude Include="..\types.h">
+ <Filter>Headerdateien</Filter>
+ </ClInclude>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/lib/libXDAAP/portability.h b/lib/libXDAAP/portability.h
new file mode 100644
index 0000000000..11d45e8b23
--- /dev/null
+++ b/lib/libXDAAP/portability.h
@@ -0,0 +1,91 @@
+/* portability stuff
+ *
+ * Copyright (c) 2004 David Hammerton
+ * crazney@crazney.net
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+
+#ifndef _PORTABILITY_H
+#define _PORTABILITY_H
+
+#if !defined(WIN32) /* POSIX */
+
+#define SYSTEM_POSIX
+
+#include <sys/types.h>
+
+#if !defined(HAVE_U_INT64_T) && defined(HAVE_UINT64_T)
+ typedef uint64_t u_int64_t;
+#endif
+#if !defined(HAVE_U_INT32_T) && defined(HAVE_UINT32_T)
+ typedef uint32_t u_int32_t;
+#endif
+#if !defined(HAVE_U_INT16_T) && defined(HAVE_UINT16_T)
+ typedef uint16_t u_int16_t;
+#endif
+#if !defined(HAVE_U_INT8_T) && defined(HAVE_UINT8_T)
+ typedef uint8_t u_int8_t;
+#endif
+
+#elif defined(WIN32)
+
+#define SYSTEM_WIN32
+
+#include <windows.h>
+#include <time.h>
+
+typedef signed __int64 int64_t;
+typedef unsigned __int64 u_int64_t;
+
+typedef signed int int32_t;
+typedef unsigned int u_int32_t;
+
+typedef signed short int16_t;
+typedef unsigned short u_int16_t;
+
+typedef signed char int8_t;
+typedef unsigned char u_int8_t;
+
+#else /* WIN32 */
+
+#include <windows.h>
+
+#include <time.h>
+typedef INT64 int64_t;
+typedef UINT64 u_int64_t;
+
+typedef signed int int32_t;
+typedef unsigned int u_int32_t;
+
+typedef signed short int16_t;
+typedef unsigned short u_int16_t;
+
+typedef signed char int8_t;
+typedef unsigned char u_int8_t;
+
+#endif
+
+#endif /* _PORTABILITY_H */
+
+
diff --git a/lib/libXDAAP/private.h b/lib/libXDAAP/private.h
new file mode 100644
index 0000000000..61db83c35e
--- /dev/null
+++ b/lib/libXDAAP/private.h
@@ -0,0 +1,204 @@
+/* private header
+ *
+ * Copyright (c) 2003 David Hammerton
+ * crazney@crazney.net
+ *
+ * private structures, function prototypes, etc
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+//#include "threadpool.h"
+//#include "discover.h"
+#include "client.h"
+#include "httpClient.h"
+#include "thread.h"
+
+//#include "mdnsd/mdnsd.h"
+
+/* function prototypes */
+//unsigned int CP_GetTickCount();
+char *safe_sprintf(const char *format, ...);
+
+/* Client */
+typedef struct DAAP_ClientHost_FakeTAG DAAP_ClientHost_Fake;
+
+struct CP_SThreadPool;
+
+struct DAAP_SClientTAG
+{
+ unsigned int uiRef;
+
+ ts_mutex mtObjectLock;
+
+ DAAP_fnClientStatus pfnCallbackStatus;
+ void *pvCallbackStatusContext;
+
+ DAAP_SClientHost *hosts;
+ DAAP_ClientHost_Fake *fakehosts;
+
+#if defined(SYSTEM_POSIX)
+ struct CP_SThreadPool *tp;
+#endif
+
+ HTTP_ConnectionWatch *update_watch;
+
+#if !defined(WIN32) && !defined(_LINUX)
+ // why is this here?
+ SDiscover *discover;
+#endif
+};
+
+typedef struct
+{
+ int id;
+ int nItems;
+ int items_size;
+ DAAP_ClientHost_DatabaseItem *items;
+} DatabaseItemContainer;
+
+typedef struct
+{
+ int id;
+ int nPlaylists;
+ int playlists_size;
+ DAAP_ClientHost_DatabasePlaylist *playlists;
+} DatabasePlaylistContainer;
+
+struct DAAP_SClientHostTAG
+{
+ unsigned int uiRef;
+
+ DAAP_SClient *parent;
+
+ char *host; /* FIXME: use an address container (IPv4 vs IPv6) */
+ HTTP_Connection *connection;
+
+ char sharename_friendly[1005];
+ char sharename[1005]; /* from mDNS */
+
+ /* dmap/daap fields */
+ int sessionid;
+ int revision_number;
+
+ int request_id;
+
+ short version_major;
+ short version_minor;
+
+ int nDatabases;
+ int databases_size;
+ DAAP_ClientHost_Database *databases;
+
+ DatabaseItemContainer *dbitems;
+ DatabasePlaylistContainer *dbplaylists;
+
+ int interrupt;
+
+ char *password;
+
+ DAAP_SClientHost *prev;
+ DAAP_SClientHost *next;
+
+ int marked; /* used for discover cb */
+};
+#if defined(SYSTEM_POSIX) /* otherwise use the structure elsewhere */
+/* Discover */
+#define DISC_RR_CACHE_SIZE 500
+struct SDiscoverTAG
+{
+ unsigned int uiRef;
+
+ ts_mutex mtObjectLock; /* this requires an object wide lock
+ since the service thread holds a reference
+ and tests it for death */
+ ts_mutex mtWorkerLock;
+
+#ifndef _LINUX
+ fnDiscUpdated pfnUpdateCallback;
+#endif
+ void *pvCallbackArg;
+
+ struct CP_SThreadPool *tp;
+
+#ifndef _LINUX
+ mdnsd mdnsd_info;
+#endif
+ int socket;
+
+ int newquery_pipe[2];
+ // answers
+ /* answers */
+ int pending_hosts;
+#ifndef _LINUX
+ SDiscover_HostList *prenamed;
+ SDiscover_HostList *pending;
+ SDiscover_HostList *have;
+#endif
+};
+
+typedef struct CP_STPJobQueueTAG CP_STPJobQueue;
+/* ThreadPool */
+struct CP_STPJobQueueTAG
+{
+ CP_STPJobQueue *prev;
+ CP_STPJobQueue *next;
+
+ void (*fnJobCallback)(void *, void *);
+ void *arg1, *arg2;
+};
+
+typedef struct CP_STPTimerQueueTAG CP_STPTimerQueue;
+struct CP_STPTimerQueueTAG
+{
+ CP_STPTimerQueue *prev;
+ CP_STPTimerQueue *next;
+
+ unsigned int uiTimeSet;
+ unsigned int uiTimeWait;
+
+ void (*fnTimerCallback)(void *, void *);
+ void *arg1, *arg2;
+};
+
+struct CP_SThreadPoolTAG
+{
+ unsigned int uiRef;
+
+ unsigned int uiMaxThreads;
+ ts_thread *prgptThreads; /* variable sized array */
+ unsigned int uiThreadCount;
+
+ ts_mutex mtJobQueueMutex;
+ unsigned int uiJobCount;
+ CP_STPJobQueue *pTPJQHead;
+ CP_STPJobQueue *pTPJQTail;
+ ts_condition cndJobPosted;
+
+ ts_mutex mtTimerQueueMutex;
+ CP_STPTimerQueue *pTPTQTail;
+ ts_condition cndTimerPosted;
+
+ unsigned int uiDying;
+};
+#endif
+
diff --git a/lib/libXDAAP/thread.h b/lib/libXDAAP/thread.h
new file mode 100644
index 0000000000..3472859411
--- /dev/null
+++ b/lib/libXDAAP/thread.h
@@ -0,0 +1,104 @@
+/* thread abstraction
+ *
+ * Copyright (C) 2004 David Hammerton
+ * crazney@crazney.net
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include "portability.h"
+
+#if defined(SYSTEM_POSIX) /* POSIX */
+
+#define THREADS_POSIX
+
+#include <pthread.h>
+#include <sys/types.h>
+#include <unistd.h>
+#define ts_thread pthread_t
+#define ts_mutex pthread_mutex_t
+#define ts_condition pthread_cond_t
+
+#ifdef __APPLE__
+ #define PTHREAD_MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE
+#endif
+
+#define ts_mutex_create(m) pthread_mutex_init(& m, NULL)
+#define ts_mutex_create_recursive(m) do { \
+ pthread_mutexattr_t attr; \
+ pthread_mutexattr_init(&attr) ; \
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP); \
+ pthread_mutex_init(& m, &attr); \
+ pthread_mutexattr_destroy(&attr) ; \
+ } while (0)
+#define ts_mutex_lock(m) pthread_mutex_lock(& m)
+#define ts_mutex_unlock(m) pthread_mutex_unlock(& m)
+#define ts_mutex_destroy(m) pthread_mutex_destroy(& m)
+
+#define ts_condition_create(c) pthread_cond_init(& c, NULL)
+#define ts_condition_wait(c, m) pthread_cond_wait(& c, & m)
+#define ts_condition_signal(c) pthread_cond_signal(& c)
+#define ts_condition_signal_all(c) pthread_cond_broadcast(& c)
+#define ts_condition_destroy(c) pthread_cond_destroy(& c)
+
+#define ts_thread_create(t, cb, data) pthread_create(& t, NULL, \
+ (void*)cb, (void*)data)
+#define ts_thread_join(t) pthread_join(t, NULL)
+#define ts_exit() pthread_exit(NULL)
+#define ts_thread_close(t) do {} while (0)
+
+#define ts_thread_cb(name) static void* name(void *arg)
+#define ts_thread_defaultret NULL
+
+#elif defined(_WIN32) /* win32 */
+
+#define THREADS_WIN32
+#include <windows.h>
+
+#define ts_thread HANDLE
+#define ts_mutex HANDLE
+#define ts_condition HANDLE
+
+#define ts_mutex_create(m) do { m = CreateMutex(NULL, FALSE, NULL); } while(0)
+#define ts_mutex_lock(m) WaitForSingleObject(m, INFINITE)
+#define ts_mutex_unlock(m) ReleaseMutex(m)
+#define ts_mutex_destroy(m) CloseHandle(m)
+
+/* threadpool.c isn't used
+#define ts_condition_create(c) do { c = CreateEvent(NULL, FALSE, FALSE, NULL) } while(0)
+*/
+#define ts_thread_create(t, cb, data) do { t = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)cb, \
+ (LPVOID)data, 0, NULL); \
+ } while(0)
+#define ts_thread_close(t) CloseHandle(t)
+
+#define ts_thread_cb(name) static DWORD WINAPI name(LPVOID arg)
+#define ts_thread_defaultret 0;
+
+#else
+
+#error IMPLEMENT ME
+
+#endif
+
+
+
+
diff --git a/lib/libXDAAP/threadlib.h b/lib/libXDAAP/threadlib.h
new file mode 100644
index 0000000000..0859f3f9bf
--- /dev/null
+++ b/lib/libXDAAP/threadlib.h
@@ -0,0 +1,52 @@
+#ifndef __THREADLIB_H__
+#define __THREADLIB_H__
+
+#include <process.h>
+#include "types.h"
+#include "compat.h"
+
+#if _XBOX
+ #define beginthread(thread, callback) \
+ _beginthread(callback, 0, (void *)NULL)
+#elif __UNIX__
+ #include <unistd.h>
+ #define Sleep(x) usleep(x)
+ #define beginthread(thread, callback) \
+ pthread_create(thread, NULL, \
+ (void *)callback, (void *)NULL)
+#endif
+
+typedef struct THREAD_HANDLEst
+{
+ THANDLE thread_handle;
+} THREAD_HANDLE;
+
+typedef void (__cdecl *CALLBACKPROC)(void *);
+
+/*********************************************************************************
+ * Public functions
+ *********************************************************************************/
+/*
+extern error_code threadlib_beginthread(THREAD_HANDLE *thread, void (*callback)(void *));
+extern BOOL threadlib_isrunning(THREAD_HANDLE *thread);
+extern void threadlib_waitforclose(THREAD_HANDLE *thread);
+extern void threadlib_endthread(THREAD_HANDLE *thread);
+extern BOOL threadlib_sem_signaled(HSEM *e);
+
+extern HSEM threadlib_create_sem();
+extern error_code threadlib_waitfor_sem(HSEM *e);
+extern error_code threadlib_signel_sem(HSEM *e);
+extern void threadlib_destroy_sem(HSEM *e);
+*/
+
+error_code threadlib_beginthread(THREAD_HANDLE *thread, void (*callback)(void *));
+BOOL threadlib_isrunning(THREAD_HANDLE *thread);
+void threadlib_waitforclose(THREAD_HANDLE *thread);
+void threadlib_endthread(THREAD_HANDLE *thread);
+
+HSEM threadlib_create_sem();
+error_code threadlib_waitfor_sem(HSEM *e);
+error_code threadlib_signel_sem(HSEM *e);
+void threadlib_destroy_sem(HSEM *e);
+
+#endif //__THREADLIB__
diff --git a/lib/libXDAAP/threadpool.c b/lib/libXDAAP/threadpool.c
new file mode 100644
index 0000000000..4c86d450d7
--- /dev/null
+++ b/lib/libXDAAP/threadpool.c
@@ -0,0 +1,368 @@
+/* threadpool class
+ *
+ * Copyright (c) 2004 David Hammerton
+ * crazney@crazney.net
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "portability.h"
+#include "thread.h"
+#include <time.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "debug.h"
+
+#include "private.h"
+#include "threadpool.h"
+
+#define DEFAULT_DEBUG_CHANNEL "threadpool"
+
+/* no need for this now, as it just uses up a thread. */
+/*#define USE_TIMER_THREAD */
+
+/* PRIVATE */
+
+/* helper functions */
+
+#ifdef USE_TIMER_THREAD
+static void TP_DeleteTimerItem(CP_SThreadPool *pTPThis,
+ CP_STPTimerQueue *pTPTQDead)
+{
+ if (pTPThis->pTPTQTail == pTPTQDead)
+ pTPThis->pTPTQTail = pTPTQDead->prev;
+ if (pTPTQDead->prev)
+ pTPTQDead->prev->next = pTPTQDead->next;
+ if (pTPTQDead->next)
+ pTPTQDead->next->prev = pTPTQDead->prev;
+
+ free(pTPTQDead);
+}
+#endif
+
+/* Threads life */
+static void TP_ThreadsLife(void *arg1)
+{
+ CP_SThreadPool *pTPOwner = (CP_SThreadPool *)arg1;
+
+ ts_mutex_lock(pTPOwner->mtJobQueueMutex);
+ while (1)
+ {
+ CP_STPJobQueue *pTPJQOurJobItem;
+ while (!pTPOwner->pTPJQHead)
+ {
+ if (pTPOwner->uiDying)
+ {
+ TRACE("(tid: %i)\n", getpid());
+ ts_mutex_unlock(pTPOwner->mtJobQueueMutex);
+ ts_exit();
+ }
+ ts_condition_wait(pTPOwner->cndJobPosted, pTPOwner->mtJobQueueMutex);
+ }
+
+ pTPJQOurJobItem = pTPOwner->pTPJQHead;
+ if (pTPOwner->pTPJQHead->next) pTPOwner->pTPJQHead->next->prev = NULL;
+ if (pTPOwner->pTPJQTail == pTPOwner->pTPJQHead) pTPOwner->pTPJQTail = NULL;
+ pTPOwner->pTPJQHead = pTPOwner->pTPJQHead->next;
+ pTPOwner->uiJobCount--;
+
+ ts_mutex_unlock(pTPOwner->mtJobQueueMutex);
+
+ pTPJQOurJobItem->fnJobCallback(pTPJQOurJobItem->arg1, pTPJQOurJobItem->arg2);
+
+ free(pTPJQOurJobItem);
+ ts_mutex_lock(pTPOwner->mtJobQueueMutex);
+ }
+ ts_exit();
+}
+
+/* Timer Threads life */
+#define TIME_TO_OFF(curtime, timer) \
+ ((int)timer->uiTimeWait - (int)(curtime - timer->uiTimeSet))
+
+#ifdef USE_TIMER_THREAD
+static void TP_TimerThreadLife(void *arg1)
+{
+ CP_SThreadPool *pTPOwner = (CP_SThreadPool *)arg1;
+
+ ts_mutex_lock(pTPOwner->mtTimerQueueMutex);
+ while (1)
+ {
+ CP_STPTimerQueue *pTPTQCurrent, *pTPTQBest;
+ int iTimeBest = 0;
+ unsigned int uiCurTime;
+
+ uiCurTime = CP_GetTickCount();
+ pTPTQBest = NULL;
+ pTPTQCurrent = pTPOwner->pTPTQTail;
+ while (pTPTQCurrent)
+ {
+ if (!pTPTQBest || TIME_TO_OFF(uiCurTime, pTPTQCurrent) <= iTimeBest)
+ {
+ iTimeBest = TIME_TO_OFF(uiCurTime, pTPTQCurrent);
+ if (iTimeBest < 0) iTimeBest = 0;
+ pTPTQBest = pTPTQCurrent;
+ }
+ pTPTQCurrent = pTPTQCurrent->prev;
+ }
+ if (pTPTQBest)
+ {
+ struct timespec ts;
+ struct timeval tvNow;
+ int ret;
+
+#ifdef THREADS_POSIX
+ gettimeofday(&tvNow, NULL);
+
+ ts.tv_sec = tvNow.tv_sec + (iTimeBest / 1000);
+ ts.tv_nsec = ((tvNow.tv_usec + (iTimeBest % 1000) * 1000) * 1000);
+
+ ret = pthread_cond_timedwait(&pTPOwner->cndTimerPosted,
+ &pTPOwner->mtTimerQueueMutex,
+ &ts);
+ if (pTPOwner->uiDying)
+ {
+ ts_mutex_unlock(pTPOwner->mtTimerQueueMutex);
+ ts_exit();
+ }
+ if (ret == ETIMEDOUT)
+#elif THREADS_WIN32
+#error IMPLEMENT ME
+#else
+#error IMPLEMENT ME
+#endif
+ {
+ void (*fnTimerCallback)(void *, void *);
+ void *arg1, *arg2;
+
+ fnTimerCallback = pTPTQBest->fnTimerCallback;
+ arg1 = pTPTQBest->arg1;
+ arg2 = pTPTQBest->arg2;
+
+ /* delete the item. can't use CP_ThreadPool_DeleteTimerItem
+ * as that sets off a chain reaction.
+ * so use some helper code
+ */
+ TP_DeleteTimerItem(pTPOwner, pTPTQBest);
+
+ ts_mutex_unlock(pTPOwner->mtTimerQueueMutex);
+
+ /* this can block, so we should unlock */
+ /* actually it can't block now, but it will be able to soon */
+ CP_ThreadPool_QueueWorkItem(pTPOwner, fnTimerCallback,
+ arg1, arg2);
+
+ ts_mutex_lock(pTPOwner->mtTimerQueueMutex);
+ }
+ }
+ else
+ {
+ ts_condition_wait(pTPOwner->cndTimerPosted,
+ pTPOwner->mtTimerQueueMutex);
+ if (pTPOwner->uiDying)
+ {
+ ts_mutex_unlock(pTPOwner->mtTimerQueueMutex);
+ ts_exit();
+ }
+ }
+ }
+ ts_exit();
+}
+#endif
+
+/* Interface */
+
+CP_SThreadPool *CP_ThreadPool_Create(unsigned int uiMaxThreads)
+{
+ unsigned int i;
+ CP_SThreadPool *pTPNewThreadPool = malloc(sizeof(CP_SThreadPool));
+
+ pTPNewThreadPool->uiRef = 1;
+ pTPNewThreadPool->uiMaxThreads = uiMaxThreads >= 3 ? uiMaxThreads : 3;
+ /* FIXME: we always need 3 threads.
+ * 1. SP listen thread
+ * 2. Worker function thread.
+ * 3. Worked timer thread.
+ */
+ pTPNewThreadPool->prgptThreads = malloc(sizeof(ts_thread) *
+ pTPNewThreadPool->uiMaxThreads);
+ pTPNewThreadPool->uiThreadCount = pTPNewThreadPool->uiMaxThreads;
+ pTPNewThreadPool->uiDying = 0;
+
+ ts_mutex_create(pTPNewThreadPool->mtJobQueueMutex);
+ ts_condition_create(pTPNewThreadPool->cndJobPosted);
+
+ pTPNewThreadPool->pTPJQHead = NULL;
+ pTPNewThreadPool->pTPJQTail = NULL;
+
+ ts_mutex_create(pTPNewThreadPool->mtTimerQueueMutex);
+ ts_condition_create(pTPNewThreadPool->cndTimerPosted);
+ pTPNewThreadPool->pTPTQTail = NULL;
+
+ /* start the threads */
+#if USE_TIMER_THREAD
+ ts_thread_create(pTPNewThreadPool->prgptThreads[0], &TP_TimerThreadLife,
+ pTPNewThreadPool);
+ for (i = 1; i < pTPNewThreadPool->uiThreadCount; i++)
+#else
+ for (i = 0; i < pTPNewThreadPool->uiThreadCount; i++)
+#endif
+ {
+ ts_thread_create(pTPNewThreadPool->prgptThreads[i], &TP_ThreadsLife,
+ pTPNewThreadPool);
+ }
+ return pTPNewThreadPool;
+}
+
+unsigned int CP_ThreadPool_AddRef(CP_SThreadPool *pTPThis)
+{
+ return ++pTPThis->uiRef;
+}
+
+unsigned int CP_ThreadPool_Release(CP_SThreadPool *pTPThis)
+{
+ unsigned int i;
+ CP_STPJobQueue *pTPJQTmpJob;
+ if (--pTPThis->uiRef) return pTPThis->uiRef;
+
+ /* remove all jobs */
+ ts_mutex_lock(pTPThis->mtJobQueueMutex);
+ pTPJQTmpJob = pTPThis->pTPJQTail;
+ while (pTPJQTmpJob)
+ {
+ pTPThis->pTPJQTail = pTPJQTmpJob->prev;
+ free(pTPJQTmpJob);
+ pTPJQTmpJob = pTPThis->pTPJQTail;
+ }
+ pTPThis->pTPJQHead = NULL;
+ ts_mutex_unlock(pTPThis->mtJobQueueMutex);
+
+ pTPThis->uiDying = 1;
+ ts_condition_signal_all(pTPThis->cndJobPosted);
+ ts_condition_signal_all(pTPThis->cndTimerPosted);
+ for (i = 0; i < pTPThis->uiThreadCount; i++)
+ ts_thread_join(pTPThis->prgptThreads[i]);
+ free(pTPThis->prgptThreads);
+
+ ts_condition_destroy(pTPThis->cndJobPosted);
+ ts_mutex_destroy(pTPThis->mtJobQueueMutex);
+
+ ts_condition_destroy(pTPThis->cndTimerPosted);
+ ts_mutex_destroy(pTPThis->mtTimerQueueMutex);
+
+ FIXME("free job queue and timer queue\n");
+
+ free(pTPThis);
+ return 0;
+}
+
+void CP_ThreadPool_QueueWorkItem(CP_SThreadPool *pTPThis, CP_TPfnJob pfnCallback,
+ void *arg1, void *arg2)
+{
+ ts_mutex_lock(pTPThis->mtJobQueueMutex);
+
+ CP_STPJobQueue *pTPJQNewJob = malloc(sizeof(CP_STPJobQueue));
+
+ pTPJQNewJob->fnJobCallback = pfnCallback;
+ pTPJQNewJob->arg1 = arg1;
+ pTPJQNewJob->arg2 = arg2;
+ pTPJQNewJob->prev = NULL;
+ pTPJQNewJob->next = NULL;
+ if (!pTPThis->pTPJQHead)
+ {
+ pTPThis->pTPJQHead = pTPJQNewJob;
+ }
+ else
+ {
+ pTPThis->pTPJQTail->next = pTPJQNewJob;
+ }
+ pTPJQNewJob->prev = pTPThis->pTPJQTail;
+ pTPThis->pTPJQTail = pTPJQNewJob;
+ pTPThis->uiJobCount++;
+
+ ts_mutex_unlock(pTPThis->mtJobQueueMutex);
+ ts_condition_signal(pTPThis->cndJobPosted);
+}
+
+unsigned int CP_ThreadPool_GetPendingJobs(CP_SThreadPool *pTPThis)
+{
+ return pTPThis->uiJobCount;
+}
+
+#if USE_TIMER_THREAD
+CP_TPTimerItem CP_ThreadPool_QueueTimerItem(CP_SThreadPool *pTPThis,
+ unsigned int uiMSTimeWait,
+ CP_TPfnJob pfnCallback,
+ void *arg1, void *arg2)
+{
+ CP_STPTimerQueue *pTPTQNew;
+
+ pTPTQNew = malloc(sizeof(CP_STPTimerQueue));
+ pTPTQNew->uiTimeSet = CP_GetTickCount();
+ pTPTQNew->uiTimeWait = uiMSTimeWait;
+ pTPTQNew->fnTimerCallback = pfnCallback;
+ pTPTQNew->arg1 = arg1;
+ pTPTQNew->arg2 = arg2;
+
+ ts_mutex_lock(pTPThis->mtTimerQueueMutex);
+
+ pTPTQNew->prev = pTPThis->pTPTQTail;
+ if (pTPThis->pTPTQTail)
+ pTPThis->pTPTQTail->next = pTPTQNew;
+ pTPThis->pTPTQTail = pTPTQNew;
+
+ ts_mutex_unlock(pTPThis->mtTimerQueueMutex);
+ ts_condition_signal(pTPThis->cndTimerPosted);
+ return pTPTQNew;
+}
+
+void CP_ThreadPool_DeleteTimerItem(CP_SThreadPool *pTPThis,
+ CP_TPTimerItem tiDeadItem)
+{
+ /* FIXME: need to check if its still in the list of timers */
+ CP_STPTimerQueue *pTPTQDead = tiDeadItem;
+
+ ts_mutex_lock(pTPThis->mtTimerQueueMutex);
+
+ TP_DeleteTimerItem(pTPThis, pTPTQDead);
+
+ ts_mutex_unlock(pTPThis->mtTimerQueueMutex);
+ ts_condition_signal(pTPThis->cndTimerPosted);
+ return;
+}
+
+void CP_ThreadPool_ResetTimerItem(CP_SThreadPool *pTPThis,
+ CP_TPTimerItem tiResetItem)
+{
+ /* FIXME: need to check if its still in the list of timers */
+ CP_STPTimerQueue *pTPTQReset = tiResetItem;
+
+ ts_mutex_lock(pTPThis->mtTimerQueueMutex);
+
+ pTPTQReset->uiTimeSet = CP_GetTickCount();
+
+ ts_mutex_unlock(pTPThis->mtTimerQueueMutex);
+ ts_condition_signal(pTPThis->cndTimerPosted);
+ return;
+}
+#endif
diff --git a/lib/libXDAAP/threadpool.h b/lib/libXDAAP/threadpool.h
new file mode 100644
index 0000000000..5855819828
--- /dev/null
+++ b/lib/libXDAAP/threadpool.h
@@ -0,0 +1,66 @@
+/* threadpool class
+ *
+ * Copyright (c) 2004 David Hammerton
+ * crazney@crazney.net
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/* PRIVATE */
+
+#ifndef _THREADPOOL_H
+#define _THREADPOOL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* type definitions */
+typedef struct CP_SThreadPoolTAG CP_SThreadPool;
+typedef struct CP_STPTimerQueueTAG *CP_TPTimerItem;
+
+/* function pointer definitions */
+typedef void (*CP_TPfnJob)(void *, void *);
+
+/* Interface */
+CP_SThreadPool *CP_ThreadPool_Create(unsigned int uiMaxThreads);
+unsigned int CP_ThreadPool_AddRef(CP_SThreadPool *pTPThis);
+unsigned int CP_ThreadPool_Release(CP_SThreadPool *pTPThis);
+void CP_ThreadPool_QueueWorkItem(CP_SThreadPool *pTPThis,
+ CP_TPfnJob pfnCallback,
+ void *arg1, void *arg2);
+unsigned int CP_ThreadPool_GetPendingJobs(CP_SThreadPool *pTPThis);
+CP_TPTimerItem CP_ThreadPool_QueueTimerItem(CP_SThreadPool *pTPThis,
+ unsigned int uiMSTimeWait,
+ CP_TPfnJob pfnCallback,
+ void *arg1, void *arg2);
+void CP_ThreadPool_DeleteTimerItem(CP_SThreadPool *pTPThis,
+ CP_TPTimerItem tiDeadItem);
+void CP_ThreadPool_ResetTimerItem(CP_SThreadPool *pTPThis,
+ CP_TPTimerItem tiResetItem);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _THREADPOOL_H */
+
diff --git a/lib/libXDAAP/types.h b/lib/libXDAAP/types.h
new file mode 100644
index 0000000000..afccaa08a3
--- /dev/null
+++ b/lib/libXDAAP/types.h
@@ -0,0 +1,54 @@
+#ifndef __SRIPPER_H__
+#define __SRIPPER_H__
+
+////////////////////////////////////////////////
+// Types
+////////////////////////////////////////////////
+
+//#if WIN32
+#if WIN32
+#include <windows.h>
+#else
+#include <sys/types.h>
+#endif
+
+typedef int error_code;
+#define BOOL int
+#define TRUE 1
+#define FALSE 0
+
+#define NO_META_INTERVAL -1
+
+//#ifndef WIN32
+//#define MAX_PATH 256
+//#endif
+
+#define MAX_HOST_LEN 512
+#define MAX_IP_LEN 3+1+3+1+3+1+3+1
+#define MAX_PATH_LEN 255
+#define MAX_HEADER_LEN 8192
+#define MAX_URL_LEN 8192
+#define MAX_ICY_STRING 4024
+#define MAX_SERVER_LEN 1024
+#define MAX_TRACK_LEN MAX_PATH
+#define MAX_URI_STRING 1024
+#define MAX_ERROR_STR (4096)
+#define MAX_USERAGENT_STR 1024
+
+#if WIN32
+ #ifndef _WINSOCKAPI_
+ #define __DEFINE_TYPES__
+ #endif
+#endif
+
+#ifdef __DEFINE_TYPES__
+typedef unsigned long u_long;
+typedef unsigned char u_char;
+typedef unsigned short u_short;
+#endif
+
+#define SR_SUCCESS 0x00
+#define SR_ERROR_INVALID_PARAM - 0x0d
+
+
+#endif //__SRIPPER_H__