diff options
author | theuni <theuni-nospam-@xbmc.org> | 2011-01-24 16:05:21 -0500 |
---|---|---|
committer | theuni <theuni-nospam-@xbmc.org> | 2011-01-24 16:05:21 -0500 |
commit | c51b1189e3d5353e842991f5859ddcea0f73e426 (patch) | |
tree | ef2cb8a6184699aa614f3655dca4ce661cdc108e /lib/libXDAAP | |
parent | be61ebdc9e897fe40c6f371111724de79ddee8d5 (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')
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__ |