merge ron-ssl branch back to trunk in preparation for windows cleanups, merge setup tweaks and credits language as well as update rend-posix from stable-aspl-free

This commit is contained in:
Ron Pedde 2007-07-31 04:34:33 +00:00
parent 1a5a131bd1
commit 4b9b1fe177
48 changed files with 6736 additions and 1228 deletions

View File

@ -1,39 +1,39 @@
/*
Correctly handle PNG transparency in Win IE 5.5 & 6.
http://homepage.ntlworld.com/bobosola. Updated 18-Jan-2006.
Use in <HEAD> with DEFER keyword wrapped in conditional comments:
<!--[if lt IE 7]>
<script defer type="text/javascript" src="pngfix.js"></script>
<![endif]-->
*/
var arVersion = navigator.appVersion.split("MSIE")
var version = parseFloat(arVersion[1])
if ((version >= 5.5) && (document.body.filters))
{
for(var i=0; i<document.images.length; i++)
{
var img = document.images[i]
var imgName = img.src.toUpperCase()
if (imgName.substring(imgName.length-3, imgName.length) == "PNG")
{
var imgID = (img.id) ? "id='" + img.id + "' " : ""
var imgClass = (img.className) ? "class='" + img.className + "' " : ""
var imgTitle = (img.title) ? "title='" + img.title + "' " : "title='" + img.alt + "' "
var imgStyle = "display:inline-block;" + img.style.cssText
if (img.align == "left") imgStyle = "float:left;" + imgStyle
if (img.align == "right") imgStyle = "float:right;" + imgStyle
if (img.parentElement.href) imgStyle = "cursor:hand;" + imgStyle
var strNewHTML = "<span " + imgID + imgClass + imgTitle
+ " style=\"" + "width:" + img.width + "px; height:" + img.height + "px;" + imgStyle + ";"
+ "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader"
+ "(src=\'" + img.src + "\', sizingMethod='scale');\"></span>"
img.outerHTML = strNewHTML
i = i-1
}
}
}
/*
Correctly handle PNG transparency in Win IE 5.5 & 6.
http://homepage.ntlworld.com/bobosola. Updated 18-Jan-2006.
Use in <HEAD> with DEFER keyword wrapped in conditional comments:
<!--[if lt IE 7]>
<script defer type="text/javascript" src="pngfix.js"></script>
<![endif]-->
*/
var arVersion = navigator.appVersion.split("MSIE")
var version = parseFloat(arVersion[1])
if ((version >= 5.5) && (document.body.filters))
{
for(var i=0; i<document.images.length; i++)
{
var img = document.images[i]
var imgName = img.src.toUpperCase()
if (imgName.substring(imgName.length-3, imgName.length) == "PNG")
{
var imgID = (img.id) ? "id='" + img.id + "' " : ""
var imgClass = (img.className) ? "class='" + img.className + "' " : ""
var imgTitle = (img.title) ? "title='" + img.title + "' " : "title='" + img.alt + "' "
var imgStyle = "display:inline-block;" + img.style.cssText
if (img.align == "left") imgStyle = "float:left;" + imgStyle
if (img.align == "right") imgStyle = "float:right;" + imgStyle
if (img.parentElement.href) imgStyle = "cursor:hand;" + imgStyle
var strNewHTML = "<span " + imgID + imgClass + imgTitle
+ " style=\"" + "width:" + img.width + "px; height:" + img.height + "px;" + imgStyle + ";"
+ "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader"
+ "(src=\'" + img.src + "\', sizingMethod='scale');\"></span>"
img.outerHTML = strNewHTML
i = i-1
}
}
}

View File

@ -47,6 +47,7 @@ db_sqlite=false
db_sqlite3=false
use_ffmpeg=false;
use_upnp=false;
use_ssl=false
STATIC_LIBS=no
CPPFLAGS="${CPPFLAGS} -g -Wall"
@ -125,6 +126,9 @@ AC_ARG_ENABLE(upnp,[ --enable-upnp Enable upnp support],
AC_ARG_ENABLE(mem-debug,[ --enable-mem-debug Enable memory debugging],
CPPFLAGS="${CPPFLAGS} -DDEBUG_MEM")
AC_ARG_ENABLE(ssl,[ --enable-ssl Enable SSL support in web server],
CPPFLAGS="${CPPFLAGS} -DUSE_SSL"; use_ssl=true; )
AM_CONDITIONAL(COND_REND_HOWL, test x$rend_howl = xtrue)
AM_CONDITIONAL(COND_REND_POSIX, test x$rend_posix = xtrue)
AM_CONDITIONAL(COND_REND_AVAHI, test x$rend_avahi = xtrue)
@ -136,6 +140,7 @@ AM_CONDITIONAL(COND_SQLITE3,test x$db_sqlite3 = xtrue)
AM_CONDITIONAL(COND_GDBM,test x$use_gdbm = xtrue)
AM_CONDITIONAL(COND_FFMPEG,test x$use_ffmpeg = xtrue)
AM_CONDITIONAL(COND_UPNP,test x$use_upnp = xtrue)
AM_CONDITIONAL(COND_SSL,test x$use_ssl = xtrue)
#AM_CONDITIONAL(COND_NEED_STRCASESTR,false)
#AM_CONDITIONAL(COND_NEED_STRSEP,false)
@ -212,6 +217,22 @@ AC_ARG_WITH(howl-libs,
fi
])
AC_ARG_WITH(ssl-includes,
[--with-ssl-includes[[=DIR]] use ssl include files in DIR],[
if test "$withval" != "no" -a "$withval" != "yes"; then
Z_DIR=$withval
CPPFLAGS="${CPPFLAGS} -I$withval"
fi
])
AC_ARG_WITH(ssl-libs,
[--with-ssl-libs[[=DIR]] use ssl lib files in DIR],[
if test "$withval" != "no" -a "$withval" != "yes"; then
Z_DIR=$withval
LDFLAGS="${LDFLAGS} -L$withval -R$withval"
fi
])
AC_ARG_WITH(gdbm-includes,
[--with-gdbm-includes[[=DIR]] use gdbm include files in DIR],[
if test "$withval" != "no" -a "$withval" != "yes"; then
@ -317,6 +338,18 @@ if test x$use_gdbm = xtrue; then
fi
fi
if test x$use_ssl = xtrue; then
AC_CHECK_HEADERS(openssl/ssl.h,, [
AC_MSG_ERROR([ssl.h not found... Must have ssl headers installed])])
AC_CHECK_LIB(ssl,SSL_library_init,,echo "Must have openssl libraries installed";exit)
if test x"$STATIC_LIBS" != x"no"; then
LIBS="${LIBS} ${STATIC_LIBS}/libssl.a ${STATIC_LIBS}/libcrypto.a"
else
LIBS="${LIBS} -lssl -lcrypto"
fi
fi
OLDLIBS=$LIBS
if test x$db_sqlite = xtrue; then
AC_CHECK_HEADERS(sqlite.h,, [

View File

@ -58,9 +58,10 @@ if COND_UPNP
UPNP=upnp.c upnp.h
endif
wavstreamer_SOURCES = wavstreamer.c
mt_daapd_SOURCES = main.c daapd.h rend.h uici.c uici.h webserver.c \
mt_daapd_SOURCES = main.c daapd.h rend.h webserver.c \
webserver.h configfile.c configfile.h err.c err.h restart.c restart.h \
mp3-scanner.h mp3-scanner.c rend-unix.h \
db-generic.c db-generic.h \
@ -69,7 +70,7 @@ mt_daapd_SOURCES = main.c daapd.h rend.h uici.c uici.h webserver.c \
smart-parser.c smart-parser.h xml-rpc.c xml-rpc.h \
os.h ll.c ll.h conf.c conf.h compat.c compat.h util.c util.h \
os-unix.h os-unix.c os.h plugin.c plugin.h db-sql-updates.c \
memdebug.c memdebug.h \
memdebug.c memdebug.h ssl.h io.h io.c bsd-snprintf.c bsd-snprintf.h \
$(PRENDSRC) $(ORENDSRC) $(HRENDSRC) $(ARENDSRC) $(OGGVORBISSRC) \
$(FLACSRC) $(MUSEPACKSRC) $(SQLITEDB) $(SQLITE3DB) $(SQLDB) $(GDBM) \
$(UPNP)

715
src/bsd-snprintf.c Normal file
View File

@ -0,0 +1,715 @@
/*
* Copyright Patrick Powell 1995
* This code is based on code written by Patrick Powell (papowell@astart.com)
* It may be used for any purpose as long as this notice remains intact
* on all source code distributions
*/
/**************************************************************
* Original:
* Patrick Powell Tue Apr 11 09:48:21 PDT 1995
* A bombproof version of doprnt (dopr) included.
* Sigh. This sort of thing is always nasty do deal with. Note that
* the version here does not include floating point...
*
* snprintf() is used instead of sprintf() as it does limit checks
* for string length. This covers a nasty loophole.
*
* The other functions are there to prevent NULL pointers from
* causing nast effects.
*
* More Recently:
* Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
* This was ugly. It is still ugly. I opted out of floating point
* numbers, but the formatter understands just about everything
* from the normal C string format, at least as far as I can tell from
* the Solaris 2.5 printf(3S) man page.
*
* Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
* Ok, added some minimal floating point support, which means this
* probably requires libm on most operating systems. Don't yet
* support the exponent (e,E) and sigfig (g,G). Also, fmtint()
* was pretty badly broken, it just wasn't being exercised in ways
* which showed it, so that's been fixed. Also, formated the code
* to mutt conventions, and removed dead code left over from the
* original. Also, there is now a builtin-test, just compile with:
* gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
* and run snprintf for results.
*
* Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
* The PGP code was using unsigned hexadecimal formats.
* Unfortunately, unsigned formats simply didn't work.
*
* Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
* The original code assumed that both snprintf() and vsnprintf() were
* missing. Some systems only have snprintf() but not vsnprintf(), so
* the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
*
* Ben Lindstrom <mouring@eviladmin.org> 09/27/00 for OpenSSH
* Welcome to the world of %lld and %qd support. With other
* long long support. This is needed for sftp-server to work
* right.
*
* Ben Lindstrom <mouring@eviladmin.org> 02/12/01 for OpenSSH
* Removed all hint of VARARGS stuff and banished it to the void,
* and did a bit of KNF style work to make things a bit more
* acceptable. Consider stealing from mutt or enlightenment.
**************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <ctype.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
static void
dopr(char *buffer, size_t maxlen, const char *format, va_list args);
static void
fmtstr(char *buffer, size_t *currlen, size_t maxlen, char *value, int flags,
int min, int max);
static void
fmturl(char *buffer, size_t *currlen, size_t maxlen, char *value, int flags,
int min, int max);
static void
fmtint(char *buffer, size_t *currlen, size_t maxlen, long value, int base,
int min, int max, int flags);
static void
fmtfp(char *buffer, size_t *currlen, size_t maxlen, long double fvalue,
int min, int max, int flags);
static void
dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c);
/*
* dopr(): poor man's version of doprintf
*/
/* format read states */
#define DP_S_DEFAULT 0
#define DP_S_FLAGS 1
#define DP_S_MIN 2
#define DP_S_DOT 3
#define DP_S_MAX 4
#define DP_S_MOD 5
#define DP_S_CONV 6
#define DP_S_DONE 7
/* format flags - Bits */
#define DP_F_MINUS (1 << 0)
#define DP_F_PLUS (1 << 1)
#define DP_F_SPACE (1 << 2)
#define DP_F_NUM (1 << 3)
#define DP_F_ZERO (1 << 4)
#define DP_F_UP (1 << 5)
#define DP_F_UNSIGNED (1 << 6)
/* Conversion Flags */
#define DP_C_SHORT 1
#define DP_C_LONG 2
#define DP_C_LDOUBLE 3
#define DP_C_LONG_LONG 4
#define char_to_int(p) (p - '0')
#define abs_val(p) (p < 0 ? -p : p)
static void
dopr(char *buffer, size_t maxlen, const char *format, va_list args)
{
char *strvalue, ch;
long value;
long double fvalue;
int min = 0, max = -1, state = DP_S_DEFAULT, flags = 0, cflags = 0;
size_t currlen = 0;
ch = *format++;
while (state != DP_S_DONE) {
if ((ch == '\0') || (currlen >= maxlen))
state = DP_S_DONE;
switch(state) {
case DP_S_DEFAULT:
if (ch == '%')
state = DP_S_FLAGS;
else
dopr_outch(buffer, &currlen, maxlen, ch);
ch = *format++;
break;
case DP_S_FLAGS:
switch (ch) {
case '-':
flags |= DP_F_MINUS;
ch = *format++;
break;
case '+':
flags |= DP_F_PLUS;
ch = *format++;
break;
case ' ':
flags |= DP_F_SPACE;
ch = *format++;
break;
case '#':
flags |= DP_F_NUM;
ch = *format++;
break;
case '0':
flags |= DP_F_ZERO;
ch = *format++;
break;
default:
state = DP_S_MIN;
break;
}
break;
case DP_S_MIN:
if (isdigit((unsigned char)ch)) {
min = 10 * min + char_to_int (ch);
ch = *format++;
} else if (ch == '*') {
min = va_arg (args, int);
ch = *format++;
state = DP_S_DOT;
} else
state = DP_S_DOT;
break;
case DP_S_DOT:
if (ch == '.') {
state = DP_S_MAX;
ch = *format++;
} else
state = DP_S_MOD;
break;
case DP_S_MAX:
if (isdigit((unsigned char)ch)) {
if (max < 0)
max = 0;
max = 10 * max + char_to_int(ch);
ch = *format++;
} else if (ch == '*') {
max = va_arg (args, int);
ch = *format++;
state = DP_S_MOD;
} else
state = DP_S_MOD;
break;
case DP_S_MOD:
switch (ch) {
case 'h':
cflags = DP_C_SHORT;
ch = *format++;
break;
case 'l':
cflags = DP_C_LONG;
ch = *format++;
if (ch == 'l') {
cflags = DP_C_LONG_LONG;
ch = *format++;
}
break;
case 'q':
cflags = DP_C_LONG_LONG;
ch = *format++;
break;
case 'L':
cflags = DP_C_LDOUBLE;
ch = *format++;
break;
default:
break;
}
state = DP_S_CONV;
break;
case DP_S_CONV:
switch (ch) {
case 'd':
case 'i':
if (cflags == DP_C_SHORT)
value = va_arg(args, int);
else if (cflags == DP_C_LONG)
value = va_arg(args, long int);
else if (cflags == DP_C_LONG_LONG)
value = va_arg (args, long long);
else
value = va_arg (args, int);
fmtint(buffer, &currlen, maxlen, value, 10, min, max, flags);
break;
case 'o':
flags |= DP_F_UNSIGNED;
if (cflags == DP_C_SHORT)
value = va_arg(args, unsigned int);
else if (cflags == DP_C_LONG)
value = va_arg(args, unsigned long int);
else if (cflags == DP_C_LONG_LONG)
value = va_arg(args, unsigned long long);
else
value = va_arg(args, unsigned int);
fmtint(buffer, &currlen, maxlen, value, 8, min, max, flags);
break;
case 'u':
flags |= DP_F_UNSIGNED;
if (cflags == DP_C_SHORT)
value = va_arg(args, unsigned int);
else if (cflags == DP_C_LONG)
value = va_arg(args, unsigned long int);
else if (cflags == DP_C_LONG_LONG)
value = va_arg(args, unsigned long long);
else
value = va_arg(args, unsigned int);
fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
break;
case 'X':
flags |= DP_F_UP;
case 'x':
flags |= DP_F_UNSIGNED;
if (cflags == DP_C_SHORT)
value = va_arg(args, unsigned int);
else if (cflags == DP_C_LONG)
value = va_arg(args, unsigned long int);
else if (cflags == DP_C_LONG_LONG)
value = va_arg(args, unsigned long long);
else
value = va_arg(args, unsigned int);
fmtint(buffer, &currlen, maxlen, value, 16, min, max, flags);
break;
case 'f':
if (cflags == DP_C_LDOUBLE)
fvalue = va_arg(args, long double);
else
fvalue = va_arg(args, double);
/* um, floating point? */
fmtfp(buffer, &currlen, maxlen, fvalue, min, max, flags);
break;
case 'E':
flags |= DP_F_UP;
case 'e':
if (cflags == DP_C_LDOUBLE)
fvalue = va_arg(args, long double);
else
fvalue = va_arg(args, double);
break;
case 'G':
flags |= DP_F_UP;
case 'g':
if (cflags == DP_C_LDOUBLE)
fvalue = va_arg(args, long double);
else
fvalue = va_arg(args, double);
break;
case 'c':
dopr_outch(buffer, &currlen, maxlen, va_arg(args, int));
break;
case 's':
strvalue = va_arg(args, char *);
if (max < 0)
max = maxlen; /* ie, no max */
fmtstr(buffer, &currlen, maxlen, strvalue, flags, min, max);
break;
case 'U':
strvalue = va_arg(args, char *);
if (max < 0)
max = maxlen;
fmturl(buffer, &currlen, maxlen, strvalue, flags, min, max);
break;
case 'p':
strvalue = va_arg(args, void *);
fmtint(buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
break;
case 'n':
if (cflags == DP_C_SHORT) {
short int *num;
num = va_arg(args, short int *);
*num = currlen;
} else if (cflags == DP_C_LONG) {
long int *num;
num = va_arg(args, long int *);
*num = currlen;
} else if (cflags == DP_C_LONG_LONG) {
long long *num;
num = va_arg(args, long long *);
*num = currlen;
} else {
int *num;
num = va_arg(args, int *);
*num = currlen;
}
break;
case '%':
dopr_outch(buffer, &currlen, maxlen, ch);
break;
case 'w': /* not supported yet, treat as next char */
ch = *format++;
break;
default: /* Unknown, skip */
break;
}
ch = *format++;
state = DP_S_DEFAULT;
flags = cflags = min = 0;
max = -1;
break;
case DP_S_DONE:
break;
default: /* hmm? */
break; /* some picky compilers need this */
}
}
if (currlen < maxlen - 1)
buffer[currlen] = '\0';
else
buffer[maxlen - 1] = '\0';
}
/**
* added 1 July 2007 by Ron Pedde to add support for U (urlencode)
*/
static void fmturl(char *buffer, size_t *currlen, size_t maxlen,
char *value, int flags, int min, int max)
{
int len=0;
char *current, *dest;
char *new_string;
int digit1, digit2;
char *digits = "0123456789abcdef";
char *safe = "abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789$-_.!*(),/@\\";
current=value;
while(*current) {
if(strchr(safe,*current)) {
len++;
} else {
len += 3;
}
current++;
}
new_string = (char*) malloc(len+1);
if(!new_string) {
fmtstr(buffer,currlen,maxlen,value,flags,min,max);
return;
}
current = value;
dest = new_string;
while(*current) {
if(strchr(safe,*current)) {
*dest++ = *current++;
} else if(*current == ' ') {
*dest++ = '+';
*current++;
} else {
*dest++ = '%';
digit1=(*current)/16;
digit2=(*current)%16;
*dest++ = digits[digit1];
*dest++ = digits[digit2];
current++;
}
}
*dest = '\0';
fmtstr(buffer,currlen,maxlen,new_string,flags,min,max);
free(new_string);
return;
}
static void
fmtstr(char *buffer, size_t *currlen, size_t maxlen,
char *value, int flags, int min, int max)
{
int cnt = 0, padlen, strln; /* amount to pad */
if (value == 0)
value = "<NULL>";
for (strln = 0; value[strln]; ++strln); /* strlen */
padlen = min - strln;
if (padlen < 0)
padlen = 0;
if (flags & DP_F_MINUS)
padlen = -padlen; /* Left Justify */
while ((padlen > 0) && (cnt < max)) {
dopr_outch(buffer, currlen, maxlen, ' ');
--padlen;
++cnt;
}
while (*value && (cnt < max)) {
dopr_outch(buffer, currlen, maxlen, *value++);
++cnt;
}
while ((padlen < 0) && (cnt < max)) {
dopr_outch(buffer, currlen, maxlen, ' ');
++padlen;
++cnt;
}
}
/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
static void
fmtint(char *buffer, size_t *currlen, size_t maxlen,
long value, int base, int min, int max, int flags)
{
unsigned long uvalue;
char convert[20];
int signvalue = 0, place = 0, caps = 0;
int spadlen = 0; /* amount to space pad */
int zpadlen = 0; /* amount to zero pad */
if (max < 0)
max = 0;
uvalue = value;
if (!(flags & DP_F_UNSIGNED)) {
if (value < 0) {
signvalue = '-';
uvalue = -value;
} else if (flags & DP_F_PLUS) /* Do a sign (+/i) */
signvalue = '+';
else if (flags & DP_F_SPACE)
signvalue = ' ';
}
if (flags & DP_F_UP)
caps = 1; /* Should characters be upper case? */
do {
convert[place++] =
(caps ? "0123456789ABCDEF" : "0123456789abcdef")
[uvalue % (unsigned)base];
uvalue = (uvalue / (unsigned)base );
} while (uvalue && (place < 20));
if (place == 20)
place--;
convert[place] = 0;
zpadlen = max - place;
spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
if (zpadlen < 0)
zpadlen = 0;
if (spadlen < 0)
spadlen = 0;
if (flags & DP_F_ZERO) {
zpadlen = MAX(zpadlen, spadlen);
spadlen = 0;
}
if (flags & DP_F_MINUS)
spadlen = -spadlen; /* Left Justifty */
/* Spaces */
while (spadlen > 0) {
dopr_outch(buffer, currlen, maxlen, ' ');
--spadlen;
}
/* Sign */
if (signvalue)
dopr_outch(buffer, currlen, maxlen, signvalue);
/* Zeros */
if (zpadlen > 0) {
while (zpadlen > 0) {
dopr_outch(buffer, currlen, maxlen, '0');
--zpadlen;
}
}
/* Digits */
while (place > 0)
dopr_outch(buffer, currlen, maxlen, convert[--place]);
/* Left Justified spaces */
while (spadlen < 0) {
dopr_outch (buffer, currlen, maxlen, ' ');
++spadlen;
}
}
static long double
io_util_pow10(int exp)
{
long double result = 1;
while (exp) {
result *= 10;
exp--;
}
return result;
}
static long
io_util_round(long double value)
{
long intpart = value;
value -= intpart;
if (value >= 0.5)
intpart++;
return intpart;
}
static void
fmtfp(char *buffer, size_t *currlen, size_t maxlen, long double fvalue,
int min, int max, int flags)
{
char iconvert[20], fconvert[20];
int signvalue = 0, iplace = 0, fplace = 0;
int padlen = 0; /* amount to pad */
int zpadlen = 0, caps = 0;
long intpart, fracpart;
long double ufvalue;
/*
* AIX manpage says the default is 0, but Solaris says the default
* is 6, and sprintf on AIX defaults to 6
*/
if (max < 0)
max = 6;
ufvalue = abs_val(fvalue);
if (fvalue < 0)
signvalue = '-';
else if (flags & DP_F_PLUS) /* Do a sign (+/i) */
signvalue = '+';
else if (flags & DP_F_SPACE)
signvalue = ' ';
intpart = ufvalue;
/*
* Sorry, we only support 9 digits past the decimal because of our
* conversion method
*/
if (max > 9)
max = 9;
/* We "cheat" by converting the fractional part to integer by
* multiplying by a factor of 10
*/
fracpart = io_util_round((io_util_pow10 (max)) * (ufvalue - intpart));
if (fracpart >= io_util_pow10 (max)) {
intpart++;
fracpart -= io_util_pow10 (max);
}
/* Convert integer part */
do {
iconvert[iplace++] =
(caps ? "0123456789ABCDEF" : "0123456789abcdef")
[intpart % 10];
intpart = (intpart / 10);
} while(intpart && (iplace < 20));
if (iplace == 20)
iplace--;
iconvert[iplace] = 0;
/* Convert fractional part */
do {
fconvert[fplace++] =
(caps ? "0123456789ABCDEF" : "0123456789abcdef")
[fracpart % 10];
fracpart = (fracpart / 10);
} while(fracpart && (fplace < 20));
if (fplace == 20)
fplace--;
fconvert[fplace] = 0;
/* -1 for decimal point, another -1 if we are printing a sign */
padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
zpadlen = max - fplace;
if (zpadlen < 0)
zpadlen = 0;
if (padlen < 0)
padlen = 0;
if (flags & DP_F_MINUS)
padlen = -padlen; /* Left Justifty */
if ((flags & DP_F_ZERO) && (padlen > 0)) {
if (signvalue) {
dopr_outch(buffer, currlen, maxlen, signvalue);
--padlen;
signvalue = 0;
}
while (padlen > 0) {
dopr_outch(buffer, currlen, maxlen, '0');
--padlen;
}
}
while (padlen > 0) {
dopr_outch(buffer, currlen, maxlen, ' ');
--padlen;
}
if (signvalue)
dopr_outch(buffer, currlen, maxlen, signvalue);
while (iplace > 0)
dopr_outch(buffer, currlen, maxlen, iconvert[--iplace]);
/*
* Decimal point. This should probably use locale to find the
* correct char to print out.
*/
dopr_outch(buffer, currlen, maxlen, '.');
while (fplace > 0)
dopr_outch(buffer, currlen, maxlen, fconvert[--fplace]);
while (zpadlen > 0) {
dopr_outch(buffer, currlen, maxlen, '0');
--zpadlen;
}
while (padlen < 0) {
dopr_outch(buffer, currlen, maxlen, ' ');
++padlen;
}
}
static void
dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c)
{
if (*currlen < maxlen)
buffer[(*currlen)++] = c;
}
int
io_util_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
{
str[0] = 0;
dopr(str, count, fmt, args);
return(strlen(str));
}
int
io_util_snprintf(char *str,size_t count,const char *fmt,...)
{
va_list ap;
va_start(ap, fmt);
(void) vsnprintf(str, count, fmt, ap);
va_end(ap);
return(strlen(str));
}

30
src/bsd-snprintf.h Normal file
View File

@ -0,0 +1,30 @@
/*
* $Id: $
*
* Created by Ron Pedde on 2007-07-01.
* Copyright (C) 2007 Firefly Media Services. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _BSD_SNPRINTF_H_
#define _BSD_SNPRINTF_H_
extern int io_util_vsnprintf(char *str, size_t count, const char *fmt, va_list args);
extern int io_util_snprintf(char *str,size_t count,const char *fmt,...);
#endif /* _BSD_SNPRINTF_H_ */

View File

@ -57,6 +57,7 @@
#include "webserver.h"
#include "xml-rpc.h"
#include "io.h"
#ifndef HOST_NAME_MAX
# define HOST_NAME_MAX 255
@ -98,7 +99,7 @@ typedef struct _CONF_MAP {
static int _conf_verify(LL_HANDLE pll);
static LL_ITEM *_conf_fetch_item(LL_HANDLE pll, char *section, char *key);
static int _conf_exists(LL_HANDLE pll, char *section, char *key);
static int _conf_write(FILE *fp, LL *pll, int sublevel, char *parent);
static int _conf_write(IOHANDLE hfile, LL *pll, int sublevel, char *parent);
static CONF_ELEMENTS *_conf_get_keyinfo(char *section, char *key);
static int _conf_makedir(char *path, char *user);
static int _conf_existdir(char *path);
@ -574,7 +575,6 @@ int conf_reload(void) {
* @returns TRUE if successful, FALSE otherwise
*/
int conf_read(char *file) {
FILE *fin;
int err;
LL_HANDLE pllnew, plltemp, pllcurrent, pllcomment;
LL_ITEM *pli;
@ -595,6 +595,8 @@ int conf_read(char *file) {
char **valuearray;
int index;
char *temp;
IOHANDLE hconfig;
uint32_t len;
char *replaced_section = NULL; /* config key/value updates */
char *replaced_key = NULL; /* config key/value updates */
@ -607,10 +609,17 @@ int conf_read(char *file) {
realpath(file,conf_file);
conf_main_file = strdup(conf_file);
fin=fopen(conf_file,"r");
if(!fin) {
hconfig = io_new();
if(!hconfig)
DPRINTF(E_FATAL,L_CONF,"Malloc eror in io_new()\n");
DPRINTF(E_DBG,L_CONF,"Loading config file %s\n",conf_file);
if(!io_open(hconfig,"file://%U",conf_file)) {
DPRINTF(E_LOG,L_MISC,"Error opening config file: %s\n",io_errstr(hconfig));
io_dispose(hconfig);
return CONF_E_FOPEN;
}
DPRINTF(E_DBG,L_CONF,"Config file open\n");
prev_comments = (char*)malloc(total_comment_length);
if(!prev_comments)
@ -619,7 +628,9 @@ int conf_read(char *file) {
if((err=ll_create(&pllnew)) != LL_E_SUCCESS) {
DPRINTF(E_LOG,L_CONF,"Error creating linked list: %d\n",err);
fclose(fin);
io_close(hconfig);
io_dispose(hconfig);
return CONF_E_UNKNOWN;
}
@ -631,7 +642,10 @@ int conf_read(char *file) {
/* got what will be the root of the config tree, now start walking through
* the input file, populating the tree
*/
while(fgets(linebuffer,CONF_LINEBUFFER,fin)) {
/* TODO: linebuffered IO support */
len = sizeof(linebuffer);
while(io_readline(hconfig,(unsigned char *)linebuffer,&len) && len) {
line++;
linebuffer[CONF_LINEBUFFER] = '\0';
ws=0;
@ -662,14 +676,16 @@ int conf_read(char *file) {
if(!value) {
ll_destroy(pllnew);
fclose(fin);
io_close(hconfig);
io_dispose(hconfig);
return CONF_E_BADHEADER;
}
*value = '\0';
if((err = ll_create(&plltemp)) != LL_E_SUCCESS) {
ll_destroy(pllnew);
fclose(fin);
io_close(hconfig);
io_dispose(hconfig);
return CONF_E_UNKNOWN;
}
@ -733,7 +749,8 @@ int conf_read(char *file) {
if((err=ll_create(&plltemp)) != LL_E_SUCCESS) {
DPRINTF(E_LOG,L_CONF,"Error creating list: %d\n",err);
ll_destroy(pllnew);
fclose(fin);
io_close(hconfig);
io_dispose(hconfig);
return CONF_E_UNKNOWN;
}
ll_add_ll(pllnew,"general",plltemp);
@ -766,7 +783,8 @@ int conf_read(char *file) {
if((err = ll_create(&pllcurrent)) != LL_E_SUCCESS) {
ll_destroy(pllnew);
DPRINTF(E_LOG,L_CONF,"Error creating linked list: %d\n",err);
fclose(fin);
io_close(hconfig);
io_dispose(hconfig);
return CONF_E_UNKNOWN;
}
ll_add_ll(pllnew,replaced_section,pllcurrent);
@ -873,6 +891,7 @@ int conf_read(char *file) {
}
}
}
len = sizeof(linebuffer);
}
if(section_name) {
@ -888,7 +907,8 @@ int conf_read(char *file) {
prev_comments = NULL;
}
fclose(fin);
io_close(hconfig);
io_dispose(hconfig);
/* Sanity check */
if(_conf_verify(pllnew)) {
@ -1223,20 +1243,26 @@ int conf_iswritable(void) {
*/
int conf_write(void) {
int retval = CONF_E_NOTWRITABLE;
FILE *fp;
IOHANDLE outfile;
if(!conf_main_file) {
DPRINTF(E_DBG,L_CONF,"Config file apparently not loaded\n");
return CONF_E_NOCONF;
}
util_mutex_lock(l_conf);
if((fp = fopen(conf_main_file,"w+")) != NULL) {
retval = _conf_write(fp,conf_main,0,NULL);
fclose(fp);
outfile = io_new();
if(!outfile)
DPRINTF(E_FATAL,L_CONF,"io_new failed in conf_write\n");
if(io_open(outfile,"file://%U?mode=w",conf_main_file)) {
retval = _conf_write(outfile,conf_main,0,NULL);
io_close(outfile);
io_dispose(outfile);
} else {
DPRINTF(E_LOG,L_CONF,"Error opening config file for write: %s\n",
strerror(errno));
io_errstr(outfile));
io_dispose(outfile);
}
util_mutex_unlock(l_conf);
@ -1251,7 +1277,7 @@ int conf_write(void) {
* @param sublevel whether this is the root, or a subkey
* @returns TRUE on success, FALSE otherwise
*/
int _conf_write(FILE *fp, LL *pll, int sublevel, char *parent) {
int _conf_write(IOHANDLE hfile, LL *pll, int sublevel, char *parent) {
LL_ITEM *pli;
LL_ITEM *ppre, *pin;
LL_ITEM *plitemp;
@ -1279,7 +1305,7 @@ int _conf_write(FILE *fp, LL *pll, int sublevel, char *parent) {
}
if(ppre) {
fprintf(fp,"%s",ppre->value.as_string);
io_printf(hfile,"%s",ppre->value.as_string);
}
switch(pli->type) {
@ -1288,22 +1314,22 @@ int _conf_write(FILE *fp, LL *pll, int sublevel, char *parent) {
/* must be multivalued */
plitemp = NULL;
first = 1;
fprintf(fp,"%s = ",pli->key);
io_printf(hfile,"%s = ",pli->key);
while((plitemp = ll_get_next(pli->value.as_ll,plitemp))) {
if(!first)
fprintf(fp,",");
io_printf(hfile,",");
first=0;
fprintf(fp,"%s",plitemp->value.as_string);
io_printf(hfile,"%s",plitemp->value.as_string);
}
fprintf(fp,"\n");
io_printf(hfile,"\n");
} else {
fprintf(fp,"[%s]",pli->key);
io_printf(hfile,"[%s]",pli->key);
if(pin) {
fprintf(fp," #%s",pin->value.as_string);
io_printf(hfile," #%s",pin->value.as_string);
}
fprintf(fp,"\n");
io_printf(hfile,"\n");
if(!_conf_write(fp, pli->value.as_ll, 1, pli->key)) {
if(!_conf_write(hfile, pli->value.as_ll, 1, pli->key)) {
DPRINTF(E_DBG,L_CONF,"Error writing key %s:%s\n",pli->key);
return FALSE;
}
@ -1311,19 +1337,19 @@ int _conf_write(FILE *fp, LL *pll, int sublevel, char *parent) {
break;
case LL_TYPE_INT:
fprintf(fp,"%s = %d",pli->key,pli->value.as_int);
io_printf(hfile,"%s = %d",pli->key,pli->value.as_int);
if(pin) {
fprintf(fp," #%s",pin->value.as_string);
io_printf(hfile," #%s",pin->value.as_string);
}
fprintf(fp,"\n");
io_printf(hfile,"\n");
break;
case LL_TYPE_STRING:
fprintf(fp,"%s = %s",pli->key,pli->value.as_string);
io_printf(hfile,"%s = %s",pli->key,pli->value.as_string);
if(pin) {
fprintf(fp," #%s",pin->value.as_string);
io_printf(hfile," #%s",pin->value.as_string);
}
fprintf(fp,"\n");
io_printf(hfile,"\n");
break;
}
@ -1333,7 +1359,7 @@ int _conf_write(FILE *fp, LL *pll, int sublevel, char *parent) {
if(!sublevel) {
pin = ll_fetch_item(conf_comments,"end");
if(pin) {
fprintf(fp,"%s",pin->value.as_string);
io_printf(hfile,"%s",pin->value.as_string);
}
}

View File

@ -84,7 +84,7 @@ static void config_emit_host(WS_CONNINFO *pwsc, void *value, char *arg);
static void config_emit_config(WS_CONNINFO *pwsc, void *value, char *arg);
static void config_emit_servername(WS_CONNINFO *pwsc, void *value, char *arg);
static void config_emit_upnp(WS_CONNINFO *pwsc, void *value, char *arg);
static void config_subst_stream(WS_CONNINFO *pwsc, int fd_src);
static void config_subst_stream(WS_CONNINFO *pwsc, IOHANDLE hfile);
static void config_content_type(WS_CONNINFO *pwsc, char *path);
/*
@ -121,31 +121,31 @@ typedef struct tag_configelement {
/** List of all valid config entries and web interface directives */
CONFIGELEMENT config_elements[] = {
/*
{ 1,1,0,CONFIG_TYPE_STRING,"runas",(void*)&config.runas,config_emit_string },
{ 1,1,0,CONFIG_TYPE_STRING,"web_root",(void*)&config.web_root,config_emit_string },
{ 1,1,0,CONFIG_TYPE_INT,"port",(void*)&config.port,config_emit_int },
{ 1,1,0,CONFIG_TYPE_STRING,"admin_pw",(void*)&config.adminpassword,config_emit_string },
{ 1,1,0,CONFIG_TYPE_STRING,"mp3_dir",(void*)&config.mp3dir,config_emit_string },
{ 1,0,0,CONFIG_TYPE_STRING,"db_dir",(void*)&config.dbdir,config_emit_string },
{ 1,0,0,CONFIG_TYPE_STRING,"db_type",(void*)&config.dbtype,config_emit_string },
{ 1,0,0,CONFIG_TYPE_STRING,"db_parms",(void*)&config.dbparms,config_emit_string },
{ 1,0,0,CONFIG_TYPE_INT,"debuglevel",(void*)NULL,config_emit_debuglevel },
{ 1,1,0,CONFIG_TYPE_STRING,"servername",(void*)&config.servername,config_emit_string },
{ 1,0,0,CONFIG_TYPE_INT,"rescan_interval",(void*)&config.rescan_interval,config_emit_int },
{ 1,0,0,CONFIG_TYPE_INT,"always_scan",(void*)&config.always_scan,config_emit_int },
{ 1,0,0,CONFIG_TYPE_INT,"latin1_tags",(void*)&config.latin1_tags,config_emit_int },
{ 1,0,0,CONFIG_TYPE_INT,"process_m3u",(void*)&config.process_m3u,config_emit_int },
{ 1,0,0,CONFIG_TYPE_INT,"scan_type",(void*)&config.scan_type,config_emit_int },
{ 1,0,0,CONFIG_TYPE_INT,"compress",(void*)&config.compress,config_emit_int },
{ 1,0,0,CONFIG_TYPE_STRING,"playlist",(void*)&config.playlist,config_emit_string },
{ 1,0,0,CONFIG_TYPE_STRING,"extensions",(void*)&config.extensions,config_emit_string },
{ 1,0,0,CONFIG_TYPE_STRING,"interface",(void*)&config.iface,config_emit_string },
{ 1,0,0,CONFIG_TYPE_STRING,"ssc_codectypes",(void*)&config.ssc_codectypes,config_emit_string },
{ 1,0,0,CONFIG_TYPE_STRING,"ssc_prog",(void*)&config.ssc_prog,config_emit_string },
{ 1,0,0,CONFIG_TYPE_STRING,"password",(void*)&config.readpassword, config_emit_string },
{ 1,0,0,CONFIG_TYPE_STRING,"compdirs",(void*)&config.compdirs, config_emit_string },
{ 1,0,0,CONFIG_TYPE_STRING,"logfile",(void*)&config.logfile, config_emit_string },
{ 1,0,0,CONFIG_TYPE_STRING,"art_filename",(void*)&config.artfilename,config_emit_string },
{ 1,1,0,CONFIG_TYPE_STRING,"runas",(void*)&config.runas,config_emit_string },
{ 1,1,0,CONFIG_TYPE_STRING,"web_root",(void*)&config.web_root,config_emit_string },
{ 1,1,0,CONFIG_TYPE_INT,"port",(void*)&config.port,config_emit_int },
{ 1,1,0,CONFIG_TYPE_STRING,"admin_pw",(void*)&config.adminpassword,config_emit_string },
{ 1,1,0,CONFIG_TYPE_STRING,"mp3_dir",(void*)&config.mp3dir,config_emit_string },
{ 1,0,0,CONFIG_TYPE_STRING,"db_dir",(void*)&config.dbdir,config_emit_string },
{ 1,0,0,CONFIG_TYPE_STRING,"db_type",(void*)&config.dbtype,config_emit_string },
{ 1,0,0,CONFIG_TYPE_STRING,"db_parms",(void*)&config.dbparms,config_emit_string },
{ 1,0,0,CONFIG_TYPE_INT,"debuglevel",(void*)NULL,config_emit_debuglevel },
{ 1,1,0,CONFIG_TYPE_STRING,"servername",(void*)&config.servername,config_emit_string },
{ 1,0,0,CONFIG_TYPE_INT,"rescan_interval",(void*)&config.rescan_interval,config_emit_int },
{ 1,0,0,CONFIG_TYPE_INT,"always_scan",(void*)&config.always_scan,config_emit_int },
{ 1,0,0,CONFIG_TYPE_INT,"latin1_tags",(void*)&config.latin1_tags,config_emit_int },
{ 1,0,0,CONFIG_TYPE_INT,"process_m3u",(void*)&config.process_m3u,config_emit_int },
{ 1,0,0,CONFIG_TYPE_INT,"scan_type",(void*)&config.scan_type,config_emit_int },
{ 1,0,0,CONFIG_TYPE_INT,"compress",(void*)&config.compress,config_emit_int },
{ 1,0,0,CONFIG_TYPE_STRING,"playlist",(void*)&config.playlist,config_emit_string },
{ 1,0,0,CONFIG_TYPE_STRING,"extensions",(void*)&config.extensions,config_emit_string },
{ 1,0,0,CONFIG_TYPE_STRING,"interface",(void*)&config.iface,config_emit_string },
{ 1,0,0,CONFIG_TYPE_STRING,"ssc_codectypes",(void*)&config.ssc_codectypes,config_emit_string },
{ 1,0,0,CONFIG_TYPE_STRING,"ssc_prog",(void*)&config.ssc_prog,config_emit_string },
{ 1,0,0,CONFIG_TYPE_STRING,"password",(void*)&config.readpassword, config_emit_string },
{ 1,0,0,CONFIG_TYPE_STRING,"compdirs",(void*)&config.compdirs, config_emit_string },
{ 1,0,0,CONFIG_TYPE_STRING,"logfile",(void*)&config.logfile, config_emit_string },
{ 1,0,0,CONFIG_TYPE_STRING,"art_filename",(void*)&config.artfilename,config_emit_string },
*/
{ 0,0,0,CONFIG_TYPE_SPECIAL,"servername",(void*)NULL,config_emit_servername },
{ 0,0,0,CONFIG_TYPE_SPECIAL,"config",(void*)NULL,config_emit_config },
@ -176,10 +176,10 @@ int config_password_required(WS_CONNINFO *pwsc, char *role) {
char *pw;
DPRINTF(E_DBG,L_MISC,"Checking if pw required for %s as %s\n",
pwsc->uri, role);
ws_uri(pwsc), role);
/* hack hack hack - this needs moved into the upnp plugin */
if(strncasecmp(pwsc->uri,"/upnp",5) == 0) {
if(strncasecmp(ws_uri(pwsc),"/upnp",5) == 0) {
DPRINTF(E_DBG,L_MISC,"Nope\n");
return FALSE;
}
@ -195,7 +195,7 @@ int config_password_required(WS_CONNINFO *pwsc, char *role) {
return FALSE;
#endif
if((pwsc->hostname) && (os_islocaladdr(pwsc->hostname))) {
if((ws_hostname(pwsc)) && (os_islocaladdr(ws_hostname(pwsc)))) {
if(pw) free(pw);
return FALSE;
}
@ -236,7 +236,7 @@ int config_matches_role(WS_CONNINFO *pwsc, char *username,
/* sanity check */
if((strcasecmp(role,"admin") != 0) &&
(strcasecmp(role,"user") !=0)) {
(strcasecmp(role,"user") !=0)) {
DPRINTF(E_LOG,L_MISC,"Unknown role: %s\n",role);
return FALSE;
}
@ -256,8 +256,8 @@ int config_matches_role(WS_CONNINFO *pwsc, char *username,
if(strcasecmp(role,"admin") == 0) {
DPRINTF(E_LOG,L_MISC,"Auth: failed attempt to gain admin privs by "
"%s from %s\n",username, pwsc->hostname);
return FALSE;
"%s from %s\n",username, ws_hostname(pwsc));
return FALSE;
}
/* we're checking for user privs */
@ -273,7 +273,7 @@ int config_matches_role(WS_CONNINFO *pwsc, char *username,
free(required_pw);
if(!result) {
DPRINTF(E_LOG,L_MISC,"Auth: failed attempt to gain user privs by "
"%s from %s\n",pwsc->hostname, username);
"%s from %s\n",ws_hostname(pwsc), username);
}
return result;
}
@ -310,7 +310,7 @@ void config_content_type(WS_CONNINFO *pwsc, char *path) {
* \param pwsc the web connection for the original page
* \param fd_src the fd of the file to do substitutions on
*/
void config_subst_stream(WS_CONNINFO *pwsc, int fd_src) {
void config_subst_stream(WS_CONNINFO *pwsc, IOHANDLE hfile) {
int in_arg;
char *argptr;
char argbuffer[256];
@ -320,8 +320,7 @@ void config_subst_stream(WS_CONNINFO *pwsc, int fd_src) {
char outbuffer[1024];
int out_index;
uint32_t bytes_read;
/* now throw out the file, with replacements */
in_arg=0;
@ -329,7 +328,9 @@ void config_subst_stream(WS_CONNINFO *pwsc, int fd_src) {
out_index=0;
while(1) {
if(r_read(fd_src,&next,1) <= 0)
/* FIXME: use a reasonable sized buffer, or implement buffered io */
bytes_read = 1;
if((!io_read(hfile,(unsigned char *)&next,&bytes_read)) || !bytes_read)
break;
if(in_arg) {
@ -369,7 +370,7 @@ void config_subst_stream(WS_CONNINFO *pwsc, int fd_src) {
/* flush whatever is in the buffer */
if(out_index) {
r_write(pwsc->fd,outbuffer,out_index);
ws_writebinary(pwsc,outbuffer,out_index);
out_index=0;
}
} else {
@ -377,14 +378,14 @@ void config_subst_stream(WS_CONNINFO *pwsc, int fd_src) {
out_index++;
if(out_index == sizeof(outbuffer)) {
r_write(pwsc->fd,outbuffer,out_index);
ws_writebinary(pwsc,outbuffer,out_index);
out_index=0;
}
}
}
}
if(out_index) {
r_write(pwsc->fd,outbuffer,out_index);
ws_writebinary(pwsc,outbuffer,out_index);
out_index=0;
}
}
@ -399,26 +400,27 @@ void config_handler(WS_CONNINFO *pwsc) {
char path[PATH_MAX];
char resolved_path[PATH_MAX];
char web_root[PATH_MAX];
int file_fd;
IOHANDLE hfile;
struct stat sb;
char *pw;
int size;
if(!pwsc->hostname) { /* ?? */
if(!ws_hostname(pwsc)) {
ws_returnerror(pwsc,500,"Couldn't determine remote hostname");
pwsc->close=1;
ws_should_close(pwsc,1);
return;
}
if(!os_islocaladdr(pwsc->hostname)) {
/* FIXME: should feed this file directly, so as to bypass perms */
if(!os_islocaladdr(ws_hostname(pwsc))) {
pw=conf_alloc_string("general","admin_pw",NULL);
if((!pw) || (strlen(pw) == 0)) {
if(pw) free(pw);
/* if we aren't getting the no_access.html page, we should */
if(strcmp(pwsc->uri,"/no_access.html") != 0) {
if(strcmp(ws_uri(pwsc),"/no_access.html") != 0) {
ws_addresponseheader(pwsc,"location","/no_access.html");
ws_returnerror(pwsc,302,"Moved Temporarily");
pwsc->close=1;
ws_should_close(pwsc,1);
return;
}
}
@ -434,19 +436,25 @@ void config_handler(WS_CONNINFO *pwsc) {
config_set_status(pwsc,0,"Serving admin pages");
pwsc->close=1;
ws_should_close(pwsc,1);
ws_addresponseheader(pwsc,"Connection","close");
if(strcasecmp(pwsc->uri,"/xml-rpc")==0) {
if(strcasecmp(ws_uri(pwsc),"/xml-rpc")==0) {
// perhaps this should get a separate handler
config_set_status(pwsc,0,"Serving xml-rpc method");
xml_handle(pwsc);
DPRINTF(E_DBG,L_CONF|L_XML,"Thread %d: xml-rpc served\n",pwsc->threadno);
DPRINTF(E_DBG,L_CONF|L_XML,"Thread %d: xml-rpc served\n",ws_threadno(pwsc));
config_set_status(pwsc,0,NULL);
return;
}
snprintf(path,PATH_MAX,"%s/%s",web_root,pwsc->uri);
if(('/' == web_root[strlen(web_root) - 1]) ||
('\\' == web_root[strlen(web_root) - 1])) {
web_root[strlen(web_root) - 1] = '\0';
}
snprintf(path,PATH_MAX,"%s/%s",web_root,ws_uri(pwsc));
DPRINTF(E_DBG,L_CONF|L_WS,"Realpathing %s\n",path);
if(!realpath(path,resolved_path)) {
pwsc->error=errno;
DPRINTF(E_WARN,L_CONF|L_WS,"Cannot resolve %s\n",path);
@ -461,33 +469,41 @@ void config_handler(WS_CONNINFO *pwsc) {
ws_addresponseheader(pwsc,"Location","/index.html");
ws_returnerror(pwsc,302,"Moved");
config_set_status(pwsc,0,NULL);
pwsc->close=1;
ws_should_close(pwsc,1);
return;
}
DPRINTF(E_DBG,L_CONF|L_WS,"Thread %d: Preparing to serve %s\n",
pwsc->threadno, resolved_path);
ws_threadno(pwsc), resolved_path);
if(strncmp(resolved_path,web_root,strlen(web_root))) {
pwsc->error=EINVAL;
DPRINTF(E_WARN,L_CONF|L_WS,"Thread %d: Requested file %s out of root\n",
pwsc->threadno,resolved_path);
ws_threadno(pwsc),resolved_path);
ws_returnerror(pwsc,403,"Forbidden");
config_set_status(pwsc,0,NULL);
return;
}
file_fd=r_open2(resolved_path,O_RDONLY);
if(file_fd == -1) {
pwsc->error=errno;
hfile = io_new();
if(!hfile) {
DPRINTF(E_FATAL,L_CONF | L_WS,"Cannot create file handle\n");
exit(-1);
}
DPRINTF(E_DBG,L_CONF|L_WS,"Opening %s\n",resolved_path);
if(!io_open(hfile,"file://%U",resolved_path)) {
ws_set_err(pwsc,E_WS_NATIVE);
DPRINTF(E_WARN,L_CONF|L_WS,"Thread %d: Error opening %s: %s\n",
pwsc->threadno,resolved_path,strerror(errno));
ws_threadno(pwsc),resolved_path,io_errstr(hfile));
ws_returnerror(pwsc,404,"Not found");
config_set_status(pwsc,0,NULL);
io_dispose(hfile);
return;
}
if(strcasecmp(pwsc->uri,"/config-update.html")==0) {
/* FIXME: use xml-rpc for all these */
if(strcasecmp(ws_uri(pwsc),"/config-update.html")==0) {
/* don't update (and turn everything to (null)) the
configuration file if what the user's really trying to do is
stop the server */
@ -509,13 +525,15 @@ void config_handler(WS_CONNINFO *pwsc) {
if((strcasecmp(&resolved_path[strlen(resolved_path) - 5],".html") == 0) ||
(strcasecmp(&resolved_path[strlen(resolved_path) - 4],".xml") == 0)) {
config_subst_stream(pwsc, file_fd);
config_subst_stream(pwsc, hfile);
} else {
copyfile(file_fd,pwsc->fd);
ws_copyfile(pwsc,hfile,NULL);
}
r_close(file_fd);
DPRINTF(E_DBG,L_CONF|L_WS,"Thread %d: Served successfully\n",pwsc->threadno);
io_close(hfile);
io_dispose(hfile);
DPRINTF(E_DBG,L_CONF|L_WS,"Thread %d: Served successfully\n",ws_threadno(pwsc));
config_set_status(pwsc,0,NULL);
return;
}
@ -747,10 +765,10 @@ void config_emit_service_status(WS_CONNINFO *pwsc, void *value, char *arg) {
}
/*
ws_writefd(pwsc,"<tr>\n");
ws_writefd(pwsc," <th>Bytes Served</th>\n");
ws_writefd(pwsc," <td>%d</td>\n",config.stats.songs_served);
ws_writefd(pwsc,"</tr>\n");
ws_writefd(pwsc,"<tr>\n");
ws_writefd(pwsc," <th>Bytes Served</th>\n");
ws_writefd(pwsc," <td>%d</td>\n",config.stats.songs_served);
ws_writefd(pwsc,"</tr>\n");
*/
ws_writefd(pwsc,"</table>\n");
@ -867,10 +885,10 @@ void config_emit_ispage(WS_CONNINFO *pwsc, void *value, char *arg) {
}
DPRINTF(E_DBG,L_CONF|L_WS,"page: %s, uri: %s\n",page,pwsc->uri);
DPRINTF(E_DBG,L_CONF|L_WS,"page: %s, uri: %s\n",page,ws_uri(pwsc));
if((strlen(page) > strlen(pwsc->uri)) ||
(strcasecmp(page,(char*)&pwsc->uri[strlen(pwsc->uri) - strlen(page)]) != 0)) {
if((strlen(page) > strlen(ws_uri(pwsc))) ||
(strcasecmp(page,(char*)&ws_uri(pwsc)[strlen(ws_uri(pwsc)) - strlen(page)]) != 0)) {
if(false) {
ws_writefd(pwsc,"%s",false);
}
@ -931,7 +949,7 @@ void config_emit_include(WS_CONNINFO *pwsc, void *value, char *arg) {
char resolved_path[PATH_MAX];
char path[PATH_MAX];
char web_root[PATH_MAX];
int file_fd;
IOHANDLE hfile;
struct stat sb;
int size;
@ -943,6 +961,11 @@ void config_emit_include(WS_CONNINFO *pwsc, void *value, char *arg) {
DPRINTF(E_DBG,L_CONF|L_WS,"Preparing to include %s\n",arg);
if(('/' == web_root[strlen(web_root) - 1]) ||
('\\' == web_root[strlen(web_root) - 1])) {
web_root[strlen(web_root) - 1] = '\0';
}
snprintf(path,PATH_MAX,"%s/%s",web_root,arg);
if(!realpath(path,resolved_path)) {
pwsc->error=errno;
@ -960,29 +983,37 @@ void config_emit_include(WS_CONNINFO *pwsc, void *value, char *arg) {
DPRINTF(E_DBG,L_CONF|L_WS,"Thread %d: Preparing to serve %s\n",
pwsc->threadno, resolved_path);
ws_threadno(pwsc), resolved_path);
if(strncmp(resolved_path,web_root,strlen(web_root))) {
pwsc->error=EINVAL;
DPRINTF(E_LOG,L_CONF|L_WS,"Thread %d: Requested file %s out of root\n",
pwsc->threadno,resolved_path);
ws_threadno(pwsc),resolved_path);
ws_writefd(pwsc,"<hr><i>error: %s out of web root</i><hr>",arg);
return;
}
file_fd=r_open2(resolved_path,O_RDONLY);
if(file_fd == -1) {
pwsc->error=errno;
hfile = io_new();
if(!hfile) {
DPRINTF(E_LOG,L_CONF | L_WS,"Thread %d: Cannot create file handle\n",
ws_threadno(pwsc));
return;
}
if(!io_open(hfile,"file://%U",resolved_path)) {
ws_set_err(pwsc,E_WS_NATIVE);
DPRINTF(E_LOG,L_CONF|L_WS,"Thread %d: Error opening %s: %s\n",
pwsc->threadno,resolved_path,strerror(errno));
ws_writefd(pwsc,"<hr><i>error: cannot open %s: %s</i><hr>",arg,strerror(errno));
ws_threadno(pwsc),resolved_path,io_errstr(pwsc));
ws_writefd(pwsc,"<hr><i>error: cannot open %s: %s</i><hr>",arg,
io_errstr(pwsc));
io_dispose(hfile);
return;
}
config_subst_stream(pwsc, file_fd);
config_subst_stream(pwsc, hfile);
r_close(file_fd);
DPRINTF(E_DBG,L_CONF|L_WS,"Thread %d: included successfully\n",pwsc->threadno);
io_close(hfile);
io_dispose(hfile);
DPRINTF(E_DBG,L_CONF|L_WS,"Thread %d: included successfully\n",ws_threadno(pwsc));
return;
}
@ -1035,8 +1066,8 @@ void config_set_status(WS_CONNINFO *pwsc, int session, char *fmt, ...) {
pfirst=(SCAN_STATUS*)malloc(sizeof(SCAN_STATUS));
if(pfirst) {
pfirst->what = NULL;
pfirst->thread = pwsc->threadno;
pfirst->host = strdup(pwsc->hostname);
pfirst->thread = ws_threadno(pwsc);
pfirst->host = strdup(ws_hostname(pwsc));
ws_set_local_storage(pwsc,pfirst,config_freescan);
} else {
DPRINTF(E_FATAL,L_CONF,"Malloc Error\n");

View File

@ -161,6 +161,9 @@ char *db_sql_strdup(const char *what) {
return what ? (strlen(what) ? strdup(what) : NULL) : NULL;
}
uint64_t db_sql_atol(const char *what) {
return what ? atoll(what) : 0;
}
/**
* escape a sql string, returning it the supplied buffer.
* note that this uses the sqlite escape format -- use %q for quoted
@ -794,6 +797,7 @@ int db_sql_add(char **pe, MP3FILE *pmp3, int *id) {
char *query;
char *path;
char sample_count[40];
char file_size[40];
DPRINTF(E_SPAM,L_DB,"Entering db_sql_add\n");
@ -826,7 +830,10 @@ int db_sql_add(char **pe, MP3FILE *pmp3, int *id) {
pmp3->play_count=0;
pmp3->time_played=0;
/* sqlite2 doesn't support 64 bit ints */
sprintf(sample_count,"%lld",pmp3->sample_count);
sprintf(file_size,"%lld",pmp3->file_size);
err=db_sql_exec_fn(pe,E_DBG,"INSERT INTO songs VALUES "
"(NULL," // id
"'%q'," // path
@ -845,7 +852,7 @@ int db_sql_add(char **pe, MP3FILE *pmp3, int *id) {
"%d," // bitrate
"%d," // samplerate
"%d," // song_length
"%d," // file_size
"%s," // file_size
"%d," // year
"%d," // track
"%d," // total_tracks
@ -887,7 +894,7 @@ int db_sql_add(char **pe, MP3FILE *pmp3, int *id) {
pmp3->bitrate,
pmp3->samplerate,
pmp3->song_length,
pmp3->file_size,
file_size,
pmp3->year,
pmp3->track,
pmp3->total_tracks,
@ -942,13 +949,16 @@ int db_sql_update(char **pe, MP3FILE *pmp3, int *id) {
char query[1024];
char *path;
char sample_count[40];
char file_size[40];
if(!pmp3->time_modified)
pmp3->time_modified = (int)time(NULL);
pmp3->db_timestamp = (int)time(NULL);
sprintf(sample_count,"%lld",pmp3->sample_count);
sprintf(file_size,"%lld",pmp3->file_size);
strcpy(query,"UPDATE songs SET "
"title='%q'," // title
"artist='%q'," // artist
@ -964,7 +974,7 @@ int db_sql_update(char **pe, MP3FILE *pmp3, int *id) {
"bitrate=%d," // bitrate
"samplerate=%d," // samplerate
"song_length=%d," // song_length
"file_size=%d," // file_size
"file_size=%s," // file_size
"year=%d," // year
"track=%d," // track
"total_tracks=%d," // total_tracks
@ -998,7 +1008,7 @@ int db_sql_update(char **pe, MP3FILE *pmp3, int *id) {
pmp3->bitrate,
pmp3->samplerate,
pmp3->song_length,
pmp3->file_size,
file_size,
pmp3->year,
pmp3->track,
pmp3->total_tracks,
@ -1444,7 +1454,7 @@ void db_sql_build_mp3file(SQL_ROW valarray, MP3FILE *pmp3) {
pmp3->bitrate=db_sql_atoi(valarray[14]);
pmp3->samplerate=db_sql_atoi(valarray[15]);
pmp3->song_length=db_sql_atoi(valarray[16]);
pmp3->file_size=db_sql_atoi(valarray[17]);
pmp3->file_size=db_sql_atol(valarray[17]);
pmp3->year=db_sql_atoi(valarray[18]);
pmp3->track=db_sql_atoi(valarray[19]);
pmp3->total_tracks=db_sql_atoi(valarray[20]);
@ -1462,7 +1472,7 @@ void db_sql_build_mp3file(SQL_ROW valarray, MP3FILE *pmp3) {
pmp3->time_played=db_sql_atoi(valarray[32]);
pmp3->db_timestamp=db_sql_atoi(valarray[33]);
pmp3->disabled=db_sql_atoi(valarray[34]);
pmp3->sample_count=db_sql_atoi(valarray[35]);
pmp3->sample_count=db_sql_atol(valarray[35]);
pmp3->force_update=db_sql_atoi(valarray[36]);
pmp3->codectype=db_sql_strdup(valarray[37]);
pmp3->index=db_sql_atoi(valarray[38]);

View File

@ -49,6 +49,8 @@
#include "daapd.h"
#include "err.h"
#include "io.h"
#ifndef ERR_LEAN
# include "os.h"
# include "plugin.h"
@ -63,7 +65,7 @@
static int err_debuglevel=0; /**< current debuglevel, set from command line with -d */
static int err_logdest=0; /**< current log destination */
static char err_filename[PATH_MAX + 1];
static FILE *err_file=NULL; /**< if logging to file, the handle of that file */
static IOHANDLE err_file = NULL;
static unsigned int err_debugmask=0xFFFFFFFF; /**< modules to debug, see \ref log_categories */
static int err_truncate = 0;
static int err_syslog_open = 0;
@ -86,13 +88,13 @@ static uint32_t _err_get_threadid(void);
*/
uint32_t _err_get_threadid(void) {
pthread_t tid;
int thread_id;
int thread_id=0;
memset((void*)&tid,0,sizeof(pthread_t));
tid = pthread_self();
if(sizeof(pthread_t) == sizeof(int)) {
thread_id = *((int*)&tid);
memcpy((void*)&thread_id,(void*)&tid,sizeof(thread_id));
} else {
thread_id = util_djb_hash_block((unsigned char *)&tid,sizeof(pthread_t));
}
@ -105,23 +107,19 @@ uint32_t _err_get_threadid(void) {
* would help for log rotation
*/
void err_reopen(void) {
int err;
if(!(err_logdest & LOGDEST_LOGFILE))
return;
fclose(err_file);
err_file = fopen(err_filename,"a");
if(!err_file) {
io_close(err_file);
if(!io_open("file://%U?mode=a",err_filename)) {
/* what to do when you lose your logging mechanism? Keep
* going?
*/
err = errno;
err_setdest(err_logdest & (~LOGDEST_LOGFILE));
err_setdest(err_logdest | LOGDEST_SYSLOG);
DPRINTF(E_LOG,L_MISC,"Could not rotate log file: %s\n",
strerror(err));
io_errstr(err_file));
return;
}
@ -166,9 +164,8 @@ void err_log(int level, unsigned int cat, char *fmt, ...)
snprintf(timebuf,sizeof(timebuf),"%04d-%02d-%02d %02d:%02d:%02d",
tm_now.tm_year + 1900, tm_now.tm_mon + 1, tm_now.tm_mday,
tm_now.tm_hour, tm_now.tm_min, tm_now.tm_sec);
fprintf(err_file,"%s (%08x): %s",timebuf,_err_get_threadid(),errbuf);
if(!level) fprintf(err_file,"%s: Aborting\n",timebuf);
fflush(err_file);
io_printf(err_file,"%s (%08x): %s",timebuf,_err_get_threadid(),errbuf);
if(!level) io_printf(err_file,"%s: Aborting\n",timebuf);
}
/* always log to stderr on fatal error */
@ -262,7 +259,15 @@ int err_setlogfile(char *file) {
*/
if(err_file) {
fclose(err_file);
io_close(err_file);
} else {
err_file = io_new();
if(!err_file) {
err_logdest &= ~LOGDEST_LOGFILE;
if(!err_syslog_open)
os_opensyslog();
os_syslog(1,"Error initializing logfile");
}
}
mode = "a";
@ -270,8 +275,8 @@ int err_setlogfile(char *file) {
strncpy(err_filename,file,sizeof(err_filename)-1);
err_file = fopen(err_filename,mode);
if(err_file == NULL) {
if(!io_open(err_file,"file://%U?mode=%s",err_filename,mode)) {
fprintf(stderr,"Error opening logfile: %s",io_errstr(err_file));
err_logdest &= ~LOGDEST_LOGFILE;
if(!err_syslog_open)
@ -298,7 +303,7 @@ void err_setdest(int destination) {
if((err_logdest & LOGDEST_LOGFILE) &&
(!(destination & LOGDEST_LOGFILE))) {
/* used to be logging to file, not any more */
fclose(err_file);
io_close(err_file);
}
err_logdest=destination;

View File

@ -80,7 +80,7 @@ typedef struct tag_mp3file {
uint32_t bitrate;
uint32_t samplerate;
uint32_t song_length;
uint32_t file_size; /* ?? */
uint64_t file_size; /* ?? */
uint32_t year; /* TDRC */
uint32_t track; /* TRCK */

View File

@ -133,7 +133,6 @@ typedef struct tag_plugin_input_fn {
int (*ws_writefd)(struct tag_ws_conninfo *, char *, ...);
int (*ws_addresponseheader)(struct tag_ws_conninfo *, char *, char *, ...);
void (*ws_emitheaders)(struct tag_ws_conninfo *);
int (*ws_fd)(struct tag_ws_conninfo *);
char* (*ws_getrequestheader)(struct tag_ws_conninfo *, char *);
int (*ws_writebinary)(struct tag_ws_conninfo *, char *, int);
char* (*ws_gethostname)(struct tag_ws_conninfo *);

372
src/io-driver.c Normal file
View File

@ -0,0 +1,372 @@
#define _CRT_SECURE_NO_WARNINGS 1
#define _CRT_SECURE_NO_DEPRECATE 1
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef WIN32
# include "getopt.h"
#endif
#include "io.h"
struct testinfo {
char *name;
int files;
int restart;
int (*handler)(void);
};
int test_readfile(void);
int test_readfile_timeout(void);
int test_servefile(void);
int test_udpserver(void);
int test_udpclient(void);
int test_buffer(void);
struct testinfo tests[] = {
{ "Read file, showing block size [uri to read]", 1, 0, test_readfile },
{ "Read file, with 10s timeout [uri to read]", 1, 0, test_readfile_timeout },
{ "Serve a file [listen://port] [uri to serve]", 2, 1, test_servefile },
{ "UDP echo server [udplisten://port]", 1, 0, test_udpserver },
{ "UDP echo client [udp://server:port]", 1, 0, test_udpclient },
{ "Buffered line read [uri]", 1, 0, test_buffer }
};
int debuglevel=1;
char *files[20];
int test_udpclient(void) {
IOHANDLE ioclient;
unsigned char buffer[256];
uint32_t len;
ioclient = io_new();
if(!io_open(ioclient,files[0])) {
printf("Can't open udp connection: %s\n", io_errstr(ioclient));
return FALSE;
}
strncpy((char*)buffer,"foo",sizeof(buffer));
len = (uint32_t)strlen((char*)buffer);
if(!io_write(ioclient,buffer,&len)) {
printf("Write error: %s\n",io_errstr(ioclient));
return FALSE;
}
/* wait for return */
len = sizeof(buffer);
if(!io_read(ioclient, buffer, &len)) {
printf("Read error: %s\n",io_errstr(ioclient));
return FALSE;
}
buffer[len] = '\0';
printf("Read %d bytes: %s\n",len,buffer);
return TRUE;
}
int test_udpserver(void) {
IOHANDLE ioserver;
IO_WAITHANDLE iow;
struct sockaddr_in si_from;
socklen_t si_len;
unsigned char buffer[256];
uint32_t len;
uint32_t timeout;
ioserver = io_new();
if(!io_open(ioserver,files[0])) {
printf("Can't open listener: %s\n", io_errstr(ioserver));
return FALSE;
}
si_len = sizeof(si_from);
iow = io_wait_new();
io_wait_add(iow, ioserver, IO_WAIT_READ | IO_WAIT_ERROR);
timeout = 30000; /* 30 seconds */
io_wait(iow, &timeout);
if(timeout == 0) {
printf("Timeout!\n");
return TRUE;
}
if(io_wait_status(iow, ioserver) & IO_WAIT_ERROR) {
printf("Error in ioserver socket\n");
return FALSE;
}
len = sizeof(buffer);
if(!io_udp_recvfrom(ioserver, buffer, &len, &si_from, &si_len)) {
printf("Error in recvfrom: %s\n",io_errstr(ioserver));
return FALSE;
}
buffer[len] = '\0';
printf("Got %s\n",buffer);
printf("Returning...\n");
io_udp_sendto(ioserver,buffer,&len,&si_from,si_len);
io_close(ioserver);
io_dispose(ioserver);
io_wait_dispose(iow);
return TRUE;
}
int test_servefile(void) {
IOHANDLE ioserver, ioclient, iofile;
IO_WAITHANDLE iow;
uint32_t timeout;
unsigned char buffer[256];
uint32_t len = sizeof(buffer);
ioserver = io_new();
if(!io_open(ioserver,files[0])) {
printf("Can't open listener: %s\n",io_errstr(ioserver));
return FALSE;
}
printf("Making new waiter...\n");
iow = io_wait_new();
printf("Adding io object to waiter\n");
io_wait_add(iow, ioserver, IO_WAIT_READ | IO_WAIT_ERROR);
timeout = 30000; /* 30 seconds */
printf("Waiting...\n");
while(io_wait(iow, &timeout)) {
printf("Done waiting.\n");
if(io_wait_status(iow, ioserver) & IO_WAIT_ERROR) {
printf("Got a status of IO_WAIT_ERROR\n");
break;
}
if(io_wait_status(iow, ioserver) & IO_WAIT_READ) {
/* got a client */
printf("Got a client...\n");
iofile = io_new();
if(!io_open(iofile,files[1])) {
printf("Can't open file to serve: %s\n",io_errstr(iofile));
io_dispose(iofile);
io_dispose(ioserver);
return FALSE;
}
printf("Opened %s to serve\n",files[1]);
/* jet it out to the client */
ioclient = io_new();
io_listen_accept(ioserver,ioclient,NULL);
printf("Got client socket\n");
len = sizeof(buffer);
while(io_read(iofile,buffer,&len) && len) {
printf("Read %d bytes\n",len);
if(!io_write(ioclient,buffer,&len)) {
printf("write error: %s\n",io_errstr(ioclient));
}
len = sizeof(buffer);
}
io_close(ioclient);
io_dispose(ioclient);
printf("Closing client connection\n");
io_close(iofile);
io_dispose(iofile);
printf("Looping to wait again.\n");
}
timeout = 30000; /* 30 seconds */
}
printf("Wait failed: timeout: %d\n",timeout);
if(!timeout) {
printf("Timeout waiting for socket\n");
return TRUE;
}
/* socket error? */
printf("Socket error: %s\n",io_errstr(ioserver));
return FALSE;
}
int test_readfile(void) {
IOHANDLE ioh;
unsigned char buffer[256];
uint32_t len = sizeof(buffer);
uint64_t file_len;
ioh = io_new();
if(ioh != INVALID_HANDLE) {
if(!io_open(ioh,files[0])) {
printf("Can't open file: %s\n",io_errstr(ioh));
return FALSE;
}
io_size(ioh, &file_len);
printf("File size: %ld bytes\n",file_len);
while(io_read(ioh,buffer,&len) && len) {
printf("Read %d bytes\n",len);
len = sizeof(buffer);
}
if(!len) {
printf("EOF!\n");
} else {
printf("Read error: %s\n",io_errstr(ioh));
}
io_close(ioh);
} else {
return FALSE;
}
return TRUE;
}
int test_buffer(void) {
IOHANDLE ioh;
unsigned char buffer[256];
uint32_t len = sizeof(buffer);
uint64_t file_len;
int line=1;
ioh = io_new();
if(ioh != INVALID_HANDLE) {
if(!io_open(ioh,files[0])) {
printf("Can't open file: %s\n",io_errstr(ioh));
return FALSE;
}
io_size(ioh, &file_len);
io_buffer(ioh);
printf("File size: %ld bytes\n",file_len);
while(io_readline(ioh,buffer,&len) && len) {
printf("Read %d bytes\n",len);
len = sizeof(buffer);
printf("Line %d: %s\n",line,buffer);
line++;
}
if(!len) {
printf("EOF!\n");
} else {
printf("Read error: %s\n",io_errstr(ioh));
}
io_close(ioh);
} else {
return FALSE;
}
return TRUE;
}
int test_readfile_timeout(void) {
IOHANDLE ioh;
unsigned char buffer[256];
uint32_t len = sizeof(buffer);
uint32_t timeout;
ioh = io_new();
if(ioh != INVALID_HANDLE) {
if(!io_open(ioh,files[0])) {
printf("Can't open file: %s\n",io_errstr(ioh));
return FALSE;
}
timeout = 1000;
while(io_read_timeout(ioh,buffer,&len,&timeout) && len) {
printf("Read %d bytes\n",len);
len = sizeof(buffer);
timeout = 1000;
}
if(!len) {
printf("EOF!\n");
} else {
if(!timeout) {
printf("Timeout\n");
} else {
printf("Read error: %s\n",io_errstr(ioh));
}
}
io_close(ioh);
} else {
return FALSE;
}
return TRUE;
}
void errhandler(int level, char *msg) {
if(level <= debuglevel) {
fprintf(stderr,"L%d: %s",level,msg);
}
if(!level) {
fflush(stdout);
exit(1);
}
}
void usage(void) {
int ntests;
int test;
fprintf(stderr,"io -t<n> [[file] [...]]\n\n");
ntests = (sizeof(tests)/sizeof(struct testinfo));
for(test=0; test < ntests; test++) {
fprintf(stderr, "Test %02d: %s\n",test+1,tests[test].name);
}
exit(-1);
}
int main(int argc, char *argv[]) {
int option;
int test = 0;
int nfiles=0;
int retval;
while((option = getopt(argc, argv, "t:d:")) != -1) {
switch(option) {
case 't':
test = atoi(optarg);
break;
case 'd':
debuglevel = atoi(optarg);
break;
}
}
if(!test || (test > (sizeof(tests)/sizeof(struct testinfo))))
usage();
test--;
while((optind + nfiles) != argc) {
files[nfiles] = argv[optind + nfiles];
nfiles++;
}
if(tests[test].files != nfiles) {
fprintf(stderr,"Test %d requres %d files, only got %d\n",
test, tests[test].files, nfiles);
exit(-1);
}
io_init();
io_set_errhandler(errhandler);
printf("Executing test: %s\n",tests[test].name);
retval = tests[test].handler();
io_deinit();
return (retval == FALSE);
}

49
src/io-errors.h Normal file
View File

@ -0,0 +1,49 @@
/*
* $Id: $
*
* Created by Ron Pedde on 2007-07-01.
* Copyright (C) 2007 Firefly Media Services. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _IO_ERRORS_H_
#define _IO_ERRORS_H_
#define IO_E_OTHER 0x00000001 /**< Native error */
#define IO_E_BADPROTO 0x01000001 /**< Bad protocol type, unhandled URI */
#define IO_E_NOTINIT 0x01000002 /**< io object not initialized with new */
#define IO_E_BADFN 0x01000003 /**< uniplemented io function */
#define IO_E_INTERNAL 0x01000004 /**< something fatal internally */
#define IO_E_FILE_OTHER 0x00000000 /**< Native error */
#define IO_E_FILE_NOTOPEN 0x02000001 /**< operation on closed file */
#define IO_E_FILE_UNKNOWN 0x02000002 /**< other inernal error */
#define IO_E_SOCKET_OTHER 0x00000000 /**< Native error */
#define IO_E_SOCKET_NOTOPEN 0x03000001 /**< operation on closed socket */
#define IO_E_SOCKET_UNKNOWN 0x03000002 /**< other internal error */
#define IO_E_SOCKET_BADHOST 0x03000003 /**< can't resolve address */
#define IO_E_SOCKET_NOTINIT 0x03000004 /**< socket not initialized with new */
#define IO_E_SOCKET_BADFN 0x03000005 /**< unimplemented io function */
#define IO_E_SOCKET_NOMCAST 0x03000006 /**< can't set up multicast */
#define IO_E_SOCKET_INVALID 0x03000007 /**< ? */
#define IO_E_SOCKET_INUSE 0x03000008 /**< EADDRINUSE */
#endif /* _IO_ERRORS_H_ */

79
src/io-plugin.h Normal file
View File

@ -0,0 +1,79 @@
/*
* $Id: $
*
* Private interface for io objects to provide plug-in io objects
*/
#ifndef _IO_PLUGIN_H_
#define _IO_PLUGIN_H_
typedef struct tag_io_privhandle IO_PRIVHANDLE;
typedef struct tag_io_fnptr IO_FNPTR;
typedef struct tag_io_optionlist IO_OPTIONLIST;
#define IO_LOG_FATAL 0
#define IO_LOG_LOG 1
#define IO_LOG_WARN 3
#define IO_LOG_INFO 5
#define IO_LOG_DEBUG 9
#define IO_LOG_SPAM 10
#ifdef WIN32
# define WAITABLE_T HANDLE
# define SOCKET_T SOCKET
# define FILE_T HANDLE
# define ERR_T DWORD
typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int64 uint64_t;
#else
# define WAITABLE_T int
# define SOCKET_T int
# define FILE_T int
# define ERR_T int
#endif
struct tag_io_fnptr {
int(*fn_open)(IO_PRIVHANDLE *, char *);
int(*fn_close)(IO_PRIVHANDLE *);
int(*fn_read)(IO_PRIVHANDLE *, unsigned char *, uint32_t *);
int(*fn_write)(IO_PRIVHANDLE *, unsigned char *, uint32_t *);
int(*fn_size)(IO_PRIVHANDLE *, uint64_t *);
int(*fn_setpos)(IO_PRIVHANDLE *, uint64_t, int);
int(*fn_getpos)(IO_PRIVHANDLE *, uint64_t *);
char*(*fn_geterrmsg)(IO_PRIVHANDLE *, ERR_T *, int *);
int(*fn_getwaitable)(IO_PRIVHANDLE *, int, WAITABLE_T *);
int(*fn_getfd)(IO_PRIVHANDLE *, FILE_T *);
int(*fn_getsocket)(IO_PRIVHANDLE *, SOCKET_T *);
};
struct tag_io_privhandle {
int open; /**< Whether file is open - don't touch */
ERR_T err; /**< Current error code - don't touch */
int is_local;
char *err_str; /**< Current error string - don't touch */
IO_FNPTR *fnptr; /**< Set on io_open by checking proto table */
IO_OPTIONLIST *pol; /**< List of passed options */
char *proto; /**< proto of file */
int buffering; /**< are we in linebuffer mode? */
int buffer_offset; /**< current offset pointer */
uint32_t buffer_len; /**< total size of buffer */
unsigned char *buffer; /**< linebuffer */
/**
* the following parameters should be set by the provider in
* the open function (or as soon as is practicable)
*/
void *private; /**< Private struct for implementation */
};
extern int io_register_handler(char *proto, IO_FNPTR *fnptr);
extern char* io_option_get(IO_PRIVHANDLE *phandle,char *option,char *dflt);
#ifndef TRUE
# define TRUE 1
# define FALSE 0
#endif
#endif /* _IO_PLUGIN_H_ */

3037
src/io.c Normal file

File diff suppressed because it is too large Load Diff

124
src/io.h Normal file
View File

@ -0,0 +1,124 @@
/*
* $Id: $
*
* Generic IO abstraction for files, sockets, http streams, etc
*/
#ifndef _IO_H_
#define _IO_H_
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifndef WIN32
# include <netinet/in.h>
#else
#define WIN32_LEAN_AND_MEAN
# include <windows.h>
# include <ws2tcpip.h>
#endif
#include "io-errors.h"
typedef void* IOHANDLE;
typedef void* IO_WAITHANDLE;
#define INVALID_HANDLE NULL
#ifdef WIN32 /* MSVC, really */
# define SOCKET_T SOCKET
# define FILE_T HANDLE
# define mode_t int
typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int64 uint64_t;
#else
# define SOCKET_T int
# define FILE_T int
#endif
/*
* Valid protocol options:
*
* udplisten:
* multicast=0/1 (default 0)
* mcast_group=multicast ip (required if multicast=1)
* mcast_ttl=<n> (defaults to 4)
*
* Example URI for UPnP:
* udplisten://1900?multicast=1&mcast_group=239.255.255.250&mcast_ttl=4
*
* listen:
* backlog=<n> (default to 5)
* reuse=0/1 (defaults to 1) - SO_REUSEADDR
*/
extern int io_init(void);
extern int io_deinit(void);
extern void io_set_errhandler(void(*err_handler)(int, char *));
extern void io_set_lockhandler(void(*lock_handler)(int));
extern IOHANDLE io_new(void);
extern int io_open(IOHANDLE io, char *fmt, ...);
extern int io_close(IOHANDLE io);
extern int io_read(IOHANDLE io, unsigned char *buf, uint32_t *len);
extern int io_read_timeout(IOHANDLE io, unsigned char *buf, uint32_t *len,
uint32_t *ms);
extern int io_readline(IOHANDLE io, unsigned char *buf, uint32_t *len);
extern int io_readline_timed(IOHANDLE io, unsigned char *buf, uint32_t *len,
uint32_t *ms);
extern int io_write(IOHANDLE io, unsigned char *buf, uint32_t *len);
extern int io_printf(IOHANDLE io, char *fmt, ...);
extern int io_size(IOHANDLE io, uint64_t *size);
extern int io_setpos(IOHANDLE io, uint64_t offset, int whence);
extern int io_getpos(IOHANDLE io, uint64_t *pos);
extern int io_buffer(IOHANDLE io); /* unimplemented */
extern int io_readline(IOHANDLE io, unsigned char *buf, uint32_t *len);
extern int io_readline_timeout(IOHANDLE io, unsigned char *buf, uint32_t *len,
uint32_t *ms);
extern char* io_errstr(IOHANDLE io);
extern int io_errcode(IOHANDLE io);
extern void io_dispose(IOHANDLE io);
/* Given a listen:// socket, accept and return the child socket.
* the child IOHANDLE should have already been allocated with io_new, and
* even on error must be io_dispose'd */
extern int io_listen_accept(IOHANDLE io, IOHANDLE child, struct in_addr *host);
/* Given an iohandle that has already been io_new'ed, attach an exising
* socket or file handle to the iohandle */
extern int io_file_attach(IOHANDLE io, FILE_T fd);
extern int io_socket_attach(IOHANDLE io, SOCKET_T fd);
/* udp equivalents to recvfrom/sendto */
extern int io_udp_recvfrom(IOHANDLE io, unsigned char *buf, uint32_t *len,
struct sockaddr_in *si_remote, socklen_t *si_len);
extern int io_udp_sendto(IOHANDLE io, unsigned char *buf, uint32_t *len,
struct sockaddr_in *si_remote, socklen_t si_len);
extern int io_isproto(IOHANDLE io, char *proto);
/* Some wrappers to level os-specific file handling */
extern int io_stat(unsigned char *file);
extern int io_find(unsigned char *file, unsigned char *current, unsigned char **returned, int len);
#define IO_WAIT_READ 1
#define IO_WAIT_WRITE 2
#define IO_WAIT_ERROR 4
extern IO_WAITHANDLE io_wait_new(void);
extern int io_wait_add(IO_WAITHANDLE wh, IOHANDLE io, int type);
extern int io_wait(IO_WAITHANDLE wh, uint32_t *ms);
extern int io_wait_status(IO_WAITHANDLE wh, IOHANDLE io);
extern int io_wait_dispose(IO_WAITHANDLE wh);
#ifndef TRUE
# define TRUE 1
# define FALSE 0
#endif
#endif /* _IO_H_ */

11
src/io.mk Normal file
View File

@ -0,0 +1,11 @@
CC=gcc
CFLAGS := $(CFLAGS) -DDEBUG -g -DHAVE_UNISTD_H
LDFLAGS := $(LDFLAGS)
TARGET = io
OBJECTS= io.o io-driver.o bsd-snprintf.o
$(TARGET): $(OBJECTS)
$(CC) -o $(TARGET) $(LDFLAGS) $(OBJECTS)
clean:
rm -f $(OBJECTS) $(TARGET)

View File

@ -89,6 +89,7 @@
#include "plugin.h"
#include "util.h"
#include "upnp.h"
#include "io.h"
#ifdef HAVE_GETOPT_H
# include "getopt.h"
@ -118,7 +119,8 @@ static void usage(char *program);
static void main_handler(WS_CONNINFO *pwsc);
static int main_auth(WS_CONNINFO *pwsc, char *username, char *password);
static void txt_add(char *txtrecord, char *fmt, ...);
static void main_io_errhandler(int level, char *msg);
static void main_ws_errhandler(int level, char *msg);
/**
* build a dns text string
@ -150,23 +152,23 @@ void txt_add(char *txtrecord, char *fmt, ...) {
void main_handler(WS_CONNINFO *pwsc) {
DPRINTF(E_DBG,L_MAIN,"in main_handler\n");
if(plugin_url_candispatch(pwsc)) {
DPRINTF(E_DBG,L_MAIN,"Dispatching %s to plugin\n",pwsc->uri);
DPRINTF(E_DBG,L_MAIN,"Dispatching %s to plugin\n",ws_uri(pwsc));
plugin_url_handle(pwsc);
return;
}
DPRINTF(E_DBG,L_MAIN,"Dispatching %s to config handler\n",pwsc->uri);
DPRINTF(E_DBG,L_MAIN,"Dispatching %s to config handler\n",ws_uri(pwsc));
config_handler(pwsc);
}
int main_auth(WS_CONNINFO *pwsc, char *username, char *password) {
DPRINTF(E_DBG,L_MAIN,"in main_auth\n");
if(plugin_url_candispatch(pwsc)) {
DPRINTF(E_DBG,L_MAIN,"Dispatching auth for %s to plugin\n",pwsc->uri);
DPRINTF(E_DBG,L_MAIN,"Dispatching auth for %s to plugin\n",ws_uri(pwsc));
return plugin_auth_handle(pwsc,username,password);
}
DPRINTF(E_DBG,L_MAIN,"Dispatching auth for %s to config auth\n",pwsc->uri);
DPRINTF(E_DBG,L_MAIN,"Dispatching auth for %s to config auth\n",ws_uri(pwsc));
return config_auth(pwsc, username, password);
}
@ -241,6 +243,25 @@ int load_plugin_dir(char *plugindir) {
return loaded;
}
/**
* set up an errorhandler for io errors
*
* @param int level of the error (0=fatal, 9=debug)
* @param msg the text error
*/
void main_io_errhandler(int level, char *msg) {
DPRINTF(level,L_MAIN,"%s",msg);
}
/**
* set up an errorhandler for webserver errors
*
* @param int level of the error (0=fatal, 9=debug)
* @param msg the text error
*/
void main_ws_errhandler(int level, char *msg) {
DPRINTF(level,L_WS,"%s",msg);
}
/**
* Kick off the daap server and wait for events.
*
@ -387,6 +408,10 @@ int main(int argc, char *argv[]) {
exit(0);
}
io_init();
io_set_errhandler(main_io_errhandler);
ws_set_errhandler(main_ws_errhandler);
/* read the configfile, if specified, otherwise
* try defaults */
config.stats.start_time=start_time=(int)time(NULL);
@ -406,7 +431,6 @@ int main(int argc, char *argv[]) {
exit(EXIT_FAILURE);
}
if(debuglevel) /* was specified, should override the config file */
err_setlevel(debuglevel);
@ -500,7 +524,15 @@ int main(int argc, char *argv[]) {
if(db_init(reload)) {
DPRINTF(E_FATAL,L_MAIN|L_DB,"Error in db_init: %s\n",strerror(errno));
}
err=db_get_song_count(&perr,&song_count);
if(err != DB_E_SUCCESS) {
DPRINTF(E_FATAL,L_MISC,"Error getting song count: %s\n",perr);
}
/* do a full reload if the db is empty */
if(!song_count)
reload = 1;
if(conf_get_array("general","mp3_dir",&mp3_dir_array)) {
if((!skip_initial) || (reload)) {
DPRINTF(E_LOG,L_MAIN|L_SCAN,"Starting mp3 scan\n");
@ -529,12 +561,19 @@ int main(int argc, char *argv[]) {
DPRINTF(E_LOG,L_MAIN|L_WS,"Starting web server from %s on port %d\n",
ws_config.web_root, ws_config.port);
config.server=ws_start(&ws_config);
config.server=ws_init(&ws_config);
if(!config.server) {
DPRINTF(E_FATAL,L_MAIN|L_WS,"Error staring web server: %s\n",strerror(errno));
/* pthreads or malloc error */
DPRINTF(E_FATAL,L_MAIN|L_WS,"Error initializing web server\n");
}
ws_registerhandler(config.server, "/",main_handler,main_auth,1);
if(E_WS_SUCCESS != ws_start(config.server)) {
/* listen or pthread error */
DPRINTF(E_FATAL,L_MAIN|L_WS,"Error starting web server\n");
}
ws_registerhandler(config.server, "/",main_handler,main_auth,
0,1);
#ifndef WITHOUT_MDNS
if(config.use_mdns) { /* register services */
@ -658,6 +697,7 @@ int main(int argc, char *argv[]) {
DPRINTF(E_LOG,L_MAIN,"Done!\n");
os_deinit();
io_deinit();
mem_dump();
return EXIT_SUCCESS;
}

View File

@ -50,6 +50,7 @@
#include "conf.h"
#include "db-generic.h"
#include "err.h"
#include "io.h"
#include "mp3-scanner.h"
#include "os.h"
#include "restart.h"
@ -425,7 +426,7 @@ int scan_static_playlist(char *path) {
char file_path[PATH_MAX];
char real_path[PATH_MAX];
char linebuffer[PATH_MAX];
int fd;
IOHANDLE hfile;
int playlistid;
M3UFILE *pm3u;
MP3FILE *pmp3;
@ -433,6 +434,7 @@ int scan_static_playlist(char *path) {
char *current;
char *perr;
char *ptr;
uint32_t len;
DPRINTF(E_WARN,L_SCAN|L_PL,"Processing static playlist: %s\n",path);
if(os_stat(path,&sb)) {
@ -467,14 +469,18 @@ int scan_static_playlist(char *path) {
db_delete_playlist(NULL,pm3u->id);
}
fd=open(path,O_RDONLY);
if(fd != -1) {
if(!(hfile = io_new())) {
DPRINTF(E_LOG,L_SCAN,"Cannot create file handle\n");
return FALSE;
}
if(io_open(hfile,"file://%U",path)) {
if(db_add_playlist(&perr,base_path,PL_STATICFILE,NULL,path,
0,&playlistid) != DB_E_SUCCESS) {
DPRINTF(E_LOG,L_SCAN,"Error adding m3u %s: %s\n",path,perr);
free(perr);
db_dispose_playlist(pm3u);
close(fd);
io_dispose(hfile);
return FALSE;
}
/* now get the *real* base_path */
@ -493,13 +499,18 @@ int scan_static_playlist(char *path) {
DPRINTF(E_INF,L_SCAN|L_PL,"Added playlist as id %d\n",playlistid);
memset(linebuffer,0x00,sizeof(linebuffer));
while(readline(fd,linebuffer,sizeof(linebuffer)) > 0) {
io_buffer(hfile);
len = sizeof(linebuffer);
while(io_readline(hfile,(unsigned char *)linebuffer,&len) && len) {
while((linebuffer[strlen(linebuffer)-1] == '\n') ||
(linebuffer[strlen(linebuffer)-1] == '\r'))
linebuffer[strlen(linebuffer)-1] = '\0';
if((linebuffer[0] == ';') || (linebuffer[0] == '#'))
if((linebuffer[0] == ';') || (linebuffer[0] == '#')) {
len = sizeof(linebuffer);
continue;
}
// FIXME - should chomp trailing comments
@ -532,10 +543,13 @@ int scan_static_playlist(char *path) {
linebuffer,perr);
free(perr);
}
len = strlen(linebuffer);
}
close(fd);
io_close(hfile);
}
io_dispose(hfile);
db_dispose_playlist(pm3u);
DPRINTF(E_WARN,L_SCAN|L_PL,"Done processing playlist\n");
return TRUE;

View File

@ -435,6 +435,7 @@ int _os_start_signal_handler(void) {
(sigaddset(&set,SIGHUP) == -1) ||
(sigaddset(&set,SIGCLD) == -1) ||
(sigaddset(&set,SIGTERM) == -1) ||
(sigaddset(&set,SIGPIPE) == -1) ||
(pthread_sigmask(SIG_BLOCK, &set, NULL) == -1)) {
DPRINTF(E_LOG,L_MAIN,"Error setting signal set\n");
return -1;

View File

@ -841,7 +841,9 @@ int os_stat(const char *path, struct _stat *sb) {
int os_lstat(const char *path, struct _stat *sb) {
return os_stat(path,sb);
}
/* FIXME: mode */
/* These should be fixed by io_ functions
int os_open(const char *filename, int oflag) {
WCHAR utf16_path[PATH_MAX+1];
int fd;
@ -863,6 +865,6 @@ FILE *os_fopen(const char *filename, const char *mode) {
util_utf8toutf16((unsigned char *)&utf16_mode,10 * 2,(char*)mode,(int)strlen(mode));
return _wfopen((wchar_t *)&utf16_path, (wchar_t *)&utf16_mode);
}
*/

View File

@ -53,16 +53,17 @@ extern int os_unregister(void);
extern char *os_configpath(void);
/* replacements for socket functions */
extern int os_opensocket(unsigned short port);
extern int os_acceptsocket(int fd, struct in_addr *hostaddr);
extern int os_shutdown(int fd, int how);
extern int os_waitfdtimed(int fd, struct timeval end);
extern int os_close(int fd);
extern int os_open(const char *filename, int oflag);
extern FILE *os_fopen(const char *filename, const char *mode);
// extern int os_opensocket(unsigned short port);
// extern int os_acceptsocket(int fd, struct in_addr *hostaddr);
// extern int os_shutdown(int fd, int how);
// extern int os_waitfdtimed(int fd, struct timeval end);
extern int os_read(int fd,void *buffer,unsigned int count);
extern int os_write(int fd, void *buffer, unsigned int count);
// extern int os_close(int fd);
// extern int os_open(const char *filename, int oflag);
// extern FILE *os_fopen(const char *filename, const char *mode);
// extern int os_read(int fd,void *buffer,unsigned int count);
// extern int os_write(int fd, void *buffer, unsigned int count);
extern int os_getuid(void);
/* missing win32 functions */

View File

@ -55,6 +55,7 @@
#include "configfile.h"
#include "db-generic.h"
#include "err.h"
#include "io.h"
#include "os.h"
#include "plugin.h"
#include "rend.h"
@ -63,6 +64,7 @@
#include "xml-rpc.h"
#include "webserver.h"
#include "ff-plugins.h"
#include "io.h"
typedef struct tag_pluginentry {
void *phandle;
@ -91,7 +93,6 @@ void _plugin_recalc_codecs(void);
int _plugin_ssc_transcode(WS_CONNINFO *pwsc, MP3FILE *pmp3, int offset, int headers);
/* webserver helpers */
char *pi_ws_uri(WS_CONNINFO *pwsc);
void pi_ws_will_close(WS_CONNINFO *pwsc);
int pi_ws_fd(WS_CONNINFO *pwsc);
char *pi_ws_gethostname(WS_CONNINFO *pwsc);
@ -117,14 +118,13 @@ void pi_conf_dispose_string(char *str);
int pi_ssc_should_transcode(WS_CONNINFO *pwsc, char *codec);
PLUGIN_INPUT_FN pi = {
pi_ws_uri,
ws_uri,
pi_ws_will_close,
ws_returnerror,
ws_getvar,
ws_writefd,
ws_addresponseheader,
ws_emitheaders,
pi_ws_fd,
ws_getrequestheader,
ws_writebinary,
pi_ws_gethostname,
@ -379,7 +379,7 @@ void plugin_url_handle(WS_CONNINFO *pwsc) {
if(ppi->pinfo->type & PLUGIN_OUTPUT) {
if((ppi->pinfo->output_fns)->can_handle(pwsc)) {
/* we have a winner */
DPRINTF(E_DBG,L_PLUG,"Dispatching %s to %s\n", pwsc->uri,
DPRINTF(E_DBG,L_PLUG,"Dispatching %s to %s\n", ws_uri(pwsc),
ppi->pinfo->server);
/* so functions must be a tag_plugin_output_fn */
@ -445,7 +445,7 @@ int plugin_auth_handle(WS_CONNINFO *pwsc, char *username, char *pw) {
if(ppi->pinfo->type & PLUGIN_OUTPUT) {
if((ppi->pinfo->output_fns)->can_handle(pwsc)) {
/* we have a winner */
DPRINTF(E_DBG,L_PLUG,"Dispatching %s to %s\n", pwsc->uri,
DPRINTF(E_DBG,L_PLUG,"Dispatching %s to %s\n", ws_uri(pwsc),
ppi->pinfo->server);
/* so functions must be a tag_plugin_output_fn */
@ -678,20 +678,12 @@ int _plugin_ssc_transcode(WS_CONNINFO *pwsc, MP3FILE *pmp3, int offset, int head
* interface to older plugins even if we get newer functions or apis
* upstream... it's a binary compatibility layer.
*/
char *pi_ws_uri(WS_CONNINFO *pwsc) {
return pwsc->uri;
}
void pi_ws_will_close(WS_CONNINFO *pwsc) {
pwsc->close=1;
}
int pi_ws_fd(WS_CONNINFO *pwsc) {
return pwsc->fd;
ws_should_close(pwsc,1);
}
char *pi_ws_gethostname(WS_CONNINFO *pwsc) {
return pwsc->hostname;
return ws_hostname(pwsc);
}
void pi_log(int level, char *fmt, ...) {
@ -851,33 +843,37 @@ void pi_db_enum_dispose(char **pe, DB_QUERY *pinfo) {
int pi_db_wait_update(WS_CONNINFO *pwsc) {
int clientver=1;
fd_set rset;
struct timeval tv;
int result;
int lastver=0;
IO_WAITHANDLE hwait;
uint32_t ms;
if(ws_getvar(pwsc,"revision-number")) {
clientver=atoi(ws_getvar(pwsc,"revision-number"));
}
/* wait for db_version to be stable for 30 seconds */
hwait = io_wait_new();
if(!hwait)
DPRINTF(E_FATAL,L_MISC,"Can't get wait handle in db_wait_update\n");
/* FIXME: Move this up into webserver to avoid groping around
* inside reserved data structures */
io_wait_add(hwait,pwsc->hclient,IO_WAIT_ERROR);
while((clientver == db_revision()) ||
(lastver && (db_revision() != lastver))) {
lastver = db_revision();
FD_ZERO(&rset);
FD_SET(pwsc->fd,&rset);
tv.tv_sec=30;
tv.tv_usec=0;
result=select(pwsc->fd+1,&rset,NULL,NULL,&tv);
if(FD_ISSET(pwsc->fd,&rset)) {
if(!io_wait(hwait,&ms) && (ms != 0)) {
/* can't be ready for read, must be error */
DPRINTF(E_DBG,L_DAAP,"Update session stopped\n");
io_wait_dispose(hwait);
return FALSE;
}
}
io_wait_dispose(hwait);
return TRUE;
}
@ -885,15 +881,15 @@ int pi_db_wait_update(WS_CONNINFO *pwsc) {
void pi_stream(WS_CONNINFO *pwsc, char *id) {
int session = 0;
MP3FILE *pmp3;
int file_fd;
int bytes_copied=0;
off_t real_len=0;
off_t file_len;
off_t offset=0;
IOHANDLE hfile;
uint64_t bytes_copied=0;
uint64_t real_len;
uint64_t file_len;
uint64_t offset=0;
int item;
/* stream out the song */
pwsc->close=1;
ws_should_close(pwsc,1);
item = atoi(id);
@ -917,7 +913,7 @@ void pi_stream(WS_CONNINFO *pwsc, char *id) {
DPRINTF(E_WARN,L_WS,
"Session %d: Streaming file '%s' to %s (offset %ld)\n",
session,pmp3->fname, pwsc->hostname,(long)offset);
session,pmp3->fname, ws_hostname(pwsc),(long)offset);
/* estimate the real length of this thing */
bytes_copied = _plugin_ssc_transcode(pwsc,pmp3,offset,1);
@ -934,21 +930,26 @@ void pi_stream(WS_CONNINFO *pwsc, char *id) {
ws_returnerror(pwsc,500,"Can't stream radio station");
return;
}
file_fd=r_open2(pmp3->path,O_RDONLY);
if(file_fd == -1) {
pwsc->error=errno;
hfile = io_new();
if(!hfile)
DPRINTF(E_FATAL,L_WS,"Cannot allocate file handle\n");
if(!io_open(hfile,"file://%U",pmp3->path)) {
/* FIXME: ws_set_errstr */
ws_set_err(pwsc,E_WS_NATIVE);
DPRINTF(E_WARN,L_WS,"Thread %d: Error opening %s: %s\n",
pwsc->threadno,pmp3->path,strerror(errno));
ws_threadno(pwsc),pmp3->path,io_errstr(hfile));
ws_returnerror(pwsc,404,"Not found");
config_set_status(pwsc,session,NULL);
db_dispose_item(pmp3);
io_dispose(hfile);
} else {
real_len=lseek(file_fd,0,SEEK_END);
lseek(file_fd,0,SEEK_SET);
io_size(hfile,&real_len);
file_len = real_len - offset;
DPRINTF(E_DBG,L_WS,"Thread %d: Length of file (remaining): %ld\n",
pwsc->threadno,(long)file_len);
DPRINTF(E_DBG,L_WS,"Thread %d: Length of file (remaining): %lld\n",
ws_threadno(pwsc),file_len);
// DWB: fix content-type to correctly reflect data
// content type (dmap tagged) should only be used on
@ -962,7 +963,7 @@ void pi_stream(WS_CONNINFO *pwsc, char *id) {
(!strncmp(ws_getrequestheader(pwsc,"user-agent"),
"Hifidelio",9))) {
ws_addresponseheader(pwsc,"Connection","Keep-Alive");
pwsc->close=0;
ws_should_close(pwsc,0);
} else {
ws_addresponseheader(pwsc,"Connection","Close");
}
@ -981,23 +982,25 @@ void pi_stream(WS_CONNINFO *pwsc, char *id) {
config_set_status(pwsc,session,"Streaming '%s' (id %d)",
pmp3->title, pmp3->id);
DPRINTF(E_WARN,L_WS,"Session %d: Streaming file '%s' to %s (offset %d)\n",
session,pmp3->fname, pwsc->hostname,(long)offset);
session,pmp3->fname, ws_hostname(pwsc),(long)offset);
if(offset) {
DPRINTF(E_INF,L_WS,"Seeking to offset %ld\n",(long)offset);
lseek(file_fd,offset,SEEK_SET);
io_setpos(hfile,offset,SEEK_SET);
}
if((bytes_copied=copyfile(file_fd,pwsc->fd)) == -1) {
if(!ws_copyfile(pwsc,hfile,&bytes_copied)) {
DPRINTF(E_INF,L_WS,"Error copying file to remote... %s\n",
strerror(errno));
strerror(errno));
ws_should_close(pwsc,1);
} else {
DPRINTF(E_INF,L_WS,"Finished streaming file to remote: %d bytes\n",
DPRINTF(E_INF,L_WS,"Finished streaming file to remote: %lld bytes\n",
bytes_copied);
}
config_set_status(pwsc,session,NULL);
r_close(file_fd);
io_close(hfile);
io_dispose(hfile);
db_dispose_item(pmp3);
}
/* update play counts */

View File

@ -42,37 +42,36 @@ PLUGIN_INFO *plugin_info(PLUGIN_INPUT_FN *ppi) {
#define MAILSLOT_NAME "\\\\.\\mailslot\\FireflyMediaServer--67A72768-4154-417e-BFA0-FA9B50C342DE"
/** NO LOG IN HERE! We'll go into an endless loop. :) */
void plugin_handler(int event_id, int intval, void *vp, int len) {
HANDLE h = CreateFile(MAILSLOT_NAME, GENERIC_WRITE,
FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if (h != INVALID_HANDLE_VALUE)
{
DWORD bytes_written;
const int packet_size = 12 + len;
unsigned char *buffer = (unsigned char *)malloc(packet_size);
if(!buffer)
return;
memset(buffer, 0, packet_size);
HANDLE h = CreateFile(MAILSLOT_NAME, GENERIC_WRITE,
FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if (h != INVALID_HANDLE_VALUE) {
DWORD bytes_written;
const int packet_size = 12 + len;
unsigned char *buffer = (unsigned char *)malloc(packet_size);
if(!buffer)
return;
memset(buffer, 0, packet_size);
buffer[0] = packet_size & 0xff;
buffer[1] = (packet_size >> 8) & 0xff;
buffer[2] = (packet_size >> 16) & 0xff;
buffer[3] = (packet_size >> 24) & 0xff;
buffer[0] = packet_size & 0xff;
buffer[1] = (packet_size >> 8) & 0xff;
buffer[2] = (packet_size >> 16) & 0xff;
buffer[3] = (packet_size >> 24) & 0xff;
buffer[4] = event_id & 0xff;
buffer[5] = (event_id >> 8) & 0xff;
buffer[6] = (event_id >> 16) & 0xff;
buffer[7] = (event_id >> 24) & 0xff;
buffer[4] = event_id & 0xff;
buffer[5] = (event_id >> 8) & 0xff;
buffer[6] = (event_id >> 16) & 0xff;
buffer[7] = (event_id >> 24) & 0xff;
buffer[8] = intval & 0xff;
buffer[9] = (intval >> 8) & 0xff;
buffer[10] = (intval >> 16) & 0xff;
buffer[11] = (intval >> 24) & 0xff;
buffer[8] = intval & 0xff;
buffer[9] = (intval >> 8) & 0xff;
buffer[10] = (intval >> 16) & 0xff;
buffer[11] = (intval >> 24) & 0xff;
memcpy(buffer + 12, vp, len);
memcpy(buffer + 12, vp, len);
/* If this fails then there's nothing we can do about it anyway. */
WriteFile(h, buffer, packet_size, &bytes_written, NULL);
CloseHandle(h);
}
/* If this fails then there's nothing we can do about it anyway. */
WriteFile(h, buffer, packet_size, &bytes_written, NULL);
CloseHandle(h);
}
}

View File

@ -1,4 +1,4 @@
/*
/* -*- Mode: C; tab-width: 4 -*-
* $Id$
*
* Do the zeroconf/mdns/rendezvous (tm) thing. This is a hacked version
@ -21,178 +21,130 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
* Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* The contents of this file constitute Original Code as defined in and
* are subject to the Apple Public Source License Version 1.2 (the
* "License"). You may not use this file except in compliance with the
* License. Please obtain a copy of the License at
* http://www.apple.com/publicsource and read it before using this file.
* http://www.apache.org/licenses/LICENSE-2.0
*
* This Original Code and all software distributed under the License are
* distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
* License for the specific language governing rights and limitations
* under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/*
File: responder.c
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
Contains: Code to implement an mDNS responder on the Posix platform.
Change History (most recent first):
Written by: Quinn
$Log: Responder.c,v $
Revision 1.33 2007/04/16 20:49:39 cheshire
Fix compile errors for mDNSPosix build
Copyright: Copyright (c) 2002 by Apple Computer, Inc., All Rights Reserved.
Revision 1.32 2006/08/14 23:24:46 cheshire
Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc.
("Apple") in consideration of your agreement to the following terms, and your
use, installation, modification or redistribution of this Apple software
constitutes acceptance of these terms. If you do not agree with these terms,
please do not use, install, modify or redistribute this Apple software.
Revision 1.31 2006/06/12 18:22:42 cheshire
<rdar://problem/4580067> mDNSResponder building warnings under Red Hat 64-bit (LP64) Linux
In consideration of your agreement to abide by the following terms, and subject
to these terms, Apple grants you a personal, non-exclusive license, under Apple's
copyrights in this original Apple software (the "Apple Software"), to use,
reproduce, modify and redistribute the Apple Software, with or without
modifications, in source and/or binary forms; provided that if you redistribute
the Apple Software in its entirety and without modifications, you must retain
this notice and the following text and disclaimers in all such redistributions of
the Apple Software. Neither the name, trademarks, service marks or logos of
Apple Computer, Inc. may be used to endorse or promote products derived from the
Apple Software without specific prior written permission from Apple. Except as
expressly stated in this notice, no other rights or licenses, express or implied,
are granted by Apple herein, including but not limited to any patent rights that
may be infringed by your derivative works or by other works in which the Apple
Software may be incorporated.
Revision 1.30 2005/10/26 22:21:16 cheshire
<rdar://problem/4149841> Potential buffer overflow in mDNSResponderPosix
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
COMBINATION WITH YOUR PRODUCTS.
Revision 1.29 2005/03/04 21:35:33 cheshire
<rdar://problem/4037201> Services.txt file not parsed properly when it contains more than one service
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Revision 1.28 2005/01/11 01:55:26 ksekar
Fix compile errors in Posix debug build
Change History (most recent first):
Revision 1.27 2004/12/01 04:28:43 cheshire
<rdar://problem/3872803> Darwin patches for Solaris and Suse
Use version of daemon() provided in mDNSUNP.c instead of local copy
$Log$
Revision 1.29 2006/03/05 08:09:27 rpedde
fix up txt records to show password info, mtd-version, and itunes version
Revision 1.26 2004/11/30 22:37:01 cheshire
Update copyright dates and add "Mode: C; tab-width: 4" headers
Revision 1.28 2006/02/26 08:46:24 rpedde
Merged win32-branch
Revision 1.25 2004/11/11 02:00:51 cheshire
Minor fixes to getopt, error message
Revision 1.27.2.2 2006/02/26 08:28:35 rpedde
unix fixes from win32 port
Revision 1.24 2004/11/09 19:32:10 rpantos
Suggestion from Ademar de Souza Reis Jr. to allow comments in services file
Revision 1.27.2.1 2006/02/23 03:19:40 rpedde
First pass at win32 port.
Revision 1.23 2004/09/17 01:08:54 cheshire
Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
declared in that file are ONLY appropriate to single-address-space embedded applications.
For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
Revision 1.27 2006/02/20 03:36:57 rpedde
Annoying fprintf
Revision 1.22 2004/09/16 01:58:22 cheshire
Fix compiler warnings
Revision 1.26 2005/09/23 07:03:19 rpedde
view persistence fixes for iTunes 5
Revision 1.21 2004/06/15 03:48:07 cheshire
Update mDNSResponderPosix to take multiple name=val arguments in a sane way
Revision 1.25 2005/08/16 02:26:32 rpedde
Add interface directive to config file -- fix stderr logging on rendezvous child
Revision 1.20 2004/05/18 23:51:26 cheshire
Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
Revision 1.24 2005/08/15 03:16:54 rpedde
specify interface to register
Revision 1.19 2004/03/12 08:03:14 cheshire
Update comments
Revision 1.23 2005/01/07 06:57:59 rpedde
fix minor errno problem
Revision 1.18 2004/01/25 00:00:55 cheshire
Change to use mDNSOpaque16fromIntVal() instead of shifting and masking
Revision 1.22 2004/12/09 05:05:54 rpedde
Logging fixes
Revision 1.17 2003/12/11 19:11:55 cheshire
Fix compiler warning
Revision 1.21 2004/11/30 04:17:32 rpedde
use pascal packed string to avoid invalid rdata error
Revision 1.16 2003/08/14 02:19:55 cheshire
<rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
Revision 1.20 2004/11/30 04:04:17 rpedde
database id txt record to store settings
Revision 1.15 2003/08/12 19:56:26 cheshire
Update to APSL 2.0
Revision 1.19 2004/11/13 07:14:26 rpedde
modularize debugging statements
Revision 1.14 2003/08/06 18:20:51 cheshire
Makefile cleanup
Revision 1.18 2004/04/19 06:19:46 rpedde
Starting to fix signal stuff
Revision 1.13 2003/07/23 00:00:04 cheshire
Add comments
Revision 1.17 2004/03/29 19:44:58 rpedde
Move mdns stuff out of mdns subdir to help compile on older automakes
Revision 1.12 2003/07/15 01:55:16 cheshire
<rdar://problem/3315777> Need to implement service registration with subtypes
Revision 1.16 2004/03/08 19:21:03 rpedde
start of background scanning
Revision 1.11 2003/07/14 18:11:54 cheshire
Fix stricter compiler warnings
Revision 1.15 2004/03/02 01:35:31 rpedde
fix domain
Revision 1.10 2003/07/10 20:27:31 cheshire
<rdar://problem/3318717> mDNSResponder Posix version is missing a 'b' in the getopt option string
Revision 1.14 2004/03/02 00:03:37 rpedde
Merge new rendezvous code
Revision 1.9 2003/07/02 21:19:59 cheshire
<rdar://problem/3313413> Update copyright notices, etc., in source code comments
Revision 1.13 2004/03/01 16:29:42 rpedde
Fix logging
Revision 1.8 2003/06/18 05:48:41 cheshire
Fix warnings
Revision 1.12 2004/02/25 16:13:37 rpedde
More -Wall cleanups
Revision 1.7 2003/05/06 00:00:50 cheshire
<rdar://problem/3248914> Rationalize naming of domainname manipulation functions
Revision 1.11 2004/02/09 18:33:59 rpedde
Pretty up
Revision 1.6 2003/03/08 00:35:56 cheshire
Switched to using new "mDNS_Execute" model (see "mDNSCore/Implementer Notes.txt")
Revision 1.10 2004/01/20 04:41:20 rpedde
merge new-rend-branch
Revision 1.5 2003/02/20 06:48:36 cheshire
<rdar://problem/3169535> Xserve RAID needs to do interface-specific registrations
Reviewed by: Josh Graessley, Bob Bradley
Revision 1.9.2.1 2004/01/16 20:51:01 rpedde
Convert rend-posix to message-based system
Revision 1.4 2003/01/28 03:07:46 cheshire
Add extra parameter to mDNS_RenameAndReregisterService(),
and add support for specifying a domain other than dot-local.
Revision 1.9 2004/01/04 05:02:23 rpedde
fix segfault on dropping privs
Revision 1.3 2002/09/21 20:44:53 zarzycki
Added APSL info
Revision 1.8 2003/12/29 23:39:18 ron
add priv dropping
Revision 1.2 2002/09/19 04:20:44 cheshire
Remove high-ascii characters that confuse some systems
Revision 1.7 2003/12/29 20:41:08 ron
Make sure all files have GPL notice
Revision 1.6 2003/11/26 06:12:53 ron
Exclude from memory checks
Revision 1.5 2003/11/23 18:13:15 ron
Exit rather than returning... shouldn't make a difference, but does. ?
Revision 1.4 2003/11/20 21:58:22 ron
More diag logging, move WS_PRIVATE into the WS_CONNINFO
Revision 1.3 2003/11/17 16:40:09 ron
add support for named db
Revision 1.2 2003/11/14 04:54:55 ron
Use port 53
Revision 1.1 2003/10/30 22:41:56 ron
Initial checkin
Revision 1.3 2002/09/21 20:44:53 zarzycki
Added APSL info
Revision 1.2 2002/09/19 04:20:44 cheshire
Remove high-ascii characters that confuse some systems
Revision 1.1 2002/09/17 06:24:35 cheshire
First checkin
Revision 1.1 2002/09/17 06:24:35 cheshire
First checkin
*/
@ -200,31 +152,41 @@
#include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
#include <assert.h>
#include <stdio.h> // For printf()
#include <stdlib.h> // For exit() etc.
#include <string.h> // For strlen() etc.
#include <unistd.h> // For select()
#include <errno.h> // For errno, EINTR
#include <stdio.h> // For printf()
#include <stdlib.h> // For exit() etc.
#include <string.h> // For strlen() etc.
#include <unistd.h> // For select()
#include <errno.h> // For errno, EINTR
#include <signal.h>
#include <fcntl.h>
#include <pwd.h>
#include <sys/types.h>
#define __IN_ERR__
#include "daapd.h"
#include "err.h"
#include "os-unix.h"
#include "rend.h"
#include "rend-unix.h"
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark ***** Globals
#endif
static mDNS mDNSStorage; // mDNS core uses this to store its globals
static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals
mDNSexport const char ProgramName[] = "mDNSResponderPosix";
static const char *gProgramName = ProgramName;
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark ***** Signals
#endif
static volatile mDNSBool gReceivedSigUsr1;
static volatile mDNSBool gReceivedSigHup;
static volatile mDNSBool gStopNow;
/* modified signal handling code - rep 21 Oct 2k3 */
// We support 4 signals. (2, now -- rp)
//
// o SIGINT causes an orderly shutdown of the program.
// o SIGQUIT causes a somewhat orderly shutdown (direct but dangerous)
//
@ -236,40 +198,48 @@ static volatile mDNSBool gStopNow;
// modify the signal mask and start a select.
static void HandleSigInt(int sigraised)
// A handler for SIGINT that causes us to break out of the
// main event loop when the user types ^C. This has the
// effect of quitting the program.
// A handler for SIGINT that causes us to break out of the
// main event loop when the user types ^C. This has the
// effect of quitting the program.
{
assert(sigraised == SIGINT);
DPRINTF(E_INF,L_REND,"SIGINT\n");
if (gMDNSPlatformPosixVerboseLevel > 0) {
fprintf(stderr, "\nSIGINT\n");
}
gStopNow = mDNStrue;
}
static void HandleSigQuit(int sigraised)
// If we get a SIGQUIT the user is desperate and we
// just call mDNS_Close directly. This is definitely
// not safe (because it could reenter mDNS), but
// we presume that the user has already tried the safe
// alternatives.
// If we get a SIGQUIT the user is desperate and we
// just call mDNS_Close directly. This is definitely
// not safe (because it could reenter mDNS), but
// we presume that the user has already tried the safe
// alternatives.
{
assert(sigraised == SIGQUIT);
DPRINTF(E_INF,L_REND,"SIGQUIT\n");
if (gMDNSPlatformPosixVerboseLevel > 0) {
fprintf(stderr, "\nSIGQUIT\n");
}
mDNS_Close(&mDNSStorage);
exit(0);
}
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark ***** Parameter Checking
#endif
/* get rid of pidfile handling - rep - 21 Oct 2k3 */
static const char kDefaultServiceType[] = "_http._tcp.";
static const char kDefaultServiceDomain[] = "local.";
enum {
kDefaultPortNumber = 80
};
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark ***** Registration
#endif
typedef struct PosixService PosixService;
struct PosixService {
@ -281,67 +251,67 @@ struct PosixService {
static PosixService *gServiceList = NULL;
static void RegistrationCallback(mDNS *const m, ServiceRecordSet *const thisRegistration, mStatus status)
// mDNS core calls this routine to tell us about the status of
// our registration. The appropriate action to take depends
// entirely on the value of status.
// mDNS core calls this routine to tell us about the status of
// our registration. The appropriate action to take depends
// entirely on the value of status.
{
switch (status) {
case mStatus_NoError:
DPRINTF(E_DBG,L_REND,"Callback: Name Registered\n");
// Do nothing; our name was successfully registered. We may
// get more call backs in the future.
break;
case mStatus_NoError:
DPRINTF(E_DBG,L_REND,"Callback: %##s Name Registered", thisRegistration->RR_SRV.resrec.name->c);
// Do nothing; our name was successfully registered. We may
// get more call backs in the future.
break;
case mStatus_NameConflict:
DPRINTF(E_WARN,L_REND,"Callback: Name Conflict\n");
case mStatus_NameConflict:
DPRINTF(E_DBG,L_REND,"Callback: %##s Name Conflict", thisRegistration->RR_SRV.resrec.name->c);
// In the event of a conflict, this sample RegistrationCallback
// just calls mDNS_RenameAndReregisterService to automatically
// pick a new unique name for the service. For a device such as a
// printer, this may be appropriate. For a device with a user
// interface, and a screen, and a keyboard, the appropriate response
// may be to prompt the user and ask them to choose a new name for
// the service.
//
// Also, what do we do if mDNS_RenameAndReregisterService returns an
// error. Right now I have no place to send that error to.
// In the event of a conflict, this sample RegistrationCallback
// just calls mDNS_RenameAndReregisterService to automatically
// pick a new unique name for the service. For a device such as a
// printer, this may be appropriate. For a device with a user
// interface, and a screen, and a keyboard, the appropriate response
// may be to prompt the user and ask them to choose a new name for
// the service.
//
// Also, what do we do if mDNS_RenameAndReregisterService returns an
// error. Right now I have no place to send that error to.
status = mDNS_RenameAndReregisterService(m, thisRegistration, mDNSNULL);
assert(status == mStatus_NoError);
break;
status = mDNS_RenameAndReregisterService(m, thisRegistration, mDNSNULL);
assert(status == mStatus_NoError);
break;
case mStatus_MemFree:
DPRINTF(E_WARN,L_REND,"Callback: Memory Free\n");
case mStatus_MemFree:
DPRINF(E_DBG,L_REND,"Callback: %##s Memory Free", thisRegistration->RR_SRV.resrec.name->c);
// When debugging is enabled, make sure that thisRegistration
// is not on our gServiceList.
// When debugging is enabled, make sure that thisRegistration
// is not on our gServiceList.
#if defined(DEBUG)
{
PosixService *cursor;
#if !defined(NDEBUG)
{
PosixService *cursor;
cursor = gServiceList;
while (cursor != NULL) {
assert(&cursor->coreServ != thisRegistration);
cursor = cursor->next;
}
}
#endif
free(thisRegistration);
break;
cursor = gServiceList;
while (cursor != NULL) {
assert(&cursor->coreServ != thisRegistration);
cursor = cursor->next;
}
}
#endif
free(thisRegistration);
break;
default:
DPRINTF(E_WARN,L_REND,"Callback: Unknown Status %d\n",status);
break;
default:
DPRINTF(E_DBG,L_REND,"Callback: %##s Unknown Status %ld", thisRegistration->RR_SRV.resrec.name->c, status);
break;
}
}
static int gServiceID = 0;
static mStatus RegisterOneService(const char * richTextHostName,
static mStatus RegisterOneService(const char * richTextName,
const char * serviceType,
const char * serviceDomain,
const char * serviceDomain,
const mDNSu8 text[],
mDNSu16 textLen,
long portNumber,
@ -349,7 +319,6 @@ static mStatus RegisterOneService(const char * richTextHostName,
{
mStatus status;
PosixService * thisServ;
mDNSOpaque16 port;
domainlabel name;
domainname type;
domainname domain;
@ -360,20 +329,17 @@ static mStatus RegisterOneService(const char * richTextHostName,
status = mStatus_NoMemoryErr;
}
if (status == mStatus_NoError) {
MakeDomainLabelFromLiteralString(&name, richTextHostName);
MakeDomainLabelFromLiteralString(&name, richTextName);
MakeDomainNameFromDNSNameString(&type, serviceType);
MakeDomainNameFromDNSNameString(&domain, serviceDomain);
port.b[0] = (portNumber >> 8) & 0x0FF;
port.b[1] = (portNumber >> 0) & 0x0FF;;
status = mDNS_RegisterService(&mDNSStorage, &thisServ->coreServ,
&name, &type, &domain,
NULL,
port,
text, textLen,
NULL, 0,
id,
RegistrationCallback, thisServ);
&name, &type, &domain, // Name, type, domain
NULL, mDNSOpaque16fromIntVal(portNumber),
text, textLen, // TXT data, length
NULL, 0, // Subtypes
id, // Interface ID
RegistrationCallback, thisServ); // Callback and context
}
if (status == mStatus_NoError) {
thisServ->serviceID = gServiceID;
@ -381,14 +347,12 @@ static mStatus RegisterOneService(const char * richTextHostName,
thisServ->next = gServiceList;
gServiceList = thisServ;
DPRINTF(E_DBG,L_REND,
"Registered service %d, name '%s', type '%s', domain '%s', port %ld\n",
thisServ->serviceID,
richTextHostName,
serviceType,
serviceDomain,
portNumber);
DPRINTF(E_DBG,L_REND,
"Registered service %d, name '%s', type '%s', port %ld\n",
thisServ->serviceID,
richTextName,
serviceType,
portNumber);
} else {
if (thisServ != NULL) {
free(thisServ);
@ -410,8 +374,9 @@ static void DeregisterOurServices(void)
mDNS_DeregisterService(&mDNSStorage, &thisServ->coreServ);
DPRINTF(E_DBG,L_REND,"Deregistered service %d\n",
thisServ->serviceID);
DPRINF(E_DBG,L_REND,
"Deregistered service %d\n",
thisServ->serviceID);
}
}
@ -441,7 +406,7 @@ mDNSInterfaceID rend_get_interface_id(char *iface) {
return mDNSInterface_Any;
}
/*
*
* rend_callback
*
* This is borrowed from the OSX rend client
@ -569,4 +534,3 @@ int rend_private_init(char *user) {
exit(result);
}

View File

@ -57,33 +57,9 @@
#include "restart.h"
#define BLKSIZE PIPE_BUF
#define MILLION 1000000L
#define D_MILLION 1000000.0
/* Private functions */
int gettimeout(struct timeval end,
struct timeval *timeoutp) {
gettimeofday(timeoutp, NULL);
timeoutp->tv_sec = end.tv_sec - timeoutp->tv_sec;
timeoutp->tv_usec = end.tv_usec - timeoutp->tv_usec;
if (timeoutp->tv_usec >= MILLION) {
timeoutp->tv_sec++;
timeoutp->tv_usec -= MILLION;
}
if (timeoutp->tv_usec < 0) {
timeoutp->tv_sec--;
timeoutp->tv_usec += MILLION;
}
if ((timeoutp->tv_sec < 0) ||
((timeoutp->tv_sec == 0) && (timeoutp->tv_usec == 0))) {
errno = ETIME;
return -1;
}
return 0;
}
int r_fdprintf(int fd, char *fmt, ...) {
char buffer[1024];
va_list ap;
@ -143,28 +119,6 @@ ssize_t r_write(int fd, void *buf, size_t size) {
/* Utility functions */
struct timeval add2currenttime(double seconds) {
struct timeval newtime;
gettimeofday(&newtime, NULL);
newtime.tv_sec += (int)seconds;
newtime.tv_usec += (int)((seconds - (int)seconds)*D_MILLION + 0.5);
if (newtime.tv_usec >= MILLION) {
newtime.tv_sec++;
newtime.tv_usec -= MILLION;
}
return newtime;
}
int copyfile(int fromfd, int tofd) {
int bytesread;
int totalbytes = 0;
while ((bytesread = readwrite(fromfd, tofd)) > 0)
totalbytes += bytesread;
return totalbytes;
}
ssize_t readblock(int fd, void *buf, size_t size) {
char *bufp;
ssize_t bytesread;
@ -214,92 +168,5 @@ int readline(int fd, char *buf, int nbytes) {
return -1;
}
int readlinetimed(int fd, char *buf, int nbytes, double seconds) {
int numread = 0;
int returnval;
while (numread < nbytes - 1) {
returnval = (int)readtimed(fd, buf + numread, 1, seconds);
if ((returnval == -1) && (errno == EINTR))
continue;
if ((returnval == 0) && (numread == 0))
return 0;
if (returnval == 0)
break;
if (returnval == -1)
return -1;
numread++;
if (buf[numread-1] == '\n') {
buf[numread] = '\0';
return numread;
}
}
errno = EINVAL;
return -1;
}
ssize_t readtimed(int fd, void *buf, size_t nbyte, double seconds) {
struct timeval timedone;
timedone = add2currenttime(seconds);
if (waitfdtimed(fd, timedone) == -1)
return (ssize_t)(-1);
return r_read(fd, buf, nbyte);
}
int readwrite(int fromfd, int tofd) {
char buf[BLKSIZE];
int bytesread;
if ((bytesread = r_read(fromfd, buf, BLKSIZE)) < 0) {
DPRINTF(E_WARN,L_MISC,"Read error: %s\n",strerror(errno));
return -1;
}
if (bytesread == 0)
return 0;
if (r_write(tofd, buf, bytesread) < 0) {
DPRINTF(E_WARN,L_MISC,"Write error: %s\n",strerror(errno));
return -1;
}
return bytesread;
}
int readwriteblock(int fromfd, int tofd, char *buf, int size) {
int bytesread;
bytesread = readblock(fromfd, buf, size);
if (bytesread != size) /* can only be 0 or -1 */
return bytesread;
return r_write(tofd, buf, size);
}
#ifndef WIN32
int waitfdtimed(int fd, struct timeval end) {
fd_set readset;
int retval;
struct timeval timeout;
if ((fd < 0) || (fd >= FD_SETSIZE)) {
errno = EINVAL;
return -1;
}
FD_ZERO(&readset);
FD_SET(fd, &readset);
if (gettimeout(end, &timeout) == -1)
return -1;
while (((retval = select(fd+1, &readset, NULL, NULL, &timeout)) == -1)
&& (errno == EINTR)) {
if (gettimeout(end, &timeout) == -1)
return -1;
FD_ZERO(&readset);
FD_SET(fd, &readset);
}
if (retval == 0) {
errno = ETIME;
return -1;
}
if (retval == -1)
return -1;
return 0;
}
#endif

View File

@ -12,11 +12,12 @@
#include <string.h>
#include "daapd.h"
#include "io.h"
#include "rxml.h"
/* Typedefs/Defines */
#define RXML_ERROR(a,b) { (a)->stdio_errno=errno; (a)->ecode=(b); return 0; };
#define RXML_ERROR(a,b) { (a)->ecode=(b); return 0; };
#define RXML_MAX_LINE 1024
#define RXML_MAX_TEXT 1024
#define RXML_MAX_TAG 256
@ -24,9 +25,8 @@
typedef struct _RXML {
RXML_EVTHANDLER handler;
char *fname;
FILE *fhandle;
IOHANDLE hfile;
void *udata;
int stdio_errno;
int ecode;
int line;
char *estring;
@ -46,6 +46,7 @@ typedef struct _RXML {
#define E_RXML_CLOSE 0x05
#define E_RXML_TAGSIZE 0x06
#define E_RXML_ENTITY 0x07
#define E_RXML_MALLOC 0x08
char *rxml_estrings[] = {
"Success",
@ -56,6 +57,7 @@ char *rxml_estrings[] = {
"Parse error: Unexpected '>'",
"Parse error: tag too big",
"Parse error: bad entity encoding",
"Internal malloc error",
NULL
};
@ -141,13 +143,21 @@ int rxml_open(RXMLHANDLE *vp, char *file,
pnew->handler = handler;
pnew->fname = file;
pnew->fhandle = fopen(file,"rb");
pnew->hfile = io_new();
if(!pnew->hfile)
RXML_ERROR(pnew,E_RXML_MALLOC);
if(!io_open(pnew->hfile, "file://%U", file)) {
io_dispose(pnew->hfile);
pnew->hfile = NULL;
RXML_ERROR(pnew,E_RXML_OPEN);
}
io_buffer(pnew->hfile);
pnew->udata = udata;
pnew->line = 0;
if(!pnew->fhandle)
RXML_ERROR(pnew,E_RXML_OPEN);
if(pnew->handler)
pnew->handler(RXML_EVT_OPEN, pnew->udata, pnew->fname);
@ -163,7 +173,10 @@ int rxml_close(RXMLHANDLE vp) {
RXML *ph = (RXML*)vp;
if(ph->handler) ph->handler(RXML_EVT_CLOSE,ph->udata,ph->fname);
if(ph->fhandle) fclose(ph->fhandle);
if(ph->hfile) {
io_close(ph->hfile);
io_dispose(ph->hfile);
}
if(ph->estring) free(ph->estring);
free(ph);
@ -188,7 +201,7 @@ char *rxml_errorstring(RXMLHANDLE vp) {
len = (int)strlen(rxml_estrings[ph->ecode]) + 16;
if((ph->ecode & 0x80)) {
estring=strerror(ph->stdio_errno);
estring=io_errstr(ph->hfile);
len += (int)strlen(estring);
}
@ -197,8 +210,8 @@ char *rxml_errorstring(RXMLHANDLE vp) {
return "Double-fault malloc error";
if((ph->ecode & 0x80)) {
snprintf(ph->estring,len,"%s%s",rxml_estrings[ph->ecode],estring);
if(((ph->ecode & 0x80) && (ph->hfile))) {
snprintf(ph->estring,len,"%s%s",rxml_estrings[ph->ecode],io_errstr(ph->hfile));
} else {
if(strncmp(rxml_estrings[ph->ecode],"Parse",5)==0) {
snprintf(ph->estring, len, "%s (Line:%d)",
@ -230,14 +243,16 @@ int rxml_parse(RXMLHANDLE vp) {
int tag_start=0;
int single_tag;
RXML *ph = (RXML*)vp;
uint32_t len;
ph->line = 0;
textbuffer[0] = '\0';
len = sizeof(linebuffer);
/* walk through and read row by row */
while(fgets(linebuffer,sizeof(linebuffer),ph->fhandle) != NULL) {
while(io_readline(ph->hfile,(unsigned char *)linebuffer,&len) && len) {
ph->line++;
offset=0;
size=(int)strlen(linebuffer);
@ -248,7 +263,7 @@ int rxml_parse(RXMLHANDLE vp) {
case '<':
if(in_tag)
RXML_ERROR(ph, E_RXML_NEST);
in_tag=TRUE;
tag_start=offset+1;
tag_end=FALSE;
@ -257,9 +272,9 @@ int rxml_parse(RXMLHANDLE vp) {
offset++;
tag_start++;
}
offset++;
in_text=0;
break;
@ -321,10 +336,10 @@ int rxml_parse(RXMLHANDLE vp) {
break;
}
}
len = sizeof(linebuffer);
}
ph->stdio_errno=errno;
if(ferror(ph->fhandle))
if(len)
RXML_ERROR(ph,E_RXML_READ);
return TRUE;

View File

@ -36,6 +36,7 @@
#include "daapd.h"
#include "err.h"
#include "io.h"
#include "mp3-scanner.h"
#include "scan-aac.h"
@ -69,18 +70,16 @@ time_t scan_aac_mac_to_unix_time(int t) {
* @param atom_length the size of the atom "drilled to"
* @returns offset of the atom, or -1 if unsuccessful
*/
off_t scan_aac_drilltoatom(FILE *aac_fp,char *atom_path,
uint64_t scan_aac_drilltoatom(IOHANDLE hfile,char *atom_path,
unsigned int *atom_length) {
long atom_offset;
off_t file_size;
uint64_t file_size,pos;
char *cur_p, *end_p;
char atom_name[5];
DPRINTF(E_SPAM,L_SCAN,"Searching for %s\n",atom_path);
fseek(aac_fp, 0, SEEK_END);
file_size = ftell(aac_fp);
rewind(aac_fp);
io_size(hfile, &file_size);
end_p = atom_path;
while (*end_p != '\0') {
@ -94,13 +93,15 @@ off_t scan_aac_drilltoatom(FILE *aac_fp,char *atom_path,
return -1;
}
strncpy(atom_name, cur_p, 4);
atom_offset = scan_aac_findatom(aac_fp, file_size,
atom_offset = scan_aac_findatom(hfile, file_size,
atom_name, atom_length);
if (atom_offset == -1) {
return -1;
}
DPRINTF(E_SPAM,L_SCAN,"Found %s atom at off %ld.\n",
atom_name, ftell(aac_fp) - 8);
io_getpos(hfile,&pos);
DPRINTF(E_SPAM,L_SCAN,"Found %s atom at off %lld.\n",
atom_name, pos - 8);
cur_p = strchr(cur_p, ':');
if (cur_p != NULL) {
@ -111,18 +112,22 @@ off_t scan_aac_drilltoatom(FILE *aac_fp,char *atom_path,
* to having child atoms. This should be dealt in a better fashion
* than this (table with skip offsets or a real mp4 parser.) */
if (!strcmp(atom_name, "meta")) {
fseek(aac_fp, 4, SEEK_CUR);
io_setpos(hfile, 4, SEEK_CUR);
} else if (!strcmp(atom_name, "stsd")) {
fseek(aac_fp, 8, SEEK_CUR);
io_setpos(hfile, 8, SEEK_CUR);
} else if (!strcmp(atom_name, "mp4a")) {
fseek(aac_fp, 28, SEEK_CUR);
io_setpos(hfile, 28, SEEK_CUR);
}
}
}
return ftell(aac_fp) - 8;
io_getpos(hfile, &pos);
return pos - 8;
}
/* FIXME: return TRUE/FALSE */
/**
* Given a file, search for a particular aac atom.
*
@ -131,22 +136,25 @@ off_t scan_aac_drilltoatom(FILE *aac_fp,char *atom_path,
* @param which_atom what atom name we are searching for
* @param atom_size this will hold the size of the atom found
*/
long scan_aac_findatom(FILE *fin, long max_offset,
uint64_t scan_aac_findatom(IOHANDLE hfile, uint64_t max_offset,
char *which_atom, unsigned int *atom_size) {
long current_offset=0;
uint64_t current_offset=0;
int size;
char atom[4];
uint32_t bytes_read;
while(current_offset < max_offset) {
if(fread((void*)&size,1,sizeof(int),fin) != sizeof(int))
bytes_read = sizeof(int);
if(!io_read(hfile,(unsigned char *)&size,&bytes_read) || (!bytes_read))
return -1;
size=ntohl(size);
if(size <= 7) /* something not right */
return -1;
if(fread(atom,1,4,fin) != 4)
bytes_read = 4;
if(!io_read(hfile,(unsigned char *)atom,&bytes_read) || (!bytes_read))
return -1;
if(strncasecmp(atom,which_atom,4) == 0) {
@ -154,7 +162,7 @@ long scan_aac_findatom(FILE *fin, long max_offset,
return current_offset;
}
fseek(fin,size-8,SEEK_CUR);
io_setpos(hfile,size-8,SEEK_CUR);
current_offset+=size;
}
@ -169,8 +177,9 @@ long scan_aac_findatom(FILE *fin, long max_offset,
* @returns FALSE if file should not be added to database, TRUE otherwise
*/
int scan_get_aacinfo(char *filename, MP3FILE *pmp3) {
FILE *fin;
long atom_offset;
IOHANDLE hfile;
uint64_t atom_offset, pos;
uint32_t bytes_read;
unsigned int atom_length;
long current_offset=0;
@ -189,27 +198,34 @@ int scan_get_aacinfo(char *filename, MP3FILE *pmp3) {
int time = 0;
if(!(fin=fopen(filename,"rb"))) {
DPRINTF(E_INF,L_SCAN,"Cannot open file %s for reading\n",filename);
hfile = io_new();
if(!hfile)
DPRINTF(E_FATAL,L_SCAN,"Malloc error in scan_get_aacinfo\n");
if(!io_open(hfile,"file://%U",filename)) {
DPRINTF(E_INF,L_SCAN,"Cannot open file %s for reading: %s\n",filename,
io_errstr(hfile));
io_dispose(hfile);
return FALSE;
}
atom_offset=scan_aac_drilltoatom(fin, "moov:udta:meta:ilst", &atom_length);
atom_offset=scan_aac_drilltoatom(hfile, "moov:udta:meta:ilst", &atom_length);
if(atom_offset != -1) {
/* found the tag section - need to walk through now */
while(current_offset < (long)atom_length) {
if(fread((void*)&current_size,1,sizeof(int),fin) != sizeof(int))
while(current_offset < (uint64_t)atom_length) {
bytes_read = sizeof(int);
if(!io_read(hfile,(unsigned char *)&current_size,&bytes_read) || !bytes_read)
break;
DPRINTF(E_SPAM,L_SCAN,"Current size: %d\n");
current_size=ntohl(current_size);
DPRINTF(E_SPAM,L_SCAN,"Current size: %d\n",current_size);
if(current_size <= 7) /* something not right */
break;
if(fread(current_atom,1,4,fin) != 4)
bytes_read = 4;
if(!io_read(hfile,(unsigned char *)current_atom,&bytes_read) || !bytes_read)
break;
DPRINTF(E_SPAM,L_SCAN,"Current Atom: %c%c%c%c\n",
@ -218,7 +234,7 @@ int scan_get_aacinfo(char *filename, MP3FILE *pmp3) {
if(current_size > 4096) { /* Does this break anything? */
/* too big! cover art, maybe? */
fseek(fin,current_size - 8,SEEK_CUR);
io_setpos(hfile,current_size - 8, SEEK_CUR);
} else {
len=current_size-7; /* for ill-formed too-short tags */
if(len < 22)
@ -227,7 +243,8 @@ int scan_get_aacinfo(char *filename, MP3FILE *pmp3) {
current_data=(char*)malloc(len); /* extra byte */
memset(current_data,0x00,len);
if(fread(current_data,1,current_size-8,fin) != current_size-8)
bytes_read = current_size - 8;
if(!io_read(hfile,(unsigned char *)current_data,&bytes_read) || (!bytes_read))
break;
if(!memcmp(current_atom,"\xA9" "nam",4)) { /* Song name */
@ -292,18 +309,26 @@ int scan_get_aacinfo(char *filename, MP3FILE *pmp3) {
}
/* got the tag info, now let's get bitrate, etc */
atom_offset = scan_aac_drilltoatom(fin, "moov:mvhd", &atom_length);
atom_offset = scan_aac_drilltoatom(hfile, "moov:mvhd", &atom_length);
if(atom_offset != -1) {
fseek(fin, 4, SEEK_CUR);
fread((void *)&time, sizeof(int), 1, fin);
io_setpos(hfile,4,SEEK_CUR);
/* FIXME: error handling */
bytes_read = sizeof(int);
io_read(hfile,(unsigned char *)&time, &bytes_read);
time = ntohl(time);
pmp3->time_added = (int)scan_aac_mac_to_unix_time(time);
fread((void *)&time, sizeof(int), 1, fin);
bytes_read = sizeof(int);
io_read(hfile,(unsigned char *)&time, &bytes_read);
time = ntohl(time);
pmp3->time_modified = (int)scan_aac_mac_to_unix_time(time);
fread((void*)&sample_size,1,sizeof(int),fin);
fread((void*)&samples,1,sizeof(int),fin);
bytes_read = sizeof(int);
io_read(hfile,(unsigned char *)&sample_size,&bytes_read);
bytes_read = sizeof(int);
io_read(hfile,(unsigned char*)&samples, &bytes_read);
sample_size=ntohl(sample_size);
samples=ntohl(samples);
@ -324,7 +349,7 @@ int scan_get_aacinfo(char *filename, MP3FILE *pmp3) {
pmp3->bitrate = 0;
/* see if it is aac or alac */
atom_offset = scan_aac_drilltoatom(fin,
atom_offset = scan_aac_drilltoatom(hfile,
"moov:trak:mdia:minf:stbl:stsd:alac",
&atom_length);
@ -339,40 +364,45 @@ int scan_get_aacinfo(char *filename, MP3FILE *pmp3) {
/* Get the sample rate from the 'mp4a' atom (timescale). This is also
found in the 'mdhd' atom which is a bit closer but we need to
navigate to the 'mp4a' atom anyways to get to the 'esds' atom. */
atom_offset=scan_aac_drilltoatom(fin,
atom_offset=scan_aac_drilltoatom(hfile,
"moov:trak:mdia:minf:stbl:stsd:mp4a",
&atom_length);
if(atom_offset == -1) {
atom_offset=scan_aac_drilltoatom(fin,
atom_offset=scan_aac_drilltoatom(hfile,
"moov:trak:mdia:minf:stbl:stsd:drms",
&atom_length);
}
if (atom_offset != -1) {
fseek(fin, atom_offset + 32, SEEK_SET);
io_setpos(hfile, atom_offset + 32, SEEK_SET);
/* Timescale here seems to be 2 bytes here (the 2 bytes before it are
* "reserved") though the timescale in the 'mdhd' atom is 4. Not sure
* how this is dealt with when sample rate goes higher than 64K. */
fread(buffer, sizeof(unsigned char), 2, fin);
bytes_read = 2;
io_read(hfile, (unsigned char *)buffer, &bytes_read);
pmp3->samplerate = (buffer[0] << 8) | (buffer[1]);
/* Seek to end of atom. */
fseek(fin, 2, SEEK_CUR);
io_setpos(hfile, 2, SEEK_CUR);
/* Get the bit rate from the 'esds' atom. We are already positioned
in the parent atom so just scan ahead. */
atom_offset = scan_aac_findatom(fin,
atom_length-(ftell(fin)-atom_offset),
io_getpos(hfile,&pos);
atom_offset = scan_aac_findatom(hfile,
atom_length-(pos-atom_offset),
"esds", &atom_length);
if (atom_offset != -1) {
/* Roku Soundbridge seems to believe anything above 320K is
* an ALAC encoded m4a. We'll lie on their behalf.
*/
fseek(fin, atom_offset + 22, SEEK_CUR);
fread((void *)&bit_rate, sizeof(unsigned int), 1, fin);
io_setpos(hfile, atom_offset + 22, SEEK_CUR);
bytes_read = sizeof(unsigned int);
io_read(hfile, (unsigned char *)&bit_rate, &bytes_read);
pmp3->bitrate = ntohl(bit_rate) / 1000;
DPRINTF(E_DBG,L_SCAN,"esds bitrate: %d\n",pmp3->bitrate);
@ -390,12 +420,13 @@ int scan_get_aacinfo(char *filename, MP3FILE *pmp3) {
if (pmp3->bitrate == 0) {
/* calculate bitrate from song length... Kinda cheesy */
DPRINTF(E_DBG,L_SCAN, "Guesstimating bit rate.\n");
atom_offset=scan_aac_drilltoatom(fin,"mdat",&atom_length);
atom_offset=scan_aac_drilltoatom(hfile,"mdat",&atom_length);
if ((atom_offset != -1) && (pmp3->song_length >= 1000)) {
pmp3->bitrate = atom_length / ((pmp3->song_length / 1000) * 128);
}
}
fclose(fin);
io_close(hfile);
io_dispose(hfile);
return TRUE; /* we'll return as much as we got. */
}

View File

@ -21,7 +21,9 @@
#ifndef _SCAN_AAC_H_
#define _SCAN_AAC_H_
extern off_t scan_aac_drilltoatom(FILE *aac_fp, char *atom_path, unsigned int *atom_length);
extern long scan_aac_findatom(FILE *fin, long max_offset, char *which_atom, unsigned int *atom_size);
#include "io.h"
extern uint64_t scan_aac_drilltoatom(IOHANDLE hfile, char *atom_path, unsigned int *atom_length);
extern uint64_t scan_aac_findatom(IOHANDLE hfile, uint64_t max_offset, char *which_atom, unsigned int *atom_size);
#endif

View File

@ -35,6 +35,7 @@
#include "daapd.h"
#include "err.h"
#include "io.h"
#include "mp3-scanner.h"
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
@ -87,18 +88,19 @@ uint16_t aif_from_be16(uint16_t *native) {
* @param infile file to read
* @return TRUE on success, FALSE otherwise
*/
int scan_aif_parse_comm(FILE *infile, MP3FILE *pmp3) {
int scan_aif_parse_comm(IOHANDLE hfile, MP3FILE *pmp3) {
AIF_COMM comm;
int sec;
int ms;
if(fread((void*)&comm,sizeof(AIF_COMM),1,infile) != 1) {
uint32_t len;
len = sizeof(AIF_COMM);
if(!io_read(hfile,(unsigned char *)&comm, &len) || !len) {
DPRINTF(E_WARN,L_SCAN,"Error reading aiff file -- bad COMM block\n");
return FALSE;
}
/* we'll brute the sample rate */
pmp3->samplerate = aif_from_be32((uint32_t*)&comm.sample_rate[2]) >> 16;
if(!pmp3->samplerate)
return TRUE;
@ -125,36 +127,49 @@ int scan_aif_parse_comm(FILE *infile, MP3FILE *pmp3) {
* @returns TRUE if song should be added to database, FALSE otherwise
*/
int scan_get_aifinfo(char *filename, MP3FILE *pmp3) {
FILE *infile;
IOHANDLE hfile;
int done=0;
AIF_CHUNK_HEADER chunk;
AIF_IFF_HEADER iff_header;
long current_pos = 0;
uint64_t current_pos = 0;
uint32_t len;
DPRINTF(E_DBG,L_SCAN,"Getting AIFF file info\n");
if(!(infile=fopen(filename,"rb"))) {
DPRINTF(E_WARN,L_SCAN,"Could not open %s for reading\n",filename);
hfile = io_new();
if(!hfile) {
DPRINTF(E_WARN,L_SCAN,"Error allocating file handle\n");
return FALSE;
}
if(!io_open(hfile,"file://%U",filename)) {
DPRINTF(E_WARN,L_SCAN,"Could not open %s for reading: %s\n",filename,
io_errstr(hfile));
io_dispose(hfile);
return FALSE;
}
/* first, verify we have a valid iff header */
if(fread((void*)&iff_header,sizeof(AIF_IFF_HEADER),1,infile) != 1) {
len = sizeof(AIF_IFF_HEADER);
if(!io_read(hfile,(unsigned char *)&iff_header,&len) || !len) {
DPRINTF(E_WARN,L_SCAN,"Error reading %s -- bad iff header\n",filename);
fclose(infile);
io_close(hfile);
io_dispose(hfile);
return FALSE;
}
if((strncmp(iff_header.id,"FORM",4) != 0) ||
(strncmp(iff_header.type,"AIFF",4) != 0)) {
DPRINTF(E_WARN,L_SCAN,"File %s is not an AIFF file\n",filename);
fclose(infile);
io_close(hfile);
io_dispose(hfile);
return FALSE;
}
/* loop around, processing chunks */
while(!done) {
if(fread((void*)&chunk,sizeof(AIF_CHUNK_HEADER),1,infile) != 1) {
len = sizeof(AIF_CHUNK_HEADER);
if(!io_read(hfile, (unsigned char *)&chunk, &len) || !len) {
done=1;
break;
}
@ -165,22 +180,24 @@ int scan_get_aifinfo(char *filename, MP3FILE *pmp3) {
DPRINTF(E_DBG,L_SCAN,"Got chunk %c%c%c%c\n",chunk.id[0],
chunk.id[1],chunk.id[2],chunk.id[3]);
current_pos = ftell(infile);
io_getpos(hfile,&current_pos);
/* process the chunk */
if(strncmp(chunk.id,"COMM",4)==0) {
if(!scan_aif_parse_comm(infile,pmp3)) {
if(!scan_aif_parse_comm(hfile,pmp3)) {
DPRINTF(E_INF,L_SCAN,"Error reading COMM block: %s\n",filename);
fclose(infile);
io_close(hfile);
io_dispose(hfile);
return FALSE;
}
}
fseek(infile,current_pos,SEEK_SET);
fseek(infile,chunk.len,SEEK_CUR);
io_setpos(hfile, current_pos, SEEK_SET);
io_setpos(hfile, chunk.len, SEEK_CUR);
}
fclose(infile);
io_close(hfile);
io_dispose(hfile);
return TRUE;
}

View File

@ -37,6 +37,7 @@
#include "daapd.h"
#include "conf.h"
#include "err.h"
#include "io.h"
#include "mp3-scanner.h"
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
@ -252,9 +253,9 @@ char *scan_winamp_genre[] = {
static int scan_mp3_get_mp3tags(char *file, MP3FILE *pmp3);
static int scan_mp3_get_mp3fileinfo(char *file, MP3FILE *pmp3);
static int scan_mp3_decode_mp3_frame(unsigned char *frame,SCAN_FRAMEINFO *pfi);
static void scan_mp3_get_average_bitrate(FILE *infile, SCAN_FRAMEINFO *pfi);
static void scan_mp3_get_average_bitrate(IOHANDLE hfile, SCAN_FRAMEINFO *pfi);
static int scan_mp3_is_numeric(char *str);
static void scan_mp3_get_frame_count(FILE *infile, SCAN_FRAMEINFO *pfi);
static void scan_mp3_get_frame_count(IOHANDLE hfile, SCAN_FRAMEINFO *pfi);
/**
@ -658,30 +659,31 @@ int scan_mp3_decode_mp3_frame(unsigned char *frame, SCAN_FRAMEINFO *pfi) {
* average bitrate from that. It might not be as accurate as a full
* frame count, but it's probably Close Enough (tm)
*
* @param infile file to scan for average bitrate
* @param hfile file to scan for average bitrate
* @param pfi pointer to frame info struct to put the bitrate into
*/
void scan_mp3_get_average_bitrate(FILE *infile, SCAN_FRAMEINFO *pfi) {
off_t file_size;
void scan_mp3_get_average_bitrate(IOHANDLE hfile, SCAN_FRAMEINFO *pfi) {
uint64_t file_size;
unsigned char frame_buffer[2900];
unsigned char header[4];
int index=0;
int found=0;
off_t pos;
uint64_t pos;
uint32_t len;
SCAN_FRAMEINFO fi;
int frame_count=0;
int bitrate_total=0;
DPRINTF(E_DBG,L_SCAN,"Starting averaging bitrate\n");
fseek(infile,0,SEEK_END);
file_size=ftell(infile);
io_size(hfile,&file_size);
pos=file_size/2;
/* now, find the first frame */
fseek(infile,pos,SEEK_SET);
if(fread(frame_buffer,1,sizeof(frame_buffer),infile) != sizeof(frame_buffer))
io_setpos(hfile,pos,SEEK_SET);
len = sizeof(frame_buffer);
if(!io_read(hfile, frame_buffer, &len) || len != sizeof(frame_buffer))
return;
while(!found) {
@ -695,8 +697,10 @@ void scan_mp3_get_average_bitrate(FILE *infile, SCAN_FRAMEINFO *pfi) {
if(!scan_mp3_decode_mp3_frame(&frame_buffer[index],&fi)) {
/* see if next frame is valid */
fseek(infile,pos + index + fi.frame_length,SEEK_SET);
if(fread(header,1,sizeof(header),infile) != sizeof(header)) {
io_setpos(hfile, pos + index + fi.frame_length, SEEK_SET);
len = sizeof(header);
if(!io_read(hfile,header,&len) || (len != sizeof(header))) {
DPRINTF(E_DBG,L_SCAN,"Could not read frame header\n");
return;
}
@ -713,11 +717,13 @@ void scan_mp3_get_average_bitrate(FILE *infile, SCAN_FRAMEINFO *pfi) {
/* found first frame. Let's move */
while(frame_count < 10) {
fseek(infile,pos,SEEK_SET);
if(fread(header,1,sizeof(header),infile) != sizeof(header)) {
io_setpos(hfile,pos,SEEK_SET);
len = sizeof(header);
if(!io_read(hfile,header,&len) || (len != sizeof(header))) {
DPRINTF(E_DBG,L_SCAN,"Could not read frame header\n");
return;
}
if(scan_mp3_decode_mp3_frame(header,&fi)) {
DPRINTF(E_DBG,L_SCAN,"Invalid frame header while averaging\n");
return;
@ -745,29 +751,31 @@ void scan_mp3_get_average_bitrate(FILE *infile, SCAN_FRAMEINFO *pfi) {
* @param infile file to scan for frame count
* @param pfi pointer to frame info struct to put framecount into
*/
void scan_mp3_get_frame_count(FILE *infile, SCAN_FRAMEINFO *pfi) {
int pos;
void scan_mp3_get_frame_count(IOHANDLE hfile, SCAN_FRAMEINFO *pfi) {
uint64_t pos;
int frames=0;
unsigned char frame_buffer[4];
SCAN_FRAMEINFO fi;
off_t file_size;
uint64_t file_size;
int err=0;
int cbr=1;
int last_bitrate=0;
uint32_t len;
DPRINTF(E_DBG,L_SCAN,"Starting frame count\n");
fseek(infile,0,SEEK_END);
file_size=ftell(infile);
io_size(hfile,&file_size);
pos=pfi->frame_offset;
while(1) {
err=1;
DPRINTF(E_SPAM,L_SCAN,"Seeking to %d\n",pos);
DPRINTF(E_SPAM,L_SCAN,"Seeking to %ld\n",pos);
fseek(infile,pos,SEEK_SET);
if(fread(frame_buffer,1,sizeof(frame_buffer),infile) == sizeof(frame_buffer)) {
io_setpos(hfile,pos,SEEK_SET);
len = sizeof(frame_buffer);
if(!io_read(hfile,frame_buffer,&len) || (len != sizeof(frame_buffer))) {
/* check for valid frame */
if(!scan_mp3_decode_mp3_frame(frame_buffer,&fi)) {
frames++;
@ -792,7 +800,7 @@ void scan_mp3_get_frame_count(FILE *infile, SCAN_FRAMEINFO *pfi) {
DPRINTF(E_DBG,L_SCAN,"Estimated frame count: %d\n",frames);
return;
} else {
DPRINTF(E_DBG,L_SCAN,"Frame count aborted on error. Pos=%d, Count=%d\n",
DPRINTF(E_DBG,L_SCAN,"Frame count aborted on error. Pos=%ld, Count=%d\n",
pos, frames);
return;
}
@ -809,12 +817,13 @@ void scan_mp3_get_frame_count(FILE *infile, SCAN_FRAMEINFO *pfi) {
* @param pmp3 where to put the found information
*/
int scan_mp3_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
FILE *infile;
IOHANDLE hfile;
SCAN_ID3HEADER *pid3;
SCAN_FRAMEINFO fi;
unsigned int size=0;
off_t fp_size=0;
off_t file_size;
uint64_t fp_size=0;
uint64_t file_size;
uint32_t len;
unsigned char buffer[1024];
int index;
@ -824,8 +833,11 @@ int scan_mp3_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
int first_check=0;
char frame_buffer[4];
if(!(infile=fopen(file,"rb"))) {
DPRINTF(E_WARN,L_SCAN,"Could not open %s for reading\n",file);
if(!(hfile = io_new()))
DPRINTF(E_FATAL,L_SCAN,"Could not allocate io handle\n");
if(!(io_open(hfile,"file://%U",file))) {
DPRINTF(E_WARN,L_SCAN,"Could not open %s for reading: %s\n",file,io_errstr(hfile));
return FALSE;
}
@ -833,13 +845,15 @@ int scan_mp3_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
memset((void*)&fi,0x00,sizeof(fi));
if(fread(buffer,1,sizeof(buffer),infile) != sizeof(buffer)) {
if(ferror(infile)) {
DPRINTF(E_LOG,L_SCAN,"Error reading: %s\n",strerror(errno));
len = sizeof(buffer);
if(!io_read(hfile,buffer,&len) || len != sizeof(buffer)) {
if(len != sizeof(buffer)) {
DPRINTF(E_LOG,L_SCAN,"Error reading: %s\n",io_errstr(hfile));
} else {
DPRINTF(E_INF,L_SCAN,"Bad mp3 file? (short read): %s\n",file);
}
fclose(infile);
io_close(hfile);
io_dispose(hfile);
return FALSE;
}
@ -865,11 +879,13 @@ int scan_mp3_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
*/
while(!found) {
fseek(infile,fp_size,SEEK_SET);
DPRINTF(E_DBG,L_SCAN,"Reading in new block at %d\n",(int)fp_size);
if(fread(buffer,1,sizeof(buffer),infile) < sizeof(buffer)) {
io_setpos(hfile,fp_size,SEEK_SET);
DPRINTF(E_DBG,L_SCAN,"Reading in new block at %ld\n",(int)fp_size);
len = sizeof(buffer);
if(!io_read(hfile,buffer,&len) || (len != sizeof(buffer))) {
DPRINTF(E_INF,L_SCAN,"Bad mp3 file? (Short read): %s\n",file);
fclose(infile);
io_close(hfile);
io_dispose(hfile);
return TRUE;
}
@ -902,20 +918,23 @@ int scan_mp3_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
/* No Xing... check for next frame */
DPRINTF(E_DBG,L_SCAN,"Found valid frame at %04x\n",(int)fp_size+index);
DPRINTF(E_DBG,L_SCAN,"Checking at %04x\n",(int)fp_size+index+fi.frame_length);
fseek(infile,fp_size + index + fi.frame_length,SEEK_SET);
if(fread(frame_buffer,1,sizeof(frame_buffer),infile) == sizeof(frame_buffer)) {
io_setpos(hfile,fp_size + index + fi.frame_length,SEEK_SET);
len =sizeof(frame_buffer);
if(io_read(hfile,(unsigned char *)frame_buffer,&len) && (len == sizeof(frame_buffer))) {
if(!scan_mp3_decode_mp3_frame((u_char*)frame_buffer,&fi)) {
found=1;
fp_size += index;
}
} else {
DPRINTF(E_LOG,L_SCAN,"Could not read frame header: %s\n",file);
fclose(infile);
io_close(hfile);
io_dispose(hfile);
return 0;
}
if(!found) {
DPRINTF(E_DBG,L_SCAN,"Didn't pan out.\n");
DPRINTF(E_DBG,L_SCAN,"Didn't pan out. Sorry about that.\n");
}
}
}
@ -941,7 +960,8 @@ int scan_mp3_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
fi.frame_offset=fp_size;
if(scan_mp3_decode_mp3_frame(&buffer[index],&fi)) {
fclose(infile);
io_close(hfile);
io_dispose(hfile);
DPRINTF(E_LOG,L_SCAN,"Could not find sync frame: %s\n",file);
DPRINTF(E_LOG,L_SCAN,"If this is a valid mp3 file that plays in "
"other applications, please email me at rpedde@users.sourceforge.net "
@ -990,10 +1010,10 @@ int scan_mp3_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
DPRINTF(E_DBG,L_SCAN,"Starting aggressive file length scan\n");
if(conf_get_int("general","scan_type",0) == 1) {
/* get average bitrate */
scan_mp3_get_average_bitrate(infile, &fi);
scan_mp3_get_average_bitrate(hfile, &fi);
} else {
/* get full frame count */
scan_mp3_get_frame_count(infile, &fi);
scan_mp3_get_frame_count(hfile, &fi);
}
pmp3->bitrate=fi.bitrate;
}
@ -1022,7 +1042,8 @@ int scan_mp3_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
DPRINTF(E_DBG,L_SCAN," Song Length: %d\n",pmp3->song_length);
fclose(infile);
io_close(hfile);
io_dispose(hfile);
return TRUE;
}

View File

@ -36,6 +36,7 @@
#include "daapd.h"
#include "err.h"
#include "io.h"
#include "mp3-scanner.h"
#include "scan-aac.h"
@ -57,12 +58,15 @@ extern time_t scan_aac_mac_to_unix_time(int t);
* @returns FALSE if file should not be added to database, TRUE otherwise
*/
int scan_get_mp4info(char *filename, MP3FILE *pmp3) {
FILE *fin;
long atom_offset;
IOHANDLE hfile;
uint64_t atom_offset;
unsigned int atom_length;
long current_offset=0;
int current_size;
uint64_t current_offset=0;
uint64_t file_pos;
uint32_t read_size;
uint32_t current_size;
char current_atom[4];
char *current_data;
unsigned short us_data;
@ -77,28 +81,43 @@ int scan_get_mp4info(char *filename, MP3FILE *pmp3) {
int time = 0;
if(!(fin=fopen(filename,"rb"))) {
DPRINTF(E_INF,L_SCAN,"Cannot open file %s for reading\n",filename);
if(!(hfile = io_new())) {
DPRINTF(E_LOG,L_SCAN,"Cannot create file handle\n");
return FALSE;
}
if(!io_open(hfile,"file://%U",filename)) {
DPRINTF(E_INF,L_SCAN,"Cannot open file %s for reading: %s\n",
filename,io_errstr(hfile));
io_dispose(hfile);
return FALSE;
}
atom_offset=scan_aac_drilltoatom(fin, "moov:udta:meta:ilst", &atom_length);
atom_offset=scan_aac_drilltoatom(hfile, "moov:udta:meta:ilst", &atom_length);
if(atom_offset != -1) {
/* found the tag section - need to walk through now */
while(current_offset < (long) atom_length) {
if(fread((void*)&current_size,1,sizeof(int),fin) != sizeof(int))
break;
read_size = sizeof(uint32_t);
if(!io_read(hfile,(unsigned char *)&current_size,&read_size) || (read_size != sizeof(uint32_t))) {
io_close(hfile);
io_dispose(hfile);
return FALSE;
}
DPRINTF(E_SPAM,L_SCAN,"Current size: %d\n");
DPRINTF(E_SPAM,L_SCAN,"Current size: %d\n",current_size);
current_size=ntohl(current_size);
if(current_size <= 7) /* something not right */
break;
if(fread(current_atom,1,4,fin) != 4)
break;
read_size = 4;
if(!io_read(hfile,(unsigned char *)&current_atom,&read_size) || (read_size != 4)) {
io_close(hfile);
io_dispose(hfile);
return FALSE;
}
DPRINTF(E_SPAM,L_SCAN,"Current Atom: %c%c%c%c\n",
current_atom[0],current_atom[1],current_atom[2],
@ -106,7 +125,7 @@ int scan_get_mp4info(char *filename, MP3FILE *pmp3) {
if(current_size > 4096) { /* Does this break anything? */
/* too big! cover art, maybe? */
fseek(fin,current_size - 8,SEEK_CUR);
io_setpos(hfile, current_size - 8, SEEK_CUR);
} else {
len=current_size-7; /* for ill-formed too-short tags */
if(len < 22)
@ -115,8 +134,12 @@ int scan_get_mp4info(char *filename, MP3FILE *pmp3) {
current_data=(char*)malloc(len); /* extra byte */
memset(current_data,0x00,len);
if(fread(current_data,1,current_size-8,fin) != current_size-8)
break;
read_size = current_size - 8;
if(!io_read(hfile,(unsigned char *)current_data,&read_size) || (current_size != 8)) {
io_close(hfile);
io_dispose(hfile);
return FALSE;
}
if(!memcmp(current_atom,"\xA9" "nam",4)) { /* Song name */
pmp3->title=strdup((char*)&current_data[16]);
@ -178,18 +201,39 @@ int scan_get_mp4info(char *filename, MP3FILE *pmp3) {
}
/* got the tag info, now let's get bitrate, etc */
atom_offset = scan_aac_drilltoatom(fin, "moov:mvhd", &atom_length);
atom_offset = scan_aac_drilltoatom(hfile, "moov:mvhd", &atom_length);
if(atom_offset != -1) {
fseek(fin, 4, SEEK_CUR);
fread((void *)&time, sizeof(int), 1, fin);
io_setpos(hfile,4,SEEK_CUR);
read_size = sizeof(int);
if(!io_read(hfile,(unsigned char *)&time, &read_size) || (read_size != sizeof(int))) {
io_dispose(hfile);
return FALSE;
}
time = ntohl(time);
pmp3->time_added = (int) scan_aac_mac_to_unix_time(time);
fread((void *)&time, sizeof(int), 1, fin);
read_size = sizeof(int);
if(!io_read(hfile,(unsigned char *)&time, &read_size) || (read_size != sizeof(int))) {
io_dispose(hfile);
return FALSE;
}
time = ntohl(time);
pmp3->time_modified = (int) scan_aac_mac_to_unix_time(time);
fread((void*)&sample_size,1,sizeof(int),fin);
fread((void*)&samples,1,sizeof(int),fin);
read_size = sizeof(int);
if(!io_read(hfile,(unsigned char *)&sample_size, &read_size) || (read_size != sizeof(int))) {
io_dispose(hfile);
return FALSE;
}
read_size = sizeof(int);
if(!io_read(hfile,(unsigned char *)&samples, &read_size) || (read_size != sizeof(int))) {
io_dispose(hfile);
return FALSE;
}
sample_size=ntohl(sample_size);
samples=ntohl(samples);
@ -212,40 +256,51 @@ int scan_get_mp4info(char *filename, MP3FILE *pmp3) {
/* Get the sample rate from the 'mp4a' atom (timescale). This is also
found in the 'mdhd' atom which is a bit closer but we need to
navigate to the 'mp4a' atom anyways to get to the 'esds' atom. */
atom_offset=scan_aac_drilltoatom(fin,
atom_offset=scan_aac_drilltoatom(hfile,
"moov:trak:mdia:minf:stbl:stsd:mp4a",
&atom_length);
if(atom_offset == -1) {
atom_offset=scan_aac_drilltoatom(fin,
atom_offset=scan_aac_drilltoatom(hfile,
"moov:trak:mdia:minf:stbl:stsd:drms",
&atom_length);
}
if (atom_offset != -1) {
fseek(fin, atom_offset + 32, SEEK_SET);
io_setpos(hfile, atom_offset + 32, SEEK_SET);
/* Timescale here seems to be 2 bytes here (the 2 bytes before it are
* "reserved") though the timescale in the 'mdhd' atom is 4. Not sure
* how this is dealt with when sample rate goes higher than 64K. */
fread(buffer, sizeof(unsigned char), 2, fin);
read_size = 2;
if(!io_read(hfile,(unsigned char *)&buffer,&read_size) || (read_size != 2)) {
io_dispose(hfile);
return FALSE;
}
pmp3->samplerate = (buffer[0] << 8) | (buffer[1]);
/* Seek to end of atom. */
fseek(fin, 2, SEEK_CUR);
io_setpos(hfile,2,SEEK_CUR);
/* Get the bit rate from the 'esds' atom. We are already positioned
in the parent atom so just scan ahead. */
atom_offset = scan_aac_findatom(fin,
atom_length-(ftell(fin)-atom_offset),
io_getpos(hfile,&file_pos);
atom_offset = scan_aac_findatom(hfile,
atom_length-(file_pos-atom_offset),
"esds", &atom_length);
if (atom_offset != -1) {
/* Roku Soundbridge seems to believe anything above 320K is
* an ALAC encoded m4a. We'll lie on their behalf.
*/
fseek(fin, atom_offset + 22, SEEK_CUR);
fread((void *)&bit_rate, sizeof(unsigned int), 1, fin);
io_setpos(hfile, atom_offset + 22, SEEK_CUR);
read_size = sizeof(unsigned int);
if(!io_read(hfile,(unsigned char *)&bit_rate, &read_size) || (read_size != sizeof(unsigned int))) {
io_dispose(hfile);
return FALSE;
}
pmp3->bitrate = ntohl(bit_rate) / 1000;
DPRINTF(E_DBG,L_SCAN,"esds bitrate: %d\n",pmp3->bitrate);
@ -263,13 +318,13 @@ int scan_get_mp4info(char *filename, MP3FILE *pmp3) {
if (pmp3->bitrate == 0) {
/* calculate bitrate from song length... Kinda cheesy */
DPRINTF(E_DBG,L_SCAN, "Guesstimating bit rate.\n");
atom_offset=scan_aac_drilltoatom(fin,"mdat",&atom_length);
atom_offset=scan_aac_drilltoatom(hfile,"mdat",&atom_length);
if ((atom_offset != -1) && (pmp3->song_length > 1000)) {
pmp3->bitrate = atom_length / ((pmp3->song_length / 1000) * 128);
}
}
fclose(fin);
io_dispose(hfile);
pmp3->has_video=1;

View File

@ -23,8 +23,48 @@
#include "daapd.h"
#include "err.h"
#include "io.h"
#include "mp3-scanner.h"
size_t scan_ogg_read(void *ptr, size_t size, size_t nmemb, void *datasource) {
IOHANDLE hfile = (IOHANDLE)datasource;
uint32_t bytes_read;
bytes_read = size * nmemb;
if(!io_read(hfile,ptr,&bytes_read))
return -1;
return (size_t)bytes_read;
}
int scan_ogg_seek(void *datasource, int64_t offset, int whence) {
IOHANDLE hfile = (IOHANDLE)datasource;
if(!io_setpos(hfile,(uint64_t)offset,whence))
return -1;
return 0;
}
int scan_ogg_close(void *datasource) {
IOHANDLE hfile = (IOHANDLE)datasource;
int retcode;
retcode = io_close(hfile);
io_dispose(hfile);
return retcode ? 0 : EOF;
}
long scan_ogg_tell(void *datasource) {
IOHANDLE hfile = (IOHANDLE)datasource;
uint64_t pos;
if(!io_getpos(hfile,&pos))
return -1;
return (long)pos;
}
/**
* get ogg metainfo
@ -34,27 +74,30 @@
* @returns TRUE if file should be added to DB, FALSE otherwise
*/
int scan_get_ogginfo(char *filename, MP3FILE *pmp3) {
FILE *f;
IOHANDLE hfile;
OggVorbis_File vf;
vorbis_comment *comment = NULL;
vorbis_info *vi = NULL;
char *val;
ov_callbacks callbacks = { scan_ogg_read, scan_ogg_seek, scan_ogg_close, scan_ogg_tell };
f = fopen(filename, "rb");
if (f == NULL) {
DPRINTF(E_LOG, L_SCAN,
"Error opening input file \"%s\": %s\n", filename,
strerror(errno));
hfile = io_new();
if(!hfile) {
DPRINTF(E_LOG,L_SCAN,"Could not create file handle\n");
return FALSE;
}
if (ov_open(f, &vf, NULL, 0) != 0) {
fclose(f);
DPRINTF(E_LOG, L_SCAN,
"Error opening Vorbis stream in \"%s\"\n", filename);
if(!io_open(hfile,"file://%U",filename)) {
DPRINTF(E_LOG,L_SCAN,"Error opening %s: %s", filename, io_errstr(hfile));
return FALSE;
}
if(ov_open_callbacks(hfile,&vf,NULL,0,callbacks) < 0) {
DPRINTF(E_LOG,L_SCAN,"Could not create oggvorbis file handler\n");
return FALSE;
}
vi=ov_info(&vf,-1);
if(vi) {
DPRINTF(E_DBG,L_SCAN," Bitrates: %d %d %d\n",vi->bitrate_upper,
@ -95,6 +138,5 @@ int scan_get_ogginfo(char *filename, MP3FILE *pmp3) {
pmp3->year = atoi(val);
}
ov_clear(&vf);
/*fclose(f);*/
return TRUE;
}

View File

@ -44,18 +44,34 @@
* @returns TRUE if file should be added to db, FALSE otherwise
*/
int scan_get_urlinfo(char *filename, MP3FILE *pmp3) {
FILE *infile;
IOHANDLE hfile;
char *head, *tail;
char linebuffer[256];
uint32_t len;
DPRINTF(E_DBG,L_SCAN,"Getting URL file info\n");
if(!(infile=fopen(filename,"rb"))) {
DPRINTF(E_WARN,L_SCAN,"Could not open %s for reading\n",filename);
if(!(hfile = io_new())) {
DPRINTF(E_LOG,L_SCAN,"Can't create file handle\n");
return FALSE;
}
if(!io_open(hfile,"file://%U",filename)) {
DPRINTF(E_WARN,L_SCAN,"Could not open %s for reading: %s\n",filename,
io_errstr(hfile));
io_dispose(hfile);
return FALSE;
}
io_buffer(hfile);
len = sizeof(linebuffer);
if(!io_readline(hfile,(unsigned char *)linebuffer,&len)) {
DPRINTF(E_WARN,L_SCAN,"Error reading from file %s: %s",filename,io_errstr(hfile));
io_close(hfile);
io_dispose(hfile);
return FALSE;
}
fgets(linebuffer,sizeof(linebuffer),infile);
while((linebuffer[strlen(linebuffer)-1] == '\n') ||
(linebuffer[strlen(linebuffer)-1] == '\r')) {
linebuffer[strlen(linebuffer)-1] = '\0';
@ -65,7 +81,8 @@ int scan_get_urlinfo(char *filename, MP3FILE *pmp3) {
tail=strchr(head,',');
if(!tail) {
DPRINTF(E_LOG,L_SCAN,"Badly formatted .url file - must be bitrate,descr,url\n");
fclose(infile);
io_close(hfile);
io_dispose(hfile);
return FALSE;
}
@ -74,7 +91,8 @@ int scan_get_urlinfo(char *filename, MP3FILE *pmp3) {
tail=strchr(head,',');
if(!tail) {
DPRINTF(E_LOG,L_SCAN,"Badly formatted .url file - must be bitrate,descr,url\n");
fclose(infile);
io_close(hfile);
io_dispose(hfile);
return FALSE;
}
@ -82,7 +100,9 @@ int scan_get_urlinfo(char *filename, MP3FILE *pmp3) {
pmp3->title=strdup(head);
pmp3->url=strdup(tail);
fclose(infile);
io_close(hfile);
io_dispose(hfile);
DPRINTF(E_DBG,L_SCAN," Title: %s\n",pmp3->title);
DPRINTF(E_DBG,L_SCAN," Bitrate: %d\n",pmp3->bitrate);

View File

@ -32,6 +32,7 @@
#include "daapd.h"
#include "err.h"
#include "io.h"
#include "mp3-scanner.h"
#define GET_WAV_INT32(p) ((((uint32_t)((p)[3])) << 24) | \
@ -52,8 +53,8 @@
* @returns TRUE if song should be added to database, FALSE otherwise
*/
int scan_get_wavinfo(char *filename, MP3FILE *pmp3) {
FILE *infile;
size_t rl;
IOHANDLE hfile;
uint32_t len;
unsigned char hdr[12];
unsigned char fmt[16];
uint32_t chunk_data_length;
@ -74,15 +75,23 @@ int scan_get_wavinfo(char *filename, MP3FILE *pmp3) {
DPRINTF(E_DBG,L_SCAN,"Getting WAV file info\n");
if(!(infile=fopen(filename,"rb"))) {
DPRINTF(E_WARN,L_SCAN,"Could not open %s for reading\n",filename);
if(!(hfile = io_new())) {
DPRINTF(E_LOG,L_SCAN,"Could not create file handle\n");
return FALSE;
}
if(!io_open(hfile,"file://%U",filename)) {
DPRINTF(E_WARN,L_SCAN,"Could not open %s for reading: %s\n",filename,
io_errstr(hfile));
io_dispose(hfile);
return FALSE;
}
rl = fread(hdr, 1, 12, infile);
if (rl != 12) {
len = 12;
if(!io_read(hfile,hdr,&len) || (len != 12)) {
DPRINTF(E_WARN,L_SCAN,"Could not read wav header from %s\n",filename);
fclose(infile);
io_close(hfile);
io_dispose(hfile);
return FALSE;
}
@ -91,7 +100,8 @@ int scan_get_wavinfo(char *filename, MP3FILE *pmp3) {
if (strncmp((char*)hdr + 0, "RIFF", 4) ||
strncmp((char*)hdr + 8, "WAVE", 4)) {
DPRINTF(E_WARN,L_SCAN,"Invalid wav header in %s\n",filename);
fclose(infile);
io_close(hfile);
io_dispose(hfile);
return FALSE;
}
@ -101,9 +111,10 @@ int scan_get_wavinfo(char *filename, MP3FILE *pmp3) {
current_offset = 12;
while(!found_fmt && !found_data) {
rl = fread(hdr, 1, 8, infile);
if (rl != 8) {
fclose(infile);
len = 8;
if(!io_read(hfile,hdr,&len) || (len != 8)) {
io_close(hfile);
io_dispose(hfile);
DPRINTF(E_WARN,L_SCAN,"Error reading block: %s\n",filename);
return FALSE;
}
@ -115,7 +126,8 @@ int scan_get_wavinfo(char *filename, MP3FILE *pmp3) {
hdr[0],hdr[1],hdr[2],hdr[3],block_len);
if(block_len < 0) {
fclose(infile);
io_close(hfile);
io_dispose(hfile);
DPRINTF(E_WARN,L_SCAN,"Bad block len: %s\n",filename);
return FALSE;
}
@ -123,9 +135,10 @@ int scan_get_wavinfo(char *filename, MP3FILE *pmp3) {
if(strncmp((char*)&hdr,"fmt ",4) == 0) {
found_fmt = TRUE;
DPRINTF(E_DBG,L_SCAN,"Found 'fmt ' header\n");
rl = fread(fmt,1,16,infile);
if(rl != 16) {
fclose(infile);
len = 16;
if(!io_read(hfile,fmt,&len) || (len != 16)) {
io_close(hfile);
io_dispose(hfile);
DPRINTF(E_WARN,L_SCAN,"Bad .wav file: can't read fmt: %s\n",
filename);
return FALSE;
@ -147,11 +160,13 @@ int scan_get_wavinfo(char *filename, MP3FILE *pmp3) {
found_data = TRUE;
}
fseek(infile,current_offset + block_len + 8,SEEK_SET);
io_setpos(hfile,current_offset + block_len + 8,SEEK_SET);
current_offset += block_len;
}
fclose(infile);
io_close(hfile);
io_dispose(hfile);
if (((format_data_length != 16) && (format_data_length != 18)) ||
(compression_code != 1) ||
(channel_count < 1)) {

View File

@ -505,6 +505,7 @@ int wma_parse_extended_content_description(int fd,int size, MP3FILE *pmp3, int e
int track, tracknumber;
char numbuff[40];
char *tmp;
unsigned char *ptr;
track = tracknumber = 0;
@ -556,8 +557,9 @@ int wma_parse_extended_content_description(int fd,int size, MP3FILE *pmp3, int e
lseek(fd,descriptor_value_int,SEEK_CUR);
descriptor_byte_value = NULL;
} else {
ptr = (unsigned char *)descriptor_byte_value;
if(!wma_file_read_bytes(fd,descriptor_value_int,
(unsigned char **)&descriptor_byte_value)){
&ptr)){
fail=1;
}
}

View File

@ -273,6 +273,10 @@ FIELDLOOKUP sp_fields_0[] = {
{ T_INT_FIELD, "force_update", NULL },
{ T_STRING_FIELD, "codectype", NULL },
{ T_INT_FIELD, "idx", NULL },
{ T_INT_FIELD, "has_video", NULL },
{ T_INT_FIELD, "contentrating", NULL },
{ T_INT_FIELD, "bits_per_sample", NULL },
{ T_STRING_FIELD, "album_artist", NULL },
/* end of db fields */
{ T_OR, "or", NULL },

202
src/ssl.c Normal file
View File

@ -0,0 +1,202 @@
/*
* $Id: $
* SSL Routines
*
* Copyright (C) 2006 Ron Pedde (rpedde@users.sourceforge.net)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include "daapd.h"
#include "err.h"
#include "webserver.h"
#include "wsprivate.h"
/* Globals */
static SSL_CTX *ws_ssl_ctx = NULL;
static char *ws_ssl_pass = NULL;
/* Forwards */
static void ws_ssl_print_error(int loglevel);
static int ws_ssl_pw_cb(char *buffer, int num, int rwflag, void *userdata);
/*
* password callback for the passphrase on the priv key
*/
static int ws_ssl_pw_cb(char *buff, int num, int rwflag, void *userdata) {
if(num < strlen(ws_ssl_pass) + 1)
return 0;
strcpy(buff,ws_ssl_pass);
return (int) strlen(ws_ssl_pass);
}
/*
* initialize ssl library
*/
int ws_ssl_init(char *keyfile, char *cert, char *password) {
SSL_METHOD *meth;
if(ws_ssl_ctx) {
return TRUE;
}
SSL_library_init();
SSL_load_error_strings();
/* Create our context*/
meth=SSLv23_method();
ws_ssl_ctx=SSL_CTX_new(meth);
/* Load our keys and certificates*/
if(!(SSL_CTX_use_certificate_chain_file(ws_ssl_ctx,cert))) {
DPRINTF(E_LOG,L_WS,"Can't read certificate file; ssl disabled\n");
return FALSE;
}
ws_ssl_pass=password;
SSL_CTX_set_default_passwd_cb(ws_ssl_ctx,ws_ssl_pw_cb);
if(!(SSL_CTX_use_PrivateKey_file(ws_ssl_ctx,keyfile,SSL_FILETYPE_PEM))) {
DPRINTF(E_LOG,L_WS,"Can't read key file; ssl disabled\n");
return FALSE;
}
return TRUE;
}
/*
* finish the ssl stuff
*/
void ws_ssl_deinit(void) {
if(ws_ssl_ctx)
SSL_CTX_free(ws_ssl_ctx);
}
/*
* this gets called immediately after an accept from the
* underlying socket.
*
* @returns 1 if handshake completed, 0 if the connection was terminated,
* but normally, and -1 if there was an error
*/
int ws_ssl_sock_init(WS_CONNINFO *pwsc, int fd) {
SSL *pssl;
int err;
if(pwsc->secure) {
if(!pwsc->secure_storage) {
pssl = SSL_new(ws_ssl_ctx);
pwsc->secure_storage = pssl;
}
pssl = (SSL*) pwsc->secure_storage;
SSL_set_fd(pssl,pwsc->fd);
err = SSL_accept(pssl);
if(err == -1) {
ws_ssl_print_error(E_LOG);
}
return err;
} else {
return 1;
}
}
/*
* print any error associated with this thread
*/
void ws_ssl_print_error(int loglevel) {
unsigned long err;
char buffer[120];
while((err = ERR_get_error())) {
ERR_error_string(err,buffer);
DPRINTF(E_LOG,loglevel,"%s\n",buffer);
}
}
/*
* write to ssl sock
*/
/*
*
*/
void ws_ssl_shutdown(WS_CONNINFO *pwsc) {
SSL *pssl;
if((pwsc->secure) && (!pwsc->secure_storage)) {
pssl = (SSL*)pwsc->secure_storage;
SSL_shutdown(pssl);
SSL_free(pssl);
pwsc->secure_storage = NULL;
}
ws_socket_shutdown(pwsc);
}
/*
*
*/
int ws_ssl_read(WS_CONNINFO *pwsc, unsigned char *buffer, int len) {
SSL *pssl;
int result;
if((pwsc->secure) && (!pwsc->secure_storage)) {
pssl = (SSL*)pwsc->secure_storage;
result = SSL_read(pssl, buffer, len);
if(len <= 0)
ws_ssl_print_error(E_LOG);
} else {
result = ws_socket_read(pwsc, buffer, len);
}
return result;
}
int ws_ssl_write(WS_CONNINFO *pwsc, unsigned char *buffer, int len) {
SSL *pssl;
int result;
if((pwsc->secure) && (!pwsc->secure_storage)) {
pssl = (SSL*)pwsc->secure_storage;
result = SSL_write(pssl, buffer, len);
if(len <= 0)
ws_ssl_print_error(E_LOG);
} else {
result = ws_socket_write(pwsc, buffer, len);
}
return result;
}

34
src/ssl.h Normal file
View File

@ -0,0 +1,34 @@
/*
* $Id: $
* SSL Routines
*
* Copyright (C) 2006 Ron Pedde (rpedde@users.sourceforge.net)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _SSL_H_
#define _SSL_H_
#ifdef USE_SSL
extern int ws_ssl_init(char *keyfile, char *cert, char *password);
extern void ws_ssl_deinit(void);
extern int ws_ssl_write(WS_CONNINFO *pwsc, unsigned char *buffer, int len);
extern int ws_ssl_read(WS_CONNINFO *pwsc, unsigned char *buffer, int len);
extern void ws_ssl_shutdown(WS_CONNINFO *pwsc);
#endif /* SSL */
#endif /* _SSL_H_ */

View File

@ -1,5 +1,9 @@
#!/bin/sh
XX=' '
echo "Searching for ${XX}."
echo "Searching for Tabs..."
egrep -l "${XX}" *.[ch]
egrep -r -l "${XX}" * | grep '[ch]$'
echo "Searching for windows line endings..."
grep -r -l $'\r' * | grep '[ch]$'

File diff suppressed because it is too large Load Diff

View File

@ -22,6 +22,8 @@
#ifndef _WEBSERVER_H_
#define _WEBSERVER_H_
#include "io.h"
/*
* Defines
*/
@ -29,7 +31,27 @@
#define RT_GET 0
#define RT_POST 1
/*
#define E_WS_SUCCESS 0
#define E_WS_NATIVE 1 /**< A native platform error */
#define E_WS_MEMORY 2 /**< Malloc error */
#define E_WS_PTHREADS 3 /**< Pthreads error */
#define E_WS_EXHAUSTED 4 /**< ports exhausted!? */
#define E_WS_LISTEN 5 /**< can't listen */
#define E_WS_CONTENTLEN 6 /**< Invalid content length in POST */
#define E_WS_READ 7 /**< read error on socket */
#define E_WS_GETVARS 8 /**< could not parse get vars */
#define E_WS_TIMEOUT 9 /**< timeout listening on socket */
#define E_WS_REQTYPE 10 /**< invalid request type (POST, GET, etc) */
#define E_WS_BADPATH 11 /**< requested path out of root */
#define L_WS_SPAM 10 /**< Logorrhea! */
#define L_WS_DBG 9 /**< Way too verbose */
#define L_WS_INF 5 /**< Good info, not too much spam */
#define L_WS_WARN 2 /**< Goes in text log, but not syslog */
#define L_WS_LOG 1 /**< Something that should go in syslog */
#define L_WS_FATAL 0 /**< Log and force an exit */
/*
* Typedefs
*/
typedef void* WSHANDLE;
@ -38,7 +60,11 @@ typedef void* WSTHREADENUM;
typedef struct tag_wsconfig {
char *web_root;
char *id;
char *ssl_cert;
char *ssl_key;
char *ssl_pw;
unsigned short port;
unsigned short ssl_port;
} WSCONFIG;
typedef struct tag_arglist {
@ -48,14 +74,19 @@ typedef struct tag_arglist {
} ARGLIST;
typedef struct tag_ws_conninfo {
int err_code;
int err_native;
char *err_msg;
WSHANDLE pwsp;
int threadno;
int error;
int fd;
IOHANDLE hclient;
int request_type;
char *uri;
char *hostname;
int close;
int secure;
void *secure_storage;
void *local_storage;
void (*storage_callback)(void*);
ARGLIST request_headers;
@ -70,13 +101,20 @@ typedef struct tag_ws_conninfo {
#define WS_REQ_HANDLER void (*)(WS_CONNINFO *)
#define WS_AUTH_HANDLER int (*)(WS_CONNINFO*, char *, char *)
extern WSHANDLE ws_start(WSCONFIG *config);
/** Server functions */
extern WSHANDLE ws_init(WSCONFIG *config);
extern int ws_start(WSHANDLE ws);
extern int ws_stop(WSHANDLE ws);
extern int ws_registerhandler(WSHANDLE ws, char *regex,
void(*handler)(WS_CONNINFO*),
int(*auth)(WS_CONNINFO*, char *, char *),
int addheaders);
extern int ws_registerhandler(WSHANDLE ws, char *stem,
void(*handler)(WS_CONNINFO*),
int(*auth)(WS_CONNINFO*, char *, char *),
int flags,
int addheaders);
extern int ws_server_errcode(WSHANDLE ws);
/** Local storage functions */
extern void ws_lock_local_storage(WS_CONNINFO *pwsc);
extern void ws_unlock_local_storage(WS_CONNINFO *pwsc);
extern void *ws_get_local_storage(WS_CONNINFO *pwsc);
@ -95,8 +133,15 @@ extern char *ws_getvar(WS_CONNINFO *pwsc, char *var);
extern char *ws_getrequestheader(WS_CONNINFO *pwsc, char *header);
extern int ws_testrequestheader(WS_CONNINFO *pwsc, char *header, char *value);
extern void ws_emitheaders(WS_CONNINFO *pwsc);
extern char *ws_uri(WS_CONNINFO *pwsc);
extern void *ws_enum_var(WS_CONNINFO *pwsc, char **key, char **value, void *last);
extern int ws_copyfile(WS_CONNINFO *pwsc, IOHANDLE hfile, uint64_t *bytes_copied);
extern void ws_should_close(WS_CONNINFO *pwsc, int should_close);
extern int ws_threadno(WS_CONNINFO *pwsc);
extern char *ws_hostname(WS_CONNINFO *pwsc);
extern void ws_set_errhandler(void(*err_handler)(int, char*));
extern int ws_set_err(WS_CONNINFO *pwsc, int ws_error);
extern int ws_get_err(WS_CONNINFO *pwsc, int *errcode, char **err_msg);
#endif /* _WEBSERVER_H_ */

View File

@ -84,11 +84,12 @@ typedef INT64 int64_t;
#define putenv _putenv
#define realpath os_realpath
#define close os_close
#define strsep os_strsep
#define open os_open
#define waitfdtimed os_waitfdtimed
#define fopen os_fopen
// #define close os_close
// #define open os_open
// #define waitfdtimed os_waitfdtimed
// #define fopen os_fopen
#define readdir_r os_readdir_r
#define closedir os_closedir
@ -98,8 +99,8 @@ typedef INT64 int64_t;
#define strerror os_strerror
/* override the uici stuff */
#define u_open os_opensocket
#define u_accept os_acceptsocket
// #define u_open os_opensocket
// #define u_accept os_acceptsocket
/* privately implemented functions: @see os-win32.c */
#define gettimeofday os_gettimeofday

30
src/wsprivate.h Normal file
View File

@ -0,0 +1,30 @@
/*
* $Id: os.h 1479 2007-01-09 18:12:39Z rpedde $
* Abstract os interface for non-unix platforms
*
* Copyright (C) 2006 Ron Pedde (rpedde@users.sourceforge.net)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _WSPRIVATE_H_
#define _WSPRIVATE_H_
extern int ws_socket_write(WS_CONNINFO *pwsc, unsigned char *buffer, int len);
extern int ws_socket_read(WS_CONNINFO *pwsc, unsigned char *buffer, int len);
extern void ws_socket_shutdown(WS_CONNINFO *pwsc);
#endif /* _WSPRIVATE_H_ */

BIN
win32/nsi/AccessControl.dll Normal file

Binary file not shown.

BIN
win32/nsi/SimpleFC.dll Normal file

Binary file not shown.

View File

@ -24,6 +24,10 @@ RequestExecutionLevel admin
!define ADMIN_ROOT "${MTDROOT}\admin-root"
!define REDIST_SOURCE "${PROJROOT}\win32\redist"
!include "FileFunc.nsh"
!insertmacro GetParameters
!insertmacro GetOptionsS
; MUI 1.67 compatible ------
!include "MUI.nsh"
@ -224,6 +228,12 @@ NoProgramItems:
File "${ADMIN_ROOT}\config.xml"
File "${ADMIN_ROOT}\aspl-license.txt"
File "${ADMIN_ROOT}\aspl-license.html"
File "${ADMIN_ROOT}\zlib-license.txt"
File "${ADMIN_ROOT}\zlib-license.html"
File "${ADMIN_ROOT}\xiph-license.txt"
File "${ADMIN_ROOT}\xiph-license.html"
File "${ADMIN_ROOT}\apache-2.0.txt"
File "${ADMIN_ROOT}\apache-2.0.html"
File "${ADMIN_ROOT}\applet.html"
File "${ADMIN_ROOT}\spinner.gif"
File "${ADMIN_ROOT}\spinner_stopped.gif"
@ -334,9 +344,15 @@ Section -Post
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Run" "FireflyShell" '"$2\FireflyShell.exe" -q'
DeleteRegValue HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Run" "FireflyShell"
${GetParameters} $R0
ClearErrors
${GetOptionsS} $R0 "/N" $R1
IfErrors 0 +2 ; Skip the write if we have the /N option
WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Run" "FireflyShell" '"$2\FireflyShell.exe" -q'
; ExecWait "$2\mt-daapd.exe -i"
nsSCM::Install "${PRODUCT_NAME}" "${PRODUCT_SERVICE}" 16 2 "$2\firefly.exe" "" "Bonjour Service" "" ""
@ -348,9 +364,23 @@ Section -Post
MessageBox MB_OK "Error installing service: $1"
lbl_install_success:
ExecWait 'netsh firewall add allowedprogram "$2\firefly.exe" "${PRODUCT_NAME}" enable'
; ExecWait 'netsh firewall add allowedprogram "$2\firefly.exe" "${PRODUCT_NAME}" enable'
; Use the SimpleFC plugin
SimpleFC::IsFirewallServiceRunning
Pop $0
Pop $1
intcmp $1 0 lbl_no_fw ; if 0, then skip fw config
SimpleFC::AddApplication "Firefly Media Server" "$2\Firefly.exe" 0 2 "" 1
lbl_no_fw:
nsSCM::Start "${PRODUCT_NAME}"
${GetParameters} $R0
ClearErrors
${GetOptionsS} $R0 "/N" $R1
IfErrors 0 +2 ; Skip the run if we have the /N option
Exec '"$2\FireflyShell.exe" -q'
SectionEnd
@ -371,6 +401,7 @@ Function .onInit
StrCpy $INSTDIR "$PROGRAMFILES\${PRODUCT_NAME}"
StrCpy $2 "$PROGRAMFILES\${PRODUCT_NAME}"
StrCpy $3 "$DOCUMENTS\My Music"
IfSilent lbl_skip_lang
Push ""
Push ${LANG_ENGLISH}
@ -393,6 +424,9 @@ Function .onInit
Pop $LANGUAGE
StrCmp $LANGUAGE "cancel" 0 +2
Abort
lbl_skip_lang:
FunctionEnd
Function LicensePost
@ -505,7 +539,6 @@ Function GetParent
FunctionEnd
Function GetRoot
Exch $0
Push $1

View File

@ -13,3 +13,7 @@ echo Fixing version info...
%SUBWC% %0\..\.. %0\..\FireflyShell\version.h.templ %0\..\FireflyShell\version.h
%SUBWC% %0\..\.. %0\..\ssc-wma\ssc-wma.rc.templ %0\..\ssc-wma\ssc-wma.rc
%SUBWC% %0\..\.. %0\..\out-daap\out-daap.rc.templ %0\..\out-daap\out-daap.rc
if exist %0\..\do_sig.cmd.templ %SUBWC% %0\..\.. %0\..\do_sig.cmd.templ %0\..\do_sig.cmd