Lots of changes, but notably:
* MiniDLNA can now pass the DLNA Conformance Test! * Dependence on libdlna has been removed, and the ffmpeg libs are used directly. * Lots of unused code has been cleaned up. * File transfers will now be forked off into a new process, so as not to tie up the server when sending data.
This commit is contained in:
parent
9867def383
commit
74d73037d0
7
Makefile
7
Makefile
@ -12,7 +12,10 @@
|
||||
#
|
||||
#CFLAGS = -Wall -O -D_GNU_SOURCE -g -DDEBUG
|
||||
#CFLAGS = -Wall -g -Os -D_GNU_SOURCE
|
||||
CFLAGS = -Wall -g -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64
|
||||
CFLAGS = -Wall -g -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 \
|
||||
-I/usr/include/ffmpeg \
|
||||
-I/usr/include/libavutil -I/usr/include/libavcodec -I/usr/include/libavformat \
|
||||
-I/usr/include/ffmpeg/libavutil -I/usr/include/ffmpeg/libavcodec -I/usr/include/ffmpeg/libavformat
|
||||
CC = gcc
|
||||
RM = rm -f
|
||||
INSTALL = install
|
||||
@ -30,7 +33,7 @@ BASEOBJS = minidlna.o upnphttp.o upnpdescgen.o upnpsoap.o \
|
||||
ALLOBJS = $(BASEOBJS) $(LNXOBJS)
|
||||
|
||||
#LIBS = -liptc
|
||||
LIBS = -lexif -ltag_c -lsqlite3 -ldlna #-lgd
|
||||
LIBS = -lexif -ltag_c -lsqlite3 -lavformat -luuid #-lgd
|
||||
|
||||
TESTUPNPDESCGENOBJS = testupnpdescgen.o upnpdescgen.o
|
||||
|
||||
|
7
TODO
7
TODO
@ -1,13 +1,8 @@
|
||||
Things left to do:
|
||||
|
||||
* Clean up more leftover MiniUPnPd code
|
||||
* Spawn a new thread for new HTTP connections
|
||||
* Persistent HTTP connection (Keep-Alive) support
|
||||
* Real Chunked transfer support
|
||||
* PNG image support
|
||||
* DLNA Profile Name (DLNA.ORG_PN) support for video files
|
||||
* DLNA Profile Name (DLNA.ORG_PN) support for audio files besides MP3
|
||||
* Video metadata support
|
||||
* DLNA Profile Name (DLNA.ORG_PN) support for more audio files
|
||||
* SortCriteria support
|
||||
* Completely redo the logging scheme.
|
||||
* Update scan support (do not require removing the database)
|
||||
|
55
config.h
55
config.h
@ -1,55 +0,0 @@
|
||||
/* MiniUPnP Project
|
||||
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||
* (c) 2006-2008 Thomas Bernard
|
||||
* generated by ./genconfig.sh on Thu Sep 11 15:05:26 PDT 2008 */
|
||||
#ifndef __CONFIG_H__
|
||||
#define __CONFIG_H__
|
||||
|
||||
#define UPNP_VERSION "20070827"
|
||||
#define USE_NETFILTER 1
|
||||
#define OS_NAME "JM"
|
||||
#define OS_VERSION "Linux/2.6.25.14-108.fc9.i686"
|
||||
#define OS_URL "http://www.kernel.org/"
|
||||
|
||||
/* syslog facility to be used by miniupnpd */
|
||||
#define LOG_MINIUPNPD LOG_DAEMON
|
||||
|
||||
/* Uncomment the following line to allow miniupnpd to be
|
||||
* controlled by miniupnpdctl */
|
||||
/*#define USE_MINIUPNPDCTL*/
|
||||
|
||||
/* Comment the following line to disable NAT-PMP operations */
|
||||
//#define ENABLE_NATPMP
|
||||
|
||||
/* Uncomment the following line to enable generation of
|
||||
* filter rules with pf */
|
||||
/*#define PF_ENABLE_FILTER_RULES*/
|
||||
|
||||
/* Uncomment the following line to enable caching of results of
|
||||
* the getifstats() function */
|
||||
/*#define ENABLE_GETIFSTATS_CACHING*/
|
||||
/* The cache duration is indicated in seconds */
|
||||
#define GETIFSTATS_CACHING_DURATION 2
|
||||
|
||||
/* Uncomment the following line to enable multiple external ip support */
|
||||
/* note : Thas is EXPERIMENTAL, do not use that unless you know perfectly what you are doing */
|
||||
/*#define MULTIPLE_EXTERNAL_IP*/
|
||||
|
||||
/* Comment the following line to use home made daemonize() func instead
|
||||
* of BSD daemon() */
|
||||
#define USE_DAEMON
|
||||
|
||||
/* Uncomment the following line to enable lease file support */
|
||||
/*#define ENABLE_LEASEFILE*/
|
||||
|
||||
/* Define one or none of the two following macros in order to make some
|
||||
* clients happy. It will change the XML Root Description of the IGD.
|
||||
* Enabling the Layer3Forwarding Service seems to be the more compatible
|
||||
* option. */
|
||||
/*#define HAS_DUMMY_SERVICE*/
|
||||
#define ENABLE_L3F_SERVICE
|
||||
|
||||
/* Experimental UPnP Events support. */
|
||||
#define ENABLE_EVENTS
|
||||
|
||||
#endif
|
45
genconfig.sh
45
genconfig.sh
@ -13,7 +13,9 @@ CONFIGMACRO="__CONFIG_H__"
|
||||
# version reported in XML descriptions
|
||||
UPNP_VERSION=20070827
|
||||
# Facility to syslog
|
||||
LOG_MINIUPNPD="LOG_DAEMON"
|
||||
LOG_MINIDLNA="LOG_DAEMON"
|
||||
# Database path
|
||||
DB_PATH="/tmp/files.db"
|
||||
|
||||
# detecting the OS name and version
|
||||
OS_NAME=`uname -s`
|
||||
@ -52,7 +54,6 @@ case $OS_NAME in
|
||||
if [ \( $MAJORVER -ge 4 \) -o \( $MAJORVER -eq 3 -a $MINORVER -ge 8 \) ]; then
|
||||
echo "#define PFRULE_INOUT_COUNTS" >> ${CONFIGFILE}
|
||||
fi
|
||||
echo "#define USE_PF 1" >> ${CONFIGFILE}
|
||||
OS_URL=http://www.openbsd.org/
|
||||
;;
|
||||
FreeBSD)
|
||||
@ -60,25 +61,14 @@ case $OS_NAME in
|
||||
if [ $VER -ge 700049 ]; then
|
||||
echo "#define PFRULE_INOUT_COUNTS" >> ${CONFIGFILE}
|
||||
fi
|
||||
if [ -f /usr/include/net/pfvar.h ] ; then
|
||||
echo "#define USE_PF 1" >> ${CONFIGFILE}
|
||||
else
|
||||
echo "#define USE_IPF 1" >> ${CONFIGFILE}
|
||||
fi
|
||||
OS_URL=http://www.freebsd.org/
|
||||
;;
|
||||
pfSense)
|
||||
# we need to detect if PFRULE_INOUT_COUNTS macro is needed
|
||||
echo "#define USE_PF 1" >> ${CONFIGFILE}
|
||||
OS_URL=http://www.pfsense.com/
|
||||
;;
|
||||
NetBSD)
|
||||
OS_URL=http://www.netbsd.org/
|
||||
if [ -f /usr/include/net/pfvar.h ] ; then
|
||||
echo "#define USE_PF 1" >> ${CONFIGFILE}
|
||||
else
|
||||
echo "#define USE_IPF 1" >> ${CONFIGFILE}
|
||||
fi
|
||||
;;
|
||||
SunOS)
|
||||
echo "#define USE_IPF 1" >> ${CONFIGFILE}
|
||||
@ -104,7 +94,7 @@ case $OS_NAME in
|
||||
OS_URL=http://www.debian.org/
|
||||
fi
|
||||
# use lsb_release (Linux Standard Base) when available
|
||||
LSB_RELEASE=`which lsb_release`
|
||||
LSB_RELEASE=`which lsb_release 2>/dev/null`
|
||||
if [ 0 -eq $? ]; then
|
||||
OS_NAME=`${LSB_RELEASE} -i -s`
|
||||
OS_VERSION=`${LSB_RELEASE} -r -s`
|
||||
@ -123,8 +113,12 @@ echo "#define OS_VERSION \"$OS_NAME/$OS_VERSION\"" >> ${CONFIGFILE}
|
||||
echo "#define OS_URL \"${OS_URL}\"" >> ${CONFIGFILE}
|
||||
echo "" >> ${CONFIGFILE}
|
||||
|
||||
echo "/* full path of the file database */" >> ${CONFIGFILE}
|
||||
echo "#define DB_PATH \"${DB_PATH}\"" >> ${CONFIGFILE}
|
||||
echo "" >> ${CONFIGFILE}
|
||||
|
||||
echo "/* syslog facility to be used by miniupnpd */" >> ${CONFIGFILE}
|
||||
echo "#define LOG_MINIUPNPD ${LOG_MINIUPNPD}" >> ${CONFIGFILE}
|
||||
echo "#define LOG_MINIDLNA ${LOG_MINIDLNA}" >> ${CONFIGFILE}
|
||||
echo "" >> ${CONFIGFILE}
|
||||
|
||||
echo "/* Uncomment the following line to allow miniupnpd to be" >> ${CONFIGFILE}
|
||||
@ -132,15 +126,6 @@ echo " * controlled by miniupnpdctl */" >> ${CONFIGFILE}
|
||||
echo "/*#define USE_MINIUPNPDCTL*/" >> ${CONFIGFILE}
|
||||
echo "" >> ${CONFIGFILE}
|
||||
|
||||
echo "/* Comment the following line to disable NAT-PMP operations */" >> ${CONFIGFILE}
|
||||
echo "#define ENABLE_NATPMP" >> ${CONFIGFILE}
|
||||
echo "" >> ${CONFIGFILE}
|
||||
|
||||
echo "/* Uncomment the following line to enable generation of" >> ${CONFIGFILE}
|
||||
echo " * filter rules with pf */" >> ${CONFIGFILE}
|
||||
echo "/*#define PF_ENABLE_FILTER_RULES*/">> ${CONFIGFILE}
|
||||
echo "" >> ${CONFIGFILE}
|
||||
|
||||
echo "/* Uncomment the following line to enable caching of results of" >> ${CONFIGFILE}
|
||||
echo " * the getifstats() function */" >> ${CONFIGFILE}
|
||||
echo "/*#define ENABLE_GETIFSTATS_CACHING*/" >> ${CONFIGFILE}
|
||||
@ -162,16 +147,12 @@ echo "/* Uncomment the following line to enable lease file support */" >> ${CONF
|
||||
echo "/*#define ENABLE_LEASEFILE*/" >> ${CONFIGFILE}
|
||||
echo "" >> ${CONFIGFILE}
|
||||
|
||||
echo "/* Define one or none of the two following macros in order to make some" >> ${CONFIGFILE}
|
||||
echo " * clients happy. It will change the XML Root Description of the IGD." >> ${CONFIGFILE}
|
||||
echo " * Enabling the Layer3Forwarding Service seems to be the more compatible" >> ${CONFIGFILE}
|
||||
echo " * option. */" >> ${CONFIGFILE}
|
||||
echo "/*#define HAS_DUMMY_SERVICE*/" >> ${CONFIGFILE}
|
||||
echo "#define ENABLE_L3F_SERVICE" >> ${CONFIGFILE}
|
||||
echo "/* Experimental UPnP Events support. */" >> ${CONFIGFILE}
|
||||
echo "#define ENABLE_EVENTS" >> ${CONFIGFILE}
|
||||
echo "" >> ${CONFIGFILE}
|
||||
|
||||
echo "/* Experimental UPnP Events support. */" >> ${CONFIGFILE}
|
||||
echo "/*#define ENABLE_EVENTS*/" >> ${CONFIGFILE}
|
||||
echo "/* Enable NETGEAR ReadyNAS-specific tweaks. */" >> ${CONFIGFILE}
|
||||
echo "/*#define READYNAS*/" >> ${CONFIGFILE}
|
||||
echo "" >> ${CONFIGFILE}
|
||||
|
||||
echo "#endif" >> ${CONFIGFILE}
|
||||
|
40
getifaddr.c
40
getifaddr.c
@ -5,6 +5,8 @@
|
||||
* This software is subject to the conditions detailed
|
||||
* in the LICENCE file provided within the distribution */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
@ -53,3 +55,41 @@ getifaddr(const char * ifname, char * buf, int len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
getifhwaddr(const char * ifname, char * buf, int len)
|
||||
{
|
||||
/* SIOCGIFADDR struct ifreq * */
|
||||
int s;
|
||||
struct ifreq ifr;
|
||||
int ifrlen;
|
||||
unsigned char addr[6];
|
||||
char mac_string[4];
|
||||
int i;
|
||||
ifrlen = sizeof(ifr);
|
||||
if( len < 12 )
|
||||
{
|
||||
return -2;
|
||||
}
|
||||
s = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if(s < 0)
|
||||
{
|
||||
syslog(LOG_ERR, "socket(PF_INET, SOCK_DGRAM): %m");
|
||||
return -1;
|
||||
}
|
||||
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
|
||||
if(ioctl(s, SIOCGIFHWADDR, &ifr, &ifrlen) < 0)
|
||||
{
|
||||
syslog(LOG_ERR, "ioctl(s, SIOCGIFHWADDR, ...): %m");
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
close(s);
|
||||
|
||||
memmove( addr, ifr.ifr_hwaddr.sa_data, 6);
|
||||
for (i=0; i<6; ++i) {
|
||||
sprintf(mac_string, "%2.2x", addr[i]);
|
||||
strcat(buf, mac_string);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -14,5 +14,8 @@
|
||||
int
|
||||
getifaddr(const char * ifname, char * buf, int len);
|
||||
|
||||
int
|
||||
getifhwaddr(const char * ifname, char * buf, int len);
|
||||
|
||||
#endif
|
||||
|
||||
|
507
metadata.c
507
metadata.c
@ -15,20 +15,23 @@
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#define LIBDLNA_SUPPORT 1
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <sqlite3.h>
|
||||
#include <taglib/tag_c.h>
|
||||
#include <libexif/exif-loader.h>
|
||||
#if LIBDLNA_SUPPORT
|
||||
#include <dlna.h>
|
||||
#endif
|
||||
#include <avutil.h>
|
||||
#include <avcodec.h>
|
||||
#include <avformat.h>
|
||||
|
||||
#include "upnpglobalvars.h"
|
||||
#include "metadata.h"
|
||||
@ -36,6 +39,17 @@
|
||||
|
||||
#define FLAG_ARTIST 0x01
|
||||
|
||||
/* Audio profile flags */
|
||||
#define MP3 0x00000001
|
||||
#define AC3 0x00000002
|
||||
#define WMA_BASE 0x00000004
|
||||
#define WMA_FULL 0x00000008
|
||||
#define WMA_PRO 0x00000010
|
||||
#define MP2 0x00000020
|
||||
#define PCM 0x00000040
|
||||
#define AAC 0x00000100
|
||||
#define AAC_MULT5 0x00000200
|
||||
|
||||
int
|
||||
ends_with(const char * haystack, const char * needle)
|
||||
{
|
||||
@ -111,6 +125,40 @@ strip_ext(char * name)
|
||||
*rindex(name, '.') = '\0';
|
||||
}
|
||||
|
||||
/* This function shamelessly copied from libdlna */
|
||||
#define MPEG_TS_SYNC_CODE 0x47
|
||||
#define MPEG_TS_PACKET_LENGTH_DLNA 192 /* prepends 4 bytes to TS packet */
|
||||
int
|
||||
dlna_timestamp_is_present(const char * filename)
|
||||
{
|
||||
unsigned char buffer[2*MPEG_TS_PACKET_LENGTH_DLNA+1];
|
||||
int fd, i;
|
||||
|
||||
/* read file header */
|
||||
fd = open(filename, O_RDONLY);
|
||||
read(fd, buffer, MPEG_TS_PACKET_LENGTH_DLNA*2);
|
||||
close(fd);
|
||||
for( i=0; i < MPEG_TS_PACKET_LENGTH_DLNA; i++ )
|
||||
{
|
||||
if( buffer[i] == MPEG_TS_SYNC_CODE )
|
||||
{
|
||||
if (buffer[i + MPEG_TS_PACKET_LENGTH_DLNA] == MPEG_TS_SYNC_CODE)
|
||||
{
|
||||
if (buffer[i] == 0x00 && buffer [i+1] == 0x00 &&
|
||||
buffer [i+2] == 0x00 && buffer [i+3] == 0x00)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sqlite_int64
|
||||
GetFolderMetadata(const char * name, const char * artist)
|
||||
{
|
||||
@ -239,23 +287,33 @@ GetAudioMetadata(const char * path, char * name)
|
||||
}
|
||||
|
||||
|
||||
if( 1 ) // Switch on audio file type
|
||||
/* Switch on audio file type */
|
||||
if( ends_with(path, ".mp3") )
|
||||
{
|
||||
strcpy(dlna_pn, "MP3;DLNA.ORG_OP=01");
|
||||
strcpy(mime, "audio/mpeg");
|
||||
}
|
||||
else if( ends_with(path, ".flac") || ends_with(path, ".fla") || ends_with(path, ".flc") )
|
||||
{
|
||||
strcpy(mime, "audio/x-flac");
|
||||
}
|
||||
else if( ends_with(path, ".m4a") || ends_with(path, ".aac") )
|
||||
{
|
||||
//strcpy(dlna_pn, "MP3;DLNA.ORG_OP=01");
|
||||
strcpy(mime, "audio/mp4");
|
||||
}
|
||||
|
||||
sql = sqlite3_mprintf( "INSERT into DETAILS"
|
||||
" (SIZE, DURATION, CHANNELS, BITRATE, SAMPLERATE, DATE,"
|
||||
" TITLE, ARTIST, ALBUM, GENRE, COMMENT, TRACK, DLNA_PN, MIME) "
|
||||
" (PATH, SIZE, DURATION, CHANNELS, BITRATE, SAMPLERATE, DATE,"
|
||||
" TITLE, CREATOR, ARTIST, ALBUM, GENRE, COMMENT, TRACK, DLNA_PN, MIME) "
|
||||
"VALUES"
|
||||
" (%llu, '%s', %d, %d, %d, %Q, %Q, %Q, %Q, %Q, %Q, %d, '%s', '%s');",
|
||||
size, duration, taglib_audioproperties_channels(properties),
|
||||
" (%Q, %llu, '%s', %d, %d, %d, %Q, %Q, %Q, %Q, %Q, %Q, %Q, %d, '%s', '%s');",
|
||||
path, size, duration, taglib_audioproperties_channels(properties),
|
||||
taglib_audioproperties_bitrate(properties)*1024,
|
||||
taglib_audioproperties_samplerate(properties),
|
||||
strlen(date) ? date : NULL,
|
||||
title,
|
||||
artist,
|
||||
artist, artist,
|
||||
album,
|
||||
genre,
|
||||
comment,
|
||||
@ -388,10 +446,10 @@ GetImageMetadata(const char * path, char * name)
|
||||
asprintf(&m.resolution, "%dx%d", width, height);
|
||||
|
||||
sql = sqlite3_mprintf( "INSERT into DETAILS"
|
||||
" (TITLE, SIZE, DATE, RESOLUTION, THUMBNAIL, CREATOR, DLNA_PN, MIME) "
|
||||
" (PATH, TITLE, SIZE, DATE, RESOLUTION, THUMBNAIL, CREATOR, DLNA_PN, MIME) "
|
||||
"VALUES"
|
||||
" ('%q', %llu, '%s', %Q, %d, '%q', %Q, %Q);",
|
||||
name, size, date, m.resolution, thumb, model, m.dlna_pn, m.mime);
|
||||
" (%Q, '%q', %llu, '%s', %Q, %d, '%q', %Q, %Q);",
|
||||
path, name, size, date, m.resolution, thumb, model, m.dlna_pn, m.mime);
|
||||
//DEBUG printf("SQL: %s\n", sql);
|
||||
if( sqlite3_exec(db, sql, 0, 0, &zErrMsg) != SQLITE_OK )
|
||||
{
|
||||
@ -414,6 +472,36 @@ GetImageMetadata(const char * path, char * name)
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
AAC_INVALID = 0,
|
||||
AAC_MAIN = 1, /* AAC Main */
|
||||
AAC_LC = 2, /* AAC Low complexity */
|
||||
AAC_SSR = 3, /* AAC SSR */
|
||||
AAC_LTP = 4, /* AAC Long term prediction */
|
||||
AAC_HE = 5, /* AAC High efficiency (SBR) */
|
||||
AAC_SCALE = 6, /* Scalable */
|
||||
AAC_TWINVQ = 7, /* TwinVQ */
|
||||
AAC_CELP = 8, /* CELP */
|
||||
AAC_HVXC = 9, /* HVXC */
|
||||
AAC_TTSI = 12, /* TTSI */
|
||||
AAC_MS = 13, /* Main synthetic */
|
||||
AAC_WAVE = 14, /* Wavetable synthesis */
|
||||
AAC_MIDI = 15, /* General MIDI */
|
||||
AAC_FX = 16, /* Algorithmic Synthesis and Audio FX */
|
||||
AAC_LC_ER = 17, /* AAC Low complexity with error recovery */
|
||||
AAC_LTP_ER = 19, /* AAC Long term prediction with error recovery */
|
||||
AAC_SCALE_ER = 20, /* AAC scalable with error recovery */
|
||||
AAC_TWINVQ_ER = 21, /* TwinVQ with error recovery */
|
||||
AAC_BSAC_ER = 22, /* BSAC with error recovery */
|
||||
AAC_LD_ER = 23, /* AAC LD with error recovery */
|
||||
AAC_CELP_ER = 24, /* CELP with error recovery */
|
||||
AAC_HXVC_ER = 25, /* HXVC with error recovery */
|
||||
AAC_HILN_ER = 26, /* HILN with error recovery */
|
||||
AAC_PARAM_ER = 27, /* Parametric with error recovery */
|
||||
AAC_SSC = 28, /* AAC SSC */
|
||||
AAC_HE_L3 = 31, /* Reserved : seems to be HeAAC L3 */
|
||||
} aac_object_type_t;
|
||||
|
||||
sqlite_int64
|
||||
GetVideoMetadata(const char * path, char * name)
|
||||
{
|
||||
@ -421,78 +509,373 @@ GetVideoMetadata(const char * path, char * name)
|
||||
struct stat file;
|
||||
char *sql;
|
||||
char *zErrMsg = NULL;
|
||||
int ret;
|
||||
int ret, i;
|
||||
struct tm *modtime;
|
||||
char date[20];
|
||||
AVFormatContext *ctx;
|
||||
int audio_stream = -1, video_stream = -1;
|
||||
int audio_profile = 0;
|
||||
ts_timestamp_t ts_timestamp = NONE;
|
||||
int duration, hours, min, sec, ms;
|
||||
aac_object_type_t aac_type = 0;
|
||||
metadata_t m;
|
||||
memset(&m, '\0', sizeof(m));
|
||||
date[0] = '\0';
|
||||
|
||||
//DEBUG printf("Parsing %s...\n", path);
|
||||
if ( stat(path, &file) == 0 )
|
||||
{
|
||||
modtime = localtime(&file.st_mtime);
|
||||
strftime(date, sizeof(date), "%FT%T", modtime);
|
||||
size = file.st_size;
|
||||
}
|
||||
strip_ext(name);
|
||||
//DEBUG printf(" * size: %d\n", size);
|
||||
|
||||
#if LIBDLNA_SUPPORT
|
||||
dlna_t *dlna;
|
||||
dlna_profile_t *p;
|
||||
dlna_item_t *item;
|
||||
|
||||
dlna = dlna_init();
|
||||
dlna_register_all_media_profiles(dlna);
|
||||
|
||||
item = dlna_item_new(dlna, path);
|
||||
if (item)
|
||||
av_register_all();
|
||||
if( av_open_input_file(&ctx, path, NULL, 0, NULL) == 0 )
|
||||
{
|
||||
if (item->properties)
|
||||
av_find_stream_info(ctx);
|
||||
//dump_format(ctx, 0, NULL, 0);
|
||||
for( i=0; i<ctx->nb_streams; i++)
|
||||
{
|
||||
if( strlen(item->properties->duration) )
|
||||
m.duration = strdup(item->properties->duration);
|
||||
if( item->properties->bitrate )
|
||||
asprintf(&m.bitrate, "%d", item->properties->bitrate);
|
||||
if( item->properties->sample_frequency )
|
||||
asprintf(&m.frequency, "%d", item->properties->sample_frequency);
|
||||
if( item->properties->bps )
|
||||
asprintf(&m.bps, "%d", item->properties->bps);
|
||||
if( item->properties->channels )
|
||||
asprintf(&m.channels, "%d", item->properties->channels);
|
||||
m.resolution = item->properties->resolution;
|
||||
if( audio_stream == -1 &&
|
||||
ctx->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO )
|
||||
{
|
||||
audio_stream = i;
|
||||
continue;
|
||||
}
|
||||
else if( video_stream == -1 &&
|
||||
ctx->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO )
|
||||
{
|
||||
video_stream = i;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p = dlna_guess_media_profile(dlna, path);
|
||||
if (p)
|
||||
{
|
||||
m.mime = strdup(p->mime);
|
||||
asprintf(&m.dlna_pn, "%s;DLNA.ORG_OP=01;DLNA.ORG_CI=0", p->id);
|
||||
if( audio_stream >= 0 )
|
||||
{
|
||||
switch( ctx->streams[audio_stream]->codec->codec_id )
|
||||
{
|
||||
case CODEC_ID_MP3:
|
||||
audio_profile = MP3;
|
||||
break;
|
||||
case CODEC_ID_AAC:
|
||||
if( !ctx->streams[audio_stream]->codec->extradata_size ||
|
||||
!ctx->streams[audio_stream]->codec->extradata )
|
||||
printf("No AAC type\n");
|
||||
else
|
||||
aac_type = ctx->streams[audio_stream]->codec->extradata[0] >> 3;
|
||||
switch( aac_type )
|
||||
{
|
||||
/* AAC Low Complexity variants */
|
||||
case AAC_LC:
|
||||
case AAC_LC_ER:
|
||||
if( ctx->streams[audio_stream]->codec->sample_rate < 8000 ||
|
||||
ctx->streams[audio_stream]->codec->sample_rate > 48000 )
|
||||
{
|
||||
printf("Unsupported AAC: sample rate is not 8000 < %d < 48000\n",
|
||||
ctx->streams[audio_stream]->codec->sample_rate);
|
||||
break;
|
||||
}
|
||||
/* AAC @ Level 1/2 */
|
||||
if( ctx->streams[audio_stream]->codec->channels <= 2 &&
|
||||
ctx->streams[audio_stream]->codec->bit_rate <= 576000 )
|
||||
audio_profile = AAC;
|
||||
else if( ctx->streams[audio_stream]->codec->channels <= 6 &&
|
||||
ctx->streams[audio_stream]->codec->bit_rate <= 1440000 )
|
||||
audio_profile = AAC_MULT5;
|
||||
else
|
||||
printf("Unhandled AAC: %d channels, %d bitrate\n",
|
||||
ctx->streams[audio_stream]->codec->channels,
|
||||
ctx->streams[audio_stream]->codec->bit_rate);
|
||||
break;
|
||||
default:
|
||||
printf("Unhandled AAC type [%d]\n", aac_type);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case CODEC_ID_AC3:
|
||||
case CODEC_ID_DTS:
|
||||
audio_profile = AC3;
|
||||
break;
|
||||
case CODEC_ID_WMAV1:
|
||||
case CODEC_ID_WMAV2:
|
||||
/* WMA Baseline: stereo, up to 48 KHz, up to 192,999 bps */
|
||||
if ( ctx->streams[audio_stream]->codec->bit_rate <= 193000 )
|
||||
audio_profile = WMA_BASE;
|
||||
/* WMA Full: stereo, up to 48 KHz, up to 385 Kbps */
|
||||
else if ( ctx->streams[audio_stream]->codec->bit_rate <= 385000 )
|
||||
audio_profile = WMA_FULL;
|
||||
break;
|
||||
case CODEC_ID_WMAPRO:
|
||||
audio_profile = WMA_PRO;
|
||||
break;
|
||||
case CODEC_ID_MP2:
|
||||
audio_profile = MP2;
|
||||
break;
|
||||
default:
|
||||
if( (ctx->streams[audio_stream]->codec->codec_id >= CODEC_ID_PCM_S16LE) &&
|
||||
(ctx->streams[audio_stream]->codec->codec_id <= CODEC_ID_PCM_F64LE) )
|
||||
audio_profile = PCM;
|
||||
else
|
||||
printf("Unhandled audio codec [%X]\n", ctx->streams[audio_stream]->codec->codec_id);
|
||||
break;
|
||||
}
|
||||
asprintf(&m.frequency, "%u", ctx->streams[audio_stream]->codec->sample_rate);
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 52
|
||||
asprintf(&m.bps, "%u", ctx->streams[audio_stream]->codec->bits_per_coded_sample);
|
||||
#else
|
||||
asprintf(&m.bps, "%u", ctx->streams[audio_stream]->codec->bits_per_sample);
|
||||
#endif
|
||||
asprintf(&m.channels, "%u", ctx->streams[audio_stream]->codec->channels);
|
||||
}
|
||||
if( video_stream >= 0 )
|
||||
{
|
||||
//DEBUG printf("Container: '%s' [%s]\n", ctx->iformat->name, path);
|
||||
asprintf(&m.resolution, "%dx%d", ctx->streams[video_stream]->codec->width, ctx->streams[video_stream]->codec->height);
|
||||
asprintf(&m.bitrate, "%u", ctx->bit_rate / 8);
|
||||
if( ctx->duration > 0 ) {
|
||||
duration = (int)(ctx->duration / AV_TIME_BASE);
|
||||
hours = (int)(duration / 3600);
|
||||
min = (int)(duration / 60 % 60);
|
||||
sec = (int)(duration % 60);
|
||||
ms = (int)(ctx->duration / (AV_TIME_BASE/1000) % 1000);
|
||||
asprintf(&m.duration, "%d:%02d:%02d.%03d", hours, min, sec, ms);
|
||||
}
|
||||
/* NOTE: The DLNA spec only provides for ASF (WMV), TS, PS, and MP4 containers -- not AVI. */
|
||||
switch( ctx->streams[video_stream]->codec->codec_id )
|
||||
{
|
||||
case CODEC_ID_MPEG1VIDEO:
|
||||
if( strcmp(ctx->iformat->name, "mpeg") == 0 )
|
||||
{
|
||||
asprintf(&m.dlna_pn, "MPEG1;DLNA.ORG_OP=01;DLNA.ORG_CI=0");
|
||||
asprintf(&m.mime, "video/mpeg");
|
||||
}
|
||||
break;
|
||||
case CODEC_ID_MPEG2VIDEO:
|
||||
if( strcmp(ctx->iformat->name, "mpegts") == 0 )
|
||||
{
|
||||
printf("Stream %d of %s is %s MPEG2 TS\n", video_stream, path, m.resolution);
|
||||
char res;
|
||||
tsinfo_t * ts = ctx->priv_data;
|
||||
if( ts->packet_size == 192 )
|
||||
{
|
||||
asprintf(&m.dlna_pn, "MPEG_TS_HD_NA;DLNA.ORG_OP=01;DLNA.ORG_CI=0");
|
||||
asprintf(&m.mime, "video/vnd.dlna.mpeg-tts");
|
||||
}
|
||||
else if( ts->packet_size == 188 )
|
||||
{
|
||||
if( (ctx->streams[video_stream]->codec->width >= 1280) &&
|
||||
(ctx->streams[video_stream]->codec->height >= 720) )
|
||||
res = 'H';
|
||||
else
|
||||
res = 'S';
|
||||
asprintf(&m.dlna_pn, "MPEG_TS_%cD_NA_ISO;DLNA.ORG_OP=01;DLNA.ORG_CI=0", res);
|
||||
asprintf(&m.mime, "video/mpeg");
|
||||
}
|
||||
}
|
||||
else if( strcmp(ctx->iformat->name, "mpeg") == 0 )
|
||||
{
|
||||
printf("Stream %d of %s is %s MPEG2 PS\n", video_stream, path, m.resolution);
|
||||
char region[5];
|
||||
if( (ctx->streams[video_stream]->codec->height == 576) ||
|
||||
(ctx->streams[video_stream]->codec->height == 288) )
|
||||
strcpy(region, "PAL");
|
||||
else
|
||||
strcpy(region, "NTSC");
|
||||
asprintf(&m.dlna_pn, "MPEG_PS_%s;DLNA.ORG_OP=01;DLNA.ORG_CI=0", region);
|
||||
asprintf(&m.mime, "video/mpeg");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Stream %d of %s [UNKNOWN CONTAINER] is %s MPEG2\n", video_stream, path, m.resolution);
|
||||
}
|
||||
break;
|
||||
case CODEC_ID_H264:
|
||||
if( strcmp(ctx->iformat->name, "mpegts") == 0 )
|
||||
{
|
||||
tsinfo_t * ts = ctx->priv_data;
|
||||
if( ts->packet_size == 192 )
|
||||
{
|
||||
if( dlna_timestamp_is_present(path) )
|
||||
ts_timestamp = VALID;
|
||||
else
|
||||
ts_timestamp = EMPTY;
|
||||
}
|
||||
char res = '\0';
|
||||
if( ctx->streams[video_stream]->codec->width <= 720 &&
|
||||
ctx->streams[video_stream]->codec->height <= 576 &&
|
||||
ctx->streams[video_stream]->codec->bit_rate <= 10000000 )
|
||||
res = 'S';
|
||||
else if( ctx->streams[video_stream]->codec->width <= 1920 &&
|
||||
ctx->streams[video_stream]->codec->height <= 1152 &&
|
||||
ctx->streams[video_stream]->codec->bit_rate <= 20000000 )
|
||||
res = 'H';
|
||||
if( res )
|
||||
{
|
||||
switch( audio_profile )
|
||||
{
|
||||
case MP3:
|
||||
asprintf(&m.dlna_pn, "AVC_TS_MP_HD_MPEG1_L3%s;DLNA.ORG_OP=01;DLNA.ORG_CI=0",
|
||||
ts_timestamp==NONE?"_ISO" : ts_timestamp==VALID?"_T":"");
|
||||
break;
|
||||
case AC3:
|
||||
asprintf(&m.dlna_pn, "AVC_TS_MP_HD_AC3%s;DLNA.ORG_OP=01;DLNA.ORG_CI=0",
|
||||
ts_timestamp==NONE?"_ISO" : ts_timestamp==VALID?"_T":"");
|
||||
break;
|
||||
case AAC_MULT5:
|
||||
asprintf(&m.dlna_pn, "AVC_TS_MP_HD_AAC_MULT5%s;DLNA.ORG_OP=01;DLNA.ORG_CI=0",
|
||||
ts_timestamp==NONE?"_ISO" : ts_timestamp==VALID?"_T":"");
|
||||
break;
|
||||
default:
|
||||
printf("No DLNA profile found for TS/AVC/%cD file %s\n", res, path);
|
||||
break;
|
||||
}
|
||||
if( m.dlna_pn && (ts_timestamp != NONE) )
|
||||
asprintf(&m.mime, "video/vnd.dlna.mpeg-tts");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Unsupported h.264 video profile! [%dx%d, %dbps]\n",
|
||||
ctx->streams[video_stream]->codec->width,
|
||||
ctx->streams[video_stream]->codec->height,
|
||||
ctx->streams[video_stream]->codec->bit_rate);
|
||||
}
|
||||
}
|
||||
else if( strcmp(ctx->iformat->name, "mov,mp4,m4a,3gp,3g2,mj2") == 0 )
|
||||
{
|
||||
/* AVC wrapped in MP4 only has SD profiles - 10 Mbps max */
|
||||
if( ctx->streams[video_stream]->codec->width <= 720 &&
|
||||
ctx->streams[video_stream]->codec->height <= 576 &&
|
||||
ctx->streams[video_stream]->codec->bit_rate <= 10000000 )
|
||||
{
|
||||
switch( audio_profile )
|
||||
{
|
||||
case AC3:
|
||||
asprintf(&m.dlna_pn, "AVC_MP4_MP_SD_AC3;DLNA.ORG_OP=01;DLNA.ORG_CI=0");
|
||||
break;
|
||||
case AAC_MULT5:
|
||||
asprintf(&m.dlna_pn, "AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=01;DLNA.ORG_CI=0");
|
||||
break;
|
||||
default:
|
||||
printf("No DLNA profile found for MP4/AVC/SD file %s\n", path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("Stream %d of %s is h.264\n", video_stream, path);
|
||||
break;
|
||||
case CODEC_ID_MPEG4:
|
||||
if( ctx->streams[video_stream]->codec->codec_tag == ff_get_fourcc("XVID") )
|
||||
{
|
||||
printf("Stream %d of %s is %s XViD\n", video_stream, path, m.resolution);
|
||||
asprintf(&m.mime, "video/divx");
|
||||
}
|
||||
else if( ctx->streams[video_stream]->codec->codec_tag == ff_get_fourcc("DX50") )
|
||||
{
|
||||
printf("Stream %d of %s is %s DiVX5\n", video_stream, path, m.resolution);
|
||||
asprintf(&m.mime, "video/divx");
|
||||
}
|
||||
else if( ctx->streams[video_stream]->codec->codec_tag == ff_get_fourcc("DIVX") )
|
||||
{
|
||||
printf("Stream %d of %s is DiVX\n", video_stream, path);
|
||||
asprintf(&m.mime, "video/divx");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Stream %d of %s is MPEG4 [%X]\n", video_stream, path, ctx->streams[video_stream]->codec->codec_tag);
|
||||
}
|
||||
break;
|
||||
case CODEC_ID_WMV3:
|
||||
case CODEC_ID_VC1:
|
||||
printf("Stream %d of %s is VC1\n", video_stream, path);
|
||||
char profile[5]; profile[0] = '\0';
|
||||
asprintf(&m.mime, "video/x-ms-wmv");
|
||||
if( (ctx->streams[video_stream]->codec->width <= 352) &&
|
||||
(ctx->streams[video_stream]->codec->height <= 288) &&
|
||||
(ctx->bit_rate/8 <= 384000) )
|
||||
{
|
||||
if( audio_profile == MP3 )
|
||||
strcpy(profile, "MP3");
|
||||
else if( audio_profile == WMA_BASE )
|
||||
strcpy(profile, "BASE");
|
||||
asprintf(&m.dlna_pn, "WMVSPML_%s;DLNA.ORG_OP=01;DLNA.ORG_CI=0", profile);
|
||||
}
|
||||
else if( (ctx->streams[video_stream]->codec->width <= 720) &&
|
||||
(ctx->streams[video_stream]->codec->height <= 576) &&
|
||||
(ctx->bit_rate/8 <= 10000000) )
|
||||
{
|
||||
if( audio_profile == WMA_PRO )
|
||||
strcpy(profile, "PRO");
|
||||
else if( audio_profile == WMA_FULL )
|
||||
strcpy(profile, "FULL");
|
||||
else if( audio_profile == WMA_BASE )
|
||||
strcpy(profile, "BASE");
|
||||
asprintf(&m.dlna_pn, "WMVMED_%s;DLNA.ORG_OP=01;DLNA.ORG_CI=0", profile);
|
||||
}
|
||||
else if( (ctx->streams[video_stream]->codec->width <= 1920) &&
|
||||
(ctx->streams[video_stream]->codec->height <= 1080) &&
|
||||
(ctx->bit_rate/8 <= 20000000) )
|
||||
{
|
||||
if( audio_profile == WMA_FULL )
|
||||
strcpy(profile, "FULL");
|
||||
else if( audio_profile == WMA_PRO )
|
||||
strcpy(profile, "PRO");
|
||||
asprintf(&m.dlna_pn, "WMVHIGH_%s;DLNA.ORG_OP=01;DLNA.ORG_CI=0", profile);
|
||||
}
|
||||
break;
|
||||
case CODEC_ID_XVID:
|
||||
printf("Stream %d of %s is %s UNKNOWN XVID\n", video_stream, path, m.resolution);
|
||||
break;
|
||||
case CODEC_ID_MSMPEG4V1:
|
||||
printf("Stream %d of %s is %s MS MPEG4 v1\n", video_stream, path, m.resolution);
|
||||
case CODEC_ID_MSMPEG4V3:
|
||||
printf("Stream %d of %s is %s MS MPEG4 v3\n", video_stream, path, m.resolution);
|
||||
asprintf(&m.mime, "video/avi");
|
||||
break;
|
||||
case CODEC_ID_H263I:
|
||||
printf("Stream %d of %s is h.263i\n", video_stream, path);
|
||||
break;
|
||||
case CODEC_ID_MJPEG:
|
||||
printf("Stream %d of %s is MJPEG\n", video_stream, path);
|
||||
break;
|
||||
default:
|
||||
printf("Stream %d of %s is %d\n", video_stream, path, ctx->streams[video_stream]->codec->codec_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( !m.mime )
|
||||
{
|
||||
if( strcmp(ctx->iformat->name, "avi") == 0 )
|
||||
asprintf(&m.mime, "video/x-msvideo");
|
||||
else if( strcmp(ctx->iformat->name, "mpegts") == 0 )
|
||||
asprintf(&m.mime, "video/mpeg");
|
||||
else if( strcmp(ctx->iformat->name, "mpeg") == 0 )
|
||||
asprintf(&m.mime, "video/mpeg");
|
||||
else if( strcmp(ctx->iformat->name, "asf") == 0 )
|
||||
asprintf(&m.mime, "video/x-ms-wmv");
|
||||
else if( strcmp(ctx->iformat->name, "mov,mp4,m4a,3gp,3g2,mj2") == 0 )
|
||||
asprintf(&m.mime, "video/mp4");
|
||||
}
|
||||
av_close_input_file(ctx);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("Unknown format [%s]\n", path);
|
||||
if( ends_with(path, ".mpg") || ends_with(path, ".mpeg") || ends_with(path, ".ts") )
|
||||
asprintf(&m.mime, "video/mpeg");
|
||||
else if( ends_with(path, ".avi") || ends_with(path, ".divx") )
|
||||
asprintf(&m.mime, "video/divx");
|
||||
printf("Opening %s failed!\n", path);
|
||||
}
|
||||
|
||||
sql = sqlite3_mprintf( "INSERT into DETAILS"
|
||||
" (SIZE, DURATION, CHANNELS, BITRATE, SAMPLERATE, RESOLUTION,"
|
||||
" (PATH, SIZE, DURATION, DATE, CHANNELS, BITRATE, SAMPLERATE, RESOLUTION,"
|
||||
" TITLE, DLNA_PN, MIME) "
|
||||
"VALUES"
|
||||
" (%lld, %Q, %Q, %Q, %Q, %Q, '%q', %Q, '%q');",
|
||||
size, m.duration,
|
||||
" (%Q, %lld, %Q, %Q, %Q, %Q, %Q, %Q, '%q', %Q, '%q');",
|
||||
path, size, m.duration,
|
||||
strlen(date) ? date : NULL,
|
||||
m.channels,
|
||||
m.bitrate,
|
||||
m.frequency,
|
||||
m.resolution,
|
||||
name, m.dlna_pn, m.mime);
|
||||
dlna_item_free(item);
|
||||
dlna_uninit(dlna);
|
||||
#else // LIBDLNA_SUPPORT
|
||||
sql = sqlite3_mprintf( "INSERT into DETAILS"
|
||||
" (TITLE, SIZE, MIME) "
|
||||
"VALUES"
|
||||
" ('%q', %d, %Q);",
|
||||
name, size, "video/mpeg");*/
|
||||
#endif // LIBDLNA_SUPPORT
|
||||
//DEBUG printf("SQL: %s\n", sql);
|
||||
if( sqlite3_exec(db, sql, 0, 0, &zErrMsg) != SQLITE_OK )
|
||||
{
|
||||
@ -512,6 +895,8 @@ GetVideoMetadata(const char * path, char * name)
|
||||
free(m.mime);
|
||||
if( m.duration )
|
||||
free(m.duration);
|
||||
if( m.resolution )
|
||||
free(m.resolution);
|
||||
if( m.bitrate )
|
||||
free(m.bitrate);
|
||||
if( m.frequency )
|
||||
|
11
metadata.h
11
metadata.h
@ -26,6 +26,17 @@ typedef struct metadata_s {
|
||||
char *dlna_pn;
|
||||
} metadata_t;
|
||||
|
||||
typedef struct tsinfo_s {
|
||||
int x;
|
||||
int packet_size;
|
||||
} tsinfo_t;
|
||||
|
||||
typedef enum {
|
||||
NONE,
|
||||
EMPTY,
|
||||
VALID
|
||||
} ts_timestamp_t;
|
||||
|
||||
int
|
||||
ends_with(const char * haystack, const char * needle);
|
||||
|
||||
|
138
minidlna.c
138
minidlna.c
@ -208,6 +208,29 @@ parselanaddr(struct lan_addr_s * lan_addr, const char * str)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
getfriendlyname(char * buf, int len)
|
||||
{
|
||||
char * hn = calloc(1, 256);
|
||||
if( gethostname(hn, 256) == 0 )
|
||||
{
|
||||
strncpy(buf, hn, len-1);
|
||||
buf[len] = '\0';
|
||||
*strstr(buf, ".") = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy(buf, "Unknown");
|
||||
}
|
||||
free(hn);
|
||||
strcat(buf, ": ");
|
||||
#ifdef READYNAS
|
||||
strncat(buf, "ReadyNAS", len-strlen(buf)-1);
|
||||
#else
|
||||
strncat(buf, getenv("LOGNAME"), len-strlen(buf)-1);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* init phase :
|
||||
* 1) read configuration file
|
||||
* 2) read command line arguments
|
||||
@ -229,6 +252,7 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
||||
/*const char * logfilename = 0;*/
|
||||
const char * presurl = 0;
|
||||
const char * optionsfile = "/etc/minidlna.conf";
|
||||
char * mac_str = calloc(1, 64);
|
||||
|
||||
/* first check if "-f" option is used */
|
||||
for(i=2; i<argc; i++)
|
||||
@ -241,6 +265,19 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
||||
}
|
||||
}
|
||||
|
||||
/* set up uuid based on mac address */
|
||||
if( (getifhwaddr("eth0", mac_str, 64) < 0) &&
|
||||
(getifhwaddr("eth1", mac_str, 64) < 0) )
|
||||
{
|
||||
printf("No MAC addresses found!\n");
|
||||
strcpy(mac_str, "554e4b4e4f57");
|
||||
}
|
||||
strcpy(uuidvalue+5, "4d696e69-444c-164e-9d41-");
|
||||
strncat(uuidvalue, mac_str, 12);
|
||||
free(mac_str);
|
||||
|
||||
getfriendlyname(friendly_name, FRIENDLYNAME_MAX_LEN);
|
||||
|
||||
/*v->n_lan_addr = 0;*/
|
||||
char ext_ip_addr[INET_ADDRSTRLEN];
|
||||
if( (getifaddr("eth0", ext_ip_addr, INET_ADDRSTRLEN) < 0) &&
|
||||
@ -270,12 +307,6 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
||||
{
|
||||
switch(ary_options[i].id)
|
||||
{
|
||||
case UPNPEXT_IFNAME:
|
||||
ext_if_name = ary_options[i].value;
|
||||
break;
|
||||
case UPNPEXT_IP:
|
||||
use_ext_ip_addr = ary_options[i].value;
|
||||
break;
|
||||
case UPNPLISTENING_IP:
|
||||
if(n_lan_addr < MAX_LAN_ADDR)/* if(v->n_lan_addr < MAX_LAN_ADDR)*/
|
||||
{
|
||||
@ -313,10 +344,6 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
||||
if(strcmp(ary_options[i].value, "yes") == 0)
|
||||
SETFLAG(LOGPACKETSMASK); /*logpackets = 1;*/
|
||||
break;
|
||||
case UPNPUUID:
|
||||
strncpy(uuidvalue+5, ary_options[i].value,
|
||||
strlen(uuidvalue+5) + 1);
|
||||
break;
|
||||
case UPNPSERIAL:
|
||||
strncpy(serialnumber, ary_options[i].value, SERIALNUMBER_MAX_LEN);
|
||||
serialnumber[SERIALNUMBER_MAX_LEN-1] = '\0';
|
||||
@ -331,20 +358,6 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
||||
case UPNPCLEANINTERVAL:
|
||||
v->clean_ruleset_interval = atoi(ary_options[i].value);
|
||||
break;
|
||||
#ifdef USE_PF
|
||||
case UPNPQUEUE:
|
||||
queue = ary_options[i].value;
|
||||
break;
|
||||
case UPNPTAG:
|
||||
tag = ary_options[i].value;
|
||||
break;
|
||||
#endif
|
||||
#ifdef PF_ENABLE_FILTER_RULES
|
||||
case UPNPQUICKRULES:
|
||||
if(strcmp(ary_options[i].value, "no") == 0)
|
||||
SETFLAG(PFNOQUICKRULESMASK);
|
||||
break;
|
||||
#endif
|
||||
case UPNPSECUREMODE:
|
||||
if(strcmp(ary_options[i].value, "yes") == 0)
|
||||
SETFLAG(SECUREMODEMASK);
|
||||
@ -355,6 +368,10 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
||||
remove(lease_file);
|
||||
break;
|
||||
#endif
|
||||
case UPNPFRIENDLYNAME:
|
||||
strncpy(friendly_name, ary_options[i].value, FRIENDLYNAME_MAX_LEN);
|
||||
friendly_name[FRIENDLYNAME_MAX_LEN-1] = '\0';
|
||||
break;
|
||||
case UPNPMEDIADIR:
|
||||
strncpy(media_dir, ary_options[i].value, MEDIADIR_MAX_LEN);
|
||||
media_dir[MEDIADIR_MAX_LEN-1] = '\0';
|
||||
@ -375,24 +392,12 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
||||
}
|
||||
else switch(argv[i][1])
|
||||
{
|
||||
case 'o':
|
||||
if(i+1 < argc)
|
||||
use_ext_ip_addr = argv[++i];
|
||||
else
|
||||
fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
|
||||
break;
|
||||
case 't':
|
||||
if(i+1 < argc)
|
||||
v->notify_interval = atoi(argv[++i]);
|
||||
else
|
||||
fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
|
||||
break;
|
||||
case 'u':
|
||||
if(i+1 < argc)
|
||||
strncpy(uuidvalue+5, argv[++i], strlen(uuidvalue+5) + 1);
|
||||
else
|
||||
fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
|
||||
break;
|
||||
case 's':
|
||||
if(i+1 < argc)
|
||||
strncpy(serialnumber, argv[++i], SERIALNUMBER_MAX_LEN);
|
||||
@ -421,26 +426,6 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
||||
case 'S':
|
||||
SETFLAG(SECUREMODEMASK);
|
||||
break;
|
||||
case 'i':
|
||||
if(i+1 < argc)
|
||||
ext_if_name = argv[++i];
|
||||
else
|
||||
fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
|
||||
break;
|
||||
#ifdef USE_PF
|
||||
case 'q':
|
||||
if(i+1 < argc)
|
||||
queue = argv[++i];
|
||||
else
|
||||
fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
|
||||
break;
|
||||
case 'T':
|
||||
if(i+1 < argc)
|
||||
tag = argv[++i];
|
||||
else
|
||||
fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
|
||||
break;
|
||||
#endif
|
||||
case 'p':
|
||||
if(i+1 < argc)
|
||||
v->port = atoi(argv[++i]);
|
||||
@ -510,19 +495,15 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
||||
fprintf(stderr, "Unknown option: %s\n", argv[i]);
|
||||
}
|
||||
}
|
||||
if(!ext_if_name || (/*v->*/n_lan_addr==0) || v->port<=0)
|
||||
if( (/*v->*/n_lan_addr==0) || (v->port<=0) )
|
||||
{
|
||||
fprintf(stderr, "Usage:\n\t"
|
||||
"%s [-f config_file] [-i ext_ifname] [-o ext_ip]\n"
|
||||
"\t\t[-a listening_ip] [-p port] [-d] [-L] [-U] [-S]\n"
|
||||
/*"[-l logfile] " not functionnal */
|
||||
"\t\t[-u uuid] [-s serial] [-m model_number] \n"
|
||||
"\t\t[-s serial] [-m model_number] \n"
|
||||
"\t\t[-t notify_interval] [-P pid_filename]\n"
|
||||
#ifdef USE_PF
|
||||
"\t\t[-B down up] [-w url] [-q queue] [-T tag]\n"
|
||||
#else
|
||||
"\t\t[-B down up] [-w url]\n"
|
||||
#endif
|
||||
"\nNotes:\n\tThere can be one or several listening_ips.\n"
|
||||
"\tNotify interval is in seconds. Default is 30 seconds.\n"
|
||||
"\tDefault pid file is %s.\n"
|
||||
@ -533,10 +514,6 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
||||
"of daemon uptime.\n"
|
||||
"\t-B sets bitrates reported by daemon in bits per second.\n"
|
||||
"\t-w sets the presentation url. Default is http address on port 80\n"
|
||||
#ifdef USE_PF
|
||||
"\t-q sets the ALTQ queue in pf.\n"
|
||||
"\t-T sets the tag name in pf.\n"
|
||||
#endif
|
||||
"", argv[0], pidfilename);
|
||||
return 1;
|
||||
}
|
||||
@ -563,7 +540,7 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
||||
openlog_option |= LOG_PERROR; /* also log on stderr */
|
||||
}
|
||||
|
||||
openlog("miniupnpd", openlog_option, LOG_MINIUPNPD);
|
||||
openlog("minidlna", openlog_option, LOG_MINIDLNA);
|
||||
|
||||
if(!debug_flag)
|
||||
{
|
||||
@ -573,7 +550,7 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
||||
|
||||
if(checkforrunning(pidfilename) < 0)
|
||||
{
|
||||
syslog(LOG_ERR, "MiniUPnPd is already running. EXITING");
|
||||
syslog(LOG_ERR, "MiniDLNA is already running. EXITING");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -594,9 +571,9 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
||||
}
|
||||
|
||||
/* set signal handler */
|
||||
signal(SIGCLD, SIG_IGN);
|
||||
memset(&sa, 0, sizeof(struct sigaction));
|
||||
sa.sa_handler = sigterm;
|
||||
|
||||
if (sigaction(SIGTERM, &sa, NULL))
|
||||
{
|
||||
syslog(LOG_ERR, "Failed to set %s handler. EXITING", "SIGTERM");
|
||||
@ -641,14 +618,31 @@ main(int argc, char * * argv)
|
||||
|
||||
LIST_INIT(&upnphttphead);
|
||||
|
||||
if( access("/tmp/files.db", F_OK) )
|
||||
if( access(DB_PATH, F_OK) )
|
||||
{
|
||||
sqlite3_open("/tmp/files.db", &db);
|
||||
sqlite3_open(DB_PATH, &db);
|
||||
freopen("/dev/null", "a", stderr);
|
||||
ScanDirectory(media_dir, NULL);
|
||||
freopen("/proc/self/fd/2", "a", stderr);
|
||||
}
|
||||
else
|
||||
{
|
||||
sqlite3_open("/tmp/files.db", &db);
|
||||
char **result;
|
||||
int rows;
|
||||
sqlite3_open(DB_PATH, &db);
|
||||
if( sqlite3_get_table(db, "pragma user_version", &result, &rows, 0, 0) == SQLITE_OK )
|
||||
{
|
||||
if( atoi(result[1]) != DB_VERSION ) {
|
||||
printf("Database version mismatch; need to recreate...\n");
|
||||
sqlite3_close(db);
|
||||
unlink(DB_PATH);
|
||||
sqlite3_open(DB_PATH, &db);
|
||||
freopen("/dev/null", "a", stderr);
|
||||
ScanDirectory(media_dir, NULL);
|
||||
freopen("/proc/self/fd/2", "a", stderr);
|
||||
}
|
||||
sqlite3_free_table(result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,6 +1,3 @@
|
||||
# WAN network interface
|
||||
ext_ifname=eth0
|
||||
|
||||
# port for HTTP (descriptions and SOAP) traffic
|
||||
port=5555
|
||||
|
||||
@ -31,8 +28,6 @@ system_uptime=no
|
||||
notify_interval=900
|
||||
|
||||
clean_ruleset_interval=600
|
||||
# uuid : generate your own with "make genuuid"
|
||||
uuid=fc4ec57e-b051-11db-88f8-0060085db3f6
|
||||
|
||||
# serial and model number the daemon will report to clients
|
||||
# in its XML description
|
||||
|
@ -133,15 +133,15 @@ OpenAndConfSSDPNotifySocket(in_addr_t addr)
|
||||
}
|
||||
|
||||
memset(&sockname, 0, sizeof(struct sockaddr_in));
|
||||
sockname.sin_family = AF_INET;
|
||||
sockname.sin_addr.s_addr = addr; /*inet_addr(addr);*/
|
||||
sockname.sin_family = AF_INET;
|
||||
sockname.sin_addr.s_addr = addr; /*inet_addr(addr);*/
|
||||
|
||||
if (bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
|
||||
if (bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
|
||||
{
|
||||
syslog(LOG_ERR, "bind(udp_notify): %m");
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
@ -13,18 +13,6 @@
|
||||
|
||||
#define ROOTDESC_PATH "/rootDesc.xml"
|
||||
|
||||
#ifdef HAS_DUMMY_SERVICE
|
||||
#define DUMMY_PATH "/dummy.xml"
|
||||
#endif
|
||||
|
||||
#define WANCFG_PATH "/WANCfg.xml"
|
||||
#define WANCFG_CONTROLURL "/ctl/CmnIfCfg"
|
||||
#define WANCFG_EVENTURL "/evt/CmnIfCfg"
|
||||
|
||||
#define WANIPC_PATH "/WANIPCn.xml"
|
||||
#define WANIPC_CONTROLURL "/ctl/IPConn"
|
||||
#define WANIPC_EVENTURL "/evt/IPConn"
|
||||
|
||||
#define CONTENTDIRECTORY_PATH "/ContentDir.xml"
|
||||
#define CONTENTDIRECTORY_CONTROLURL "/ctl/ContentDir"
|
||||
#define CONTENTDIRECTORY_EVENTURL "/evt/ContentDir"
|
||||
@ -37,11 +25,5 @@
|
||||
#define X_MS_MEDIARECEIVERREGISTRAR_CONTROLURL "/ctl/X_MS_MediaReceiverRegistrar"
|
||||
#define X_MS_MEDIARECEIVERREGISTRAR_EVENTURL "/evt/X_MS_MediaReceiverRegistrar"
|
||||
|
||||
#ifdef ENABLE_L3F_SERVICE
|
||||
#define L3F_PATH "/L3F.xml"
|
||||
#define L3F_CONTROLURL "/ctl/L3F"
|
||||
#define L3F_EVENTURL "/evt/L3F"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
11
options.c
11
options.c
@ -36,20 +36,11 @@ static const struct {
|
||||
{ UPNPMODEL_NUMBER, "model_number"},
|
||||
{ UPNPCLEANTHRESHOLD, "clean_ruleset_threshold"},
|
||||
{ UPNPCLEANINTERVAL, "clean_ruleset_interval"},
|
||||
#ifdef ENABLE_NATPMP
|
||||
{ UPNPENABLENATPMP, "enable_natpmp"},
|
||||
#endif
|
||||
{ UPNPENABLE, "enable_upnp"},
|
||||
#ifdef USE_PF
|
||||
{ UPNPQUEUE, "queue"},
|
||||
{ UPNPTAG, "tag"},
|
||||
#endif
|
||||
#ifdef PF_ENABLE_FILTER_RULES
|
||||
{ UPNPQUICKRULES, "quickrules" },
|
||||
#endif
|
||||
#ifdef ENABLE_LEASEFILE
|
||||
{ UPNPLEASEFILE, "lease_file"},
|
||||
#endif
|
||||
{ UPNPFRIENDLYNAME, "friendly_name"},
|
||||
{ UPNPMEDIADIR, "media_dir"},
|
||||
{ UPNPSECUREMODE, "secure_mode"}
|
||||
};
|
||||
|
@ -29,17 +29,11 @@ enum upnpconfigoptions {
|
||||
UPNPCLEANTHRESHOLD, /* clean_ruleset_threshold */
|
||||
UPNPCLEANINTERVAL, /* clean_ruleset_interval */
|
||||
UPNPENABLENATPMP, /* enable_natpmp */
|
||||
#ifdef USE_PF
|
||||
UPNPQUEUE, /* queue */
|
||||
UPNPTAG, /* tag */
|
||||
#endif
|
||||
#ifdef PF_ENABLE_FILTER_RULES
|
||||
UPNPQUICKRULES, /* quickrules */
|
||||
#endif
|
||||
UPNPSECUREMODE, /* secure_mode */
|
||||
#ifdef ENABLE_LEASEFILE
|
||||
UPNPLEASEFILE, /* lease_file */
|
||||
#endif
|
||||
UPNPFRIENDLYNAME, /* how the system should show up to DLNA clients */
|
||||
UPNPMEDIADIR, /* directory to search for UPnP-A/V content */
|
||||
UPNPENABLE /* enable_upnp */
|
||||
};
|
||||
|
108
scanner.c
108
scanner.c
@ -33,14 +33,19 @@
|
||||
int
|
||||
is_video(const char * file)
|
||||
{
|
||||
return (ends_with(file, ".mpg") || ends_with(file, ".mpeg") ||
|
||||
ends_with(file, ".ts") || ends_with(file, ".avi"));
|
||||
return (ends_with(file, ".mpg") || ends_with(file, ".mpeg") ||
|
||||
ends_with(file, ".asf") || ends_with(file, ".wmv") ||
|
||||
ends_with(file, ".mp4") || ends_with(file, ".m4v") ||
|
||||
ends_with(file, ".mts") || ends_with(file, ".m2ts") ||
|
||||
ends_with(file, ".vob") || ends_with(file, ".ts") ||
|
||||
ends_with(file, ".avi") || ends_with(file, ".xvid"));
|
||||
}
|
||||
|
||||
int
|
||||
is_audio(const char * file)
|
||||
{
|
||||
return (ends_with(file, ".mp3") ||
|
||||
return (ends_with(file, ".mp3") || ends_with(file, ".flac") ||
|
||||
ends_with(file, ".fla") || ends_with(file, ".flc") ||
|
||||
ends_with(file, ".m4a") || ends_with(file, ".aac"));
|
||||
}
|
||||
|
||||
@ -124,21 +129,27 @@ insert_containers(const char * name, const char *path, const char * refID, const
|
||||
|
||||
if( strstr(class, "imageItem") )
|
||||
{
|
||||
char *date = result[12+cols], *cam = result[16+cols];
|
||||
char *date = result[13+cols], *cam = result[16+cols];
|
||||
char date_taken[11]; date_taken[10] = '\0';
|
||||
static int last_all_objectID = 0;
|
||||
if( date )
|
||||
strncpy(date_taken, date, 10);
|
||||
if( date )
|
||||
{
|
||||
if( strcmp(date, "0000-00-00") == 0 )
|
||||
{
|
||||
strcpy(date_taken, "Unknown");
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(date_taken, date, 10);
|
||||
}
|
||||
container = insert_container("DATES", date_taken, "3$12", NULL, "album.photoAlbum", NULL);
|
||||
parentID = container>>32;
|
||||
objectID = container;
|
||||
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
||||
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, PATH, NAME) "
|
||||
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
|
||||
"VALUES"
|
||||
" ('3$12$%X$%X', '3$12$%X', '%s', '%s', %lu, %Q, %Q)",
|
||||
parentID, objectID, parentID, refID, class, detailID, path, name);
|
||||
" ('3$12$%X$%X', '3$12$%X', '%s', '%s', %lu, %Q)",
|
||||
parentID, objectID, parentID, refID, class, detailID, name);
|
||||
sql_exec(db, sql);
|
||||
sqlite3_free(sql);
|
||||
}
|
||||
@ -153,26 +164,26 @@ insert_containers(const char * name, const char *path, const char * refID, const
|
||||
int subParentID = subcontainer>>32;
|
||||
int subObjectID = subcontainer;
|
||||
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
||||
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, PATH, NAME) "
|
||||
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
|
||||
"VALUES"
|
||||
" ('3$13$%X$%X$%X', '3$13$%X$%X', '%s', '%s', %lu, %Q, %Q)",
|
||||
parentID, subParentID, subObjectID, parentID, subParentID, refID, class, detailID, path, name);
|
||||
" ('3$13$%X$%X$%X', '3$13$%X$%X', '%s', '%s', %lu, %Q)",
|
||||
parentID, subParentID, subObjectID, parentID, subParentID, refID, class, detailID, name);
|
||||
sql_exec(db, sql);
|
||||
sqlite3_free(sql);
|
||||
}
|
||||
/* All Images */
|
||||
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
||||
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, PATH, NAME) "
|
||||
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
|
||||
"VALUES"
|
||||
" ('3$11$%X', '3$11', '%s', '%s', %lu, %Q, %Q)",
|
||||
last_all_objectID++, refID, class, detailID, path, name);
|
||||
" ('3$11$%X', '3$11', '%s', '%s', %lu, %Q)",
|
||||
last_all_objectID++, refID, class, detailID, name);
|
||||
sql_exec(db, sql);
|
||||
sqlite3_free(sql);
|
||||
}
|
||||
else if( strstr(class, "audioItem") )
|
||||
{
|
||||
char *artist = result[6+cols], *album = result[7+cols], *genre = result[8+cols];
|
||||
static char last_artist[1024];
|
||||
char *artist = cols ? result[7+cols]:NULL, *album = cols ? result[8+cols]:NULL, *genre = cols ? result[9+cols]:NULL;
|
||||
static char last_artist[1024] = "0";
|
||||
static int last_artist_parentID, last_artist_objectID;
|
||||
static char last_album[1024];
|
||||
static int last_album_parentID, last_album_objectID;
|
||||
@ -197,10 +208,10 @@ insert_containers(const char * name, const char *path, const char * refID, const
|
||||
last_artist_parentID = parentID;
|
||||
}
|
||||
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
||||
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, PATH, NAME) "
|
||||
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
|
||||
"VALUES"
|
||||
" ('1$6$%X$%X', '1$6$%X', '%s', '%s', %lu, %Q, %Q)",
|
||||
parentID, objectID, parentID, refID, class, detailID, path, name);
|
||||
" ('1$6$%X$%X', '1$6$%X', '%s', '%s', %lu, %Q)",
|
||||
parentID, objectID, parentID, refID, class, detailID, name);
|
||||
sql_exec(db, sql);
|
||||
sqlite3_free(sql);
|
||||
}
|
||||
@ -221,10 +232,10 @@ insert_containers(const char * name, const char *path, const char * refID, const
|
||||
last_album_parentID = parentID;
|
||||
}
|
||||
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
||||
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, PATH, NAME) "
|
||||
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
|
||||
"VALUES"
|
||||
" ('1$7$%X$%X', '1$7$%X', '%s', '%s', %lu, %Q, %Q)",
|
||||
parentID, objectID, parentID, refID, class, detailID, path, name);
|
||||
" ('1$7$%X$%X', '1$7$%X', '%s', '%s', %lu, %Q)",
|
||||
parentID, objectID, parentID, refID, class, detailID, name);
|
||||
sql_exec(db, sql);
|
||||
sqlite3_free(sql);
|
||||
}
|
||||
@ -245,19 +256,19 @@ insert_containers(const char * name, const char *path, const char * refID, const
|
||||
last_genre_parentID = parentID;
|
||||
}
|
||||
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
||||
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, PATH, NAME) "
|
||||
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
|
||||
"VALUES"
|
||||
" ('1$5$%X$%X', '1$5$%X', '%s', '%s', %lu, %Q, %Q)",
|
||||
parentID, objectID, parentID, refID, class, detailID, path, name);
|
||||
" ('1$5$%X$%X', '1$5$%X', '%s', '%s', %lu, %Q)",
|
||||
parentID, objectID, parentID, refID, class, detailID, name);
|
||||
sql_exec(db, sql);
|
||||
sqlite3_free(sql);
|
||||
}
|
||||
/* All Music */
|
||||
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
||||
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, PATH, NAME) "
|
||||
" (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
|
||||
"VALUES"
|
||||
" ('1$4$%X', '1$4', '%s', '%s', %lu, %Q, %Q)",
|
||||
last_all_objectID++, refID, class, detailID, path, name);
|
||||
" ('1$4$%X', '1$4', '%s', '%s', %lu, %Q)",
|
||||
last_all_objectID++, refID, class, detailID, name);
|
||||
sql_exec(db, sql);
|
||||
sqlite3_free(sql);
|
||||
}
|
||||
@ -278,10 +289,10 @@ insert_directory(const char * name, const char * path, const char * parentID, in
|
||||
for( i=0; base[i]; i++ )
|
||||
{
|
||||
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
||||
" (OBJECT_ID, PARENT_ID, REF_ID, DETAIL_ID, CLASS, PATH, NAME) "
|
||||
" (OBJECT_ID, PARENT_ID, REF_ID, DETAIL_ID, CLASS, NAME) "
|
||||
"VALUES"
|
||||
" ('%s%s$%X', '%s%s', %Q, '%lld', '%s', '%q', '%q')",
|
||||
base[i], parentID, objectID, base[i], parentID, refID, detailID, class, path, name);
|
||||
" ('%s%s$%X', '%s%s', %Q, '%lld', '%s', '%q')",
|
||||
base[i], parentID, objectID, base[i], parentID, refID, detailID, class, name);
|
||||
//DEBUG printf("SQL: %s\n", sql);
|
||||
ret = sql_exec(db, sql);
|
||||
sqlite3_free(sql);
|
||||
@ -327,12 +338,14 @@ insert_file(char * name, const char * path, const char * parentID, int object)
|
||||
detailID = GetVideoMetadata(path, name);
|
||||
}
|
||||
//DEBUG printf("Got DetailID %lu!\n", detailID);
|
||||
if( !detailID )
|
||||
return -1;
|
||||
|
||||
sql = sqlite3_mprintf( "INSERT into OBJECTS"
|
||||
" (OBJECT_ID, PARENT_ID, CLASS, DETAIL_ID, PATH, NAME) "
|
||||
" (OBJECT_ID, PARENT_ID, CLASS, DETAIL_ID, NAME) "
|
||||
"VALUES"
|
||||
" ('%s', '%s%s', '%s', %lu, '%q', '%q')",
|
||||
objectID, BROWSEDIR_ID, parentID, class, detailID, path, name);
|
||||
" ('%s', '%s%s', '%s', %lu, '%q')",
|
||||
objectID, BROWSEDIR_ID, parentID, class, detailID, name);
|
||||
//DEBUG printf("SQL: %s\n", sql);
|
||||
sql_exec(db, sql);
|
||||
sqlite3_free(sql);
|
||||
@ -378,6 +391,10 @@ create_database(void)
|
||||
sql_exec(db, "pragma synchronous = OFF;");
|
||||
sql_exec(db, "pragma cache_size = 8192;");
|
||||
|
||||
//JM: Set up a db version number, so we know if we need to rebuild due to a new structure.
|
||||
sprintf(sql_buf, "pragma user_version = %d;", DB_VERSION);
|
||||
sql_exec(db, sql_buf);
|
||||
|
||||
ret = sql_exec(db, "CREATE TABLE OBJECTS ( "
|
||||
"ID INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
"OBJECT_ID TEXT NOT NULL, "
|
||||
@ -385,13 +402,13 @@ create_database(void)
|
||||
"REF_ID TEXT DEFAULT NULL, "
|
||||
"CLASS TEXT NOT NULL, "
|
||||
"DETAIL_ID INTEGER DEFAULT NULL, "
|
||||
"PATH TEXT DEFAULT NULL, "
|
||||
"NAME TEXT DEFAULT NULL"
|
||||
");");
|
||||
if( ret != SQLITE_OK )
|
||||
goto sql_failed;
|
||||
ret = sql_exec(db, "CREATE TABLE DETAILS ( "
|
||||
"ID INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
"PATH TEXT DEFAULT NULL, "
|
||||
"SIZE INTEGER, "
|
||||
"TITLE TEXT, "
|
||||
"DURATION TEXT, "
|
||||
@ -433,7 +450,7 @@ create_database(void)
|
||||
sql_exec(db, "create INDEX IDX_OBJECTS_PARENT_ID ON OBJECTS(PARENT_ID);");
|
||||
sql_exec(db, "create INDEX IDX_OBJECTS_DETAIL_ID ON OBJECTS(DETAIL_ID);");
|
||||
sql_exec(db, "create INDEX IDX_OBJECTS_CLASS ON OBJECTS(CLASS);");
|
||||
sql_exec(db, "create INDEX IDX_OBJECTS_PATH ON OBJECTS(PATH);");
|
||||
sql_exec(db, "create INDEX IDX_DETAILS_PATH ON DETAILS(PATH);");
|
||||
sql_exec(db, "create INDEX IDX_DETAILS_ID ON DETAILS(ID);");
|
||||
|
||||
|
||||
@ -467,6 +484,9 @@ ScanDirectory(const char * dir, const char * parent)
|
||||
char parent_id[PATH_MAX];
|
||||
char full_path[PATH_MAX];
|
||||
char * name;
|
||||
#if USE_FORK
|
||||
pid_t newpid;
|
||||
#endif
|
||||
|
||||
if( !parent )
|
||||
{
|
||||
@ -475,12 +495,17 @@ ScanDirectory(const char * dir, const char * parent)
|
||||
fprintf(stderr, "Error creating database!\n");
|
||||
return;
|
||||
}
|
||||
#if USE_FORK
|
||||
newpid = fork();
|
||||
if( newpid )
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
setlocale(LC_COLLATE, "");
|
||||
if( chdir(dir) != 0 )
|
||||
return;
|
||||
printf("\nScanning %s\n", dir);
|
||||
printf("\nScanning %s\n", dir);
|
||||
n = scandir(".", &namelist, filter_media, alphasort);
|
||||
if (n < 0) {
|
||||
fprintf(stderr, "Error scanning %s [scandir]\n", dir);
|
||||
@ -512,4 +537,11 @@ printf("\nScanning %s\n", dir);
|
||||
}
|
||||
free(namelist);
|
||||
chdir("..");
|
||||
if( !parent )
|
||||
{
|
||||
printf("Scanning \"%s\" finished!\n", dir);
|
||||
#if USE_FORK
|
||||
_exit(0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -13,12 +13,12 @@
|
||||
#include "upnpdescgen.h"
|
||||
|
||||
char uuidvalue[] = "uuid:12345678-0000-0000-0000-00000000abcd";
|
||||
char friendly_name[] = "localhost: system_type";
|
||||
char serialnumber[] = "12345678";
|
||||
char modelnumber[] = "1";
|
||||
char presentationurl[] = "http://192.168.0.1:8080/";
|
||||
|
||||
char * use_ext_ip_addr = NULL;
|
||||
const char * ext_if_name = "eth0";
|
||||
|
||||
int getifaddr(const char * ifname, char * buf, int len)
|
||||
{
|
||||
|
@ -85,10 +85,14 @@ static const char * const upnpallowedvalues[] =
|
||||
"STOPPED",
|
||||
0,
|
||||
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN," /* 44 */
|
||||
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=01,"
|
||||
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED,"
|
||||
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=01,"
|
||||
"http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC,"
|
||||
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
|
||||
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
|
||||
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
|
||||
"http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
|
||||
"http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
|
||||
"http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_HD_NA_ISO;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
|
||||
"http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AC3_T;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
|
||||
"http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
|
||||
"http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01,"
|
||||
"http-get:*:audio/x-ms-wma:*,"
|
||||
"http-get:*:audio/wav:*,"
|
||||
@ -129,7 +133,7 @@ static const struct XMLElt rootDesc[] =
|
||||
{"/major", "1"},
|
||||
{"/minor", "0"},
|
||||
{"/deviceType", "urn:schemas-upnp-org:device:MediaServer:1"},
|
||||
{"/friendlyName", ROOTDEV_FRIENDLYNAME}, /* required */
|
||||
{"/friendlyName", friendly_name}, /* required */
|
||||
{"/manufacturer", ROOTDEV_MANUFACTURER}, /* required */
|
||||
{"/manufacturerURL", ROOTDEV_MANUFACTURERURL}, /* optional */
|
||||
{"/modelDescription", ROOTDEV_MODELDESCRIPTION}, /* recommended */
|
||||
@ -175,12 +179,6 @@ static const struct argument AddPortMappingArgs[] =
|
||||
{NULL, 0, 0}
|
||||
};
|
||||
|
||||
static const struct argument GetExternalIPAddressArgs[] =
|
||||
{
|
||||
{NULL, 2, 7},
|
||||
{NULL, 0, 0}
|
||||
};
|
||||
|
||||
static const struct argument DeletePortMappingArgs[] =
|
||||
{
|
||||
{NULL, 1, 11},
|
||||
@ -202,14 +200,6 @@ static const struct argument GetConnectionTypeInfoArgs[] =
|
||||
{NULL, 0, 0}
|
||||
};
|
||||
|
||||
static const struct argument GetStatusInfoArgs[] =
|
||||
{
|
||||
{NULL, 2, 2},
|
||||
{NULL, 2, 4},
|
||||
{NULL, 2, 3},
|
||||
{NULL, 0, 0}
|
||||
};
|
||||
|
||||
static const struct argument GetNATRSIPStatusArgs[] =
|
||||
{
|
||||
{NULL, 2, 5},
|
||||
@ -494,71 +484,6 @@ static const struct argument GetTotalPacketsReceivedArgs[] =
|
||||
{NULL, 0, 0}
|
||||
};
|
||||
|
||||
static const struct action WANCfgActions[] =
|
||||
{
|
||||
{"GetCommonLinkProperties", GetCommonLinkPropertiesArgs}, /* Required */
|
||||
{"GetTotalBytesSent", GetTotalBytesSentArgs}, /* optional */
|
||||
{"GetTotalBytesReceived", GetTotalBytesReceivedArgs}, /* optional */
|
||||
{"GetTotalPacketsSent", GetTotalPacketsSentArgs}, /* optional */
|
||||
{"GetTotalPacketsReceived", GetTotalPacketsReceivedArgs}, /* optional */
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
/* See UPnP_IGD_WANCommonInterfaceConfig 1.0.pdf */
|
||||
static const struct stateVar WANCfgVars[] =
|
||||
{
|
||||
{"WANAccessType", 0, 0, 1},
|
||||
/* Allowed Values : DSL / POTS / Cable / Ethernet
|
||||
* Default value : empty string */
|
||||
{"Layer1UpstreamMaxBitRate", 3, 0},
|
||||
{"Layer1DownstreamMaxBitRate", 3, 0},
|
||||
{"PhysicalLinkStatus", 0|0x80, 0, 6, 6},
|
||||
/* allowed values :
|
||||
* Up / Down / Initializing (optional) / Unavailable (optionnal)
|
||||
* no Default value
|
||||
* Evented */
|
||||
{"TotalBytesSent", 3, 0}, /* Optional */
|
||||
{"TotalBytesReceived", 3, 0}, /* Optional */
|
||||
{"TotalPacketsSent", 3, 0}, /* Optional */
|
||||
{"TotalPacketsReceived", 3, 0},/* Optional */
|
||||
/*{"MaximumActiveConnections", 2, 0}, // allowed Range value // OPTIONAL */
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
static const struct serviceDesc scpdWANCfg =
|
||||
{ WANCfgActions, WANCfgVars };
|
||||
|
||||
#ifdef ENABLE_L3F_SERVICE
|
||||
/* Read UPnP_IGD_Layer3Forwarding_1.0.pdf */
|
||||
static const struct argument SetDefaultConnectionServiceArgs[] =
|
||||
{
|
||||
{NULL, 1, 0}, /* in */
|
||||
{NULL, 0, 0}
|
||||
};
|
||||
|
||||
static const struct argument GetDefaultConnectionServiceArgs[] =
|
||||
{
|
||||
{NULL, 2, 0}, /* out */
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
static const struct action L3FActions[] =
|
||||
{
|
||||
{"SetDefaultConnectionService", SetDefaultConnectionServiceArgs}, /* Req */
|
||||
{"GetDefaultConnectionService", GetDefaultConnectionServiceArgs}, /* Req */
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
static const struct stateVar L3FVars[] =
|
||||
{
|
||||
{"DefaultConnectionService", 0|0x80, 0, 0, 255}, /* Required */
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
static const struct serviceDesc scpdL3F =
|
||||
{ L3FActions, L3FVars };
|
||||
#endif
|
||||
|
||||
static const struct serviceDesc scpdContentDirectory =
|
||||
{ ContentDirectoryActions, ContentDirectoryVars };
|
||||
//{ ContentDirectoryActions, ContentDirectoryVars };
|
||||
@ -709,9 +634,7 @@ genRootDesc(int * len)
|
||||
char *ret = calloc(1, 8192);
|
||||
sprintf(ret, "<?xml version='1.0' encoding='UTF-8' ?>\r\n"
|
||||
"<root xmlns=\"urn:schemas-upnp-org:device-1-0\" xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\"><specVersion><major>1</major><minor>0</minor></specVersion><device><deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType><friendlyName>MiniDLNA (MaggardMachine2)</friendlyName><manufacturer>NETGEAR</manufacturer><manufacturerURL>http://www.netgear.com</manufacturerURL><modelDescription>NETGEAR ReadyNAS NV</modelDescription><modelName>ReadyNAS</modelName><modelNumber>NV</modelNumber><modelURL>http://www.netgear.com</modelURL><UDN>uuid:aefc3d94-8cf7-11dd-b3bb-ff0d6f9a7e6d</UDN><dlna:X_DLNADOC>DMS-1.50</dlna:X_DLNADOC>\r\n"
|
||||
//"<root xmlns=\"urn:schemas-upnp-org:device-1-0\" xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\"><specVersion><major>1</major><minor>0</minor></specVersion><device><deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType><friendlyName>MiniDLNA (MaggardMachine2)</friendlyName><manufacturer>NETGEAR</manufacturer><manufacturerURL>http://www.netgear.com</manufacturerURL><modelDescription>NETGEAR ReadyNAS NV</modelDescription><modelName>ReadyNAS</modelName><modelNumber>NV</modelNumber><modelURL>http://www.netgear.com</modelURL><UDN>uuid:aefc3d94-8cf7-11dd-b3bb-ff0d6f9a7e6d</UDN><dlna:X_DLNACAP>av-upload,image-upload,create-child-container,audio-upload</dlna:X_DLNACAP><dlna:X_DLNADOC>DMS-1.50</dlna:X_DLNADOC>\r\n"
|
||||
"<serviceList><service><serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType><serviceId>urn:upnp-org:serviceId:ConnectionManager</serviceId><SCPDURL>/ConnectionMgr.xml</SCPDURL><controlURL>/ctl/ConnectionMgr</controlURL><eventSubURL>/evt/ConnectionMgr</eventSubURL></service><service><serviceType>urn:schemas-upnp-org:service:ContentDirectory:1</serviceType><serviceId>urn:upnp-org:serviceId:ContentDirectory</serviceId><SCPDURL>/ContentDir.xml</SCPDURL><controlURL>/ctl/ContentDir</controlURL><eventSubURL>/evt/ContentDir</eventSubURL></service></serviceList></device></root>");
|
||||
//"</iconList><serviceList><service><serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType><serviceId>urn:upnp-org:serviceId:ConnectionManager</serviceId><SCPDURL>/ConnectionMgr.xml</SCPDURL><controlURL>/ctl/ConnectionMgr</controlURL><eventSubURL>/evt/ConnectionMgr</eventSubURL></service><service><serviceType>urn:schemas-upnp-org:service:ContentDirectory:1</serviceType><serviceId>urn:upnp-org:serviceId:ContentDirectory</serviceId><SCPDURL>/ContentDir.xml</SCPDURL><controlURL>/ctl/ContentDir</controlURL><eventSubURL>/evt/ContentDir</eventSubURL></service><service><serviceType>urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1</serviceType><serviceId>763f907c-8cfb-11dd-a382-c9c0ad9eae41</serviceId><SCPDURL>/upnp/aefc3d94-8cf7-11dd-b3bb-ff0d6f9a7e6d/aefdc437-8cf7-11dd-b3bb-ff0d6f9a7e6d</SCPDURL><controlURL>/upnp/aefc3d94-8cf7-11dd-b3bb-ff0d6f9a7e6d/control/aefdc437-8cf7-11dd-b3bb-ff0d6f9a7e6d</controlURL><eventSubURL>/upnp/aefc3d94-8cf7-11dd-b3bb-ff0d6f9a7e6d/events/aefdc437-8cf7-11dd-b3bb-ff0d6f9a7e6d</eventSubURL></service></serviceList></device></root>\r\n");
|
||||
* len = strlen(ret);
|
||||
return ret;
|
||||
#endif
|
||||
|
@ -68,11 +68,6 @@ genWANIPCn(int * len);
|
||||
char *
|
||||
genWANCfg(int * len);
|
||||
|
||||
#ifdef ENABLE_L3F_SERVICE
|
||||
char *
|
||||
genL3F(int * len);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_EVENTS
|
||||
char *
|
||||
getVarsContentDirectory(int * len);
|
||||
@ -81,13 +76,7 @@ char *
|
||||
getVarsConnectionManager(int * len);
|
||||
|
||||
char *
|
||||
getVarsWANIPCn(int * len);
|
||||
|
||||
char *
|
||||
getVarsWANCfg(int * len);
|
||||
|
||||
char *
|
||||
getVarsL3F(int * len);
|
||||
getVarsX_MS_MediaReceiverRegistrar(int * len);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -9,30 +9,14 @@
|
||||
#include "config.h"
|
||||
|
||||
/* strings used in the root device xml description */
|
||||
#define ROOTDEV_FRIENDLYNAME "MiniDLNA ReadyNAS:"
|
||||
#ifdef READYNAS
|
||||
#define ROOTDEV_MANUFACTURER "NETGEAR"
|
||||
#else
|
||||
#define ROOTDEV_MANUFACTURER "Justin Maggard"
|
||||
#endif
|
||||
#define ROOTDEV_MANUFACTURERURL OS_URL
|
||||
#define ROOTDEV_MODELNAME "Windows Media Connect compatible (minidlna)"
|
||||
#define ROOTDEV_MODELDESCRIPTION OS_NAME " *ReadyNAS dev DLNA"
|
||||
#define ROOTDEV_MODELURL OS_URL
|
||||
|
||||
#define WANDEV_FRIENDLYNAME "WANDevice"
|
||||
#define WANDEV_MANUFACTURER "MiniUPnP"
|
||||
#define WANDEV_MANUFACTURERURL "http://miniupnp.free.fr/"
|
||||
#define WANDEV_MODELNAME "WAN Device"
|
||||
#define WANDEV_MODELDESCRIPTION "WAN Device"
|
||||
#define WANDEV_MODELNUMBER UPNP_VERSION
|
||||
#define WANDEV_MODELURL "http://miniupnp.free.fr/"
|
||||
#define WANDEV_UPC "MINIUPNPD"
|
||||
|
||||
#define WANCDEV_FRIENDLYNAME "WANConnectionDevice"
|
||||
#define WANCDEV_MANUFACTURER WANDEV_MANUFACTURER
|
||||
#define WANCDEV_MANUFACTURERURL WANDEV_MANUFACTURERURL
|
||||
#define WANCDEV_MODELNAME "MiniUPnPd"
|
||||
#define WANCDEV_MODELDESCRIPTION "MiniUPnP daemon"
|
||||
#define WANCDEV_MODELNUMBER UPNP_VERSION
|
||||
#define WANCDEV_MODELURL "http://miniupnp.free.fr/"
|
||||
#define WANCDEV_UPC "MINIUPNPD"
|
||||
|
||||
#endif
|
||||
|
||||
|
32
upnpevents.c
32
upnpevents.c
@ -24,6 +24,12 @@
|
||||
#include "upnpglobalvars.h"
|
||||
#include "upnpdescgen.h"
|
||||
|
||||
#define HAVE_UUID 1
|
||||
|
||||
#ifdef HAVE_UUID
|
||||
#include <uuid/uuid.h>
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_EVENTS
|
||||
/*enum subscriber_service_enum {
|
||||
EWanCFG = 1,
|
||||
@ -80,18 +86,12 @@ newSubscriber(const char * eventurl, const char * callback, int callbacklen)
|
||||
if(!eventurl || !callback || !callbacklen)
|
||||
return NULL;
|
||||
tmp = calloc(1, sizeof(struct subscriber)+callbacklen+1);
|
||||
if(strcmp(eventurl, WANCFG_EVENTURL)==0)
|
||||
tmp->service = EWanCFG;
|
||||
else if(strcmp(eventurl, CONTENTDIRECTORY_EVENTURL)==0)
|
||||
if(strcmp(eventurl, CONTENTDIRECTORY_EVENTURL)==0)
|
||||
tmp->service = EContentDirectory;
|
||||
else if(strcmp(eventurl, CONNECTIONMGR_EVENTURL)==0)
|
||||
tmp->service = EConnectionManager;
|
||||
else if(strcmp(eventurl, WANIPC_EVENTURL)==0)
|
||||
tmp->service = EWanIPC;
|
||||
#ifdef ENABLE_L3F_SERVICE
|
||||
else if(strcmp(eventurl, L3F_EVENTURL)==0)
|
||||
tmp->service = EL3F;
|
||||
#endif
|
||||
else if(strcmp(eventurl, X_MS_MEDIARECEIVERREGISTRAR_EVENTURL)==0)
|
||||
tmp->service = EMSMediaReceiverRegistrar;
|
||||
else {
|
||||
free(tmp);
|
||||
return NULL;
|
||||
@ -99,10 +99,15 @@ newSubscriber(const char * eventurl, const char * callback, int callbacklen)
|
||||
memcpy(tmp->callback, callback, callbacklen);
|
||||
tmp->callback[callbacklen] = '\0';
|
||||
/* make a dummy uuid */
|
||||
/* TODO: improve that */
|
||||
strncpy(tmp->uuid, uuidvalue, sizeof(tmp->uuid));
|
||||
#ifdef HAVE_UUID
|
||||
uuid_t uuid;
|
||||
uuid_generate_time(uuid);
|
||||
uuid_unparse_lower(uuid, tmp->uuid+5);
|
||||
#else
|
||||
tmp->uuid[sizeof(tmp->uuid)-1] = '\0';
|
||||
snprintf(tmp->uuid+37, 5, "%04lx", random() & 0xffff);
|
||||
#endif
|
||||
return tmp;
|
||||
}
|
||||
|
||||
@ -136,7 +141,7 @@ renewSubscription(const char * sid, int sidlen, int timeout)
|
||||
{
|
||||
struct subscriber * sub;
|
||||
for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) {
|
||||
if(memcmp(sid, sub->uuid, 41)) {
|
||||
if(memcmp(sid, sub->uuid, 41) == 0) {
|
||||
sub->timeout = (timeout ? time(NULL) + timeout : 0);
|
||||
return 0;
|
||||
}
|
||||
@ -290,11 +295,14 @@ static void upnp_event_prepare(struct upnp_event_notify * obj)
|
||||
case EConnectionManager:
|
||||
xml = getVarsConnectionManager(&l);
|
||||
break;
|
||||
case EMSMediaReceiverRegistrar:
|
||||
xml = getVarsX_MS_MediaReceiverRegistrar(&l);
|
||||
break;
|
||||
default:
|
||||
xml = NULL;
|
||||
l = 0;
|
||||
}
|
||||
obj->buffersize = 1536;
|
||||
obj->buffersize = 2048;
|
||||
obj->buffer = malloc(obj->buffersize);
|
||||
/*if(!obj->buffer) {
|
||||
}*/
|
||||
|
@ -8,11 +8,9 @@
|
||||
#define __UPNPEVENTS_H__
|
||||
#ifdef ENABLE_EVENTS
|
||||
enum subscriber_service_enum {
|
||||
EWanCFG = 1,
|
||||
EContentDirectory,
|
||||
EContentDirectory = 1,
|
||||
EConnectionManager,
|
||||
EWanIPC,
|
||||
EL3F
|
||||
EMSMediaReceiverRegistrar
|
||||
};
|
||||
|
||||
void
|
||||
|
@ -11,9 +11,6 @@
|
||||
#include "config.h"
|
||||
#include "upnpglobalvars.h"
|
||||
|
||||
/* network interface for internet */
|
||||
const char * ext_if_name = 0;
|
||||
|
||||
/* file to store leases */
|
||||
#ifdef ENABLE_LEASEFILE
|
||||
const char* lease_file = 0;
|
||||
@ -39,9 +36,6 @@ int sysuptime = 0;
|
||||
/* log packets flag */
|
||||
int logpackets = 0;
|
||||
|
||||
#ifdef ENABLE_NATPMP
|
||||
int enablenatpmp = 0;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
int runtime_flags = 0;
|
||||
@ -61,21 +55,10 @@ char presentationurl[PRESENTATIONURL_MAX_LEN];
|
||||
struct upnpperm * upnppermlist = 0;
|
||||
unsigned int num_upnpperm = 0;
|
||||
|
||||
#ifdef ENABLE_NATPMP
|
||||
/* NAT-PMP */
|
||||
unsigned int nextnatpmptoclean_timestamp = 0;
|
||||
unsigned short nextnatpmptoclean_eport = 0;
|
||||
unsigned short nextnatpmptoclean_proto = 0;
|
||||
#endif
|
||||
|
||||
#ifdef USE_PF
|
||||
const char * queue = 0;
|
||||
const char * tag = 0;
|
||||
#endif
|
||||
|
||||
int n_lan_addr = 0;
|
||||
struct lan_addr_s lan_addr[MAX_LAN_ADDR];
|
||||
|
||||
/* UPnP-A/V [DLNA] */
|
||||
sqlite3 *db;
|
||||
char media_dir[256];
|
||||
char media_dir[MEDIADIR_MAX_LEN];
|
||||
char friendly_name[FRIENDLYNAME_MAX_LEN];
|
||||
|
@ -13,8 +13,8 @@
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
/* name of the network interface used to acces internet */
|
||||
extern const char * ext_if_name;
|
||||
#define USE_FORK 1
|
||||
#define DB_VERSION 1
|
||||
|
||||
/* file to store all leases */
|
||||
#ifdef ENABLE_LEASEFILE
|
||||
@ -36,16 +36,9 @@ extern time_t startup_time;
|
||||
extern int runtime_flags;
|
||||
#define LOGPACKETSMASK 0x0001
|
||||
#define SYSUPTIMEMASK 0x0002
|
||||
#ifdef ENABLE_NATPMP
|
||||
#define ENABLENATPMPMASK 0x0004
|
||||
#endif
|
||||
#define CHECKCLIENTIPMASK 0x0008
|
||||
#define SECUREMODEMASK 0x0010
|
||||
|
||||
#ifdef PF_ENABLE_FILTER_RULES
|
||||
#define PFNOQUICKRULESMASK 0x0040
|
||||
#endif
|
||||
|
||||
#define SETFLAG(mask) runtime_flags |= mask
|
||||
#define GETFLAG(mask) runtime_flags & mask
|
||||
#define CLEARFLAG(mask) runtime_flags &= ~mask
|
||||
@ -67,19 +60,6 @@ extern char presentationurl[];
|
||||
extern struct upnpperm * upnppermlist;
|
||||
extern unsigned int num_upnpperm;
|
||||
|
||||
#ifdef ENABLE_NATPMP
|
||||
/* NAT-PMP */
|
||||
extern unsigned int nextnatpmptoclean_timestamp;
|
||||
extern unsigned short nextnatpmptoclean_eport;
|
||||
extern unsigned short nextnatpmptoclean_proto;
|
||||
#endif
|
||||
|
||||
#ifdef USE_PF
|
||||
/* queue and tag for PF rules */
|
||||
extern const char * queue;
|
||||
extern const char * tag;
|
||||
#endif
|
||||
|
||||
/* lan addresses */
|
||||
/* MAX_LAN_ADDR : maximum number of interfaces
|
||||
* to listen to SSDP traffic */
|
||||
@ -91,5 +71,7 @@ extern struct lan_addr_s lan_addr[];
|
||||
extern sqlite3 *db;
|
||||
#define MEDIADIR_MAX_LEN (256)
|
||||
extern char media_dir[];
|
||||
#define FRIENDLYNAME_MAX_LEN (64)
|
||||
extern char friendly_name[];
|
||||
|
||||
#endif
|
||||
|
86
upnphttp.c
86
upnphttp.c
@ -367,25 +367,6 @@ findendheaders(const char * s, int len)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef HAS_DUMMY_SERVICE
|
||||
static void
|
||||
sendDummyDesc(struct upnphttp * h)
|
||||
{
|
||||
static const char xml_desc[] = "<?xml version=\"1.0\"?>\r\n"
|
||||
"<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">"
|
||||
" <specVersion>"
|
||||
" <major>1</major>"
|
||||
" <minor>0</minor>"
|
||||
" </specVersion>"
|
||||
" <actionList />"
|
||||
" <serviceStateTable />"
|
||||
"</scpd>\r\n";
|
||||
BuildResp_upnphttp(h, xml_desc, sizeof(xml_desc)-1);
|
||||
SendResp_upnphttp(h);
|
||||
CloseSocket_upnphttp(h);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Sends the description generated by the parameter */
|
||||
static void
|
||||
sendXMLdesc(struct upnphttp * h, char * (f)(int *))
|
||||
@ -551,17 +532,31 @@ ProcessHttpQuery_upnphttp(struct upnphttp * h)
|
||||
HttpVer[i] = '\0';
|
||||
syslog(LOG_INFO, "HTTP REQUEST : %s %s (%s)",
|
||||
HttpCommand, HttpUrl, HttpVer);
|
||||
//DEBUG printf("HTTP REQUEST:\n%s\n", h->req_buf);
|
||||
//DEBUG printf("HTTP REQUEST:\n%.*s\n", h->req_buflen, h->req_buf);
|
||||
ParseHttpHeaders(h);
|
||||
|
||||
if( (h->reqflags & FLAG_CHUNKED) && (h->req_chunklen > (h->req_buflen - h->req_contentoff) || h->req_chunklen < 0) )
|
||||
/* see if we need to wait for remaining data */
|
||||
if( (h->reqflags & FLAG_CHUNKED) )
|
||||
{
|
||||
/* waiting for remaining data */
|
||||
printf("*** %d < %d\n", (h->req_buflen - h->req_contentoff), h->req_contentlen);
|
||||
printf("Chunked request [%ld]. Need more input.\n", h->req_chunklen);
|
||||
char * chunkstart = h->req_buf+h->req_contentoff;
|
||||
char * numstart;
|
||||
h->state = 2;
|
||||
while( h->req_chunklen )
|
||||
{
|
||||
if( chunkstart >= (h->req_buf+h->req_buflen) )
|
||||
return;
|
||||
numstart = chunkstart+h->req_chunklen+2;
|
||||
h->req_chunklen = strtol(numstart, &chunkstart, 16);
|
||||
if( !h->req_chunklen && (chunkstart == numstart) )
|
||||
{
|
||||
printf("Chunked request needs more input.\n");
|
||||
return;
|
||||
}
|
||||
chunkstart = chunkstart+2;
|
||||
}
|
||||
h->state = 100;
|
||||
}
|
||||
else if(strcmp("POST", HttpCommand) == 0)
|
||||
if(strcmp("POST", HttpCommand) == 0)
|
||||
{
|
||||
h->req_command = EPost;
|
||||
ProcessHTTPPOST_upnphttp(h);
|
||||
@ -602,12 +597,6 @@ ProcessHttpQuery_upnphttp(struct upnphttp * h)
|
||||
{
|
||||
sendXMLdesc(h, genX_MS_MediaReceiverRegistrar);
|
||||
}
|
||||
#ifdef HAS_DUMMY_SERVICE
|
||||
else if(strcmp(DUMMY_PATH, HttpUrl) == 0)
|
||||
{
|
||||
sendDummyDesc(h);
|
||||
}
|
||||
#endif
|
||||
else if(strncmp(HttpUrl, "/MediaItems/", 12) == 0)
|
||||
{
|
||||
SendResp_dlnafile(h, HttpUrl+12);
|
||||
@ -864,6 +853,7 @@ SendResp_thumbnail(struct upnphttp * h, char * object)
|
||||
char header[1500];
|
||||
char sql_buf[256];
|
||||
char **result;
|
||||
int rows;
|
||||
char date[30];
|
||||
time_t curtime = time(NULL);
|
||||
int n;
|
||||
@ -879,8 +869,14 @@ SendResp_thumbnail(struct upnphttp * h, char * object)
|
||||
return;
|
||||
}
|
||||
|
||||
sprintf(sql_buf, "SELECT PATH from OBJECTS where OBJECT_ID = '%s'", object);
|
||||
sqlite3_get_table(db, sql_buf, &result, 0, 0, 0);
|
||||
sprintf(sql_buf, "SELECT d.PATH from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID) where OBJECT_ID = '%s'", object);
|
||||
sqlite3_get_table(db, sql_buf, &result, &rows, 0, 0);
|
||||
if( !rows )
|
||||
{
|
||||
syslog(LOG_NOTICE, "%s not found, responding ERROR 404", object);
|
||||
Send404(h);
|
||||
goto error;
|
||||
}
|
||||
printf("Serving up thumbnail for ObjectId: %s [%s]\n", object, result[1]);
|
||||
|
||||
if( access(result[1], F_OK) == 0 )
|
||||
@ -976,7 +972,7 @@ SendResp_resizedimg(struct upnphttp * h, char * object)
|
||||
return;
|
||||
}
|
||||
|
||||
sprintf(sql_buf, "SELECT o.PATH, d.WIDTH, d.HEIGHT from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID) where OBJECT_ID = '%s'", object);
|
||||
sprintf(sql_buf, "SELECT d.PATH, d.WIDTH, d.HEIGHT from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID) where OBJECT_ID = '%s'", object);
|
||||
sqlite3_get_table(db, sql_buf, &result, 0, 0, 0);
|
||||
printf("Serving up resized image for ObjectId: %s [%s]\n", object, result[1]);
|
||||
|
||||
@ -1073,17 +1069,26 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
|
||||
time_t curtime = time(NULL);
|
||||
off_t total, send_size;
|
||||
char *path, *mime, *dlna;
|
||||
|
||||
#if USE_FORK
|
||||
pid_t newpid = 0;
|
||||
#endif
|
||||
|
||||
memset(header, 0, 1500);
|
||||
|
||||
sprintf(sql_buf, "SELECT o.PATH, d.MIME, d.DLNA_PN from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID) where OBJECT_ID = '%s'", object);
|
||||
sprintf(sql_buf, "SELECT d.PATH, d.MIME, d.DLNA_PN from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID) where OBJECT_ID = '%s'", object);
|
||||
sqlite3_get_table(db, sql_buf, &result, &rows, 0, 0);
|
||||
if( !rows )
|
||||
{
|
||||
syslog(LOG_NOTICE, "%s not found, responding ERROR 404", object);
|
||||
Send404(h);
|
||||
goto error;
|
||||
sqlite3_free_table(result);
|
||||
return;
|
||||
}
|
||||
#if USE_FORK
|
||||
newpid = fork();
|
||||
if( newpid )
|
||||
return;
|
||||
#endif
|
||||
|
||||
path = result[3];
|
||||
mime = result[4];
|
||||
@ -1186,8 +1191,8 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
|
||||
}
|
||||
else //if( h->reqflags & FLAG_XFERINTERACTIVE )
|
||||
{
|
||||
if( (strncmp(mime, "vide", 4) == 0) ||
|
||||
(strncmp(mime, "audi", 4) == 0) )
|
||||
if( (strncmp(mime, "video", 5) == 0) ||
|
||||
(strncmp(mime, "audio", 5) == 0) )
|
||||
strcat(header, "transferMode.dlna.org: Streaming\r\n");
|
||||
else
|
||||
strcat(header, "transferMode.dlna.org: Interactive\r\n");
|
||||
@ -1240,4 +1245,7 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
|
||||
}
|
||||
error:
|
||||
sqlite3_free_table(result);
|
||||
#if USE_FORK
|
||||
_exit(0);
|
||||
#endif
|
||||
}
|
||||
|
101
upnpsoap.c
101
upnpsoap.c
@ -64,28 +64,6 @@ BuildSendAndCloseSoapResp(struct upnphttp * h,
|
||||
CloseSocket_upnphttp(h);
|
||||
}
|
||||
|
||||
static void
|
||||
GetStatusInfo(struct upnphttp * h, const char * action)
|
||||
{
|
||||
static const char resp[] =
|
||||
"<u:%sResponse "
|
||||
"xmlns:u=\"%s\">"
|
||||
"<NewConnectionStatus>Connected</NewConnectionStatus>"
|
||||
"<NewLastConnectionError>ERROR_NONE</NewLastConnectionError>"
|
||||
"<NewUptime>%ld</NewUptime>"
|
||||
"</u:%sResponse>";
|
||||
|
||||
char body[512];
|
||||
int bodylen;
|
||||
time_t uptime;
|
||||
|
||||
uptime = (time(NULL) - startup_time);
|
||||
bodylen = snprintf(body, sizeof(body), resp,
|
||||
action, "urn:schemas-upnp-org:service:WANIPConnection:1",
|
||||
(long)uptime, action);
|
||||
BuildSendAndCloseSoapResp(h, body, bodylen);
|
||||
}
|
||||
|
||||
static void
|
||||
GetSystemUpdateID(struct upnphttp * h, const char * action)
|
||||
{
|
||||
@ -129,16 +107,15 @@ GetProtocolInfo(struct upnphttp * h, const char * action)
|
||||
"<u:%sResponse "
|
||||
"xmlns:u=\"%s\">"
|
||||
"<Source>"
|
||||
/*"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=01;DLNA.ORG_CI=1,"
|
||||
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=01,"
|
||||
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=01,"
|
||||
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=01,"
|
||||
"http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=01,"*/
|
||||
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN,"
|
||||
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=01,"
|
||||
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED,"
|
||||
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=01,"
|
||||
"http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC,"
|
||||
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
|
||||
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
|
||||
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
|
||||
"http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
|
||||
"http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
|
||||
"http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_HD_NA_ISO;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
|
||||
"http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AC3_T;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
|
||||
"http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
|
||||
"http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01,"
|
||||
"http-get:*:audio/x-ms-wma:*,"
|
||||
"http-get:*:audio/wav:*,"
|
||||
@ -212,7 +189,7 @@ GetCurrentConnectionIDs(struct upnphttp * h, const char * action)
|
||||
static const char resp[] =
|
||||
"<u:%sResponse "
|
||||
"xmlns:u=\"%s\">"
|
||||
"<ConnectionIDs>-1</ConnectionIDs>"
|
||||
"<ConnectionIDs>0</ConnectionIDs>"
|
||||
"</u:%sResponse>";
|
||||
|
||||
char body[512];
|
||||
@ -378,13 +355,16 @@ static int callback(void *args, int argc, char **argv, char **azColName)
|
||||
sprintf(str_buf, "childCount=\"%s\"", result[1]);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
}
|
||||
/* If the client calls for BrowseMetadata on root, we have to include our "upnp:searchClass"'s */
|
||||
/* If the client calls for BrowseMetadata on root, we have to include our "upnp:searchClass"'s, unless they're filtered out */
|
||||
if( (passed_args->requested == 1) && (strcmp(id, "0") == 0) )
|
||||
{
|
||||
strcat(passed_args->resp, ">"
|
||||
"<upnp:searchClass includeDerived=\"1\">object.item.audioItem</upnp:searchClass>"
|
||||
"<upnp:searchClass includeDerived=\"1\">object.item.imageItem</upnp:searchClass>"
|
||||
"<upnp:searchClass includeDerived=\"1\">object.item.videoItem</upnp:searchClass");
|
||||
if( !passed_args->filter || strstr(passed_args->filter, "upnp:searchClass") )
|
||||
{
|
||||
strcat(passed_args->resp, ">"
|
||||
"<upnp:searchClass includeDerived=\"1\">object.item.audioItem</upnp:searchClass>"
|
||||
"<upnp:searchClass includeDerived=\"1\">object.item.imageItem</upnp:searchClass>"
|
||||
"<upnp:searchClass includeDerived=\"1\">object.item.videoItem</upnp:searchClass");
|
||||
}
|
||||
}
|
||||
sprintf(str_buf, ">"
|
||||
"<dc:title>%s</dc:title>"
|
||||
@ -575,49 +555,6 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
|
||||
free(resp);
|
||||
}
|
||||
|
||||
static void
|
||||
GetExternalIPAddress(struct upnphttp * h, const char * action)
|
||||
{
|
||||
static const char resp[] =
|
||||
"<u:%sResponse "
|
||||
"xmlns:u=\"%s\">"
|
||||
"<NewExternalIPAddress>%s</NewExternalIPAddress>"
|
||||
"</u:%sResponse>";
|
||||
|
||||
char body[512];
|
||||
int bodylen;
|
||||
char ext_ip_addr[INET_ADDRSTRLEN];
|
||||
|
||||
#ifndef MULTIPLE_EXTERNAL_IP
|
||||
if(use_ext_ip_addr)
|
||||
{
|
||||
strncpy(ext_ip_addr, use_ext_ip_addr, INET_ADDRSTRLEN);
|
||||
}
|
||||
else if(getifaddr(ext_if_name, ext_ip_addr, INET_ADDRSTRLEN) < 0)
|
||||
{
|
||||
syslog(LOG_ERR, "Failed to get ip address for interface %s",
|
||||
ext_if_name);
|
||||
strncpy(ext_ip_addr, "0.0.0.0", INET_ADDRSTRLEN);
|
||||
}
|
||||
#else
|
||||
int i;
|
||||
strncpy(ext_ip_addr, "0.0.0.0", INET_ADDRSTRLEN);
|
||||
for(i = 0; i<n_lan_addr; i++)
|
||||
{
|
||||
if( (h->clientaddr.s_addr & lan_addr[i].mask.s_addr)
|
||||
== (lan_addr[i].addr.s_addr & lan_addr[i].mask.s_addr))
|
||||
{
|
||||
strncpy(ext_ip_addr, lan_addr[i].ext_ip_str, INET_ADDRSTRLEN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
bodylen = snprintf(body, sizeof(body), resp,
|
||||
action, "urn:schemas-upnp-org:service:WANIPConnection:1",
|
||||
ext_ip_addr, action);
|
||||
BuildSendAndCloseSoapResp(h, body, bodylen);
|
||||
}
|
||||
|
||||
/*
|
||||
If a control point calls QueryStateVariable on a state variable that is not
|
||||
buffered in memory within (or otherwise available from) the service,
|
||||
@ -659,7 +596,7 @@ QueryStateVariable(struct upnphttp * h, const char * action)
|
||||
BuildSendAndCloseSoapResp(h, body, bodylen);
|
||||
}
|
||||
#if 0
|
||||
/* not usefull */
|
||||
/* not useful */
|
||||
else if(strcmp(var_name, "ConnectionType") == 0)
|
||||
{
|
||||
bodylen = snprintf(body, sizeof(body), resp, "IP_Routed");
|
||||
@ -687,9 +624,7 @@ static const struct
|
||||
}
|
||||
soapMethods[] =
|
||||
{
|
||||
{ "GetExternalIPAddress", GetExternalIPAddress},
|
||||
{ "QueryStateVariable", QueryStateVariable},
|
||||
{ "GetStatusInfo", GetStatusInfo},
|
||||
{ "Browse", BrowseContentDirectory},
|
||||
{ "Search", SearchContentDirectory},
|
||||
{ "GetSearchCapabilities", GetSearchCapabilities},
|
||||
|
Loading…
x
Reference in New Issue
Block a user