mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-24 06:05:56 -05:00
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:
parent
1a5a131bd1
commit
4b9b1fe177
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
33
configure.in
33
configure.in
@ -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,, [
|
||||
|
@ -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
715
src/bsd-snprintf.c
Normal 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
30
src/bsd-snprintf.h
Normal 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_ */
|
||||
|
94
src/conf.c
94
src/conf.c
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
195
src/configfile.c
195
src/configfile.c
@ -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");
|
||||
|
24
src/db-sql.c
24
src/db-sql.c
@ -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]);
|
||||
|
39
src/err.c
39
src/err.c
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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
372
src/io-driver.c
Normal 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
49
src/io-errors.h
Normal 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
79
src/io-plugin.h
Normal 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_ */
|
124
src/io.h
Normal file
124
src/io.h
Normal 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
11
src/io.mk
Normal 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)
|
60
src/main.c
60
src/main.c
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
@ -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 */
|
||||
|
99
src/plugin.c
99
src/plugin.c
@ -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 */
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
420
src/rend-posix.c
420
src/rend-posix.c
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
133
src/restart.c
133
src/restart.c
@ -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
|
||||
|
51
src/rxml.c
51
src/rxml.c
@ -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;
|
||||
|
129
src/scan-aac.c
129
src/scan-aac.c
@ -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*)¤t_size,1,sizeof(int),fin) != sizeof(int))
|
||||
while(current_offset < (uint64_t)atom_length) {
|
||||
bytes_read = sizeof(int);
|
||||
if(!io_read(hfile,(unsigned char *)¤t_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. */
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,¤t_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;
|
||||
}
|
||||
|
||||
|
109
src/scan-mp3.c
109
src/scan-mp3.c
@ -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;
|
||||
}
|
||||
|
||||
|
119
src/scan-mp4.c
119
src/scan-mp4.c
@ -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*)¤t_size,1,sizeof(int),fin) != sizeof(int))
|
||||
break;
|
||||
read_size = sizeof(uint32_t);
|
||||
if(!io_read(hfile,(unsigned char *)¤t_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 *)¤t_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*)¤t_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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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)) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
202
src/ssl.c
Normal 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
34
src/ssl.h
Normal 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_ */
|
@ -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]$'
|
1234
src/webserver.c
1234
src/webserver.c
File diff suppressed because it is too large
Load Diff
@ -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_ */
|
||||
|
13
src/win32.h
13
src/win32.h
@ -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
30
src/wsprivate.h
Normal 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
BIN
win32/nsi/AccessControl.dll
Normal file
Binary file not shown.
BIN
win32/nsi/SimpleFC.dll
Normal file
BIN
win32/nsi/SimpleFC.dll
Normal file
Binary file not shown.
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user