From b183afb04050143a3f9faccf6ff52219e60c34d6 Mon Sep 17 00:00:00 2001 From: thlo Date: Sun, 17 Mar 2013 09:41:35 +0100 Subject: First step of repo restructuring --- Makefile | 109 ++ httpresource.c | 2905 +++++++++++++++++++++++++++++++++++++++ httpresource.h | 187 +++ log.c | 85 ++ log.h | 50 + smarttvfactory.c | 408 ++++++ smarttvfactory.h | 84 ++ smarttvweb.c | 152 ++ smarttvweb.conf | 28 + stvw_cfg.c | 196 +++ stvw_cfg.h | 75 + url.c | 340 +++++ url.h | 50 + vdr-smarttvweb/Makefile | 109 -- vdr-smarttvweb/httpresource.c | 2905 --------------------------------------- vdr-smarttvweb/httpresource.h | 187 --- vdr-smarttvweb/log.c | 85 -- vdr-smarttvweb/log.h | 50 - vdr-smarttvweb/smarttvfactory.c | 408 ------ vdr-smarttvweb/smarttvfactory.h | 84 -- vdr-smarttvweb/smarttvweb.c | 152 -- vdr-smarttvweb/smarttvweb.conf | 28 - vdr-smarttvweb/stvw_cfg.c | 196 --- vdr-smarttvweb/stvw_cfg.h | 75 - vdr-smarttvweb/url.c | 340 ----- vdr-smarttvweb/url.h | 50 - vdr-smarttvweb/web/Data.js | 296 ---- vdr-smarttvweb/web/Server.js | 180 --- vdr-smarttvweb/web/favicon.ico | Bin 1502 -> 0 bytes vdr-smarttvweb/web/index.html | 104 -- vdr-smarttvweb/widget.conf | 11 - web/Data.js | 296 ++++ web/Server.js | 180 +++ web/favicon.ico | Bin 0 -> 1502 bytes web/index.html | 104 ++ widget.conf | 11 + 36 files changed, 5260 insertions(+), 5260 deletions(-) create mode 100644 Makefile create mode 100755 httpresource.c create mode 100755 httpresource.h create mode 100644 log.c create mode 100644 log.h create mode 100755 smarttvfactory.c create mode 100644 smarttvfactory.h create mode 100644 smarttvweb.c create mode 100644 smarttvweb.conf create mode 100755 stvw_cfg.c create mode 100755 stvw_cfg.h create mode 100644 url.c create mode 100644 url.h delete mode 100644 vdr-smarttvweb/Makefile delete mode 100755 vdr-smarttvweb/httpresource.c delete mode 100755 vdr-smarttvweb/httpresource.h delete mode 100644 vdr-smarttvweb/log.c delete mode 100644 vdr-smarttvweb/log.h delete mode 100755 vdr-smarttvweb/smarttvfactory.c delete mode 100644 vdr-smarttvweb/smarttvfactory.h delete mode 100644 vdr-smarttvweb/smarttvweb.c delete mode 100644 vdr-smarttvweb/smarttvweb.conf delete mode 100755 vdr-smarttvweb/stvw_cfg.c delete mode 100755 vdr-smarttvweb/stvw_cfg.h delete mode 100644 vdr-smarttvweb/url.c delete mode 100644 vdr-smarttvweb/url.h delete mode 100755 vdr-smarttvweb/web/Data.js delete mode 100755 vdr-smarttvweb/web/Server.js delete mode 100755 vdr-smarttvweb/web/favicon.ico delete mode 100755 vdr-smarttvweb/web/index.html delete mode 100644 vdr-smarttvweb/widget.conf create mode 100755 web/Data.js create mode 100755 web/Server.js create mode 100755 web/favicon.ico create mode 100755 web/index.html create mode 100644 widget.conf diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b04d143 --- /dev/null +++ b/Makefile @@ -0,0 +1,109 @@ +# +# Makefile for a Video Disk Recorder plugin +# +# +# The official name of this plugin. +# This name will be used in the '-P...' option of VDR to load the plugin. +# By default the main source file also carries this name. +# +PLUGIN = smarttvweb + +### The version number of this plugin (taken from the main source file): + +VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g') + +### The C++ compiler and options: + + +CXX ?= g++ +ifdef DEBUG +CXXFLAGS ?= -g -O0 -fPIC -Wall -Woverloaded-virtual #-Werror +else +CXXFLAGS ?= -fPIC -Wall -Woverloaded-virtual #-Werror +#CXXFLAGS ?= -O2 -fPIC -Wall -Woverloaded-virtual #-Werror +endif + +### The directory environment: + +#VDRDIR = ../../.. +VDRDIR = /usr/include/vdr +LIBDIR = . +#LIBDIR = ../../lib +TMPDIR = /tmp + +### Allow user defined options to overwrite defaults: + +#-include $(VDRDIR)/Make.config + +### read standlone settings if there +-include .standalone + +### The version number of VDR (taken from VDR's "config.h"): + +VDRVERSION = $(shell grep 'define VDRVERSION ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g') +APIVERSION = $(shell sed -ne '/define APIVERSION/s/^.*"\(.*\)".*$$/\1/p' $(VDRDIR)/config.h) + +### The name of the distribution archive: + +ARCHIVE = $(PLUGIN)-$(VERSION) +PACKAGE = vdr-$(ARCHIVE) + +### Includes and Defines (add further entries here): + +INCLUDES += -I$(VDRDIR)/include -I$(DVBDIR)/include + +DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' +DEFINES += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE + +### The object files (add further files here): + +OBJS = $(PLUGIN).o smarttvfactory.o httpresource.o log.o url.o stvw_cfg.o + +OBJS2 = + +### Implicit rules: + +%.o: %.c + $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $< + +# Dependencies: + +MAKEDEP = g++ -MM -MG +DEPFILE = .dependencies +$(DEPFILE): Makefile + @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@ + +-include $(DEPFILE) + +### Targets: + +all: allbase libvdr-$(PLUGIN).so +standalone: standalonebase smarttvweb-standalone + +objectsstandalone: $(OBJS) +objects: $(OBJS) $(OBJS2) + +allbase: + ( if [ -f .standalone ] ; then ( rm -f .standalone; make clean ; make objects ) ; else exit 0 ;fi ) +standalonebase: + ( if [ ! -f .standalone ] ; then ( make clean; echo "DEFINES+=-DSTANDALONE" > .standalone; echo "DEFINES+=-D_FILE_OFFSET_BITS=64" >> .standalone; make objectsstandalone ) ; else exit 0 ;fi ) + +libvdr-$(PLUGIN).so: objects + $(CXX) $(CXXFLAGS) -shared $(OBJS) $(OBJS2) -o $@ + @cp $@ $(LIBDIR)/$@.$(APIVERSION) + +smarttvweb-standalone: objectsstandalone + $(CXX) $(CXXFLAGS) $(OBJS) -lpthread -o $@ + chmod u+x $@ + +dist: clean + @-rm -rf $(TMPDIR)/$(ARCHIVE) + @mkdir $(TMPDIR)/$(ARCHIVE) + @cp -a * $(TMPDIR)/$(ARCHIVE) + @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE) + @-rm -rf $(TMPDIR)/$(ARCHIVE) + @echo Distribution package created as $(PACKAGE).tgz + +clean: + rm -f $(OBJS) $(OBJS2) $(DEPFILE) libvdr*.so.* *.tgz core* *~ .standalone smarttvweb-standalone + diff --git a/httpresource.c b/httpresource.c new file mode 100755 index 0000000..79d39bc --- /dev/null +++ b/httpresource.c @@ -0,0 +1,2905 @@ +/* + * httpresource.c: VDR on Smart TV plugin + * + * Copyright (C) 2012, 2013 T. Lohmar + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "httpresource.h" +#include "smarttvfactory.h" +#include "stvw_cfg.h" + +#include "url.h" + +#ifndef STANDALONE +#include +#include +#include +#include +#include + +#endif + +#define PROTOCOL "HTTP/1.1" +#define RFC1123FMT "%a, %d %b %Y %H:%M:%S GMT" + +#define MAXLEN 4096 +#define OKAY 0 +#define ERROR (-1) +#define DEBUG_REGHEADERS +#define DEBUGPREFIX "mReqId= " << mReqId << " fd= " << mFd +#define DEBUGHDR " " << __PRETTY_FUNCTION__ << " (" << __LINE__ << ") " + +#define DEBUG + +#define SEGMENT_DURATION 10 + +using namespace std; + +class cResumeEntry { + public: + string mFilename; + float mResume; + + friend ostream& operator<<(ostream& out, const cResumeEntry& o) { + out << "mFilename= " << o.mFilename << " mResume= " << o.mResume << endl; + return out; + }; + cResumeEntry():mFilename(), mResume(-1.0) {}; +}; + + +struct sVdrFileEntry { + uint64_t sSize; + uint64_t sFirstOffset; + int sIdx; + + sVdrFileEntry () {}; + sVdrFileEntry (uint64_t s, uint64_t t, int i) + : sSize(s), sFirstOffset(t), sIdx(i) {}; +}; + + +struct sTimerEntry { + string name; + time_t startTime; + int duration; +sTimerEntry(string t, time_t s, int d) : name(t), startTime(s), duration(d) {}; +}; + +// 8 Byte Per Entry +struct tIndexPes { + uint32_t offset; + uchar type; + uchar number; + uint16_t reserved; + }; + + +// 8 Byte per entry +struct tIndexTs { + uint64_t offset:40; // up to 1TB per file (not using long long int here - must definitely be exactly 64 bit!) + int reserved:7; // reserved for future use + int independent:1; // marks frames that can be displayed by themselves (for trick modes) + uint16_t number:16; // up to 64K files per recording + }; + + +/* +union tIndexRead { + struct tIndexTs in; + char buf[8]; +}; +*/ + +cHttpResource::cHttpResource(int f, int id, int port, SmartTvServer* factory): mFactory(factory), mLog(), + mServerPort(port), mFd(f), mReqId(id), mConnTime(0), mHandleReadCount(0), + mConnected(true), mConnState(WAITING), mContentType(NYD), mReadBuffer(), + mMethod(), mResponseMessagePos(0), mBlkData(NULL), mBlkPos(0), mBlkLen(0), + mPath(), mVersion(), protocol(), mReqContentLength(0), + mPayload(), mUserAgent(), + mAcceptRanges(true), rangeHdr(), mFileSize(-1), mStreamToEnd(false), mRemLength(0), mFile(NULL), mVdrIdx(1), mFileStructure(), + mIsRecording(false), mRecProgress(0.0) { + + mLog = Log::getInstance(); + mPath = ""; + mConnTime = time(NULL); + setNonBlocking(); + mBlkData = new char[MAXLEN]; + + //#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " cHttpResource created" << endl; + //#endif +} + + +cHttpResource::~cHttpResource() { + //#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " Destructor of cHttpResource called" + << endl; + //#endif + delete[] mBlkData; + if (mFile != NULL) { + *(mLog->log())<< DEBUGPREFIX + << " ERROR: mFile still open. Closing now..." << endl; + fclose(mFile); + mFile = NULL; + } + +} + +int cHttpResource::checkStatus() { + time_t now = time(NULL); + + switch (mConnState) { + case WAITING: + case READHDR: + case READPAYLOAD: + if (now - mConnTime > 2) { + *(mLog->log()) << DEBUGPREFIX + << " checkStatus: no activity for 2sec " + << "mmConnState= " << getConnStateName() + << endl; + return ERROR; + } + break; + case TOCLOSE: + return ERROR; + break; + case SERVING: + break; + } + + // check for how much time the + return OKAY; +} + +void cHttpResource::setNonBlocking() { + int oldflags = fcntl(mFd, F_GETFL, 0); + oldflags |= O_NONBLOCK; + fcntl(mFd, F_SETFL, oldflags); +} + + +int cHttpResource::handleRead() { + mHandleReadCount ++; + if (mConnState == SERVING) { + *(mLog->log())<< DEBUGPREFIX + << " handleRead() in wrong state= " << getConnStateName() + << endl; + return OKAY; + } + if (mConnState == TOCLOSE) { + *(mLog->log())<< DEBUGPREFIX + << " handleRead() in wrong state= " << getConnStateName() + << endl; + return ERROR; + } + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " handleRead() state= " << getConnStateName() + << endl; +#endif + + char buf[MAXLEN]; + int buflen = sizeof(buf); + + int line_count = 0; + // bool is_req = true; + string rem_hdr = ""; + + buflen = read(mFd, buf, sizeof(buf)); + + if (buflen == -1) { + *(mLog->log())<< " Some Error, no data received" << endl; + return ERROR; // Nothing to read + } + + // if (( mConnState == WAITING) and ((time(NULL) - mConnTime) > 1)) { + if (( mConnState == WAITING) and (mHandleReadCount > 1000)) { + *(mLog->log()) << DEBUGPREFIX << " hmm, handleRead() no data since 1sec -> closing. mHandleReadCount= " << mHandleReadCount << endl; + return ERROR; // Nothing to read + } + + if ( ((mConnState == READHDR) or (mConnState == READPAYLOAD)) and (mHandleReadCount > 5000)) { + *(mLog->log()) << DEBUGPREFIX << " ERROR Still not finished after mHandleReadCount= " << mHandleReadCount << endl; + return ERROR; // Nothing to read + } + + if (buflen == 0) { + return OKAY; // Nothing to read + } + + if (mConnState == READPAYLOAD) { + mPayload += string(buf, buflen); + if (mPayload.size() == mReqContentLength) { + //Done + mConnState = SERVING; + return processRequest(); + } + } + + if (mConnState == WAITING) { + mConnState = READHDR; + } + + string req_line = mReadBuffer + string(buf, buflen); + buflen += rem_hdr.size(); + + size_t last_pos = 0; + // Parse http header lines + while (true) { + line_count ++; + size_t pos = req_line.find ("\r\n", last_pos); + + if ((pos > buflen) or (pos == string::npos)) { + mReadBuffer = req_line.substr(last_pos, buflen - last_pos); +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX << " No HdrEnd Found, read more data. rem_hdr= " << mReadBuffer.size() + << " buflen= " << buflen + << DEBUGHDR << endl; + *(mLog->log())<< cUrlEncode::hexDump(mReadBuffer) << endl; +#endif + return OKAY; + } + + if ((last_pos - pos) == 0) { + // Header End +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX << " ---- Header End Found" << DEBUGHDR << endl; +#endif + + if (mReqContentLength != 0) { + mConnState = READPAYLOAD; + mPayload = req_line.substr(pos +2, buflen - (pos +2)); + if (mPayload.size() != mReqContentLength) + return OKAY; + else { + mConnState = SERVING; + return processRequest(); + } + } // if(content_length != 0) + else { + mConnState = SERVING; + return processRequest(); + } + } // if (header end) + + string line = req_line.substr(last_pos, (pos-last_pos)); +#ifdef DEBUG_REQHEADERS + *(mLog->log())<< " Line= " << line << endl; +#endif + last_pos = pos +2; + if (mPath.size() == 0) { +#ifndef DEBUG + *(mLog->log())<< " parsing Request Line= " << line << endl; +#endif + // is_req = false; + // Parse the request line + if (parseHttpRequestLine(line) != OKAY) { + return ERROR; + }; + } + else { + parseHttpHeaderLine (line); + } + } + return OKAY; +} + +int cHttpResource::processRequest() { + // do stuff based on the request and the query +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX << " processRequest for mPath= " << mPath << DEBUGHDR << endl; +#endif + struct stat statbuf; + bool ok_to_serve = false; + + if (mMethod.compare("POST")==0) { + return handlePost(); + } + + if (!((strcasecmp(mMethod.c_str(), "GET") == 0) or (strcasecmp(mMethod.c_str(), "HEAD") == 0))) { + sendError(501, "Not supported", NULL, "Method is not supported."); + return OKAY; + } + +#ifndef STANDALONE + if (mPath.compare("/recordings.xml") == 0) { + if (handleHeadRequest() != 0) + return OKAY; + + sendRecordingsXml( &statbuf); + return OKAY; + } + + if (mPath.compare("/channels.xml") == 0) { + if (handleHeadRequest() != 0) + return OKAY; + + sendChannelsXml( &statbuf); + return OKAY; + } + + if (mPath.compare("/epg.xml") == 0) { + if (handleHeadRequest() != 0) + return OKAY; + + sendEpgXml( &statbuf); + return OKAY; + } + + if (mPath.compare("/setResume.xml") == 0) { + if (handleHeadRequest() != 0) + return OKAY; + + receiveResume(); + return OKAY; + } + + if (mPath.compare("/getResume.xml") == 0) { + if (handleHeadRequest() != 0) + return OKAY; + + sendResumeXml(); + return OKAY; + } + + if (mPath.compare("/vdrstatus.xml") == 0) { + if (handleHeadRequest() != 0) + return OKAY; + + sendVdrStatusXml( &statbuf); + return OKAY; + } + + +#endif + + if (mPath.compare("/media.xml") == 0) { + if (handleHeadRequest() != 0) + return OKAY; + sendMediaXml( &statbuf); + return OKAY; + } + + if (mPath.compare("/widget.conf") == 0) { + mPath = mFactory->getConfigDir() + "/widget.conf"; + + if (stat(mPath.c_str(), &statbuf) < 0) { + sendError(404, "Not Found", NULL, "File not found."); + return OKAY; + } + if (handleHeadRequest() != 0) + return OKAY; + + mFileSize = statbuf.st_size; + mContentType = SINGLEFILE; + return sendFile(&statbuf); + } + + if (mPath.compare("/favicon.ico") == 0) { + mPath = mFactory->getConfigDir() + "/web/favicon.ico"; + + if (stat(mPath.c_str(), &statbuf) < 0) { + sendError(404, "Not Found", NULL, "File not found."); + return OKAY; + } + if (handleHeadRequest() != 0) + return OKAY; + + mFileSize = statbuf.st_size; + mContentType = SINGLEFILE; + return sendFile(&statbuf); + } + + + if (mPath.size() > 8) { + if (mPath.compare(mPath.size() -8, 8, "-seg.mpd") == 0) { + if (handleHeadRequest() != 0) + return OKAY; + sendManifest( &statbuf, false); + return OKAY; + } + } + + if (mPath.size() > 9) { + if (mPath.compare(mPath.size() -9, 9, "-seg.m3u8") == 0) { + if (handleHeadRequest() != 0) + return OKAY; + sendManifest( &statbuf, true); + return OKAY; + } + } + + if (mPath.size() > 7) { + if (mPath.compare(mPath.size() -7, 7, "-seg.ts") == 0) { + if (handleHeadRequest() != 0) + return OKAY; + + sendMediaSegment( &statbuf); + return OKAY; + } + } + + if (mPath.find("/web/") == 0) { + mPath = mFactory->getConfigDir() + mPath; + *(mLog->log())<< DEBUGPREFIX + << " Found web request. serving " << mPath << endl; + ok_to_serve = true; + } + + + if (stat(mPath.c_str(), &statbuf) < 0) { + // checking, whether the file or directory exists + *(mLog->log())<< DEBUGPREFIX + << " File Not found " << mPath << endl; + sendError(404, "Not Found", NULL, "File not found."); + + return OKAY; + } + + if (S_ISDIR(statbuf.st_mode)) { + // Do Folder specific checkings +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " processRequest: isDir - mPath: " << mPath.c_str() << endl; +#endif + + if (mPath.size() >4) { + if (mPath.compare(mPath.size() - 4, 4, ".rec") == 0) { + // Handle any recording directory specifically + if (handleHeadRequest() != 0) + return OKAY; + + mContentType = VDRDIR; + return sendVdrDir( &statbuf); + } + } + + if (!((ok_to_serve) or (mPath.compare(0, (mFactory->getConfig()->getMediaFolder()).size(), mFactory->getConfig()->getMediaFolder()) == 0))) { + // No directory access outside of MediaFolder (and also VDRCONG/web) + sendError(404, "Not Found", NULL, "File not found."); + return OKAY; + } + if (handleHeadRequest() != 0) + return OKAY; + + mContentType = MEMBLOCK; + sendDir( &statbuf); + return OKAY; + } + else { + // mPath is not a folder, thus it is a file +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " processRequest: file send\n"; +#endif + + // Check, if requested file is in Media Directory + if (!((ok_to_serve) or (mPath.compare(0, (mFactory->getConfig()->getMediaFolder()).size(), mFactory->getConfig()->getMediaFolder()) == 0))) { + sendError(404, "Not Found", NULL, "File not found."); + return OKAY; + } + if (handleHeadRequest() != 0) + return OKAY; + mFileSize = statbuf.st_size; + mContentType = SINGLEFILE; + return sendFile(&statbuf); + } + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " processRequest: Not Handled SHOULD not be here\n"; +#endif + return ERROR; +} + + +int cHttpResource::handleWrite() { + + if (mConnState == TOCLOSE) { + *(mLog->log())<< DEBUGPREFIX + << " handleWrite() in wrong state= " << getConnStateName() + << endl; + return ERROR; + } + + if (mConnState != SERVING) { + return OKAY; + } + + if (mBlkLen == mBlkPos) { + // note the mBlk may be filled with header info first. + if (fillDataBlk() != OKAY) { + return ERROR; + } + } + + int this_write = write(mFd, &mBlkData[mBlkPos], mBlkLen - mBlkPos); + if (this_write <=0) { + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " ERROR after write: Stopped (Client terminated Connection)" + << " mBlkPos= " << mBlkPos << " mBlkLen= " << mBlkLen + << DEBUGHDR << endl; +#endif + mConnState = TOCLOSE; + mConnected = false; + return ERROR; + } + mBlkPos += this_write; + + return OKAY; +} + + +int cHttpResource::fillDataBlk() { + char pathbuf[4096]; + + mBlkPos = 0; + int to_read = 0; + + switch(mContentType) { + case NYD: + + break; + case VDRDIR: + // Range requests are assumed to be all open + if (mFile == NULL) { + *(mLog->log()) << DEBUGPREFIX << " no open file anymore " + << "--> Done " << endl; + return ERROR; + } + if ((mRemLength == 0) && !mStreamToEnd){ +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX << " mRemLength is zero " + << "--> Done " << endl; +#endif + fclose(mFile); + mFile = NULL; + return ERROR; + } + if (!mStreamToEnd) + to_read = ((mRemLength > MAXLEN) ? MAXLEN : mRemLength); + else + to_read = MAXLEN; + + mBlkLen = fread(mBlkData, 1, to_read, mFile); + if (!mStreamToEnd) + mRemLength -= mBlkLen; + + if ((mRemLength == 0) && (!mStreamToEnd)) { +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX << " last Block read " + << "--> Almost Done " << endl; +#endif + return OKAY; + } + + // if (mBlkLen != MAXLEN) { // thlo verify + if (mBlkLen != to_read) { + fclose(mFile); + mFile = NULL; + mVdrIdx ++; + + snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), mDir.c_str(), mVdrIdx); + mPath = pathbuf; + + if (openFile(pathbuf) != OKAY) { + *(mLog->log())<< DEBUGPREFIX << " Failed to open file= " << pathbuf << " mRemLength= " << mRemLength<< endl; + mFile = NULL; + if (mBlkLen == 0) { + *(mLog->log()) << DEBUGPREFIX << " mBlkLen is zero --> Done " << endl; + return ERROR; + } + else + *(mLog->log()) << DEBUGPREFIX << " Still data to send mBlkLen= " << mBlkLen <<" --> continue " << endl; + return OKAY; + } // Error: Open next file failed + + if (mBlkLen == 0) { + if (!mStreamToEnd) + to_read = ((mRemLength > MAXLEN) ? MAXLEN : mRemLength); + else + to_read = MAXLEN; + mBlkLen = fread(mBlkData, 1, to_read, mFile); + if (!mStreamToEnd) + mRemLength -= mBlkLen ; + } + } + break; + case SINGLEFILE: + + to_read = ((mRemLength > MAXLEN) ? MAXLEN : mRemLength); + mBlkLen = fread(mBlkData, 1, to_read, mFile); + mRemLength -= mBlkLen; + if (mBlkLen == 0) { + + // read until EOF + fclose(mFile); + mFile = NULL; + return ERROR; + } + break; + case MEMBLOCK: + int rem_len = mResponseMessage->size() - mResponseMessagePos; + if (rem_len == 0) { + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX << " fillDataBlock: MEMBLOCK done" << endl; +#endif + delete mResponseMessage; + mResponseMessagePos = 0; + mConnState = TOCLOSE; + return ERROR; + } + if (rem_len > MAXLEN) + rem_len = MAXLEN; + + string sub_msg = mResponseMessage->substr(mResponseMessagePos, rem_len); + mResponseMessagePos += rem_len; + mBlkLen = sub_msg.size(); + memcpy(mBlkData, sub_msg.c_str(), rem_len); + break; + } + + return OKAY; +} + + +int cHttpResource::parseResume(cResumeEntry &entry, string &id) { + bool done = false; + size_t cur_pos = 0; + + // The asset_id should the the filename, which is provided by the link element in the xml + // the link is url-safe encoded. + + // bool have_devid = false; + bool have_filename = false; + bool have_resume = false; + + while (!done) { + size_t pos = mPayload.find('\n', cur_pos); + if (pos == string::npos) { + done = true; + continue; + } + size_t pos_col = mPayload.find(':', cur_pos); + string attr= mPayload.substr(cur_pos, (pos_col- cur_pos)); + string val = mPayload.substr(pos_col +1, (pos - pos_col-1)); + + /* if (attr== "devid") { + have_devid = true; + id = val; + } + else */ + if (attr == "filename") { + have_filename = true; + entry.mFilename = cUrlEncode::doXmlSaveDecode(val); + *(mLog->log())<< DEBUGPREFIX + << " filename= " << entry.mFilename + << endl; + } + else if (attr == "resume") { + have_resume = true; + entry.mResume = atof(val.c_str()); + *(mLog->log())<< DEBUGPREFIX + << " mResume= " << entry.mResume + << endl; + } + else { + *(mLog->log())<< DEBUGPREFIX + << " parseResume: ERROR: Unknown attr= " << attr + << " with val= " << val + << endl; + } + cur_pos = pos +1; + if (cur_pos >= mPayload.size()) + done= true; + } + if (have_resume && have_filename ) + return OKAY; + else + return ERROR; +} + +int cHttpResource::handleHeadRequest() { + if (mMethod.compare("HEAD") != 0) { + return 0; + } + *(mLog->log())<< DEBUGPREFIX + << " Handle HEAD Request for Url " << mPath << endl; + mConnState = SERVING; + mContentType = MEMBLOCK; + + // sent an empty response message with just the OK header + mResponseMessage = new string(); + *mResponseMessage = ""; + mResponseMessagePos = 0; + + sendHeaders(200, "OK", NULL, NULL, -1, -1); + return 1; +} + +int cHttpResource::handlePost() { + mConnState = SERVING; + mContentType = MEMBLOCK; + + // sent an empty response message with just the OK header + mResponseMessage = new string(); + *mResponseMessage = ""; + mResponseMessagePos = 0; + + if (mPath.compare("/log") == 0) { + *(mLog->log())<< mPayload + << endl; + + sendHeaders(200, "OK", NULL, NULL, -1, -1); + return OKAY; + } + + if (mPath.compare("/getResume.xml") == 0) { + if (handleHeadRequest() != 0) + return OKAY; + + // sendResumeXml( &statbuf); + sendResumeXml( ); + return OKAY; + } + + if (mPath.compare("/setResume.xml") == 0) { + if (handleHeadRequest() != 0) + return OKAY; + + receiveResume(); + return OKAY; + } + + if (mPath.compare("/deleteRecording.xml") == 0) { + if (handleHeadRequest() != 0) + return OKAY; + + deleteRecording(); + return OKAY; + } + + // Should not reach the end of the function + return ERROR; +} + + +void cHttpResource::sendError(int status, const char *title, const char *extra, const char *text) { + char f[400]; + + mConnState = SERVING; + mContentType = MEMBLOCK; + mResponseMessage = new string(); + *mResponseMessage = ""; + mResponseMessagePos = 0; + + string hdr = ""; + // sendHeaders(status, title, extra, "text/html", -1, -1); + sendHeaders(status, title, extra, "text/plain", -1, -1); + + /* snprintf(f, sizeof(f), "%d %s\r\n", status, title); + hdr += f; + snprintf(f, sizeof(f), "

%d %s

\r\n", status, title); + hdr += f; +*/ + snprintf(f, sizeof(f), "%s\r\n", text); + hdr += f; + /* snprintf(f, sizeof(f), "\r\n"); + hdr += f; +*/ + + strcpy(&(mBlkData[mBlkLen]), hdr.c_str()); + mBlkLen += hdr.size(); + +} + + +int cHttpResource::sendDir(struct stat *statbuf) { + char pathbuf[4096]; + char f[400]; + int len; + + mConnState = SERVING; + +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX << " sendDir: mPath= " << mPath << endl; +#endif + len = mPath.length(); + // int ret = OKAY; + + if (len == 0 || mPath[len - 1] != '/') { + snprintf(pathbuf, sizeof(pathbuf), "Location: %s/", mPath.c_str()); + sendError(302, "Found", pathbuf, "Directories must end with a slash."); + return OKAY; + } + + snprintf(pathbuf, sizeof(pathbuf), "%sindex.html", mPath.c_str()); + if (stat(pathbuf, statbuf) >= 0) { + mPath = pathbuf; + mFileSize = statbuf->st_size; + mContentType = SINGLEFILE; + return sendFile(statbuf); + } + +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX << " sendDir: create index.html " << endl; +#endif + DIR *dir; + struct dirent *de; + + mResponseMessage = new string(); + mResponseMessagePos = 0; + *mResponseMessage = ""; + + string hdr = ""; + snprintf(f, sizeof(f), "Index of %s\r\n", mPath.c_str()); + hdr += f; + snprintf(f, sizeof(f), "

Index of %s

\r\n
\n", mPath.c_str());
+  hdr += f;
+  snprintf(f, sizeof(f), "Name                             Last Modified              Size\r\n");
+  hdr += f;
+  snprintf(f, sizeof(f), "
\r\n"); + hdr += f; + + *mResponseMessage += hdr; + hdr = ""; + + if (len > 1) { + snprintf(f, sizeof(f), "..\r\n"); + hdr += f; + } + *mResponseMessage += hdr; + + dir = opendir(mPath.c_str()); + while ((de = readdir(dir)) != NULL) { + char timebuf[32]; + struct tm *tm; + strcpy(pathbuf, mPath.c_str()); + // printf (" -Entry: %s\n", de->d_name); + strcat(pathbuf, de->d_name); + + stat(pathbuf, statbuf); + tm = gmtime(&(statbuf->st_mtime)); + strftime(timebuf, sizeof(timebuf), "%d-%b-%Y %H:%M:%S", tm); + + hdr = ""; + snprintf(f, sizeof(f), "", de->d_name, S_ISDIR(statbuf->st_mode) ? "/" : ""); + hdr += f; + + snprintf(f, sizeof(f), "%s%s", de->d_name, S_ISDIR(statbuf->st_mode) ? "/" : " "); + hdr += f; + + if (strlen(de->d_name) < 32) { + snprintf(f, sizeof(f), "%*s", 32 - strlen(de->d_name), ""); + hdr += f; + } + if (S_ISDIR(statbuf->st_mode)) { + snprintf(f, sizeof(f), "%s\r\n", timebuf); + hdr += f; + } + else { + snprintf(f, sizeof(f), "%s\r\n", timebuf); + hdr += f; + } + + *mResponseMessage += hdr; + } + closedir(dir); + snprintf(f, sizeof(f), "
\r\n
\r\n
%s
\r\n\r\n", SERVER); + *mResponseMessage += f; + + sendHeaders(200, "OK", NULL, "text/html", mResponseMessage->size(), statbuf->st_mtime); + + mRemLength = 0; + return OKAY; +} + + +int cHttpResource::writeXmlItem(string name, string link, string programme, string desc, string guid, int no, time_t start, int dur, double fps, int is_pes, int is_new) { + string hdr = ""; + char f[400]; + + hdr += "\n"; + // snprintf(f, sizeof(f), "%s - %s", ); + hdr += "" + name +"\n"; + hdr += "" +link + "\n"; + hdr += "\n"; + + hdr += "" + guid + "\n"; + + snprintf(f, sizeof(f), "%d", no); + hdr += ""; + hdr += f; + hdr += "\n"; + + hdr += "" + programme +"\n"; + hdr += "" + desc + "\n"; + + snprintf(f, sizeof(f), "%ld", start); + hdr += ""; + hdr += f; + hdr += "\n"; + + snprintf(f, sizeof(f), "%d", dur); + hdr += ""; + hdr += f; + hdr += "\n"; + + if (fps != -1) + snprintf(f, sizeof(f), "%.2f\n", fps); + else + snprintf(f, sizeof(f), "unknown\n"); + hdr += f; + + switch (is_pes){ + case -1: + // unknown + hdr += "unknown\n"; + break; + case 0: + // true + hdr += "true\n"; + break; + case 1: + // false + hdr += "false\n"; + break; + default: + break; + } + + switch (is_new){ + case -1: + // unknown + hdr += "unknown\n"; + break; + case 0: + // true + hdr += "true\n"; + break; + case 1: + // false + hdr += "false\n"; + break; + default: + break; + } + + hdr += "\n"; + + *mResponseMessage += hdr; + + + + + // return writeToClient(hdr.c_str(), hdr.size()); + return OKAY; +} + +int cHttpResource::parseQueryLine (vector *avps) { + bool done = false; + size_t cur_pos = 0; + while (!done) { + size_t end_pos = mQuery.find('&', cur_pos); + size_t pos_eq = mQuery.find('=', cur_pos); + + if (pos_eq != cur_pos) { + avps->push_back(sQueryAVP(mQuery.substr(cur_pos, (pos_eq -cur_pos)), mQuery.substr(pos_eq+1, (end_pos -pos_eq-1)) )); + } + if (end_pos == string::npos) + done = true; + else + cur_pos = end_pos +1; + } + + return OKAY; +} + +int cHttpResource::getQueryAttributeValue(vector *avps, string attr, string &val) { + int found = ERROR; + for (uint i = 0; i < avps->size(); i++) { + if ((*avps)[i].attribute == attr) { + val = (*avps)[i].value; + found = OKAY; + } +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " a= " + << (*avps)[i].attribute + << " v= " << (*avps)[i].value + << endl; +#endif + } + return found; +} + +int cHttpResource::parseFiles(vector *entries, string prefix, string dir_base, string dir_name, struct stat *statbuf) { + char pathbuf[4096]; + string link; + DIR *dir; + struct dirent *de; + string dir_comp; + dir_comp = dir_base + dir_name + "/"; + +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX + << " parseFiles: Prefix= " << prefix + << " base= " << dir_base + << " dir= " << dir_name + << " comp= " << dir_comp + << endl; +#endif + + dir = opendir(dir_comp.c_str()); + if (stat(dir_comp.c_str(), statbuf) < 0) + return ERROR; + + while ((de = readdir(dir)) != NULL) { + if ((strcmp(de->d_name, ".") == 0) or (strcmp(de->d_name, "..") == 0)) { + continue; + } + + strcpy(pathbuf, dir_comp.c_str()); + strcat(pathbuf, de->d_name); + + stat(pathbuf, statbuf); + + if (S_ISDIR(statbuf->st_mode)) { + if (strcmp(&(pathbuf[strlen(pathbuf)-4]), ".rec") == 0) { + // vdr folder + time_t now = time(NULL); + struct tm tm_r; + struct tm t = *localtime_r(&now, &tm_r); + t.tm_isdst = -1; + // char [20] rest; + int start = -1; + sscanf(de->d_name, "%4d-%02d-%02d.%02d.%02d", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min); + + // sscanf(de->d_name, "%4d-%02d-%02d.%02d%.%02d", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min); + t.tm_year -= 1900; + t.tm_mon--; + t.tm_sec = 0; + start = mktime(&t); + +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX + << " Vdr Folder Found: " << pathbuf << " start= " << start << endl; +#endif + + entries->push_back(sFileEntry(dir_name, pathbuf, start)); + } + else { + // regular file + parseFiles(entries, prefix + de->d_name + "~", dir_comp, de->d_name, statbuf); + } + } + else { + if ((de->d_name)[0] != '.' ) + entries->push_back(sFileEntry(prefix+de->d_name, pathbuf, 1)); + } + } + closedir(dir); + return OKAY; +} + +int cHttpResource::sendManifest (struct stat *statbuf, bool is_hls) { +#ifndef STANDALONE + + size_t pos = mPath.find_last_of ("/"); + + mDir = mPath.substr(0, pos); + string mpd_name = mPath.substr(pos+1); + + float seg_dur = mFactory->getConfig()->getSegmentDuration() *1.0; + + cRecordings* recordings = &Recordings; + cRecording* rec = recordings->GetByName(mDir.c_str()); + double duration = rec->NumFrames() / rec->FramesPerSecond(); + + int bitrate = (int)((getVdrFileSize() *8.0 * mFactory->getConfig()->getHasBitrateCorrection()/ duration) +0.5); + + time_t now = time(NULL); + + if (rec->Info() != NULL){ + if (rec->Info()->GetEvent() != NULL) { + if (rec->Info()->GetEvent()->EndTime() > now) { + + float corr = (now - rec->Info()->GetEvent()->StartTime()) - duration; + duration = rec->Info()->GetEvent()->Duration() -int(corr); + *(mLog->log()) << DEBUGPREFIX + << " is Recording: Duration= " << duration << " sec" + << " correction: " << int(corr) + << endl; + } + } + else + *(mLog->log()) << DEBUGPREFIX << " WARNING: rec-Info()->GetEvent() is NULL " << endl; + } + else + *(mLog->log()) << DEBUGPREFIX << " WARNING: rec-Info() is NULL " << endl; + + // duration is now either the actual duration of the asset or the target duration of the asset + int end_seg = int (duration / seg_dur) +1; + + *(mLog->log()) << DEBUGPREFIX + << " Manifest for mDir= " << mDir + << " duration= " << duration + << " seg_dur= " << seg_dur + << " end_seg= " << end_seg + << endl; + + + + if (is_hls) { + writeM3U8(duration, bitrate, seg_dur, end_seg); + } + else { + writeMPD(duration, bitrate, seg_dur, end_seg); + } + +#endif + return OKAY; +} + +void cHttpResource::writeM3U8(double duration, int bitrate, float seg_dur, int end_seg) { + mResponseMessage = new string(); + mResponseMessagePos = 0; + *mResponseMessage = ""; + mContentType = MEMBLOCK; + + mConnState = SERVING; + char buf[30]; + + string hdr = ""; + + + *mResponseMessage += "#EXTM3U\n"; + // snprintf(buf, sizeof(buf), "#EXT-X-TARGETDURATION:%d\n", (seg_dur-1)); + snprintf(buf, sizeof(buf), "#EXT-X-TARGETDURATION:%d\n", int(seg_dur)); + hdr = buf; + *mResponseMessage += hdr; + + *mResponseMessage += "#EXT-X-MEDIA-SEQUENCE:1\n"; + *mResponseMessage += "#EXT-X-KEY:METHOD=NONE\n"; + + for (int i = 1; i < end_seg; i++){ + // snprintf(buf, sizeof(buf), "#EXTINF:%.1f,\n", (seg_dur-0.5)); + snprintf(buf, sizeof(buf), "#EXTINF:%.2f,\n", seg_dur); + hdr = buf; + *mResponseMessage += hdr; + + snprintf(buf, sizeof(buf), "%d-seg.ts\n", i); + hdr = buf; + *mResponseMessage += hdr; + } + *mResponseMessage += "#EXT-X-ENDLIST\n"; + + sendHeaders(200, "OK", NULL, "application/x-mpegURL", mResponseMessage->size(), -1); +} + + +void cHttpResource::writeMPD(double duration, int bitrate, float seg_dur, int end_seg) { + mResponseMessage = new string(); + mResponseMessagePos = 0; + *mResponseMessage = ""; + mContentType = MEMBLOCK; + + mConnState = SERVING; + char buf[30]; + char line[400]; + + string hdr = ""; + + *mResponseMessage += "\n"; + + snprintf(line, sizeof(line), "getConfig()->getHasMinBufferTime(), duration); + *mResponseMessage = *mResponseMessage + line; + + *mResponseMessage += " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema\" xmlns=\"urn:mpeg:mpegB:schema:DASH:MPD:DIS2011\" "; + *mResponseMessage += "xsi:schemaLocation=\"urn:mpeg:mpegB:schema:DASH:MPD:DIS2011\">\n"; + *mResponseMessage += "\n"; + *mResponseMessage += "\n"; + *mResponseMessage += "\n"; + *mResponseMessage += "\n"; + // SD: 720x 576 + // HD: 1280x 720 + // snprintf(line, sizeof(line), "\n", mFactory->getConfig()->getHasBitrate()); + snprintf(line, sizeof(line), "\n", + bitrate, ((bitrate < 10000000)? "width=\"720\" height=\"576\"" : "width=\"1280\" height=\"720\"")); + *mResponseMessage = *mResponseMessage + line; + + hdr = "\n"; + *mResponseMessage += hdr; + + snprintf(buf, sizeof(buf), "\"%d\"", end_seg); + *mResponseMessage += "\n"; + + *mResponseMessage += "\n"; + *mResponseMessage += "\n"; + *mResponseMessage += "\n"; + *mResponseMessage += ""; + + sendHeaders(200, "OK", NULL, "application/x-mpegURL", mResponseMessage->size(), -1); +} + +int cHttpResource::sendMediaSegment (struct stat *statbuf) { +#ifndef STANDALONE + + *(mLog->log()) << DEBUGPREFIX << " sendMediaSegment " << mPath << endl; + size_t pos = mPath.find_last_of ("/"); + + mDir = mPath.substr(0, pos); + string seg_name = mPath.substr(pos+1); + int seg_number; + + int seg_dur = mFactory->getConfig()->getSegmentDuration(); + int frames_per_seg = 0; + + sscanf(seg_name.c_str(), "%d-seg.ts", &seg_number); + + //FIXME: Do some consistency checks on the seg_number + //* Does the segment exist + + cRecordings* recordings = &Recordings; + cRecording* rec = recordings->GetByName(mDir.c_str()); + if (rec != NULL) { + frames_per_seg = seg_dur * rec->FramesPerSecond(); + } + else { + *(mLog->log()) << DEBUGPREFIX << " ERROR: Ooops, rec is NULL, assuming 25 fps " << endl; + frames_per_seg = seg_dur * 25; + } + //FIXME: HD Fix + // frames_per_seg = seg_dur * 25; + + *(mLog->log()) << DEBUGPREFIX + << " mDir= " << mDir + << " seg_name= " << seg_name + << " seg_number= "<< seg_number + << " fps= " << rec->FramesPerSecond() + << " frames_per_seg= " << frames_per_seg + << endl; + int start_frame_count = (seg_number -1) * frames_per_seg; + + FILE* idx_file = fopen((mDir +"/index").c_str(), "r"); + if (idx_file == NULL){ + *(mLog->log()) << DEBUGPREFIX + << " failed to open idx file = "<< (mDir +"/index").c_str() + << endl; + sendError(404, "Not Found", NULL, "Failed to open Index file"); + return OKAY; + } + + char *index_buf = new char[(frames_per_seg +3) *8]; + + // fseek to start_frame_count * sizeof(in_read) + fseek(idx_file, start_frame_count * 8, SEEK_SET); + + // read to (seg_number * frames_per_seg +1) * sizeof(in_read) + // buffersize is frames_per_seg * seg_number * sizeof(in_read) + int buffered_indexes = fread(index_buf, 8, (frames_per_seg +2), idx_file); + + fclose(idx_file); + + if(buffered_indexes <= 0 ) { + *(mLog->log())<log()) << DEBUGPREFIX + << " GenSegment: start (no/idx)= " << start_idx << " / " << start_offset + << " to (no/idx)= " << end_idx << " / " << end_offset + << endl; + /*#endif*/ + + delete[] index_buf; + + int rem_len = 0; + bool error = false; + if (start_idx == end_idx){ + mRemLength = (end_offset - start_offset); + +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX + << " start_idx == end_idx: mRemLength= " <log()) << DEBUGPREFIX + << " start_idx < end_idx " + << endl; +#endif + snprintf(seg_fn, sizeof(seg_fn), mFileStructure.c_str(), mDir.c_str(), mVdrIdx); + if (stat(seg_fn, statbuf) < 0) { + *(mLog->log()) << DEBUGPREFIX + << " file= " <st_size - start_offset; // remaining length of the first segment + + // loop over all idx files between start_idx and end_idx + for (int idx = (start_idx+1); idx < end_idx; idx ++) { + snprintf(seg_fn, sizeof(seg_fn), mFileStructure.c_str(), mDir.c_str(), idx); + if (stat(seg_fn, statbuf) < 0) { + *(mLog->log()) << DEBUGPREFIX + << " for loop file= " <st_size; // remaining length of the first segment + } + rem_len += end_offset; // + mRemLength = rem_len; + snprintf(seg_fn, sizeof(seg_fn), mFileStructure.c_str(), mDir.c_str(), mVdrIdx); + +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX + << " start_idx= " << start_idx << " != end_idx= "<< end_idx <<": mRemLength= " <log())<< DEBUGPREFIX << " Failed to open file= " << seg_fn + << " mRemLength= " << mRemLength<< endl; + sendError(404, "Not Found", NULL, "File not found."); + return OKAY; + } + fseek(mFile, start_offset, SEEK_SET); + + sendHeaders(200, "OK", NULL, "video/mpeg", mRemLength, -1); + +#endif + return OKAY; +} + +int cHttpResource::sendMediaXml (struct stat *statbuf) { + char pathbuf[4096]; + string link; + string media_folder = mFactory->getConfig()->getMediaFolder(); + + mResponseMessage = new string(); + mResponseMessagePos = 0; + *mResponseMessage = ""; + mContentType = MEMBLOCK; + + mConnState = SERVING; + +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX << " sendMedia " << endl; +#endif + + string own_ip = getOwnIp(mFd); + *(mLog->log()) << " OwnIP= " << own_ip << endl; + + vector entries; + + if (parseFiles(&entries, "", media_folder, "", statbuf) == ERROR) { + sendError(404, "Not Found", NULL, "Media Folder likely not configured."); + return OKAY; + } + + string hdr = ""; + hdr += "\n"; + hdr += "\n"; + hdr+= "\n"; + + *mResponseMessage += hdr; + + hdr = ""; + + for (uint i=0; i < entries.size(); i++) { + + // snprintf(pathbuf, sizeof(pathbuf), "http://%s:%d%s", mServerAddr.c_str(), mServerPort, + snprintf(pathbuf, sizeof(pathbuf), "http://%s:%d%s", own_ip.c_str(), mServerPort, + cUrlEncode::doUrlSaveEncode(entries[i].sPath).c_str()); + if (writeXmlItem(cUrlEncode::doXmlSaveEncode(entries[i].sName), pathbuf, "NA", "NA", "-", + -1, entries[i].sStart, -1, -1, -1, -1) == ERROR) + return ERROR; + + } + + hdr = "\n"; + hdr += "\n"; + *mResponseMessage += hdr; + sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), statbuf->st_mtime); + + return OKAY; +} + +int cHttpResource::sendVdrStatusXml (struct stat *statbuf) { + +#ifndef STANDALONE + + char f[400]; + mResponseMessage = new string(); + *mResponseMessage = ""; + mResponseMessagePos = 0; + mContentType = MEMBLOCK; + + mConnState = SERVING; + + int free; + int used; + int percent; + + percent = VideoDiskSpace(&free, &used); + + *mResponseMessage += "\n"; + *mResponseMessage += "\n"; + + *mResponseMessage += "\n"; + snprintf(f, sizeof(f), "%d", free); + *mResponseMessage += f; + + snprintf(f, sizeof(f), "%d", used); + *mResponseMessage += f; + snprintf(f, sizeof(f), "%d", percent); + *mResponseMessage += f; + *mResponseMessage += "\n"; + + *mResponseMessage += "\n"; + + sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), -1); + +#endif + return OKAY; +} + +int cHttpResource::sendEpgXml (struct stat *statbuf) { +#ifndef STANDALONE + + char f[400]; + mResponseMessage = new string(); + *mResponseMessage = ""; + mResponseMessagePos = 0; + mContentType = MEMBLOCK; + + mConnState = SERVING; + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " generating /epg.xml" + << DEBUGHDR << endl; +#endif + vector avps; + parseQueryLine(&avps); + string id = "S19.2E-1-1107-17500"; + string mode = ""; + bool add_desc = true; + + if (getQueryAttributeValue(&avps, "id", id) == ERROR){ + *(mLog->log())<< DEBUGPREFIX + << " ERROR: id not found" + << DEBUGHDR << endl; + delete mResponseMessage; + sendError(400, "Bad Request", NULL, "no id in query line"); + return OKAY; + } + + if (getQueryAttributeValue(&avps, "mode", mode) == OKAY){ + if (mode == "nodesc") { + add_desc = false; + *(mLog->log())<< DEBUGPREFIX + << " **** Mode: No Description ****" + << endl; + } + } + + /* for (int i = 0; i < avps.size(); i++) { + if (avps[i].attribute == "id") + id = avps[i].value; + *(mLog->log())<< DEBUGPREFIX + << " a= " + << avps[i].attribute + << " v= " << avps[i].value + << endl; + }*/ + tChannelID chan_id = tChannelID::FromString (id.c_str()); + if ( chan_id == tChannelID::InvalidID) { + *(mLog->log())<< DEBUGPREFIX + << " ERROR: Not possible to get the ChannelId from the string" + << DEBUGHDR << endl; + delete mResponseMessage; + sendError(400, "Bad Request", NULL, "Invalid Channel ID."); + return OKAY; + } + + cSchedulesLock * lock = new cSchedulesLock(false, 500); + const cSchedules *schedules = cSchedules::Schedules(*lock); + + const cSchedule *schedule = schedules->GetSchedule(chan_id); + if (schedule == NULL) { + *(mLog->log())<< DEBUGPREFIX + << "ERROR: Schedule is zero for guid= " << id + << endl; + delete mResponseMessage; + sendError(500, "Internal Server Error", NULL, "Schedule is zero."); + return OKAY; + } + + time_t now = time(NULL); + const cEvent *ev = NULL; + for(cEvent* e = schedule->Events()->First(); e; e = schedule->Events()->Next(e)) { + if ( (e->StartTime() <= now) && (e->EndTime() > now)) { + ev = e; + + } else if (e->StartTime() > now + 3600) { + break; + } + } + + // const cEvent * ev = schedule->GetPresentEvent(); + + if (ev == NULL) { + *(mLog->log())<< DEBUGPREFIX + << "ERROR: Event is zero for guid= " << id + << endl; + delete mResponseMessage; + sendError(500, "Internal Server Error", NULL, "Event is zero."); + return OKAY; + } + + string hdr = ""; + hdr += "\n"; + hdr += "\n"; + hdr+= "\n"; + + *mResponseMessage += hdr; + // Payload here + + hdr = ""; + if (ev->Title() != NULL) { + string title = cUrlEncode::doXmlSaveEncode(ev->Title()); + hdr += "" + title +"\n"; + } + else { + *(mLog->log())<< DEBUGPREFIX + << " ERROR: title is zero for guid= " << id << endl; + hdr += "Empty\n"; + + delete mResponseMessage; + sendError(500, "Internal Server Error", NULL, "Title is zero."); + return OKAY; + } + + hdr += "" + id + "\n"; + + *(mLog->log())<< DEBUGPREFIX + << " guid= " << id + << " title= " << ev->Title() + << " start= " << ev->StartTime() + << " end= " << ev->EndTime() + << " now= " << now + << endl; + if (add_desc) { + hdr += ""; + if (ev->Description() != NULL) { + hdr += cUrlEncode::doXmlSaveEncode(ev->Description()); + } + else { + *(mLog->log())<< DEBUGPREFIX + << " ERROR: description is zero for guid= " << id << endl; + + delete mResponseMessage; + sendError(500, "Internal Server Error", NULL, "Description is zero."); + return OKAY; + } + hdr += "\n"; + } + else { + hdr += "No Description Available\n"; + } + snprintf(f, sizeof(f), "%ld\n", ev->StartTime()); + hdr += f ; + + snprintf(f, sizeof(f), "%ld\n", ev->EndTime()); + hdr += f; + + snprintf(f, sizeof(f), "%d\n", ev->Duration()); + hdr += f; + *mResponseMessage += hdr; + + hdr = "\n"; + hdr += "\n"; + + *mResponseMessage += hdr; + + delete lock; + + sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), -1); + +#endif + return OKAY; +} + +int cHttpResource::sendChannelsXml (struct stat *statbuf) { +#ifndef STANDALONE + + char f[400]; + mResponseMessage = new string(); + *mResponseMessage = ""; + mResponseMessagePos = 0; + mContentType = MEMBLOCK; + + mConnState = SERVING; + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " generating /channels.xml" + << DEBUGHDR << endl; +#endif + string own_ip = getOwnIp(mFd); + *(mLog->log()) << " OwnIP= " << own_ip << endl; + + vector avps; + parseQueryLine(&avps); + string mode = ""; + bool add_desc = true; + + string no_channels_str = ""; + int no_channels = -1; + string group_sep = ""; + + if (getQueryAttributeValue(&avps, "mode", mode) == OKAY){ + if (mode == "nodesc") { + add_desc = false; + *(mLog->log())<< DEBUGPREFIX + << " Mode: No Description" + << endl; + } + else { + *(mLog->log())<< DEBUGPREFIX + << " Mode: Unknown" + << endl; + } + } + if (getQueryAttributeValue(&avps, "channels", no_channels_str) == OKAY){ + no_channels = atoi(no_channels_str.c_str()) ; + } + + + string hdr = ""; + hdr += "\n"; + hdr += "\n"; + hdr+= "\n"; + hdr+= "VDR Channels List\n"; + + + *mResponseMessage += hdr; + + int count = mFactory->getConfig()->getLiveChannels(); + if (no_channels > 0) + count = no_channels +1; + + cSchedulesLock * lock = new cSchedulesLock(false, 500); + const cSchedules *schedules = cSchedules::Schedules(*lock); + + for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) { + if (channel->GroupSep()) { + if (mFactory->getConfig()->getGroupSep() != IGNORE) { + // if emtpyFolderDown, always. + // otherwise, only when not empty + if (!((strcmp(channel->Name(), "") == 0) && (mFactory->getConfig()->getGroupSep() == EMPTYIGNORE))) + group_sep = cUrlEncode::doXmlSaveEncode(channel->Name()); + + } + continue; + } + if (--count == 0) { + break; + } + + // snprintf(f, sizeof(f), "http://%s:3000/%s.ts", mServerAddr.c_str(), *(channel->GetChannelID()).ToString()); + snprintf(f, sizeof(f), "http://%s:3000/%s.ts", own_ip.c_str(), *(channel->GetChannelID()).ToString()); + string link = f; + + const cSchedule *schedule = schedules->GetSchedule(channel->GetChannelID()); + string desc = "No description available"; + string title = "Not available"; + time_t start_time = 0; + int duration = 0; + + if (schedule != NULL) { + const cEvent *ev = schedule->GetPresentEvent(); + if (ev != NULL) { + if ((ev->Description() != NULL) && add_desc) + desc = cUrlEncode::doXmlSaveEncode(ev->Description()); + + if ((ev->Title() != NULL) && add_desc) + title = cUrlEncode::doXmlSaveEncode(ev->Title()); + start_time = ev->StartTime(); + duration = ev->Duration(); + } + else { + *(mLog->log())<< DEBUGPREFIX + << " Event Info is Zero for Count= " + << count + << " Name= " << channel->Name() << endl; + } + } + else { + *(mLog->log())<< DEBUGPREFIX + << " Schedule is Zero for Count= " + << count + << " Name= " << channel->Name() << endl; + } + + string c_name = (group_sep != "") ? (group_sep + "~" + cUrlEncode::doXmlSaveEncode(channel->Name())) + : cUrlEncode::doXmlSaveEncode(channel->Name()); + // if (writeXmlItem(channel->Name(), link, title, desc, *(channel->GetChannelID()).ToString(), start_time, duration) == ERROR) + if (writeXmlItem(c_name, link, title, desc, *(channel->GetChannelID()).ToString(), channel->Number(), start_time, duration, -1, -1, -1) == ERROR) + return ERROR; + + } + + hdr = "\n"; + hdr += "\n"; + + *mResponseMessage += hdr; + delete lock; + sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), statbuf->st_mtime); + +#endif + return OKAY; +} + +int cHttpResource::receiveResume() { + string dev_id; + cResumeEntry entry; + if (parseResume(entry, dev_id) == ERROR) { + *(mLog->log())<< DEBUGPREFIX + << " ERROR parsing resume" + << endl; + } + + *(mLog->log())<< DEBUGPREFIX + << " Resume: id= " << dev_id + << " resume= " << entry << endl; + + vector avps; + parseQueryLine(&avps); + string guid; + string resume_str; + + if (getQueryAttributeValue(&avps, "guid", guid) == OKAY){ + entry.mFilename = guid; + *(mLog->log())<< DEBUGPREFIX + << " Found a id Parameter: " << guid + << endl; + } + if (getQueryAttributeValue(&avps, "resume", resume_str) == OKAY){ + entry.mResume = atof(resume_str.c_str()); + *(mLog->log())<< DEBUGPREFIX + << " Found a resume Parameter: " << entry.mResume + << endl; + } + + + + +#ifndef STANDALONE + cRecording *rec = Recordings.GetByName(entry.mFilename.c_str()); + if (rec == NULL) { + //Error 404 + sendError(404, "Not Found", NULL, "Failed to find recording."); + return OKAY; + } + + cResumeFile resume(entry.mFilename.c_str(), rec->IsPesRecording()); + *(mLog->log())<< DEBUGPREFIX + << " Resume: " << entry.mFilename + << " saving Index= " << int(entry.mResume * rec->FramesPerSecond() ) + << " mResume= " < avps; + parseQueryLine(&avps); + string guid; + + if (getQueryAttributeValue(&avps, "guid", guid) == OKAY){ + entry.mFilename = guid; + *(mLog->log())<< DEBUGPREFIX + << " Found a id Parameter: " << guid + << endl; + } + + + cRecording *rec = Recordings.GetByName(entry.mFilename.c_str()); + if (rec == NULL) { + //Error 404 + *(mLog->log())<< DEBUGPREFIX + << " sendResume: File Not Found filename= " << entry.mFilename << endl; + sendError(404, "Not Found", NULL, "Failed to find recording."); + return OKAY; + } + if (rec->IsNew()) { + *(mLog->log())<< DEBUGPREFIX + << " sendResume: file is new " << endl; + sendError(400, "Bad Request", NULL, "File is new."); + return OKAY; + } + cResumeFile resume(entry.mFilename.c_str(), rec->IsPesRecording()); + + *(mLog->log())<< DEBUGPREFIX + << " resume request for " << entry.mFilename + << " resume= " << resume.Read() + << " (" << resume.Read() *1.0 / rec->FramesPerSecond() << "sec)" + << endl; + + *mResponseMessage += "\n"; + *mResponseMessage += ""; + snprintf(f, sizeof(f), "%.02f", resume.Read() *1.0 / rec->FramesPerSecond()); + *mResponseMessage += f; + *mResponseMessage += "\n"; + + + sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), -1); + + return OKAY; +#endif +} + +int cHttpResource::deleteRecording() { + vector avps; + parseQueryLine(&avps); + string id = ""; + + if (getQueryAttributeValue(&avps, "id", id) == ERROR){ + *(mLog->log())<< DEBUGPREFIX + << " ERROR: id not found in query." + << DEBUGHDR << endl; + sendError(400, "Bad Request", NULL, "no id in query line"); + return OKAY; + } + mPath = cUrlEncode::doUrlSaveDecode(id); + + cRecording* rec = Recordings.GetByName(mPath.c_str()); + if (rec == NULL) { + *(mLog->log())<< DEBUGPREFIX + << " ERROR: Recording not found. Deletion failed: mPath= " << mPath + << endl; + sendError(404, "Not Found.", NULL, "Recording not found. Deletion failed!"); + return OKAY; + } + if ( rec->Delete() ) { + Recordings.DelByName(rec->FileName()); + // Recordings.DelByName(mPath.c_str()); + } + else { + *(mLog->log())<< DEBUGPREFIX + << " ERROR: rec->Delete() returns false. mPath= " << mPath + << endl; + sendError(500, "Internal Server Error", NULL, "deletion failed!"); + return OKAY; + } + + *(mLog->log())<< DEBUGPREFIX + << " Deleted." + << endl; + sendHeaders(200, "OK", NULL, NULL, -1, -1); + return OKAY; +} + + +int cHttpResource::sendRecordingsXml(struct stat *statbuf) { +#ifndef STANDALONE + + mResponseMessage = new string(); + *mResponseMessage = ""; + mResponseMessagePos = 0; + mContentType = MEMBLOCK; + + mConnState = SERVING; + + string own_ip = getOwnIp(mFd); + *(mLog->log()) << " OwnIP= " << own_ip << endl; + + vector avps; + parseQueryLine(&avps); + string model = ""; + string link_ext = ""; + string type = ""; + string has_4_hd_str = ""; + bool has_4_hd = true; + string mode = ""; + bool add_desc = true; + + if (getQueryAttributeValue(&avps, "model", model) == OKAY){ + *(mLog->log())<< DEBUGPREFIX + << " Found a Model Parameter: " << model + << endl; + } + + if (getQueryAttributeValue(&avps, "type", type) == OKAY){ + *(mLog->log())<< DEBUGPREFIX + << " Found a Type Parameter: " << type + << endl; + if (type == "hls") { + if (model == "samsung") + link_ext = "/manifest-seg.m3u8|COMPONENT=HLS"; + else + link_ext = "/manifest-seg.m3u8"; + } + if (type == "has") { + if (model == "samsung") + link_ext = "/manifest-seg.mpd|COMPONENT=HAS"; + else + link_ext = "/manifest-seg.mpd"; + } + } + + if (getQueryAttributeValue(&avps, "mode", mode) == OKAY){ + if (mode == "nodesc") { + add_desc = false; + *(mLog->log())<< DEBUGPREFIX + << " Mode: No Description" + << endl; + } + } + + if (getQueryAttributeValue(&avps, "has4hd", has_4_hd_str) == OKAY){ + *(mLog->log())<< DEBUGPREFIX + << " Found a Has4Hd Parameter: " << has_4_hd_str + << endl; + if (has_4_hd_str == "false") + has_4_hd = false; + } + + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " generating /recordings.xml" + << DEBUGHDR << endl; +#endif + sendHeaders(200, "OK", NULL, "application/xml", -1, statbuf->st_mtime); + + string hdr = ""; + hdr += "\n"; + hdr += "\n"; + hdr+= "\n"; + hdr+= "VDR Recordings List\n"; + + *mResponseMessage += hdr; + + /* + if (writeXmlItem("HAS - Big Bugs Bunny", "http://192.168.1.122/sm/BBB-DASH/HAS_BigBuckTS.xml|COMPONENT=HAS", "NA", "Big Bucks Bunny - HAS", + "-", 0, 0) == ERROR) + return ERROR; + + if (writeXmlItem("HLS - Big Bugs Bunny", "http://192.168.1.122/sm/BBB-DASH/HLS_BigBuckTS.m3u8|COMPONENT=HLS", "NA", "Big Bucks Bunny - HLS", + "-", 0, 0) == ERROR) + return ERROR; + if (writeXmlItem("HAS - Big Bugs Bunny", "http://192.168.1.122:8000/hd2/mpeg/BBB-DASH/HAS_BigBuckTS.xml|COMPONENT=HAS", "NA", "Big Bucks Bunny - HAS from own Server", + "-", 0, 0) == ERROR) + return ERROR; +*/ + //-------------------- + cRecordings* recordings = &Recordings; + char f[600]; + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " recordings->Count()= " << recordings->Count() + << DEBUGHDR << endl; +#endif + + // List of recording timer + time_t now = time(NULL); + + vector act_rec; + /*#ifndef DEBUG*/ + *(mLog->log())<< DEBUGPREFIX + << " checking avtive timer" + << endl; + /*#endif*/ + for (cTimer * ti = Timers.First(); ti; ti = Timers.Next(ti)){ + ti->Matches(); + + /* *(mLog->log()) << DEBUGPREFIX + << " Active Timer: " << ti->File() + << " EvStart= " << ti->Event()->StartTime() + << " EvDuration= " << ti->Event()->Duration() + << endl;*/ + /* *(mLog->log()) << DEBUGPREFIX + << " TiStart= " << ti->StartTime() + << " TiDuration= " << (ti->StopTime() - ti->StartTime()) + << endl; +*/ + if (ti->HasFlags(tfRecording) ) { + /* + if (ti->Event() == NULL) { + *(mLog->log()) << DEBUGPREFIX + << " WARNING: Active recording for " << ti->File() + << " is skipped (No Event()" << endl; + continue; + } + */ + /* *(mLog->log()) << DEBUGPREFIX + << " Active Timer: " << ti->File() + << " Start= " << ti->Event()->StartTime() + << " Duration= " << ti->Event()->Duration() + << endl; +*/ + *(mLog->log()) << DEBUGPREFIX + << " Active Timer: " << ti->File() + << " Start= " << ti->StartTime() + << " Duration= " << (ti->StopTime() - ti->StartTime()) + << endl; + // act_rec.push_back(sTimerEntry(ti->File(), ti->Event()->StartTime(), ti->Event()->Duration())); + act_rec.push_back(sTimerEntry(ti->File(), ti->StartTime(), (ti->StopTime() - ti->StartTime()))); + } + } + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " Found " << act_rec.size() + << " running timers" + << endl; +#endif + + int rec_dur = 0; + for (cRecording *recording = recordings->First(); recording; recording = recordings->Next(recording)) { + hdr = ""; + + if (recording->IsPesRecording() or ((recording->FramesPerSecond() > 30.0) and !has_4_hd )) + // snprintf(f, sizeof(f), "http://%s:%d%s", mServerAddr.c_str(), mServerPort, + snprintf(f, sizeof(f), "http://%s:%d%s", own_ip.c_str(), mServerPort, + cUrlEncode::doUrlSaveEncode(recording->FileName()).c_str()); + else + // snprintf(f, sizeof(f), "http://%s:%d%s%s", mServerAddr.c_str(), mServerPort, + snprintf(f, sizeof(f), "http://%s:%d%s%s", own_ip.c_str(), mServerPort, + cUrlEncode::doUrlSaveEncode(recording->FileName()).c_str(), link_ext.c_str()); + + string link = f; + string desc = "No description available"; + rec_dur = recording->LengthInSeconds(); + + string name = recording->Name(); + + for (uint x = 0; x < act_rec.size(); x++) { + if (act_rec[x].name == name) { + + /* *(mLog->log())<< DEBUGPREFIX + << " !!!!! Found active Recording !!! " + << endl; +*/ + rec_dur += (act_rec[x].startTime + act_rec[x].duration - now); + + + } + } // for + + if (recording->Info() != NULL) { + if ((recording->Info()->Description() != NULL) && add_desc) { + desc = cUrlEncode::doXmlSaveEncode(recording->Info()->Description()); + } + } + + if (writeXmlItem(cUrlEncode::doXmlSaveEncode(recording->Name()), link, "NA", desc, + cUrlEncode::doUrlSaveEncode(recording->FileName()).c_str(), + -1, + recording->Start(), rec_dur, recording->FramesPerSecond(), + (recording->IsPesRecording() ? 0: 1), (recording->IsNew() ? 0: 1)) == ERROR) + // Better Internal Server Error + return ERROR; + + } + + hdr = "\n"; + hdr += "\n"; + + *mResponseMessage += hdr; + + +#endif + return OKAY; +} + +bool cHttpResource::isTimeRequest(struct stat *statbuf) { + + vector avps; + parseQueryLine(&avps); + string time_str = ""; + string mode = ""; + float time = 0.0; + + if (getQueryAttributeValue(&avps, "time", time_str) != OKAY){ + return false; + } + + if (getQueryAttributeValue(&avps, "mode", mode) == OKAY){ + if (mode.compare ("streamtoend") ==0) { + mStreamToEnd = true; + } + } + + if (mIsRecording) + mStreamToEnd = true; + + time = atof(time_str.c_str()); + *(mLog->log())<< DEBUGPREFIX + << " Found a Time Parameter: " << time + << " streamToEnd= " << ((mStreamToEnd) ? "true" : "false") + << endl; + + mDir = mPath; + cRecording *rec = Recordings.GetByName(mPath.c_str()); + if (rec == NULL) { + *(mLog->log())<< DEBUGPREFIX + << " Error: Did not find recording= " << mPath << endl; + sendError(404, "Not Found", NULL, "File not found."); + return true; + } + + double fps = rec->FramesPerSecond(); + double dur = rec->NumFrames() * fps; + bool is_pes = rec->IsPesRecording(); + if (dur < time) { + sendError(400, "Bad Request", NULL, "Time to large."); + return true; + } + + int start_frame = int(time * fps) -25; + + FILE* idx_file= NULL; + + if (is_pes){ + idx_file = fopen((mDir +"/index.vdr").c_str(), "r"); + // sendError(400, "Bad Request", NULL, "PES not yet supported."); + // return true; + } + else { + idx_file = fopen((mDir +"/index").c_str(), "r"); + } + + if (idx_file == NULL){ + *(mLog->log()) << DEBUGPREFIX + << " failed to open idx file = "<< (mDir +"/index").c_str() + << endl; + sendError(404, "Not Found", NULL, "Failed to open Index file"); + return OKAY; + } + + int buffered_frames = 50; + char *index_buf = new char[8 *buffered_frames]; // 50 indexes + + *(mLog->log()) << DEBUGPREFIX + << " seeking to start_frame= " << start_frame + << " fps= " << fps << endl; + fseek(idx_file, start_frame * 8, SEEK_SET); + + int buffered_indexes = fread(index_buf, 8, (buffered_frames), idx_file); + + fclose(idx_file); + + if(buffered_indexes <= 0 ) { + *(mLog->log())<log()) << DEBUGPREFIX + << " Finding I-Frame now" << endl; + + + bool found_it = false; + int i = 0; + + uint32_t offset = 0; + int idx =0; + int type =0; + + for (i= 0; i < buffered_indexes; i++){ + if (is_pes) { + tIndexPes in_read_pes; + memcpy (&in_read_pes, &(index_buf[i*8]), 8); + type = in_read_pes.type == 1; + idx = in_read_pes.number; + offset = in_read_pes.offset; + } + else{ + tIndexTs in_read_ts; + memcpy (&in_read_ts, &(index_buf[i*8]), 8); + type = in_read_ts.independent; + idx = in_read_ts.number; + offset = in_read_ts.offset; + } + + *(mLog->log()) << DEBUGPREFIX + << " Frame= " << i + << " idx= "<< idx + << " offset= " << offset + << " type= " << type + << endl; + if (type){ + found_it = true; + break; + } + } + + if (!found_it) { + delete[] index_buf; + sendError(404, "Not Found", NULL, "Failed to read Index file"); + return OKAY; + } + + mVdrIdx = idx; + + *(mLog->log()) << DEBUGPREFIX + << " idx= "<< mVdrIdx + << " offset= " << offset + << endl; + + delete[] index_buf; + + char pathbuf[4096]; + snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), mPath.c_str(), mVdrIdx); + + *(mLog->log()) << DEBUGPREFIX + << " Opening Path= " + << pathbuf << endl; + if (openFile(pathbuf) != OKAY) { + sendError(403, "Forbidden", NULL, "Access denied."); + return true; + } + + fseek(mFile, offset, SEEK_SET); + + if (mStreamToEnd) { + sendHeaders(200, "OK", NULL, "video/mpeg", -1, -1); + return true; + } + + uint64_t file_size = 0; + bool more_to_go = true; + int vdr_idx = mVdrIdx; + while (more_to_go) { + snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), mPath.c_str(), vdr_idx); + if (stat(pathbuf, statbuf) >= 0) { + *(mLog->log())<< " found for " << pathbuf << endl; + file_size += statbuf->st_size; + } + else { + more_to_go = false; + } + vdr_idx ++; + } + mRemLength = file_size - offset; + + *(mLog->log()) << DEBUGPREFIX + << " Done. Start Streaming " + << endl; + + if (rangeHdr.isRangeRequest) { + snprintf(pathbuf, sizeof(pathbuf), "Content-Range: bytes 0-%lld/%lld", (mRemLength -1), mRemLength); + sendHeaders(206, "Partial Content", pathbuf, "video/mpeg", mRemLength, statbuf->st_mtime); + } + else { + sendHeaders(200, "OK", NULL, "video/mpeg", mRemLength, -1); + } + return true; +} + +int cHttpResource::sendVdrDir(struct stat *statbuf) { + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX << " *** sendVdrDir mPath= " << mPath << endl; +#endif + char pathbuf[4096]; + char f[400]; + int vdr_idx = 0; + uint64_t total_file_size = 0; + // int ret = OKAY; + string vdr_dir = mPath; + vector file_sizes; + bool more_to_go = true; + + checkRecording(); + + mVdrIdx = 1; + mFileStructure = "%s/%03d.vdr"; + + snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), mPath.c_str(), 1); + if (stat(pathbuf, statbuf) < 0) { + mFileStructure = "%s/%05d.ts"; +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX << " using dir format: " << mFileStructure.c_str() << endl; +#endif + } + + // The range request functions are activated, when a time header is detected + if (isTimeRequest(statbuf)) { + *(mLog->log())<< DEBUGPREFIX + << " isTimeRequest is true" + << endl; + return OKAY; + } + + // --- looup all vdr files in the dir --- + while (more_to_go) { + vdr_idx ++; + snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), mPath.c_str(), vdr_idx); + if (stat(pathbuf, statbuf) >= 0) { +#ifndef DEBUG + *(mLog->log())<< " found for " << pathbuf << endl; +#endif + file_sizes.push_back(sVdrFileEntry(statbuf->st_size, total_file_size, vdr_idx)); + total_file_size += statbuf->st_size; + } + else { + more_to_go = false; + } + } + if (file_sizes.size() < 1) { + // There seems to be vdr video file in the directory + *(mLog->log())<< DEBUGPREFIX + << " No video file in the directory" + << DEBUGHDR << endl; + sendError(404, "Not Found", NULL, "File not found."); + return OKAY; + } + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " vdr filesize list " + << DEBUGHDR << endl; + for (uint i = 0; i < file_sizes.size(); i++) + *(mLog->log())<< " i= " << i << " size= " << file_sizes[i].sSize << " firstOffset= " << file_sizes[i].sFirstOffset << endl; + *(mLog->log())<< " total_file_size= " << total_file_size << endl << endl; +#endif + + // total_file_size (on disk) + + // ---------------- file sizes --------------------- + + uint cur_idx = 0; + +#ifndef DEBUG + if (mIsRecording) { + snprintf(f, sizeof(f), " CurFileSize= %lld mRecProgress= %f ExtFileSize= %lld", total_file_size, mRecProgress, (long long int)(mRecProgress * total_file_size)); + *(mLog->log()) << DEBUGPREFIX + << endl << " isRecording: " << f + << endl; + } +#endif + + if (!rangeHdr.isRangeRequest) { + snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), mPath.c_str(), file_sizes[cur_idx].sIdx); + + if (openFile(pathbuf) != OKAY) { + sendError(403, "Forbidden", NULL, "Access denied."); + return OKAY; + } + + mRemLength = total_file_size; + + sendHeaders(200, "OK", NULL, "video/mpeg", ((mIsRecording) ? (mRecProgress * total_file_size): total_file_size ), statbuf->st_mtime); + } + else { // Range request + // idenify the first file +#ifndef DEBUG + *(mLog->log()) << DEBUGPREFIX + << endl <<" --- Range Request Handling ---" + << DEBUGHDR << endl; +#endif + if (mIsRecording && (rangeHdr.begin > total_file_size)) { + *(mLog->log()) << DEBUGPREFIX + << " ERROR: Not yet available" << endl; + sendError(404, "Not Found", NULL, "File not found."); + return OKAY; + } + cur_idx = file_sizes.size() -1; + for (uint i = 1; i < file_sizes.size(); i++) { + if (rangeHdr.begin < file_sizes[i].sFirstOffset ) { + cur_idx = i -1; + break; + } + } + +#ifndef DEBUG + *(mLog->log())<< " Identified Record i= " << cur_idx << " file_sizes[i].sFirstOffset= " + << file_sizes[cur_idx].sFirstOffset << " rangeHdr.begin= " << rangeHdr.begin + << " vdr_no= " << file_sizes[cur_idx].sIdx << endl; +#endif + + mVdrIdx = file_sizes[cur_idx].sIdx; + snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), mPath.c_str(), file_sizes[cur_idx].sIdx); +#ifndef DEBUG + *(mLog->log())<< " file identified= " << pathbuf << endl; +#endif + if (openFile(pathbuf) != OKAY) { + *(mLog->log())<< "----- fopen failed dump ----------" << endl; + *(mLog->log())<< DEBUGPREFIX + << " vdr filesize list " + << DEBUGHDR << endl; + for (uint i = 0; i < file_sizes.size(); i++) + *(mLog->log())<< " i= " << i << " size= " << file_sizes[i].sSize << " firstOffset= " << file_sizes[i].sFirstOffset << endl; + *(mLog->log())<< " total_file_size= " << total_file_size << endl << endl; + + *(mLog->log())<< " Identified Record i= " << cur_idx << " file_sizes[i].sFirstOffset= " + << file_sizes[cur_idx].sFirstOffset << " rangeHdr.begin= " << rangeHdr.begin + << " vdr_no= " << file_sizes[cur_idx].sIdx << endl; + *(mLog->log())<< "---------------" << endl; + sendError(403, "Forbidden", NULL, "Access denied."); + return OKAY; + } + mDir = mPath; + mPath = pathbuf; +#ifndef DEBUG + *(mLog->log())<< " Seeking into file= " << (rangeHdr.begin - file_sizes[cur_idx].sFirstOffset) + << " cur_idx= " << cur_idx + << " file_sizes[cur_idx].sFirstOffset= " << file_sizes[cur_idx].sFirstOffset + << endl; +#endif + fseek(mFile, (rangeHdr.begin - file_sizes[cur_idx].sFirstOffset), SEEK_SET); + if (rangeHdr.end == 0) + rangeHdr.end = ((mIsRecording) ? (mRecProgress * total_file_size): total_file_size); + + mRemLength = (rangeHdr.end-rangeHdr.begin); + + snprintf(f, sizeof(f), "Content-Range: bytes %lld-%lld/%lld", rangeHdr.begin, (rangeHdr.end -1), + ((mIsRecording) ? (long long int)(mRecProgress * total_file_size): total_file_size)); + + sendHeaders(206, "Partial Content", f, "video/mpeg", (rangeHdr.end-rangeHdr.begin), statbuf->st_mtime); + } + +#ifndef DEBUG + *(mLog->log())<< " ***** Yes, vdr dir found ***** mPath= " << mPath<< endl; +#endif + + return OKAY; // handleRead() done +} + +void cHttpResource::sendHeaders(int status, const char *title, const char *extra, const char *mime, + long long int length, time_t date) { + + time_t now; + char timebuf[128]; + char f[400]; + + string hdr = ""; + snprintf(f, sizeof(f), "%s %d %s\r\n", PROTOCOL, status, title); + hdr += f; + snprintf(f, sizeof(f), "Server: %s\r\n", SERVER); + hdr += f; + now = time(NULL); + strftime(timebuf, sizeof(timebuf), RFC1123FMT, gmtime(&now)); + snprintf(f, sizeof(f), "Date: %s\r\n", timebuf); + hdr += f; + if (extra) { + snprintf(f, sizeof(f), "%s\r\n", extra); + *(mLog->log())<< DEBUGPREFIX << " " << f; + hdr += f; + } + if (mime) { + snprintf(f, sizeof(f), "Content-Type: %s\r\n", mime); + hdr += f; + } + if (length >= 0) { + snprintf(f, sizeof(f), "Content-Length: %lld\r\n", length); + // *(mLog->log())<< DEBUGPREFIX << " " << f << endl; + hdr += f; + } + if (date != -1) { + strftime(timebuf, sizeof(timebuf), RFC1123FMT, gmtime(&date)); + snprintf(f, sizeof(f), "Last-Modified: %s\r\n", timebuf); + hdr += f; + } + if (mAcceptRanges) { + snprintf(f, sizeof(f), "Accept-Ranges: bytes\r\n"); + hdr += f; + } + snprintf(f, sizeof(f), "Connection: close\r\n"); + hdr += f; + + snprintf(f, sizeof(f), "\r\n"); + hdr += f; + + + if (mBlkLen != 0) { + *(mLog->log())<< DEBUGPREFIX + << " ERROR in SendHeader: mBlkLen != 0!!! --> Overwriting" << endl; + } + mBlkLen = hdr.size(); + strcpy(mBlkData, hdr.c_str()); + +} + +int cHttpResource::sendFile(struct stat *statbuf32) { + // Send the First Datachunk, incl all headers + struct stat64 statbuf; + + if (stat64(mPath.c_str(), &statbuf) < 0) { + sendError(404, "Not Found", NULL, "File not found."); + return OKAY; + } + *(mLog->log())<< DEBUGPREFIX + << mReqId << " SendFile mPath= " << mPath + << endl; + + char f[400]; + + if (openFile(mPath.c_str()) == ERROR) { + sendError(403, "Forbidden", NULL, "Access denied."); + return OKAY; + } + mFile = fopen(mPath.c_str(), "r"); + // int ret = OKAY; + + if (!mFile) { + sendError(403, "Forbidden", NULL, "Access denied."); + return OKAY; + } + + mFileSize = S_ISREG(statbuf.st_mode) ? statbuf.st_size : -1; + + *(mLog->log())<< "fd= " << mFd << " mReqId= "<< mReqId + << " mFileSize= " <log())<< "fd= " << mFd << " mReqId= "<< mReqId + << " rangeHdr.begin= " <log())<< "fd= " << mFd << " mReqId= "<< mReqId + << ": Done mRemLength= "<< mRemLength << " mContentType= " << mContentType + << endl; +#endif + mConnState = SERVING; + + return OKAY; + +} + +const char *cHttpResource::getMimeType(const char *name) { + char *ext = strrchr((char*)name, '.'); + if (!ext) + return NULL; + // if (ext.compare(".html") || ext.compare(".htm")) return "text/html"; + if (strcmp(ext, ".html") == 0 || strcmp(ext, ".htm") == 0) return "text/html"; + if (strcmp(ext, ".jpg") == 0 || strcmp(ext, ".jpeg") == 0) return "image/jpeg"; + if (strcmp(ext, ".gif") == 0) return "image/gif"; + if (strcmp(ext, ".png") == 0) return "image/png"; + if (strcmp(ext, ".xml") == 0) return "application/xml"; + if (strcmp(ext, ".css") == 0) return "text/css"; + if (strcmp(ext, ".js") == 0) return "text/javascript"; + if (strcmp(ext, ".au") == 0) return "audio/basic"; + if (strcmp(ext, ".wav") == 0) return "audio/wav"; + if (strcmp(ext, ".avi") == 0) return "video/x-msvideo"; + if (strcmp(ext, ".mp4") == 0) return "video/mp4"; + if (strcmp(ext, ".vdr") == 0) return "video/mpeg"; + if (strcmp(ext, ".ts") == 0) return "video/mpeg"; + if (strcmp(ext, ".mpeg") == 0 || strcmp(ext, ".mpg") == 0) return "video/mpeg"; + if (strcmp(ext, ".mp3") == 0) return "audio/mpeg"; + if (strcmp(ext, ".mpd") == 0) return "application/dash+xml"; + if (strcmp(ext, ".m3u8") == 0) return "application/x-mpegURL"; + + return NULL; +} + + +string cHttpResource::getConnStateName() { + string state_string; + switch (mConnState) { + case WAITING: + state_string = "WAITING"; + break; + case READHDR: + state_string = "READ Req HDR"; + break; + case READPAYLOAD: + state_string = "READ Req Payload"; + break; + case SERVING: + state_string = "SERVING"; + break; + case TOCLOSE: + state_string = "TOCLOSE"; + break; + default: + state_string = "UNKNOWN"; + break; + } + return state_string; +} + + +string cHttpResource::getOwnIp(int fd) { + struct sockaddr_in sock; + socklen_t len_inet = sizeof(sock); + int ret = getsockname(fd, (struct sockaddr *)&sock, &len_inet); + if ( ret == -1 ) { + *(mLog->log()) << "Error: Cannot obtain own ip address" << endl; + return string("0.0.0.0"); + // exit(1); /* Failed */ + } + return string (inet_ntoa(sock.sin_addr)); +} + +uint64_t cHttpResource::getVdrFileSize() { + // iter over all vdr files and get file size + struct stat statbuf; + string file_structure = "%s/%05d.ts"; // Only ts supported for HLS and HAS + char pathbuf[4096]; + int vdr_idx = 0; + uint64_t total_file_size = 0; + bool more_to_go = true; + + while (more_to_go) { + vdr_idx ++; + snprintf(pathbuf, sizeof(pathbuf), file_structure.c_str(), mDir.c_str(), vdr_idx); + if (stat(pathbuf, &statbuf) >= 0) { + total_file_size += statbuf.st_size; + } + else { + more_to_go = false; + } + } + return total_file_size; +} + +int cHttpResource::parseHttpRequestLine(string line) { + mMethod = line.substr(0, line.find_first_of(" ")); + mRequest = line.substr(line.find_first_of(" ") +1, (line.find_last_of(" ") - line.find_first_of(" ") -1)); + mVersion = line.substr(line.find_last_of(" ") +1); +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " ReqLine= " << line << endl; +#endif + if (mVersion.compare(0, 4, "HTTP") != 0) { +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " ERROR: No HTTP request -> Closing Connection" << line << endl; +#endif + return ERROR; + } + + size_t pos = mRequest.find('?'); + if (pos != string::npos) + mQuery = mRequest.substr (pos+1, string::npos); + mPath = cUrlEncode::doUrlSaveDecode(mRequest.substr(0, mRequest.find('?'))); + *(mLog->log())<< DEBUGPREFIX + << " mMethod= " << mMethod + << " mPath= " << mPath + << " mVer= " << mVersion + << " mQuery= " << mQuery + // << " HexDump= " << endl << cUrlEncode::hexDump(mPath) << endl + << endl; + return OKAY; +} + +int cHttpResource::parseHttpHeaderLine (string line) { + string hdr_name = line.substr(0, line.find_first_of(":")); + string hdr_val = line.substr(line.find_first_of(":") +2); + + if (hdr_name.compare("Range") == 0) { + parseRangeHeaderValue(hdr_val); + *(mLog->log()) << DEBUGPREFIX + << " Range: Begin= " << rangeHdr.begin + << " End= " << rangeHdr.end + << endl; + } + if (hdr_name.compare("User-Agent") == 0) { + mUserAgent = hdr_val; + *(mLog->log())<< " User-Agent: " << hdr_val + << endl; + } + if (hdr_name.compare("Content-Length") == 0) { + mReqContentLength = atoll(hdr_val.c_str()); + //#ifndef DEBUG + *(mLog->log())<< " Content-Length: " << mReqContentLength + << endl; + //#endif + } + return 0; +} + +void cHttpResource::checkRecording() { + // sets mIsRecording to true when the recording is still on-going + mIsRecording = false; + time_t now = time(NULL); + +#ifndef STANDALONE + + // cRecordings* recordings = mFactory->getRecordings(); + cRecordings* recordings = &Recordings; +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " GetByName(" <GetByName(mPath.c_str()); + if (rec != NULL) { + const cEvent *ev = rec->Info()->GetEvent(); + if (ev != NULL) { + if (now < ev->EndTime()) { + // still recording + mIsRecording = true; + + // mRecProgress * curFileSize = estimated File Size + mRecProgress = (ev->EndTime() - ev->StartTime()) *1.1 / (rec->NumFrames() / rec->FramesPerSecond()); + // mRecProgress = (ev->EndTime() - ev->StartTime()) *1.0/ (now - ev->StartTime()); +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " **** is still recording for mIsRecording= " + << mIsRecording + << " mRecProgress= " << mRecProgress << endl; +#endif + } + } + +#ifndef DEBUG + *(mLog->log())<< DEBUGPREFIX + << " checking, whether recording is on-going" + << " now= " << now << " start= " << rec->Start() + << " curDur= " << rec->LengthInSeconds() + << endl; +#endif + } +#ifndef DEBUG + else { + *(mLog->log())<< DEBUGPREFIX + << " **** Recording Entry Not found **** " << endl; + } +#endif +#endif +} + +int cHttpResource::parseRangeHeaderValue(string val) { + rangeHdr.isRangeRequest = true; + size_t pos_equal = val.find_first_of('='); + size_t pos_minus = val.find_first_of('-'); + + string range_type = val.substr(0, pos_equal); + string first_val= val.substr(pos_equal+1, pos_minus -(pos_equal+1)); + rangeHdr.begin = atoll(first_val.c_str()); + + string sec_val = ""; + if ((pos_minus +1)< val.size()){ + sec_val = val.substr(pos_minus+1); + rangeHdr.end = atoll(sec_val.c_str()); + } + return 0; +} + +int cHttpResource::openFile(const char *name) { + mFile = fopen(name, "r"); + if (!mFile) { + *(mLog->log())<< DEBUGPREFIX + << " fopen failed pathbuf= " << name + << endl; + // sendError(403, "Forbidden", NULL, "Access denied."); + return ERROR; + } + return OKAY; +} + diff --git a/httpresource.h b/httpresource.h new file mode 100755 index 0000000..fbbab4d --- /dev/null +++ b/httpresource.h @@ -0,0 +1,187 @@ +/* + * httpresource.h: VDR on Smart TV plugin + * + * Copyright (C) 2012, 2013 T. Lohmar + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +#ifndef __HTTPREQUEST_H__ +#define __HTTPREQUEST_H__ + +#include +#include +#include +#include "log.h" + +using namespace std; + +struct cRange { +cRange(): isRangeRequest(false), begin(0), end(0) {}; + bool isRangeRequest; + unsigned long long begin; + unsigned long long end; +}; + +struct sQueryAVP { + string attribute; + string value; +sQueryAVP(string a, string v) : attribute (a), value(v) {}; +}; + + +enum eConnState { + WAITING, + READHDR, + READPAYLOAD, + SERVING, + TOCLOSE +}; + +enum eContentType { + NYD, // Not Yet Defined + VDRDIR, + SINGLEFILE, + MEMBLOCK +}; + +struct sFileEntry { + string sName; + string sPath; + int sStart; + +sFileEntry(string n, string l, int s) : sName(n), sPath(l), sStart(s) { + }; +}; + +class SmartTvServer; +class cResumeEntry; + +class cHttpResource { + + public: + cHttpResource(int, int, int, SmartTvServer*); + virtual ~cHttpResource(); + + int handleRead(); + int handleWrite(); + + int checkStatus(); + + int readFromClient(); + void threadLoop(); + int run(); + + private: + SmartTvServer* mFactory; + Log* mLog; + + int mServerPort; + int mFd; + int mReqId; + + time_t mConnTime; + int mHandleReadCount; + bool mConnected; + eConnState mConnState; + eContentType mContentType; + + string mReadBuffer; + string mMethod; + + string *mResponseMessage; + int mResponseMessagePos; + char* mBlkData; + int mBlkPos; + int mBlkLen; + + string mRequest; + string mQuery; + string mPath; + string mDir; + string mVersion; + string protocol; + unsigned long long mReqContentLength; + string mPayload; + string mUserAgent; + + bool mAcceptRanges; + cRange rangeHdr; + unsigned long long mFileSize; + bool mStreamToEnd; + uint64_t mRemLength; + FILE *mFile; + int mVdrIdx; + string mFileStructure; + bool mIsRecording; + float mRecProgress; + + void setNonBlocking(); + int fillDataBlk(); + + int handlePost(); + int handleHeadRequest(); + int processRequest(); + int processHttpHeaderNew(); + + int readRequestPayload(); + void sendError(int status, const char *title, const char *extra, const char *text); + int sendDir(struct stat *statbuf); + int sendVdrDir(struct stat *statbuf); + int sendRecordingsXml (struct stat *statbuf); + int sendChannelsXml (struct stat *statbuf); + int sendResumeXml (); + int sendVdrStatusXml (struct stat *statbuf); + int sendEpgXml (struct stat *statbuf); + int sendMediaXml (struct stat *statbuf); + + int sendManifest (struct stat *statbuf, bool is_hls = true); + + int receiveResume(); + int deleteRecording(); + + void writeM3U8(double duration, int bitrate, float seg_dur, int end_seg); + void writeMPD(double duration, int bitrate, float seg_dur, int end_seg); + + + int sendMediaSegment (struct stat *statbuf); + + void sendHeaders(int status, const char *title, const char *extra, const char *mime, + long long int length, time_t date); + + int sendFile(struct stat *statbuf); + + // Helper Functions + const char *getMimeType(const char *name); + string getConnStateName(); + string getOwnIp(int fd); + uint64_t getVdrFileSize(); + void checkRecording(); + bool isTimeRequest(struct stat *statbuf); + int parseRangeHeaderValue(string); + int parseHttpRequestLine(string); + int parseHttpHeaderLine (string); + int parseQueryLine (vector *avps); + int parseResume(cResumeEntry &entry, string &id); + + int parseFiles(vector *entries, string prefix, string dir_base, string dir_name, struct stat *statbuf); + + int getQueryAttributeValue(vector *avps, string id, string &val); + int openFile(const char *name); + int writeXmlItem(string title, string link, string programme, string desc, string guid, int no, time_t start, int dur, double fps, int is_pes, int is_new); +}; +#endif diff --git a/log.c b/log.c new file mode 100644 index 0000000..685bce3 --- /dev/null +++ b/log.c @@ -0,0 +1,85 @@ +/* + * log.c: VDR on Smart TV plugin + * + * Copyright (C) 2012 Thorsten Lohmar + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + + +#include "log.h" +#include +#include + +Log* Log::instance = NULL; + +Log::Log() { + if (instance) + return; + instance = this; + mLogFile = NULL; +} + +Log::~Log() { + instance = NULL; +} + +Log* Log::getInstance() { + return instance; +} +int Log::init(string fileName) { + char timebuf[128]; + time_t now = time(NULL); + strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now)); + + if (fileName != "") { + mLogFile = new ofstream(); + + mLogFile->open(fileName.c_str(), ios::out ); + *mLogFile << "Log Created: " << timebuf << endl; + } + else + mLogFile = new ofstream("/dev/null"); + return 0; +} + +int Log::init(char* fileName) { + + char timebuf[128]; + time_t now = time(NULL); + strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now)); + + if (strcmp(fileName, "") !=0) { + mLogFile = new ofstream(); + mLogFile->open(fileName, ios::out ); + *mLogFile << "Log Created: " << timebuf << endl; + } + else + mLogFile = new ofstream("/dev/null"); + return 0; +} + +int Log::shutdown() { + if (mLogFile) + mLogFile->close(); + return 1; +} + +ofstream* Log::log() { + return mLogFile; +} + diff --git a/log.h b/log.h new file mode 100644 index 0000000..8a09fe3 --- /dev/null +++ b/log.h @@ -0,0 +1,50 @@ +/* + * log.h.h: VDR on Smart TV plugin + * + * Copyright (C) 2012 Thorsten Lohmar + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + + +#ifndef LOG_H +#define LOG_H + +#include +#include + +using namespace std; + +class Log +{ + public: + Log(); + ~Log(); + static Log* getInstance(); + + int init(char* fileName); + int init(string fileName); + int shutdown(); + ofstream* log(); + + private: + static Log* instance; + + ofstream *mLogFile; +}; + +#endif diff --git a/smarttvfactory.c b/smarttvfactory.c new file mode 100755 index 0000000..3478e9d --- /dev/null +++ b/smarttvfactory.c @@ -0,0 +1,408 @@ +/* + * smarttvfactory.h: VDR on Smart TV plugin + * + * Copyright (C) 2012 T. Lohmar + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +#ifndef STANDALONE +#include +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +#include "smarttvfactory.h" + +#ifndef STANDALONE +#define PORT 8000 +#else +#define PORT 9000 +#endif + +#define OKAY 0 +#define ERROR (-1) + +#define DEBUG + + +using namespace std; + +void SmartTvServerStartThread(void* arg) { + SmartTvServer* m = (SmartTvServer*)arg; + m->threadLoop(); + delete m; + pthread_exit(NULL); +} + +SmartTvServer::SmartTvServer(): mRequestCount(0), isInited(false), serverPort(PORT), mServerFd(-1), + mSegmentDuration(10), mHasMinBufferTime(40), mLiveChannels(20), + clientList(), mActiveSessions(0), mConfig(NULL) { +} + + +SmartTvServer::~SmartTvServer() { + + if (mConfig != NULL) + delete mConfig; +} + +void SmartTvServer::cleanUp() { + // close listening ports + for (uint idx= 0; idx < clientList.size(); idx++) { + if (clientList[idx] != NULL) { + close(idx); + delete clientList[idx]; + clientList[idx] = NULL; + } + } + + // close server port + close(mServerFd); + + // Leave thread + pthread_cancel(mThreadId); + pthread_join(mThreadId, NULL); + + mLog.shutdown(); +} + +int SmartTvServer::runAsThread() { + int res = pthread_create(&mThreadId, NULL, (void*(*)(void*))SmartTvServerStartThread, (void *)this); + if (res != 0) { + *(mLog.log()) << " Error creating thread. res= " << res + << endl; + return 0; + } + return 1; +} + +void SmartTvServer::threadLoop() { + *(mLog.log()) << " SmartTvServer Thread Started " << endl; + + loop(); + + *(mLog.log()) << " SmartTvServer Thread Stopped " << endl; +} + + +void SmartTvServer::loop() { + socklen_t addr_size = 0; + int rfd; + sockaddr_in sadr; + int req_id = 0; + int ret = 0; + struct timeval timeout; + + int maxfd; + + fd_set read_set; + fd_set write_set; + + FD_ZERO(&read_set); + FD_ZERO(&write_set); + + FD_ZERO(&mReadState); + FD_ZERO(&mWriteState); + + FD_SET(mServerFd, &mReadState); + maxfd = mServerFd; + + *(mLog.log()) << "mServerFd= " << mServerFd << endl; + + int handeled_fds = 0; + + for (;;) { + FD_ZERO(&read_set); + FD_ZERO(&write_set); + read_set = mReadState; + write_set = mWriteState; + + if (ret != handeled_fds) { + // Only ok, when the server has closed a handing HTTP connection + *(mLog.log()) << "WARNING: Select-ret= " << ret + << " != handeled_fds= " << handeled_fds << endl; + /* FD_ZERO(&mReadState); + FD_ZERO(&mWriteState); + FD_SET(mServerFd, &mReadState); + maxfd = mServerFd; + + read_set = mReadState; + write_set = mWriteState; + for (uint idx= 0; idx < clientList.size(); idx++) { + if (clientList[idx] != NULL) { + close(idx); + delete clientList[idx]; + clientList[idx] = NULL; + } + } + mActiveSessions = 0; +*/ + } + + handeled_fds = 0; + timeout.tv_sec = 5; + timeout.tv_usec = 0; + + ret = select(maxfd + 1, &read_set, &write_set, NULL, &timeout); + + if (ret == 0) { + // timeout: Check for dead TCP connections + for (uint idx= 0; idx < clientList.size(); idx++) { + if (clientList[idx] != NULL) + if (clientList[idx]->checkStatus() == ERROR) { + close(idx); + delete clientList[idx]; + clientList[idx] = NULL; + mActiveSessions--; + FD_CLR(idx, &mReadState); /* dead client */ + FD_CLR(idx, &mWriteState); + *(mLog.log()) << "WARNING: Timeout - Dead Client fd=" << idx << endl; + } + } + continue; + } // timeout + + if (ret < 0){ + *(mLog.log()) << "ERROR: select error " << errno << endl; + continue; + } // Error + + // new accept + if (FD_ISSET(mServerFd, &read_set)) { + handeled_fds ++; + if((rfd = accept(mServerFd, (sockaddr*)&sadr, &addr_size))!= -1){ + req_id ++; + +#ifndef DEBUG + *(mLog.log()) << "fd= " << rfd + << " --------------------- Received connection ---------------------" << endl; +#endif + + FD_SET(rfd, &mReadState); + FD_SET(rfd, &mWriteState); + + if (rfd > maxfd) { + maxfd = rfd; + } + + if (clientList.size() < (rfd+1)) { + clientList.resize(rfd+1, NULL); // Check. + } + clientList[rfd] = new cHttpResource(rfd, req_id, serverPort, this); + mActiveSessions ++; + *(mLog.log()) << " + mActiveSessions= " << mActiveSessions << endl; + } + else{ + *(mLog.log()) << "Error accepting " << errno << endl; + } + } + + // Check for data on already accepted connections + for (rfd = 0; rfd < clientList.size(); rfd++) { + if (clientList[rfd] == NULL) + continue; + if (FD_ISSET(rfd, &read_set)) { + handeled_fds ++; + // HandleRead + if (clientList[rfd] == NULL) { + *(mLog.log()) << "ERROR in Check Read: oops - no cHttpResource anymore fd= " << rfd << endl; + close(rfd); + FD_CLR(rfd, &mReadState); /* remove dead client */ + FD_CLR(rfd, &mWriteState); + continue; + } + if ( clientList[rfd]->handleRead() < 0){ +#ifndef DEBUG + *(mLog.log()) << "fd= " << rfd << " --------------------- Check Read: Closing ---------------------" << endl; +#endif + close(rfd); + delete clientList[rfd]; + clientList[rfd] = NULL; + mActiveSessions--; + *(mLog.log()) << " - Check Read: mActiveSessions= " << mActiveSessions << endl; + FD_CLR(rfd, &mReadState); /* dead client */ + FD_CLR(rfd, &mWriteState); + } + } + } + + // Check for write + for (rfd = 0; rfd < clientList.size(); rfd++) { + if (clientList[rfd] == NULL) + continue; + if (FD_ISSET(rfd, &write_set)) { + handeled_fds++; + // HandleWrite + if (clientList[rfd] == NULL) { + close(rfd); + FD_CLR(rfd, &mReadState); + FD_CLR(rfd, &mWriteState); + continue; + } + if ( clientList[rfd]->handleWrite() < 0){ +#ifndef DEBUG + *(mLog.log()) << "fd= " << rfd << " --------------------- Check Write: Closing ---------------------" << endl; +#endif + close(rfd); + delete clientList[rfd]; + clientList[rfd] = NULL; + mActiveSessions--; + *(mLog.log()) << " - Check Write: mActiveSessions= " << mActiveSessions << endl; + FD_CLR(rfd, &mReadState); + FD_CLR(rfd, &mWriteState); + } + } + } + + // selfcheck + /* *(mLog.log()) << "Select Summary: ret= " << ret + << " handeled_fds=" << handeled_fds + << " mActiveSessions= " << mActiveSessions + << " clientList.size()= " << clientList.size() + << endl; +*/ + //Check for active sessions + /* + *(mLog.log()) << "checking number of active sessions clientList.size()= " << clientList.size() << endl; + + int act_ses = 0; + for (uint idx= 0; idx < clientList.size(); idx++) { + if (clientList[idx] != NULL) + act_ses++; + } + if (act_ses != mActiveSessions) { + *(mLog.log()) << "ERROR: Lost somewhere a session: " + << "mActiveSessions= " << mActiveSessions + << "act_ses= " << act_ses + << endl; + mActiveSessions = act_ses; + } + *(mLog.log()) << "checking number of active sessions - done mActiveSessions= " << mActiveSessions << endl; +*/ + } // for (;;) +} // org bracket + +int SmartTvServer::isServing() { + return (mActiveSessions != 0 ? true : false); +} + +void SmartTvServer::initServer(string dir) { + /* This function initialtes the listening socket for the server + * and sets isInited to true + */ + mConfigDir = dir; + int ret; + struct sockaddr_in sock; + int yes = 1; + + +#ifndef STANDALONE + mConfig = new cSmartTvConfig(dir); + mLog.init(mConfig->getLogFile()); + esyslog("SmartTvWeb: Logfile created"); + + *(mLog.log()) << mConfig->getLogFile() << endl; + +#else + mConfig = new cSmartTvConfig("."); + mLog.init(mConfig->getLogFile()); + cout << "SmartTvWeb: Logfile created" << endl; + cout << "SmartTvWeb: Listening on port= " << PORT << endl; + +#endif + + // mConfig->printConfig(); + + mSegmentDuration= mConfig->getSegmentDuration(); + mHasMinBufferTime= mConfig->getHasMinBufferTime(); + mLiveChannels = mConfig->getLiveChannels(); + + *(mLog.log()) <<"HTTP server listening on port " << serverPort << endl; + + mServerFd = socket(PF_INET, SOCK_STREAM, 0); + if (mServerFd <0) { + *(mLog.log()) << "Error: Cannot create serving socket, exit" << endl; + exit(1); + } + + ret = setsockopt(mServerFd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); + if (ret <0) { + *(mLog.log()) << "Error: Cannot set sockopts on serving socket, exit" << endl; + exit(1); + } + + memset((char *) &sock, 0, sizeof(sock)); + sock.sin_family = AF_INET; + + if (mConfig->getServerAddress() == "") + sock.sin_addr.s_addr = htonl(INADDR_ANY); + else { + *(mLog.log()) << "Binding Server to " << mConfig->getServerAddress() << endl; + sock.sin_addr.s_addr = inet_addr(mConfig->getServerAddress().c_str()); + } + sock.sin_port = htons(serverPort); + + ret = bind(mServerFd, (struct sockaddr *) &sock, sizeof(sock)); + if (ret !=0) { + *(mLog.log()) << "Error: Cannot bind serving socket, exit" << endl; + exit(1); + } + + /* + struct ifreq ifr; + + ifr.ifr_addr.sa_family = AF_INET; + strncpy(ifr.ifr_name, "eth0", IFNAMSIZ-1); + ioctl(mServerFd, SIOCGIFADDR, &ifr); + string own_ip = inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr); + *(mLog.log()) << " own if ip= " << own_ip << endl; +*/ + + ret = listen(mServerFd, 5); + if (ret <0) { + *(mLog.log()) << "Error: Cannot set listening on serving socket, exit" << endl; + exit(1); + } + + isInited = true; +} + + + + diff --git a/smarttvfactory.h b/smarttvfactory.h new file mode 100644 index 0000000..1073ab0 --- /dev/null +++ b/smarttvfactory.h @@ -0,0 +1,84 @@ +/* + * smarttvfactory.h: VDR on Smart TV plugin + * + * Copyright (C) 2012 T. Lohmar + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + + +#ifndef __SMARTTVSERVER_H__ +#define __SMARTTVSERVER_H__ + +#include +#include +#include +#include +#include "httpresource.h" +#include "log.h" +#include "stvw_cfg.h" + +#ifndef STANDALONE +#include +#endif + +using namespace std; + +#define PLG_VERSION "0.9.6" +#define SERVER "SmartTvWeb/0.9.6" + +class SmartTvServer { + public: + SmartTvServer(); + virtual ~SmartTvServer(); + + void initServer(string c_dir); + void loop(); + void cleanUp(); + int runAsThread(); + void threadLoop(); + + Log mLog; + + void readRecordings(); + int isServing(); + + string getConfigDir() { return mConfigDir; }; + cSmartTvConfig* getConfig() { return mConfig; }; + + private: + pthread_t mThreadId; + int mRequestCount; + bool isInited; + int serverPort; + int mServerFd; + unsigned int mSegmentDuration; + int mHasMinBufferTime; + int mLiveChannels; + + vector clientList; + int mActiveSessions; + string mConfigDir; + cSmartTvConfig *mConfig; + + int mMaxFd; + fd_set mReadState; + fd_set mWriteState; +}; + + +#endif diff --git a/smarttvweb.c b/smarttvweb.c new file mode 100644 index 0000000..9e3269e --- /dev/null +++ b/smarttvweb.c @@ -0,0 +1,152 @@ +/* + * smarttvweb.c: VDR on Smart TV plugin + * + * Copyright (C) 2012 T. Lohmar + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + + +#ifndef STANDALONE +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "smarttvfactory.h" + + +static const char *VERSION = PLG_VERSION; +static const char *DESCRIPTION = "SmartTV Web Server"; + + +using namespace std; + +#ifndef STANDALONE +class cPluginSmartTvWeb : public cPlugin +{ +public: + cPluginSmartTvWeb(void); + virtual ~cPluginSmartTvWeb(); + virtual const char *Version(void) { return VERSION; } + virtual const char *Description(void) { return DESCRIPTION; } + virtual const char *CommandLineHelp(void); + virtual bool ProcessArgs(int argc, char *argv[]); + virtual bool Initialize(void); + virtual bool Start(void); + virtual bool SetupParse(const char *Name, const char *Value); +#if VDRVERSNUM > 10300 + virtual cString Active(void); +#endif + +private: + SmartTvServer mServer; + string mConfigDir; +}; + +cPluginSmartTvWeb::cPluginSmartTvWeb(void) { + // Initialize any member variables here. + // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL + // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT! + mConfigDir = ""; +} + +bool cPluginSmartTvWeb::Start(void) { + // Start any background activities the plugin shall perform. + + if (mConfigDir.compare("") == 0) { + const char* dir_name = cPlugin::ConfigDirectory(Name()); + if (!dir_name) { + dsyslog("SmartTvWeb: Could not get config dir from VDR"); + } + else + mConfigDir = string(dir_name); + } + + mServer.initServer(mConfigDir); + int success = mServer.runAsThread(); + + esyslog("SmartTvWeb: started %s", (success == 1) ? "sucessfully" : "failed!!!!"); + + return ((success == 1) ? true: false); +} + +cPluginSmartTvWeb::~cPluginSmartTvWeb() { + // Clean up after yourself! + mServer.cleanUp(); +} + +const char *cPluginSmartTvWeb::CommandLineHelp(void) +{ + // Return a string that describes all known command line options. + return " \n"; +} + +bool cPluginSmartTvWeb::ProcessArgs(int argc, char *argv[]) { + // Implement command line argument processing here if applicable. + return true; +} + +bool cPluginSmartTvWeb::Initialize(void) { + // Initialize any background activities the plugin shall perform. + esyslog("SmartTvWeb: Initialize called"); + + return true; +} + +bool cPluginSmartTvWeb::SetupParse(const char *Name, const char *Value) +{ + // Parse your own setup parameters and store their values. + return false; +} + +#if VDRVERSNUM > 10300 + +cString cPluginSmartTvWeb::Active(void) { + esyslog("SmartTvWeb: Active called Checkme"); + if (mServer.isServing()) + return tr("SmartTV client(s) serving"); + else + return NULL; +} + +#endif + +VDRPLUGINCREATOR(cPluginSmartTvWeb); // Don't touch this! + +#else //VOMPSTANDALONE + + +int main(int argc, char *argv[]) { + printf ("Starting up\n"); + + SmartTvServer server; + server.initServer("."); + server.loop(); +} +#endif diff --git a/smarttvweb.conf b/smarttvweb.conf new file mode 100644 index 0000000..07920dc --- /dev/null +++ b/smarttvweb.conf @@ -0,0 +1,28 @@ +# + +LogFile /tmp/smarttvweb.txt + +MediaFolder /multimedia/video + +# Media Segment Duration for HLS/ DASH +SegmentDuration 10 + +# minBufferTime value for the DASH MPD +HasMinBufferTime 30 + +# Bitrate correction parameter for Hls and DASH MPD +HasBitrateCorrection 1.1 + +# Default number of Live Channel entries included in the channels.xml, when not requested specifically. +LiveChannels 30 + +# Influence behavior, when you channels.conf contain group separators +# Valid Values (case sensitive): +# - Ignore : Ignore group separators) +# - EmptyIgnore : Ignore empty group separators (which might be set to assign channel numbers) +# - EmptyFolderDown : Interpretes an empty group channel as "cd ..", so that subsequent channels are not part of the group. +GroupSeparators Ignore + +# Bind the web server to a specific IP address. Otherwise, the Web Server is listening on ALL interfaces. +#ServerAddress 127.0.0.1 + diff --git a/stvw_cfg.c b/stvw_cfg.c new file mode 100755 index 0000000..75b8de6 --- /dev/null +++ b/stvw_cfg.c @@ -0,0 +1,196 @@ +/* + * stvw_cfg.h: VDR on Smart TV plugin + * + * Copyright (C) 2012, 2013 T. Lohmar + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +#include "stvw_cfg.h" + +#ifndef STANDALONE +#include +#endif + +#include +#include +#include +#include + +cSmartTvConfig::cSmartTvConfig(string d): mConfigDir(d), mLog(NULL), mCfgFile(NULL), + mLogFile(), mMediaFolder(), mSegmentDuration(), mHasMinBufferTime(), mHasBitrateCorrection(), + mLiveChannels(), mGroupSep(IGNORE), mServerAddress("") { + +#ifndef STANDALONE + mLogFile= ""; +#else + mLogFile= "./smartvvweblog-standalone.txt"; +#endif + + // Defaults + mMediaFolder= "/hd2/mpeg"; + mSegmentDuration = 10; + mHasMinBufferTime = 40; + mHasBitrateCorrection = 1.1; + mLiveChannels = 30; + + readConfig(); +} + +cSmartTvConfig::~cSmartTvConfig() { +} + +void cSmartTvConfig::printConfig() { + mLog = Log::getInstance(); + + + *(mLog->log()) << "printConfig: " << endl; + *(mLog->log()) << " ConfigDir: " << mConfigDir << endl; + *(mLog->log()) << " LogFile: " << mLogFile << endl; + *(mLog->log()) << " MediaFolder:" << mMediaFolder << endl; + *(mLog->log()) << " SegmentDuration: " << mSegmentDuration << endl; + *(mLog->log()) << " HasMinBufferTime: " << mHasMinBufferTime << endl; + *(mLog->log()) << " HasBitrateCorrection: " << mHasBitrateCorrection << endl; + *(mLog->log()) << " LiveChannels: " << mLiveChannels << endl; + *(mLog->log()) << " GroupSeparators: " << ((mGroupSep==IGNORE)? "Ignore" : ((mGroupSep==EMPTYIGNORE)? "EmptyIgnore": "EmptyFolderDown")) << endl; + *(mLog->log()) << " ServerAddress: " << mServerAddress << endl; +} + + +void cSmartTvConfig::readConfig() { + string line; + char attr[200]; + char value[200]; + + ifstream myfile ((mConfigDir +"/smarttvweb.conf").c_str()); + + if (!myfile.is_open()) { +#ifndef STANDALONE + esyslog ("ERROR in SmartTvWeb: Cannot open config file. Expecting %s", (mConfigDir +"/smarttvweb.conf").c_str() ); +#else + cout << "ERROR: Cannot open config file. Expecting "<< (mConfigDir +"/smarttvweb.conf") << endl; +#endif + return; + } + + while ( myfile.good() ) { + getline (myfile, line); + + if ((line == "") or (line[0] == '#')) + continue; + + sscanf(line.c_str(), "%s %s", attr, value); + + if (strcmp(attr, "LogFile")==0) { + mLogFile = string(value); + // cout << " Found mLogFile= " << mLogFile << endl; + continue; + } + + if (strcmp(attr, "MediaFolder") == 0) { + mMediaFolder = string (value); + // cout << " Found mMediaFolder= " << mMediaFolder << endl; + continue; + } + if (strcmp(attr, "SegmentDuration") == 0) { + mSegmentDuration = atoi(value); + // cout << " Found mSegmentDuration= " << mSegmentDuration << endl; + continue; + } + + if (strcmp(attr, "HasMinBufferTime") == 0) { + mHasMinBufferTime = atoi(value); + // cout << " Found mHasMinBufferTime= " << mHasMinBufferTime << endl; + continue; + } + if (strcmp(attr, "HasBitrateCorrection") == 0) { + mHasBitrateCorrection = atof(value); + // cout << " Found mHasBitrate= " <log()) << " readConfig: " << line << endl; + if (line == "") + continue; + + string t; + time_t st; + int r; + time_t lv; + int count = scanf (line.c_str(), "%s %lld %d %lld", &t, &st, &r, &lv); + if (count == 4) { + *(mLog->log()) << " read: " << t << " st= " << st << " r= " << r << " lv= " << lv << endl; + } + // first title + + + } + myfile.close(); + } + + else { + *(mLog->log()) << " readConfig: Cannot open file " << f << endl; + return NULL; + } + + + // open the file + // read the lines + return NULL; +} +*/ diff --git a/stvw_cfg.h b/stvw_cfg.h new file mode 100755 index 0000000..d894820 --- /dev/null +++ b/stvw_cfg.h @@ -0,0 +1,75 @@ +/* + * stvw_cfg.h: VDR on Smart TV plugin + * + * Copyright (C) 2012, 2013 T. Lohmar + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + + +#ifndef __SMARTTV_CONFIG_H__ +#define __SMARTTV_CONFIG_H__ + +#include +#include +#include +#include +#include "log.h" + +using namespace std; + + +enum eGroupSep { + IGNORE, + EMPTYIGNORE, + EMPTYFOLDERDOWN +}; + +class cSmartTvConfig { + private: + string mConfigDir; + Log* mLog; + FILE *mCfgFile; + + string mLogFile; + string mMediaFolder; + unsigned int mSegmentDuration; + int mHasMinBufferTime; + float mHasBitrateCorrection; + int mLiveChannels; + + eGroupSep mGroupSep; + string mServerAddress; + + public: + cSmartTvConfig(string dir); + ~cSmartTvConfig(); + + void readConfig(); + void printConfig(); + + string getLogFile() { return mLogFile; }; + string getMediaFolder() { return mMediaFolder; }; + unsigned int getSegmentDuration() {return mSegmentDuration; }; + int getHasMinBufferTime() { return mHasMinBufferTime; }; + float getHasBitrateCorrection() { return mHasBitrateCorrection; }; + int getLiveChannels() {return mLiveChannels; }; + eGroupSep getGroupSep() { return mGroupSep; }; + string getServerAddress() { return mServerAddress; }; +}; + +#endif diff --git a/url.c b/url.c new file mode 100644 index 0000000..6446654 --- /dev/null +++ b/url.c @@ -0,0 +1,340 @@ +/* + * url.c: VDR on Smart TV plugin + * + * Copyright (C) 2012, 2013 T. Lohmar + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +#include +#include"url.h" + + +//http://www.blooberry.com/indexdot/html/topics/urlencoding.htm +string cUrlEncode::doUrlSaveEncode(string in) { + string res = ""; + unsigned char num = 0; + char buf[5]; + + bool done = false; + unsigned int idx = 0; + while (!done) { + if (idx == in.size()) { + done = true; + continue; + } + num = in[idx]; + switch (num) { + case '&': + res += "%26"; + break; + case '%': + res += "%25"; + break; + case '#': + // break; + if (in.compare(idx, 3, "#3F") == 0) { + res += "%3F"; + idx +=3; + continue; + } + if (in.compare(idx, 3, "#3A") == 0) { + res += "%3A"; + idx += 3; + continue; + } + if (in.compare(idx, 3, "#2F") == 0) { + res += "%2F"; + idx += 3; + continue; + } + res += "%23"; // just a '#' char + break; + default: + // Copy the normal chars + if (num < 128) + res += char(num); + else { + sprintf (buf, "%02hhX", num); + res += "%"; + res += buf; + } + break; + } // switch + + idx ++; + } + return res; +} + + +string cUrlEncode::doUrlSaveDecode(string input) { + string res = ""; + unsigned int idx = 0; + int x; + while (idx < input.size()){ + if (input[idx] == '%') { + string num = input.substr(idx+1, 2); + sscanf(num.c_str(), "%X", &x); + idx+= 3; + switch (x) { + case 0x23: // '#' + res += "#"; + break; + case 0x25: // '%' + res += "%"; + break; + case 0x2f: // '/' + res += "#2F"; + break; + case 0x3a: // ':' + res += "#3A"; + break; + case 63: // '?' + res += "#3F"; + break; + default: + res += char(x); + break; + } + } + else { + res += input[idx]; + idx ++; + } + } + return res; +} + +string cUrlEncode::doXmlSaveEncode(string in) { + string res = ""; + unsigned char num = 0; + // char buf[5]; + + bool done = false; + unsigned int idx = 0; + while (!done) { + if (idx == in.size()) { + done = true; + continue; + } + num = in[idx]; + switch (num) { + case 0x26: // '&': + res += "&"; + break; + case 0x27: // '\'': + res += "'"; + break; + case 0x3c: // '<': + res += "<"; + break; + case 0x3e: // '>': + res += ">"; + break; + case 0x22: // '\"': + res += """; + break; + + /* case 0xc4: // Ä + res += "Ä"; + break; + case 0xe4: //'ä': + res += "ä"; + break; + case 0xd6: // Ö + res += "Ö"; + break; + case 0xf6: // 'ö': + res += "ö"; + break; + case 0xdc: //'Ü': + res += "Ü"; + break; + case 0xfc: //'ü': + res += "ü"; + break; + case 0xdf: //'ß': + res += "ß"; + break; +*/ + default: + // Copy the normal chars + res += char(num); + break; + } // switch + + idx ++; + } + return res; +} + +string cUrlEncode::doXmlSaveDecode(string input) { + string res = ""; + unsigned int idx = 0; + while (idx < input.size()){ + if (input[idx] == '&') { + if (input.compare(idx, 4, "<") == 0){ + res += "<"; + idx += 4; + } + else if (input.compare(idx, 4, ">") == 0){ + res += ">"; + idx += 4; + } + else if (input.compare(idx, 5, "&") == 0){ + res += "&"; + idx += 5; + } + else if (input.compare(idx, 6, """) == 0){ + res += "\""; + idx += 6; + } + else if (input.compare(idx, 6, "'") == 0){ + res += "\'"; + idx += 6; + } + else { + // ERROR + idx = input.size(); + res = ""; + } + } + else { + res += input[idx]; + idx ++; + } + + } + return res; +} + +string cUrlEncode::removeEtChar(string line, bool xml) { + bool done = false; + size_t cur_pos = 0; + size_t pos = 0; + string res = ""; + + int end_after_done = 0; + + while (!done) { + pos = line.find('&', cur_pos); + if (pos == string::npos) { + done = true; + res += line.substr(cur_pos); + break; + } + if (pos >= 0) { + if (xml) + res += line.substr(cur_pos, (pos-cur_pos)) + "&"; // xml save encoding + else + res += line.substr(cur_pos, (pos-cur_pos)) + "%26"; // url save encoding + // cur_pos = cur_pos+ pos +1; + cur_pos = pos +1; + end_after_done ++; + } + } + return res; +} + +string cUrlEncode::hexDump(char *line, int line_len) { + string res = ""; + string ascii = ""; + char buf[10]; + + int line_count = 0; + for (unsigned int i = 0; i < line_len; i++) { + unsigned char num = line[i]; + sprintf (buf, "%02hhX", num); + res += buf; + if ((num >= 32) && (num < 127)) { + ascii += char(num); + } + else + ascii += '.'; + + line_count++; + switch (line_count) { + case 8: + res += " "; + ascii += " "; + break; + case 17: + res += " " + ascii; + res += "\r\n"; + ascii = ""; + line_count = 0; + break; + default: + res += " "; + break; + } + } + if (line_count != 0) { + for (int i = 0; i < ((17 - line_count) * 3 ); i++) + res += " "; + if (line_count >= 8) + res += " "; + + res += " "; + res += ascii; + } + return res; +} + +string cUrlEncode::hexDump(string in) { + string res = ""; + string ascii = ""; + char buf[10]; + + int line_count = 0; + for (unsigned int i = 0; i < in.size(); i++) { + unsigned char num = in[i]; + sprintf (buf, "%02hhX", num); + res += buf; + if ((num >= 32) && (num < 127)) { + ascii += char(num); + } + else + ascii += '.'; + + line_count++; + switch (line_count) { + case 8: + res += " "; + ascii += " "; + break; + case 16: + res += " " + ascii; + res += "\r\n"; + ascii = ""; + line_count = 0; + break; + default: + res += " "; + break; + } + } + if (line_count != 0) { + for (int i = 0; i < ((16 - line_count) * 3 ); i++) + res += " "; + if (line_count >= 8) + res += " "; + res += ascii; + } + return res; +} diff --git a/url.h b/url.h new file mode 100644 index 0000000..4656a2a --- /dev/null +++ b/url.h @@ -0,0 +1,50 @@ +/* + * url.h: VDR on Smart TV plugin + * + * Copyright (C) 2012 T. Lohmar + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + + +#ifndef __URL_H__ +#define __URL_H__ + +#include + +using namespace std; + +class cUrlEncode { + + public: + cUrlEncode() {}; + cUrlEncode(string &) {}; + + static string doUrlSaveEncode (string); + static string doUrlSaveDecode (string); + + static string doXmlSaveEncode (string); + static string doXmlSaveDecode (string); + static string removeEtChar(string line, bool xml=true); + static string hexDump(char *line, int line_len); + static string hexDump(string); + + private: + +}; + +#endif diff --git a/vdr-smarttvweb/Makefile b/vdr-smarttvweb/Makefile deleted file mode 100644 index b04d143..0000000 --- a/vdr-smarttvweb/Makefile +++ /dev/null @@ -1,109 +0,0 @@ -# -# Makefile for a Video Disk Recorder plugin -# -# -# The official name of this plugin. -# This name will be used in the '-P...' option of VDR to load the plugin. -# By default the main source file also carries this name. -# -PLUGIN = smarttvweb - -### The version number of this plugin (taken from the main source file): - -VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g') - -### The C++ compiler and options: - - -CXX ?= g++ -ifdef DEBUG -CXXFLAGS ?= -g -O0 -fPIC -Wall -Woverloaded-virtual #-Werror -else -CXXFLAGS ?= -fPIC -Wall -Woverloaded-virtual #-Werror -#CXXFLAGS ?= -O2 -fPIC -Wall -Woverloaded-virtual #-Werror -endif - -### The directory environment: - -#VDRDIR = ../../.. -VDRDIR = /usr/include/vdr -LIBDIR = . -#LIBDIR = ../../lib -TMPDIR = /tmp - -### Allow user defined options to overwrite defaults: - -#-include $(VDRDIR)/Make.config - -### read standlone settings if there --include .standalone - -### The version number of VDR (taken from VDR's "config.h"): - -VDRVERSION = $(shell grep 'define VDRVERSION ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g') -APIVERSION = $(shell sed -ne '/define APIVERSION/s/^.*"\(.*\)".*$$/\1/p' $(VDRDIR)/config.h) - -### The name of the distribution archive: - -ARCHIVE = $(PLUGIN)-$(VERSION) -PACKAGE = vdr-$(ARCHIVE) - -### Includes and Defines (add further entries here): - -INCLUDES += -I$(VDRDIR)/include -I$(DVBDIR)/include - -DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' -DEFINES += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE - -### The object files (add further files here): - -OBJS = $(PLUGIN).o smarttvfactory.o httpresource.o log.o url.o stvw_cfg.o - -OBJS2 = - -### Implicit rules: - -%.o: %.c - $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $< - -# Dependencies: - -MAKEDEP = g++ -MM -MG -DEPFILE = .dependencies -$(DEPFILE): Makefile - @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@ - --include $(DEPFILE) - -### Targets: - -all: allbase libvdr-$(PLUGIN).so -standalone: standalonebase smarttvweb-standalone - -objectsstandalone: $(OBJS) -objects: $(OBJS) $(OBJS2) - -allbase: - ( if [ -f .standalone ] ; then ( rm -f .standalone; make clean ; make objects ) ; else exit 0 ;fi ) -standalonebase: - ( if [ ! -f .standalone ] ; then ( make clean; echo "DEFINES+=-DSTANDALONE" > .standalone; echo "DEFINES+=-D_FILE_OFFSET_BITS=64" >> .standalone; make objectsstandalone ) ; else exit 0 ;fi ) - -libvdr-$(PLUGIN).so: objects - $(CXX) $(CXXFLAGS) -shared $(OBJS) $(OBJS2) -o $@ - @cp $@ $(LIBDIR)/$@.$(APIVERSION) - -smarttvweb-standalone: objectsstandalone - $(CXX) $(CXXFLAGS) $(OBJS) -lpthread -o $@ - chmod u+x $@ - -dist: clean - @-rm -rf $(TMPDIR)/$(ARCHIVE) - @mkdir $(TMPDIR)/$(ARCHIVE) - @cp -a * $(TMPDIR)/$(ARCHIVE) - @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE) - @-rm -rf $(TMPDIR)/$(ARCHIVE) - @echo Distribution package created as $(PACKAGE).tgz - -clean: - rm -f $(OBJS) $(OBJS2) $(DEPFILE) libvdr*.so.* *.tgz core* *~ .standalone smarttvweb-standalone - diff --git a/vdr-smarttvweb/httpresource.c b/vdr-smarttvweb/httpresource.c deleted file mode 100755 index 79d39bc..0000000 --- a/vdr-smarttvweb/httpresource.c +++ /dev/null @@ -1,2905 +0,0 @@ -/* - * httpresource.c: VDR on Smart TV plugin - * - * Copyright (C) 2012, 2013 T. Lohmar - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include "httpresource.h" -#include "smarttvfactory.h" -#include "stvw_cfg.h" - -#include "url.h" - -#ifndef STANDALONE -#include -#include -#include -#include -#include - -#endif - -#define PROTOCOL "HTTP/1.1" -#define RFC1123FMT "%a, %d %b %Y %H:%M:%S GMT" - -#define MAXLEN 4096 -#define OKAY 0 -#define ERROR (-1) -#define DEBUG_REGHEADERS -#define DEBUGPREFIX "mReqId= " << mReqId << " fd= " << mFd -#define DEBUGHDR " " << __PRETTY_FUNCTION__ << " (" << __LINE__ << ") " - -#define DEBUG - -#define SEGMENT_DURATION 10 - -using namespace std; - -class cResumeEntry { - public: - string mFilename; - float mResume; - - friend ostream& operator<<(ostream& out, const cResumeEntry& o) { - out << "mFilename= " << o.mFilename << " mResume= " << o.mResume << endl; - return out; - }; - cResumeEntry():mFilename(), mResume(-1.0) {}; -}; - - -struct sVdrFileEntry { - uint64_t sSize; - uint64_t sFirstOffset; - int sIdx; - - sVdrFileEntry () {}; - sVdrFileEntry (uint64_t s, uint64_t t, int i) - : sSize(s), sFirstOffset(t), sIdx(i) {}; -}; - - -struct sTimerEntry { - string name; - time_t startTime; - int duration; -sTimerEntry(string t, time_t s, int d) : name(t), startTime(s), duration(d) {}; -}; - -// 8 Byte Per Entry -struct tIndexPes { - uint32_t offset; - uchar type; - uchar number; - uint16_t reserved; - }; - - -// 8 Byte per entry -struct tIndexTs { - uint64_t offset:40; // up to 1TB per file (not using long long int here - must definitely be exactly 64 bit!) - int reserved:7; // reserved for future use - int independent:1; // marks frames that can be displayed by themselves (for trick modes) - uint16_t number:16; // up to 64K files per recording - }; - - -/* -union tIndexRead { - struct tIndexTs in; - char buf[8]; -}; -*/ - -cHttpResource::cHttpResource(int f, int id, int port, SmartTvServer* factory): mFactory(factory), mLog(), - mServerPort(port), mFd(f), mReqId(id), mConnTime(0), mHandleReadCount(0), - mConnected(true), mConnState(WAITING), mContentType(NYD), mReadBuffer(), - mMethod(), mResponseMessagePos(0), mBlkData(NULL), mBlkPos(0), mBlkLen(0), - mPath(), mVersion(), protocol(), mReqContentLength(0), - mPayload(), mUserAgent(), - mAcceptRanges(true), rangeHdr(), mFileSize(-1), mStreamToEnd(false), mRemLength(0), mFile(NULL), mVdrIdx(1), mFileStructure(), - mIsRecording(false), mRecProgress(0.0) { - - mLog = Log::getInstance(); - mPath = ""; - mConnTime = time(NULL); - setNonBlocking(); - mBlkData = new char[MAXLEN]; - - //#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX - << " cHttpResource created" << endl; - //#endif -} - - -cHttpResource::~cHttpResource() { - //#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX - << " Destructor of cHttpResource called" - << endl; - //#endif - delete[] mBlkData; - if (mFile != NULL) { - *(mLog->log())<< DEBUGPREFIX - << " ERROR: mFile still open. Closing now..." << endl; - fclose(mFile); - mFile = NULL; - } - -} - -int cHttpResource::checkStatus() { - time_t now = time(NULL); - - switch (mConnState) { - case WAITING: - case READHDR: - case READPAYLOAD: - if (now - mConnTime > 2) { - *(mLog->log()) << DEBUGPREFIX - << " checkStatus: no activity for 2sec " - << "mmConnState= " << getConnStateName() - << endl; - return ERROR; - } - break; - case TOCLOSE: - return ERROR; - break; - case SERVING: - break; - } - - // check for how much time the - return OKAY; -} - -void cHttpResource::setNonBlocking() { - int oldflags = fcntl(mFd, F_GETFL, 0); - oldflags |= O_NONBLOCK; - fcntl(mFd, F_SETFL, oldflags); -} - - -int cHttpResource::handleRead() { - mHandleReadCount ++; - if (mConnState == SERVING) { - *(mLog->log())<< DEBUGPREFIX - << " handleRead() in wrong state= " << getConnStateName() - << endl; - return OKAY; - } - if (mConnState == TOCLOSE) { - *(mLog->log())<< DEBUGPREFIX - << " handleRead() in wrong state= " << getConnStateName() - << endl; - return ERROR; - } - -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX - << " handleRead() state= " << getConnStateName() - << endl; -#endif - - char buf[MAXLEN]; - int buflen = sizeof(buf); - - int line_count = 0; - // bool is_req = true; - string rem_hdr = ""; - - buflen = read(mFd, buf, sizeof(buf)); - - if (buflen == -1) { - *(mLog->log())<< " Some Error, no data received" << endl; - return ERROR; // Nothing to read - } - - // if (( mConnState == WAITING) and ((time(NULL) - mConnTime) > 1)) { - if (( mConnState == WAITING) and (mHandleReadCount > 1000)) { - *(mLog->log()) << DEBUGPREFIX << " hmm, handleRead() no data since 1sec -> closing. mHandleReadCount= " << mHandleReadCount << endl; - return ERROR; // Nothing to read - } - - if ( ((mConnState == READHDR) or (mConnState == READPAYLOAD)) and (mHandleReadCount > 5000)) { - *(mLog->log()) << DEBUGPREFIX << " ERROR Still not finished after mHandleReadCount= " << mHandleReadCount << endl; - return ERROR; // Nothing to read - } - - if (buflen == 0) { - return OKAY; // Nothing to read - } - - if (mConnState == READPAYLOAD) { - mPayload += string(buf, buflen); - if (mPayload.size() == mReqContentLength) { - //Done - mConnState = SERVING; - return processRequest(); - } - } - - if (mConnState == WAITING) { - mConnState = READHDR; - } - - string req_line = mReadBuffer + string(buf, buflen); - buflen += rem_hdr.size(); - - size_t last_pos = 0; - // Parse http header lines - while (true) { - line_count ++; - size_t pos = req_line.find ("\r\n", last_pos); - - if ((pos > buflen) or (pos == string::npos)) { - mReadBuffer = req_line.substr(last_pos, buflen - last_pos); -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX << " No HdrEnd Found, read more data. rem_hdr= " << mReadBuffer.size() - << " buflen= " << buflen - << DEBUGHDR << endl; - *(mLog->log())<< cUrlEncode::hexDump(mReadBuffer) << endl; -#endif - return OKAY; - } - - if ((last_pos - pos) == 0) { - // Header End -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX << " ---- Header End Found" << DEBUGHDR << endl; -#endif - - if (mReqContentLength != 0) { - mConnState = READPAYLOAD; - mPayload = req_line.substr(pos +2, buflen - (pos +2)); - if (mPayload.size() != mReqContentLength) - return OKAY; - else { - mConnState = SERVING; - return processRequest(); - } - } // if(content_length != 0) - else { - mConnState = SERVING; - return processRequest(); - } - } // if (header end) - - string line = req_line.substr(last_pos, (pos-last_pos)); -#ifdef DEBUG_REQHEADERS - *(mLog->log())<< " Line= " << line << endl; -#endif - last_pos = pos +2; - if (mPath.size() == 0) { -#ifndef DEBUG - *(mLog->log())<< " parsing Request Line= " << line << endl; -#endif - // is_req = false; - // Parse the request line - if (parseHttpRequestLine(line) != OKAY) { - return ERROR; - }; - } - else { - parseHttpHeaderLine (line); - } - } - return OKAY; -} - -int cHttpResource::processRequest() { - // do stuff based on the request and the query -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX << " processRequest for mPath= " << mPath << DEBUGHDR << endl; -#endif - struct stat statbuf; - bool ok_to_serve = false; - - if (mMethod.compare("POST")==0) { - return handlePost(); - } - - if (!((strcasecmp(mMethod.c_str(), "GET") == 0) or (strcasecmp(mMethod.c_str(), "HEAD") == 0))) { - sendError(501, "Not supported", NULL, "Method is not supported."); - return OKAY; - } - -#ifndef STANDALONE - if (mPath.compare("/recordings.xml") == 0) { - if (handleHeadRequest() != 0) - return OKAY; - - sendRecordingsXml( &statbuf); - return OKAY; - } - - if (mPath.compare("/channels.xml") == 0) { - if (handleHeadRequest() != 0) - return OKAY; - - sendChannelsXml( &statbuf); - return OKAY; - } - - if (mPath.compare("/epg.xml") == 0) { - if (handleHeadRequest() != 0) - return OKAY; - - sendEpgXml( &statbuf); - return OKAY; - } - - if (mPath.compare("/setResume.xml") == 0) { - if (handleHeadRequest() != 0) - return OKAY; - - receiveResume(); - return OKAY; - } - - if (mPath.compare("/getResume.xml") == 0) { - if (handleHeadRequest() != 0) - return OKAY; - - sendResumeXml(); - return OKAY; - } - - if (mPath.compare("/vdrstatus.xml") == 0) { - if (handleHeadRequest() != 0) - return OKAY; - - sendVdrStatusXml( &statbuf); - return OKAY; - } - - -#endif - - if (mPath.compare("/media.xml") == 0) { - if (handleHeadRequest() != 0) - return OKAY; - sendMediaXml( &statbuf); - return OKAY; - } - - if (mPath.compare("/widget.conf") == 0) { - mPath = mFactory->getConfigDir() + "/widget.conf"; - - if (stat(mPath.c_str(), &statbuf) < 0) { - sendError(404, "Not Found", NULL, "File not found."); - return OKAY; - } - if (handleHeadRequest() != 0) - return OKAY; - - mFileSize = statbuf.st_size; - mContentType = SINGLEFILE; - return sendFile(&statbuf); - } - - if (mPath.compare("/favicon.ico") == 0) { - mPath = mFactory->getConfigDir() + "/web/favicon.ico"; - - if (stat(mPath.c_str(), &statbuf) < 0) { - sendError(404, "Not Found", NULL, "File not found."); - return OKAY; - } - if (handleHeadRequest() != 0) - return OKAY; - - mFileSize = statbuf.st_size; - mContentType = SINGLEFILE; - return sendFile(&statbuf); - } - - - if (mPath.size() > 8) { - if (mPath.compare(mPath.size() -8, 8, "-seg.mpd") == 0) { - if (handleHeadRequest() != 0) - return OKAY; - sendManifest( &statbuf, false); - return OKAY; - } - } - - if (mPath.size() > 9) { - if (mPath.compare(mPath.size() -9, 9, "-seg.m3u8") == 0) { - if (handleHeadRequest() != 0) - return OKAY; - sendManifest( &statbuf, true); - return OKAY; - } - } - - if (mPath.size() > 7) { - if (mPath.compare(mPath.size() -7, 7, "-seg.ts") == 0) { - if (handleHeadRequest() != 0) - return OKAY; - - sendMediaSegment( &statbuf); - return OKAY; - } - } - - if (mPath.find("/web/") == 0) { - mPath = mFactory->getConfigDir() + mPath; - *(mLog->log())<< DEBUGPREFIX - << " Found web request. serving " << mPath << endl; - ok_to_serve = true; - } - - - if (stat(mPath.c_str(), &statbuf) < 0) { - // checking, whether the file or directory exists - *(mLog->log())<< DEBUGPREFIX - << " File Not found " << mPath << endl; - sendError(404, "Not Found", NULL, "File not found."); - - return OKAY; - } - - if (S_ISDIR(statbuf.st_mode)) { - // Do Folder specific checkings -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX - << " processRequest: isDir - mPath: " << mPath.c_str() << endl; -#endif - - if (mPath.size() >4) { - if (mPath.compare(mPath.size() - 4, 4, ".rec") == 0) { - // Handle any recording directory specifically - if (handleHeadRequest() != 0) - return OKAY; - - mContentType = VDRDIR; - return sendVdrDir( &statbuf); - } - } - - if (!((ok_to_serve) or (mPath.compare(0, (mFactory->getConfig()->getMediaFolder()).size(), mFactory->getConfig()->getMediaFolder()) == 0))) { - // No directory access outside of MediaFolder (and also VDRCONG/web) - sendError(404, "Not Found", NULL, "File not found."); - return OKAY; - } - if (handleHeadRequest() != 0) - return OKAY; - - mContentType = MEMBLOCK; - sendDir( &statbuf); - return OKAY; - } - else { - // mPath is not a folder, thus it is a file -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX - << " processRequest: file send\n"; -#endif - - // Check, if requested file is in Media Directory - if (!((ok_to_serve) or (mPath.compare(0, (mFactory->getConfig()->getMediaFolder()).size(), mFactory->getConfig()->getMediaFolder()) == 0))) { - sendError(404, "Not Found", NULL, "File not found."); - return OKAY; - } - if (handleHeadRequest() != 0) - return OKAY; - mFileSize = statbuf.st_size; - mContentType = SINGLEFILE; - return sendFile(&statbuf); - } - -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX - << " processRequest: Not Handled SHOULD not be here\n"; -#endif - return ERROR; -} - - -int cHttpResource::handleWrite() { - - if (mConnState == TOCLOSE) { - *(mLog->log())<< DEBUGPREFIX - << " handleWrite() in wrong state= " << getConnStateName() - << endl; - return ERROR; - } - - if (mConnState != SERVING) { - return OKAY; - } - - if (mBlkLen == mBlkPos) { - // note the mBlk may be filled with header info first. - if (fillDataBlk() != OKAY) { - return ERROR; - } - } - - int this_write = write(mFd, &mBlkData[mBlkPos], mBlkLen - mBlkPos); - if (this_write <=0) { - -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX - << " ERROR after write: Stopped (Client terminated Connection)" - << " mBlkPos= " << mBlkPos << " mBlkLen= " << mBlkLen - << DEBUGHDR << endl; -#endif - mConnState = TOCLOSE; - mConnected = false; - return ERROR; - } - mBlkPos += this_write; - - return OKAY; -} - - -int cHttpResource::fillDataBlk() { - char pathbuf[4096]; - - mBlkPos = 0; - int to_read = 0; - - switch(mContentType) { - case NYD: - - break; - case VDRDIR: - // Range requests are assumed to be all open - if (mFile == NULL) { - *(mLog->log()) << DEBUGPREFIX << " no open file anymore " - << "--> Done " << endl; - return ERROR; - } - if ((mRemLength == 0) && !mStreamToEnd){ -#ifndef DEBUG - *(mLog->log()) << DEBUGPREFIX << " mRemLength is zero " - << "--> Done " << endl; -#endif - fclose(mFile); - mFile = NULL; - return ERROR; - } - if (!mStreamToEnd) - to_read = ((mRemLength > MAXLEN) ? MAXLEN : mRemLength); - else - to_read = MAXLEN; - - mBlkLen = fread(mBlkData, 1, to_read, mFile); - if (!mStreamToEnd) - mRemLength -= mBlkLen; - - if ((mRemLength == 0) && (!mStreamToEnd)) { -#ifndef DEBUG - *(mLog->log()) << DEBUGPREFIX << " last Block read " - << "--> Almost Done " << endl; -#endif - return OKAY; - } - - // if (mBlkLen != MAXLEN) { // thlo verify - if (mBlkLen != to_read) { - fclose(mFile); - mFile = NULL; - mVdrIdx ++; - - snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), mDir.c_str(), mVdrIdx); - mPath = pathbuf; - - if (openFile(pathbuf) != OKAY) { - *(mLog->log())<< DEBUGPREFIX << " Failed to open file= " << pathbuf << " mRemLength= " << mRemLength<< endl; - mFile = NULL; - if (mBlkLen == 0) { - *(mLog->log()) << DEBUGPREFIX << " mBlkLen is zero --> Done " << endl; - return ERROR; - } - else - *(mLog->log()) << DEBUGPREFIX << " Still data to send mBlkLen= " << mBlkLen <<" --> continue " << endl; - return OKAY; - } // Error: Open next file failed - - if (mBlkLen == 0) { - if (!mStreamToEnd) - to_read = ((mRemLength > MAXLEN) ? MAXLEN : mRemLength); - else - to_read = MAXLEN; - mBlkLen = fread(mBlkData, 1, to_read, mFile); - if (!mStreamToEnd) - mRemLength -= mBlkLen ; - } - } - break; - case SINGLEFILE: - - to_read = ((mRemLength > MAXLEN) ? MAXLEN : mRemLength); - mBlkLen = fread(mBlkData, 1, to_read, mFile); - mRemLength -= mBlkLen; - if (mBlkLen == 0) { - - // read until EOF - fclose(mFile); - mFile = NULL; - return ERROR; - } - break; - case MEMBLOCK: - int rem_len = mResponseMessage->size() - mResponseMessagePos; - if (rem_len == 0) { - -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX << " fillDataBlock: MEMBLOCK done" << endl; -#endif - delete mResponseMessage; - mResponseMessagePos = 0; - mConnState = TOCLOSE; - return ERROR; - } - if (rem_len > MAXLEN) - rem_len = MAXLEN; - - string sub_msg = mResponseMessage->substr(mResponseMessagePos, rem_len); - mResponseMessagePos += rem_len; - mBlkLen = sub_msg.size(); - memcpy(mBlkData, sub_msg.c_str(), rem_len); - break; - } - - return OKAY; -} - - -int cHttpResource::parseResume(cResumeEntry &entry, string &id) { - bool done = false; - size_t cur_pos = 0; - - // The asset_id should the the filename, which is provided by the link element in the xml - // the link is url-safe encoded. - - // bool have_devid = false; - bool have_filename = false; - bool have_resume = false; - - while (!done) { - size_t pos = mPayload.find('\n', cur_pos); - if (pos == string::npos) { - done = true; - continue; - } - size_t pos_col = mPayload.find(':', cur_pos); - string attr= mPayload.substr(cur_pos, (pos_col- cur_pos)); - string val = mPayload.substr(pos_col +1, (pos - pos_col-1)); - - /* if (attr== "devid") { - have_devid = true; - id = val; - } - else */ - if (attr == "filename") { - have_filename = true; - entry.mFilename = cUrlEncode::doXmlSaveDecode(val); - *(mLog->log())<< DEBUGPREFIX - << " filename= " << entry.mFilename - << endl; - } - else if (attr == "resume") { - have_resume = true; - entry.mResume = atof(val.c_str()); - *(mLog->log())<< DEBUGPREFIX - << " mResume= " << entry.mResume - << endl; - } - else { - *(mLog->log())<< DEBUGPREFIX - << " parseResume: ERROR: Unknown attr= " << attr - << " with val= " << val - << endl; - } - cur_pos = pos +1; - if (cur_pos >= mPayload.size()) - done= true; - } - if (have_resume && have_filename ) - return OKAY; - else - return ERROR; -} - -int cHttpResource::handleHeadRequest() { - if (mMethod.compare("HEAD") != 0) { - return 0; - } - *(mLog->log())<< DEBUGPREFIX - << " Handle HEAD Request for Url " << mPath << endl; - mConnState = SERVING; - mContentType = MEMBLOCK; - - // sent an empty response message with just the OK header - mResponseMessage = new string(); - *mResponseMessage = ""; - mResponseMessagePos = 0; - - sendHeaders(200, "OK", NULL, NULL, -1, -1); - return 1; -} - -int cHttpResource::handlePost() { - mConnState = SERVING; - mContentType = MEMBLOCK; - - // sent an empty response message with just the OK header - mResponseMessage = new string(); - *mResponseMessage = ""; - mResponseMessagePos = 0; - - if (mPath.compare("/log") == 0) { - *(mLog->log())<< mPayload - << endl; - - sendHeaders(200, "OK", NULL, NULL, -1, -1); - return OKAY; - } - - if (mPath.compare("/getResume.xml") == 0) { - if (handleHeadRequest() != 0) - return OKAY; - - // sendResumeXml( &statbuf); - sendResumeXml( ); - return OKAY; - } - - if (mPath.compare("/setResume.xml") == 0) { - if (handleHeadRequest() != 0) - return OKAY; - - receiveResume(); - return OKAY; - } - - if (mPath.compare("/deleteRecording.xml") == 0) { - if (handleHeadRequest() != 0) - return OKAY; - - deleteRecording(); - return OKAY; - } - - // Should not reach the end of the function - return ERROR; -} - - -void cHttpResource::sendError(int status, const char *title, const char *extra, const char *text) { - char f[400]; - - mConnState = SERVING; - mContentType = MEMBLOCK; - mResponseMessage = new string(); - *mResponseMessage = ""; - mResponseMessagePos = 0; - - string hdr = ""; - // sendHeaders(status, title, extra, "text/html", -1, -1); - sendHeaders(status, title, extra, "text/plain", -1, -1); - - /* snprintf(f, sizeof(f), "%d %s\r\n", status, title); - hdr += f; - snprintf(f, sizeof(f), "

%d %s

\r\n", status, title); - hdr += f; -*/ - snprintf(f, sizeof(f), "%s\r\n", text); - hdr += f; - /* snprintf(f, sizeof(f), "\r\n"); - hdr += f; -*/ - - strcpy(&(mBlkData[mBlkLen]), hdr.c_str()); - mBlkLen += hdr.size(); - -} - - -int cHttpResource::sendDir(struct stat *statbuf) { - char pathbuf[4096]; - char f[400]; - int len; - - mConnState = SERVING; - -#ifndef DEBUG - *(mLog->log()) << DEBUGPREFIX << " sendDir: mPath= " << mPath << endl; -#endif - len = mPath.length(); - // int ret = OKAY; - - if (len == 0 || mPath[len - 1] != '/') { - snprintf(pathbuf, sizeof(pathbuf), "Location: %s/", mPath.c_str()); - sendError(302, "Found", pathbuf, "Directories must end with a slash."); - return OKAY; - } - - snprintf(pathbuf, sizeof(pathbuf), "%sindex.html", mPath.c_str()); - if (stat(pathbuf, statbuf) >= 0) { - mPath = pathbuf; - mFileSize = statbuf->st_size; - mContentType = SINGLEFILE; - return sendFile(statbuf); - } - -#ifndef DEBUG - *(mLog->log()) << DEBUGPREFIX << " sendDir: create index.html " << endl; -#endif - DIR *dir; - struct dirent *de; - - mResponseMessage = new string(); - mResponseMessagePos = 0; - *mResponseMessage = ""; - - string hdr = ""; - snprintf(f, sizeof(f), "Index of %s\r\n", mPath.c_str()); - hdr += f; - snprintf(f, sizeof(f), "

Index of %s

\r\n
\n", mPath.c_str());
-  hdr += f;
-  snprintf(f, sizeof(f), "Name                             Last Modified              Size\r\n");
-  hdr += f;
-  snprintf(f, sizeof(f), "
\r\n"); - hdr += f; - - *mResponseMessage += hdr; - hdr = ""; - - if (len > 1) { - snprintf(f, sizeof(f), "..\r\n"); - hdr += f; - } - *mResponseMessage += hdr; - - dir = opendir(mPath.c_str()); - while ((de = readdir(dir)) != NULL) { - char timebuf[32]; - struct tm *tm; - strcpy(pathbuf, mPath.c_str()); - // printf (" -Entry: %s\n", de->d_name); - strcat(pathbuf, de->d_name); - - stat(pathbuf, statbuf); - tm = gmtime(&(statbuf->st_mtime)); - strftime(timebuf, sizeof(timebuf), "%d-%b-%Y %H:%M:%S", tm); - - hdr = ""; - snprintf(f, sizeof(f), "", de->d_name, S_ISDIR(statbuf->st_mode) ? "/" : ""); - hdr += f; - - snprintf(f, sizeof(f), "%s%s", de->d_name, S_ISDIR(statbuf->st_mode) ? "/" : " "); - hdr += f; - - if (strlen(de->d_name) < 32) { - snprintf(f, sizeof(f), "%*s", 32 - strlen(de->d_name), ""); - hdr += f; - } - if (S_ISDIR(statbuf->st_mode)) { - snprintf(f, sizeof(f), "%s\r\n", timebuf); - hdr += f; - } - else { - snprintf(f, sizeof(f), "%s\r\n", timebuf); - hdr += f; - } - - *mResponseMessage += hdr; - } - closedir(dir); - snprintf(f, sizeof(f), "
\r\n
\r\n
%s
\r\n\r\n", SERVER); - *mResponseMessage += f; - - sendHeaders(200, "OK", NULL, "text/html", mResponseMessage->size(), statbuf->st_mtime); - - mRemLength = 0; - return OKAY; -} - - -int cHttpResource::writeXmlItem(string name, string link, string programme, string desc, string guid, int no, time_t start, int dur, double fps, int is_pes, int is_new) { - string hdr = ""; - char f[400]; - - hdr += "\n"; - // snprintf(f, sizeof(f), "%s - %s", ); - hdr += "" + name +"\n"; - hdr += "" +link + "\n"; - hdr += "\n"; - - hdr += "" + guid + "\n"; - - snprintf(f, sizeof(f), "%d", no); - hdr += ""; - hdr += f; - hdr += "\n"; - - hdr += "" + programme +"\n"; - hdr += "" + desc + "\n"; - - snprintf(f, sizeof(f), "%ld", start); - hdr += ""; - hdr += f; - hdr += "\n"; - - snprintf(f, sizeof(f), "%d", dur); - hdr += ""; - hdr += f; - hdr += "\n"; - - if (fps != -1) - snprintf(f, sizeof(f), "%.2f\n", fps); - else - snprintf(f, sizeof(f), "unknown\n"); - hdr += f; - - switch (is_pes){ - case -1: - // unknown - hdr += "unknown\n"; - break; - case 0: - // true - hdr += "true\n"; - break; - case 1: - // false - hdr += "false\n"; - break; - default: - break; - } - - switch (is_new){ - case -1: - // unknown - hdr += "unknown\n"; - break; - case 0: - // true - hdr += "true\n"; - break; - case 1: - // false - hdr += "false\n"; - break; - default: - break; - } - - hdr += "\n"; - - *mResponseMessage += hdr; - - - - - // return writeToClient(hdr.c_str(), hdr.size()); - return OKAY; -} - -int cHttpResource::parseQueryLine (vector *avps) { - bool done = false; - size_t cur_pos = 0; - while (!done) { - size_t end_pos = mQuery.find('&', cur_pos); - size_t pos_eq = mQuery.find('=', cur_pos); - - if (pos_eq != cur_pos) { - avps->push_back(sQueryAVP(mQuery.substr(cur_pos, (pos_eq -cur_pos)), mQuery.substr(pos_eq+1, (end_pos -pos_eq-1)) )); - } - if (end_pos == string::npos) - done = true; - else - cur_pos = end_pos +1; - } - - return OKAY; -} - -int cHttpResource::getQueryAttributeValue(vector *avps, string attr, string &val) { - int found = ERROR; - for (uint i = 0; i < avps->size(); i++) { - if ((*avps)[i].attribute == attr) { - val = (*avps)[i].value; - found = OKAY; - } -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX - << " a= " - << (*avps)[i].attribute - << " v= " << (*avps)[i].value - << endl; -#endif - } - return found; -} - -int cHttpResource::parseFiles(vector *entries, string prefix, string dir_base, string dir_name, struct stat *statbuf) { - char pathbuf[4096]; - string link; - DIR *dir; - struct dirent *de; - string dir_comp; - dir_comp = dir_base + dir_name + "/"; - -#ifndef DEBUG - *(mLog->log()) << DEBUGPREFIX - << " parseFiles: Prefix= " << prefix - << " base= " << dir_base - << " dir= " << dir_name - << " comp= " << dir_comp - << endl; -#endif - - dir = opendir(dir_comp.c_str()); - if (stat(dir_comp.c_str(), statbuf) < 0) - return ERROR; - - while ((de = readdir(dir)) != NULL) { - if ((strcmp(de->d_name, ".") == 0) or (strcmp(de->d_name, "..") == 0)) { - continue; - } - - strcpy(pathbuf, dir_comp.c_str()); - strcat(pathbuf, de->d_name); - - stat(pathbuf, statbuf); - - if (S_ISDIR(statbuf->st_mode)) { - if (strcmp(&(pathbuf[strlen(pathbuf)-4]), ".rec") == 0) { - // vdr folder - time_t now = time(NULL); - struct tm tm_r; - struct tm t = *localtime_r(&now, &tm_r); - t.tm_isdst = -1; - // char [20] rest; - int start = -1; - sscanf(de->d_name, "%4d-%02d-%02d.%02d.%02d", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min); - - // sscanf(de->d_name, "%4d-%02d-%02d.%02d%.%02d", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min); - t.tm_year -= 1900; - t.tm_mon--; - t.tm_sec = 0; - start = mktime(&t); - -#ifndef DEBUG - *(mLog->log()) << DEBUGPREFIX - << " Vdr Folder Found: " << pathbuf << " start= " << start << endl; -#endif - - entries->push_back(sFileEntry(dir_name, pathbuf, start)); - } - else { - // regular file - parseFiles(entries, prefix + de->d_name + "~", dir_comp, de->d_name, statbuf); - } - } - else { - if ((de->d_name)[0] != '.' ) - entries->push_back(sFileEntry(prefix+de->d_name, pathbuf, 1)); - } - } - closedir(dir); - return OKAY; -} - -int cHttpResource::sendManifest (struct stat *statbuf, bool is_hls) { -#ifndef STANDALONE - - size_t pos = mPath.find_last_of ("/"); - - mDir = mPath.substr(0, pos); - string mpd_name = mPath.substr(pos+1); - - float seg_dur = mFactory->getConfig()->getSegmentDuration() *1.0; - - cRecordings* recordings = &Recordings; - cRecording* rec = recordings->GetByName(mDir.c_str()); - double duration = rec->NumFrames() / rec->FramesPerSecond(); - - int bitrate = (int)((getVdrFileSize() *8.0 * mFactory->getConfig()->getHasBitrateCorrection()/ duration) +0.5); - - time_t now = time(NULL); - - if (rec->Info() != NULL){ - if (rec->Info()->GetEvent() != NULL) { - if (rec->Info()->GetEvent()->EndTime() > now) { - - float corr = (now - rec->Info()->GetEvent()->StartTime()) - duration; - duration = rec->Info()->GetEvent()->Duration() -int(corr); - *(mLog->log()) << DEBUGPREFIX - << " is Recording: Duration= " << duration << " sec" - << " correction: " << int(corr) - << endl; - } - } - else - *(mLog->log()) << DEBUGPREFIX << " WARNING: rec-Info()->GetEvent() is NULL " << endl; - } - else - *(mLog->log()) << DEBUGPREFIX << " WARNING: rec-Info() is NULL " << endl; - - // duration is now either the actual duration of the asset or the target duration of the asset - int end_seg = int (duration / seg_dur) +1; - - *(mLog->log()) << DEBUGPREFIX - << " Manifest for mDir= " << mDir - << " duration= " << duration - << " seg_dur= " << seg_dur - << " end_seg= " << end_seg - << endl; - - - - if (is_hls) { - writeM3U8(duration, bitrate, seg_dur, end_seg); - } - else { - writeMPD(duration, bitrate, seg_dur, end_seg); - } - -#endif - return OKAY; -} - -void cHttpResource::writeM3U8(double duration, int bitrate, float seg_dur, int end_seg) { - mResponseMessage = new string(); - mResponseMessagePos = 0; - *mResponseMessage = ""; - mContentType = MEMBLOCK; - - mConnState = SERVING; - char buf[30]; - - string hdr = ""; - - - *mResponseMessage += "#EXTM3U\n"; - // snprintf(buf, sizeof(buf), "#EXT-X-TARGETDURATION:%d\n", (seg_dur-1)); - snprintf(buf, sizeof(buf), "#EXT-X-TARGETDURATION:%d\n", int(seg_dur)); - hdr = buf; - *mResponseMessage += hdr; - - *mResponseMessage += "#EXT-X-MEDIA-SEQUENCE:1\n"; - *mResponseMessage += "#EXT-X-KEY:METHOD=NONE\n"; - - for (int i = 1; i < end_seg; i++){ - // snprintf(buf, sizeof(buf), "#EXTINF:%.1f,\n", (seg_dur-0.5)); - snprintf(buf, sizeof(buf), "#EXTINF:%.2f,\n", seg_dur); - hdr = buf; - *mResponseMessage += hdr; - - snprintf(buf, sizeof(buf), "%d-seg.ts\n", i); - hdr = buf; - *mResponseMessage += hdr; - } - *mResponseMessage += "#EXT-X-ENDLIST\n"; - - sendHeaders(200, "OK", NULL, "application/x-mpegURL", mResponseMessage->size(), -1); -} - - -void cHttpResource::writeMPD(double duration, int bitrate, float seg_dur, int end_seg) { - mResponseMessage = new string(); - mResponseMessagePos = 0; - *mResponseMessage = ""; - mContentType = MEMBLOCK; - - mConnState = SERVING; - char buf[30]; - char line[400]; - - string hdr = ""; - - *mResponseMessage += "\n"; - - snprintf(line, sizeof(line), "getConfig()->getHasMinBufferTime(), duration); - *mResponseMessage = *mResponseMessage + line; - - *mResponseMessage += " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema\" xmlns=\"urn:mpeg:mpegB:schema:DASH:MPD:DIS2011\" "; - *mResponseMessage += "xsi:schemaLocation=\"urn:mpeg:mpegB:schema:DASH:MPD:DIS2011\">\n"; - *mResponseMessage += "\n"; - *mResponseMessage += "\n"; - *mResponseMessage += "\n"; - *mResponseMessage += "\n"; - // SD: 720x 576 - // HD: 1280x 720 - // snprintf(line, sizeof(line), "\n", mFactory->getConfig()->getHasBitrate()); - snprintf(line, sizeof(line), "\n", - bitrate, ((bitrate < 10000000)? "width=\"720\" height=\"576\"" : "width=\"1280\" height=\"720\"")); - *mResponseMessage = *mResponseMessage + line; - - hdr = "\n"; - *mResponseMessage += hdr; - - snprintf(buf, sizeof(buf), "\"%d\"", end_seg); - *mResponseMessage += "\n"; - - *mResponseMessage += "\n"; - *mResponseMessage += "\n"; - *mResponseMessage += "\n"; - *mResponseMessage += ""; - - sendHeaders(200, "OK", NULL, "application/x-mpegURL", mResponseMessage->size(), -1); -} - -int cHttpResource::sendMediaSegment (struct stat *statbuf) { -#ifndef STANDALONE - - *(mLog->log()) << DEBUGPREFIX << " sendMediaSegment " << mPath << endl; - size_t pos = mPath.find_last_of ("/"); - - mDir = mPath.substr(0, pos); - string seg_name = mPath.substr(pos+1); - int seg_number; - - int seg_dur = mFactory->getConfig()->getSegmentDuration(); - int frames_per_seg = 0; - - sscanf(seg_name.c_str(), "%d-seg.ts", &seg_number); - - //FIXME: Do some consistency checks on the seg_number - //* Does the segment exist - - cRecordings* recordings = &Recordings; - cRecording* rec = recordings->GetByName(mDir.c_str()); - if (rec != NULL) { - frames_per_seg = seg_dur * rec->FramesPerSecond(); - } - else { - *(mLog->log()) << DEBUGPREFIX << " ERROR: Ooops, rec is NULL, assuming 25 fps " << endl; - frames_per_seg = seg_dur * 25; - } - //FIXME: HD Fix - // frames_per_seg = seg_dur * 25; - - *(mLog->log()) << DEBUGPREFIX - << " mDir= " << mDir - << " seg_name= " << seg_name - << " seg_number= "<< seg_number - << " fps= " << rec->FramesPerSecond() - << " frames_per_seg= " << frames_per_seg - << endl; - int start_frame_count = (seg_number -1) * frames_per_seg; - - FILE* idx_file = fopen((mDir +"/index").c_str(), "r"); - if (idx_file == NULL){ - *(mLog->log()) << DEBUGPREFIX - << " failed to open idx file = "<< (mDir +"/index").c_str() - << endl; - sendError(404, "Not Found", NULL, "Failed to open Index file"); - return OKAY; - } - - char *index_buf = new char[(frames_per_seg +3) *8]; - - // fseek to start_frame_count * sizeof(in_read) - fseek(idx_file, start_frame_count * 8, SEEK_SET); - - // read to (seg_number * frames_per_seg +1) * sizeof(in_read) - // buffersize is frames_per_seg * seg_number * sizeof(in_read) - int buffered_indexes = fread(index_buf, 8, (frames_per_seg +2), idx_file); - - fclose(idx_file); - - if(buffered_indexes <= 0 ) { - *(mLog->log())<log()) << DEBUGPREFIX - << " GenSegment: start (no/idx)= " << start_idx << " / " << start_offset - << " to (no/idx)= " << end_idx << " / " << end_offset - << endl; - /*#endif*/ - - delete[] index_buf; - - int rem_len = 0; - bool error = false; - if (start_idx == end_idx){ - mRemLength = (end_offset - start_offset); - -#ifndef DEBUG - *(mLog->log()) << DEBUGPREFIX - << " start_idx == end_idx: mRemLength= " <log()) << DEBUGPREFIX - << " start_idx < end_idx " - << endl; -#endif - snprintf(seg_fn, sizeof(seg_fn), mFileStructure.c_str(), mDir.c_str(), mVdrIdx); - if (stat(seg_fn, statbuf) < 0) { - *(mLog->log()) << DEBUGPREFIX - << " file= " <st_size - start_offset; // remaining length of the first segment - - // loop over all idx files between start_idx and end_idx - for (int idx = (start_idx+1); idx < end_idx; idx ++) { - snprintf(seg_fn, sizeof(seg_fn), mFileStructure.c_str(), mDir.c_str(), idx); - if (stat(seg_fn, statbuf) < 0) { - *(mLog->log()) << DEBUGPREFIX - << " for loop file= " <st_size; // remaining length of the first segment - } - rem_len += end_offset; // - mRemLength = rem_len; - snprintf(seg_fn, sizeof(seg_fn), mFileStructure.c_str(), mDir.c_str(), mVdrIdx); - -#ifndef DEBUG - *(mLog->log()) << DEBUGPREFIX - << " start_idx= " << start_idx << " != end_idx= "<< end_idx <<": mRemLength= " <log())<< DEBUGPREFIX << " Failed to open file= " << seg_fn - << " mRemLength= " << mRemLength<< endl; - sendError(404, "Not Found", NULL, "File not found."); - return OKAY; - } - fseek(mFile, start_offset, SEEK_SET); - - sendHeaders(200, "OK", NULL, "video/mpeg", mRemLength, -1); - -#endif - return OKAY; -} - -int cHttpResource::sendMediaXml (struct stat *statbuf) { - char pathbuf[4096]; - string link; - string media_folder = mFactory->getConfig()->getMediaFolder(); - - mResponseMessage = new string(); - mResponseMessagePos = 0; - *mResponseMessage = ""; - mContentType = MEMBLOCK; - - mConnState = SERVING; - -#ifndef DEBUG - *(mLog->log()) << DEBUGPREFIX << " sendMedia " << endl; -#endif - - string own_ip = getOwnIp(mFd); - *(mLog->log()) << " OwnIP= " << own_ip << endl; - - vector entries; - - if (parseFiles(&entries, "", media_folder, "", statbuf) == ERROR) { - sendError(404, "Not Found", NULL, "Media Folder likely not configured."); - return OKAY; - } - - string hdr = ""; - hdr += "\n"; - hdr += "\n"; - hdr+= "\n"; - - *mResponseMessage += hdr; - - hdr = ""; - - for (uint i=0; i < entries.size(); i++) { - - // snprintf(pathbuf, sizeof(pathbuf), "http://%s:%d%s", mServerAddr.c_str(), mServerPort, - snprintf(pathbuf, sizeof(pathbuf), "http://%s:%d%s", own_ip.c_str(), mServerPort, - cUrlEncode::doUrlSaveEncode(entries[i].sPath).c_str()); - if (writeXmlItem(cUrlEncode::doXmlSaveEncode(entries[i].sName), pathbuf, "NA", "NA", "-", - -1, entries[i].sStart, -1, -1, -1, -1) == ERROR) - return ERROR; - - } - - hdr = "\n"; - hdr += "\n"; - *mResponseMessage += hdr; - sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), statbuf->st_mtime); - - return OKAY; -} - -int cHttpResource::sendVdrStatusXml (struct stat *statbuf) { - -#ifndef STANDALONE - - char f[400]; - mResponseMessage = new string(); - *mResponseMessage = ""; - mResponseMessagePos = 0; - mContentType = MEMBLOCK; - - mConnState = SERVING; - - int free; - int used; - int percent; - - percent = VideoDiskSpace(&free, &used); - - *mResponseMessage += "\n"; - *mResponseMessage += "\n"; - - *mResponseMessage += "\n"; - snprintf(f, sizeof(f), "%d", free); - *mResponseMessage += f; - - snprintf(f, sizeof(f), "%d", used); - *mResponseMessage += f; - snprintf(f, sizeof(f), "%d", percent); - *mResponseMessage += f; - *mResponseMessage += "\n"; - - *mResponseMessage += "\n"; - - sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), -1); - -#endif - return OKAY; -} - -int cHttpResource::sendEpgXml (struct stat *statbuf) { -#ifndef STANDALONE - - char f[400]; - mResponseMessage = new string(); - *mResponseMessage = ""; - mResponseMessagePos = 0; - mContentType = MEMBLOCK; - - mConnState = SERVING; - -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX - << " generating /epg.xml" - << DEBUGHDR << endl; -#endif - vector avps; - parseQueryLine(&avps); - string id = "S19.2E-1-1107-17500"; - string mode = ""; - bool add_desc = true; - - if (getQueryAttributeValue(&avps, "id", id) == ERROR){ - *(mLog->log())<< DEBUGPREFIX - << " ERROR: id not found" - << DEBUGHDR << endl; - delete mResponseMessage; - sendError(400, "Bad Request", NULL, "no id in query line"); - return OKAY; - } - - if (getQueryAttributeValue(&avps, "mode", mode) == OKAY){ - if (mode == "nodesc") { - add_desc = false; - *(mLog->log())<< DEBUGPREFIX - << " **** Mode: No Description ****" - << endl; - } - } - - /* for (int i = 0; i < avps.size(); i++) { - if (avps[i].attribute == "id") - id = avps[i].value; - *(mLog->log())<< DEBUGPREFIX - << " a= " - << avps[i].attribute - << " v= " << avps[i].value - << endl; - }*/ - tChannelID chan_id = tChannelID::FromString (id.c_str()); - if ( chan_id == tChannelID::InvalidID) { - *(mLog->log())<< DEBUGPREFIX - << " ERROR: Not possible to get the ChannelId from the string" - << DEBUGHDR << endl; - delete mResponseMessage; - sendError(400, "Bad Request", NULL, "Invalid Channel ID."); - return OKAY; - } - - cSchedulesLock * lock = new cSchedulesLock(false, 500); - const cSchedules *schedules = cSchedules::Schedules(*lock); - - const cSchedule *schedule = schedules->GetSchedule(chan_id); - if (schedule == NULL) { - *(mLog->log())<< DEBUGPREFIX - << "ERROR: Schedule is zero for guid= " << id - << endl; - delete mResponseMessage; - sendError(500, "Internal Server Error", NULL, "Schedule is zero."); - return OKAY; - } - - time_t now = time(NULL); - const cEvent *ev = NULL; - for(cEvent* e = schedule->Events()->First(); e; e = schedule->Events()->Next(e)) { - if ( (e->StartTime() <= now) && (e->EndTime() > now)) { - ev = e; - - } else if (e->StartTime() > now + 3600) { - break; - } - } - - // const cEvent * ev = schedule->GetPresentEvent(); - - if (ev == NULL) { - *(mLog->log())<< DEBUGPREFIX - << "ERROR: Event is zero for guid= " << id - << endl; - delete mResponseMessage; - sendError(500, "Internal Server Error", NULL, "Event is zero."); - return OKAY; - } - - string hdr = ""; - hdr += "\n"; - hdr += "\n"; - hdr+= "\n"; - - *mResponseMessage += hdr; - // Payload here - - hdr = ""; - if (ev->Title() != NULL) { - string title = cUrlEncode::doXmlSaveEncode(ev->Title()); - hdr += "" + title +"\n"; - } - else { - *(mLog->log())<< DEBUGPREFIX - << " ERROR: title is zero for guid= " << id << endl; - hdr += "Empty\n"; - - delete mResponseMessage; - sendError(500, "Internal Server Error", NULL, "Title is zero."); - return OKAY; - } - - hdr += "" + id + "\n"; - - *(mLog->log())<< DEBUGPREFIX - << " guid= " << id - << " title= " << ev->Title() - << " start= " << ev->StartTime() - << " end= " << ev->EndTime() - << " now= " << now - << endl; - if (add_desc) { - hdr += ""; - if (ev->Description() != NULL) { - hdr += cUrlEncode::doXmlSaveEncode(ev->Description()); - } - else { - *(mLog->log())<< DEBUGPREFIX - << " ERROR: description is zero for guid= " << id << endl; - - delete mResponseMessage; - sendError(500, "Internal Server Error", NULL, "Description is zero."); - return OKAY; - } - hdr += "\n"; - } - else { - hdr += "No Description Available\n"; - } - snprintf(f, sizeof(f), "%ld\n", ev->StartTime()); - hdr += f ; - - snprintf(f, sizeof(f), "%ld\n", ev->EndTime()); - hdr += f; - - snprintf(f, sizeof(f), "%d\n", ev->Duration()); - hdr += f; - *mResponseMessage += hdr; - - hdr = "\n"; - hdr += "\n"; - - *mResponseMessage += hdr; - - delete lock; - - sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), -1); - -#endif - return OKAY; -} - -int cHttpResource::sendChannelsXml (struct stat *statbuf) { -#ifndef STANDALONE - - char f[400]; - mResponseMessage = new string(); - *mResponseMessage = ""; - mResponseMessagePos = 0; - mContentType = MEMBLOCK; - - mConnState = SERVING; - -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX - << " generating /channels.xml" - << DEBUGHDR << endl; -#endif - string own_ip = getOwnIp(mFd); - *(mLog->log()) << " OwnIP= " << own_ip << endl; - - vector avps; - parseQueryLine(&avps); - string mode = ""; - bool add_desc = true; - - string no_channels_str = ""; - int no_channels = -1; - string group_sep = ""; - - if (getQueryAttributeValue(&avps, "mode", mode) == OKAY){ - if (mode == "nodesc") { - add_desc = false; - *(mLog->log())<< DEBUGPREFIX - << " Mode: No Description" - << endl; - } - else { - *(mLog->log())<< DEBUGPREFIX - << " Mode: Unknown" - << endl; - } - } - if (getQueryAttributeValue(&avps, "channels", no_channels_str) == OKAY){ - no_channels = atoi(no_channels_str.c_str()) ; - } - - - string hdr = ""; - hdr += "\n"; - hdr += "\n"; - hdr+= "\n"; - hdr+= "VDR Channels List\n"; - - - *mResponseMessage += hdr; - - int count = mFactory->getConfig()->getLiveChannels(); - if (no_channels > 0) - count = no_channels +1; - - cSchedulesLock * lock = new cSchedulesLock(false, 500); - const cSchedules *schedules = cSchedules::Schedules(*lock); - - for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) { - if (channel->GroupSep()) { - if (mFactory->getConfig()->getGroupSep() != IGNORE) { - // if emtpyFolderDown, always. - // otherwise, only when not empty - if (!((strcmp(channel->Name(), "") == 0) && (mFactory->getConfig()->getGroupSep() == EMPTYIGNORE))) - group_sep = cUrlEncode::doXmlSaveEncode(channel->Name()); - - } - continue; - } - if (--count == 0) { - break; - } - - // snprintf(f, sizeof(f), "http://%s:3000/%s.ts", mServerAddr.c_str(), *(channel->GetChannelID()).ToString()); - snprintf(f, sizeof(f), "http://%s:3000/%s.ts", own_ip.c_str(), *(channel->GetChannelID()).ToString()); - string link = f; - - const cSchedule *schedule = schedules->GetSchedule(channel->GetChannelID()); - string desc = "No description available"; - string title = "Not available"; - time_t start_time = 0; - int duration = 0; - - if (schedule != NULL) { - const cEvent *ev = schedule->GetPresentEvent(); - if (ev != NULL) { - if ((ev->Description() != NULL) && add_desc) - desc = cUrlEncode::doXmlSaveEncode(ev->Description()); - - if ((ev->Title() != NULL) && add_desc) - title = cUrlEncode::doXmlSaveEncode(ev->Title()); - start_time = ev->StartTime(); - duration = ev->Duration(); - } - else { - *(mLog->log())<< DEBUGPREFIX - << " Event Info is Zero for Count= " - << count - << " Name= " << channel->Name() << endl; - } - } - else { - *(mLog->log())<< DEBUGPREFIX - << " Schedule is Zero for Count= " - << count - << " Name= " << channel->Name() << endl; - } - - string c_name = (group_sep != "") ? (group_sep + "~" + cUrlEncode::doXmlSaveEncode(channel->Name())) - : cUrlEncode::doXmlSaveEncode(channel->Name()); - // if (writeXmlItem(channel->Name(), link, title, desc, *(channel->GetChannelID()).ToString(), start_time, duration) == ERROR) - if (writeXmlItem(c_name, link, title, desc, *(channel->GetChannelID()).ToString(), channel->Number(), start_time, duration, -1, -1, -1) == ERROR) - return ERROR; - - } - - hdr = "\n"; - hdr += "\n"; - - *mResponseMessage += hdr; - delete lock; - sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), statbuf->st_mtime); - -#endif - return OKAY; -} - -int cHttpResource::receiveResume() { - string dev_id; - cResumeEntry entry; - if (parseResume(entry, dev_id) == ERROR) { - *(mLog->log())<< DEBUGPREFIX - << " ERROR parsing resume" - << endl; - } - - *(mLog->log())<< DEBUGPREFIX - << " Resume: id= " << dev_id - << " resume= " << entry << endl; - - vector avps; - parseQueryLine(&avps); - string guid; - string resume_str; - - if (getQueryAttributeValue(&avps, "guid", guid) == OKAY){ - entry.mFilename = guid; - *(mLog->log())<< DEBUGPREFIX - << " Found a id Parameter: " << guid - << endl; - } - if (getQueryAttributeValue(&avps, "resume", resume_str) == OKAY){ - entry.mResume = atof(resume_str.c_str()); - *(mLog->log())<< DEBUGPREFIX - << " Found a resume Parameter: " << entry.mResume - << endl; - } - - - - -#ifndef STANDALONE - cRecording *rec = Recordings.GetByName(entry.mFilename.c_str()); - if (rec == NULL) { - //Error 404 - sendError(404, "Not Found", NULL, "Failed to find recording."); - return OKAY; - } - - cResumeFile resume(entry.mFilename.c_str(), rec->IsPesRecording()); - *(mLog->log())<< DEBUGPREFIX - << " Resume: " << entry.mFilename - << " saving Index= " << int(entry.mResume * rec->FramesPerSecond() ) - << " mResume= " < avps; - parseQueryLine(&avps); - string guid; - - if (getQueryAttributeValue(&avps, "guid", guid) == OKAY){ - entry.mFilename = guid; - *(mLog->log())<< DEBUGPREFIX - << " Found a id Parameter: " << guid - << endl; - } - - - cRecording *rec = Recordings.GetByName(entry.mFilename.c_str()); - if (rec == NULL) { - //Error 404 - *(mLog->log())<< DEBUGPREFIX - << " sendResume: File Not Found filename= " << entry.mFilename << endl; - sendError(404, "Not Found", NULL, "Failed to find recording."); - return OKAY; - } - if (rec->IsNew()) { - *(mLog->log())<< DEBUGPREFIX - << " sendResume: file is new " << endl; - sendError(400, "Bad Request", NULL, "File is new."); - return OKAY; - } - cResumeFile resume(entry.mFilename.c_str(), rec->IsPesRecording()); - - *(mLog->log())<< DEBUGPREFIX - << " resume request for " << entry.mFilename - << " resume= " << resume.Read() - << " (" << resume.Read() *1.0 / rec->FramesPerSecond() << "sec)" - << endl; - - *mResponseMessage += "\n"; - *mResponseMessage += ""; - snprintf(f, sizeof(f), "%.02f", resume.Read() *1.0 / rec->FramesPerSecond()); - *mResponseMessage += f; - *mResponseMessage += "\n"; - - - sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), -1); - - return OKAY; -#endif -} - -int cHttpResource::deleteRecording() { - vector avps; - parseQueryLine(&avps); - string id = ""; - - if (getQueryAttributeValue(&avps, "id", id) == ERROR){ - *(mLog->log())<< DEBUGPREFIX - << " ERROR: id not found in query." - << DEBUGHDR << endl; - sendError(400, "Bad Request", NULL, "no id in query line"); - return OKAY; - } - mPath = cUrlEncode::doUrlSaveDecode(id); - - cRecording* rec = Recordings.GetByName(mPath.c_str()); - if (rec == NULL) { - *(mLog->log())<< DEBUGPREFIX - << " ERROR: Recording not found. Deletion failed: mPath= " << mPath - << endl; - sendError(404, "Not Found.", NULL, "Recording not found. Deletion failed!"); - return OKAY; - } - if ( rec->Delete() ) { - Recordings.DelByName(rec->FileName()); - // Recordings.DelByName(mPath.c_str()); - } - else { - *(mLog->log())<< DEBUGPREFIX - << " ERROR: rec->Delete() returns false. mPath= " << mPath - << endl; - sendError(500, "Internal Server Error", NULL, "deletion failed!"); - return OKAY; - } - - *(mLog->log())<< DEBUGPREFIX - << " Deleted." - << endl; - sendHeaders(200, "OK", NULL, NULL, -1, -1); - return OKAY; -} - - -int cHttpResource::sendRecordingsXml(struct stat *statbuf) { -#ifndef STANDALONE - - mResponseMessage = new string(); - *mResponseMessage = ""; - mResponseMessagePos = 0; - mContentType = MEMBLOCK; - - mConnState = SERVING; - - string own_ip = getOwnIp(mFd); - *(mLog->log()) << " OwnIP= " << own_ip << endl; - - vector avps; - parseQueryLine(&avps); - string model = ""; - string link_ext = ""; - string type = ""; - string has_4_hd_str = ""; - bool has_4_hd = true; - string mode = ""; - bool add_desc = true; - - if (getQueryAttributeValue(&avps, "model", model) == OKAY){ - *(mLog->log())<< DEBUGPREFIX - << " Found a Model Parameter: " << model - << endl; - } - - if (getQueryAttributeValue(&avps, "type", type) == OKAY){ - *(mLog->log())<< DEBUGPREFIX - << " Found a Type Parameter: " << type - << endl; - if (type == "hls") { - if (model == "samsung") - link_ext = "/manifest-seg.m3u8|COMPONENT=HLS"; - else - link_ext = "/manifest-seg.m3u8"; - } - if (type == "has") { - if (model == "samsung") - link_ext = "/manifest-seg.mpd|COMPONENT=HAS"; - else - link_ext = "/manifest-seg.mpd"; - } - } - - if (getQueryAttributeValue(&avps, "mode", mode) == OKAY){ - if (mode == "nodesc") { - add_desc = false; - *(mLog->log())<< DEBUGPREFIX - << " Mode: No Description" - << endl; - } - } - - if (getQueryAttributeValue(&avps, "has4hd", has_4_hd_str) == OKAY){ - *(mLog->log())<< DEBUGPREFIX - << " Found a Has4Hd Parameter: " << has_4_hd_str - << endl; - if (has_4_hd_str == "false") - has_4_hd = false; - } - - -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX - << " generating /recordings.xml" - << DEBUGHDR << endl; -#endif - sendHeaders(200, "OK", NULL, "application/xml", -1, statbuf->st_mtime); - - string hdr = ""; - hdr += "\n"; - hdr += "\n"; - hdr+= "\n"; - hdr+= "VDR Recordings List\n"; - - *mResponseMessage += hdr; - - /* - if (writeXmlItem("HAS - Big Bugs Bunny", "http://192.168.1.122/sm/BBB-DASH/HAS_BigBuckTS.xml|COMPONENT=HAS", "NA", "Big Bucks Bunny - HAS", - "-", 0, 0) == ERROR) - return ERROR; - - if (writeXmlItem("HLS - Big Bugs Bunny", "http://192.168.1.122/sm/BBB-DASH/HLS_BigBuckTS.m3u8|COMPONENT=HLS", "NA", "Big Bucks Bunny - HLS", - "-", 0, 0) == ERROR) - return ERROR; - if (writeXmlItem("HAS - Big Bugs Bunny", "http://192.168.1.122:8000/hd2/mpeg/BBB-DASH/HAS_BigBuckTS.xml|COMPONENT=HAS", "NA", "Big Bucks Bunny - HAS from own Server", - "-", 0, 0) == ERROR) - return ERROR; -*/ - //-------------------- - cRecordings* recordings = &Recordings; - char f[600]; - -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX - << " recordings->Count()= " << recordings->Count() - << DEBUGHDR << endl; -#endif - - // List of recording timer - time_t now = time(NULL); - - vector act_rec; - /*#ifndef DEBUG*/ - *(mLog->log())<< DEBUGPREFIX - << " checking avtive timer" - << endl; - /*#endif*/ - for (cTimer * ti = Timers.First(); ti; ti = Timers.Next(ti)){ - ti->Matches(); - - /* *(mLog->log()) << DEBUGPREFIX - << " Active Timer: " << ti->File() - << " EvStart= " << ti->Event()->StartTime() - << " EvDuration= " << ti->Event()->Duration() - << endl;*/ - /* *(mLog->log()) << DEBUGPREFIX - << " TiStart= " << ti->StartTime() - << " TiDuration= " << (ti->StopTime() - ti->StartTime()) - << endl; -*/ - if (ti->HasFlags(tfRecording) ) { - /* - if (ti->Event() == NULL) { - *(mLog->log()) << DEBUGPREFIX - << " WARNING: Active recording for " << ti->File() - << " is skipped (No Event()" << endl; - continue; - } - */ - /* *(mLog->log()) << DEBUGPREFIX - << " Active Timer: " << ti->File() - << " Start= " << ti->Event()->StartTime() - << " Duration= " << ti->Event()->Duration() - << endl; -*/ - *(mLog->log()) << DEBUGPREFIX - << " Active Timer: " << ti->File() - << " Start= " << ti->StartTime() - << " Duration= " << (ti->StopTime() - ti->StartTime()) - << endl; - // act_rec.push_back(sTimerEntry(ti->File(), ti->Event()->StartTime(), ti->Event()->Duration())); - act_rec.push_back(sTimerEntry(ti->File(), ti->StartTime(), (ti->StopTime() - ti->StartTime()))); - } - } - -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX - << " Found " << act_rec.size() - << " running timers" - << endl; -#endif - - int rec_dur = 0; - for (cRecording *recording = recordings->First(); recording; recording = recordings->Next(recording)) { - hdr = ""; - - if (recording->IsPesRecording() or ((recording->FramesPerSecond() > 30.0) and !has_4_hd )) - // snprintf(f, sizeof(f), "http://%s:%d%s", mServerAddr.c_str(), mServerPort, - snprintf(f, sizeof(f), "http://%s:%d%s", own_ip.c_str(), mServerPort, - cUrlEncode::doUrlSaveEncode(recording->FileName()).c_str()); - else - // snprintf(f, sizeof(f), "http://%s:%d%s%s", mServerAddr.c_str(), mServerPort, - snprintf(f, sizeof(f), "http://%s:%d%s%s", own_ip.c_str(), mServerPort, - cUrlEncode::doUrlSaveEncode(recording->FileName()).c_str(), link_ext.c_str()); - - string link = f; - string desc = "No description available"; - rec_dur = recording->LengthInSeconds(); - - string name = recording->Name(); - - for (uint x = 0; x < act_rec.size(); x++) { - if (act_rec[x].name == name) { - - /* *(mLog->log())<< DEBUGPREFIX - << " !!!!! Found active Recording !!! " - << endl; -*/ - rec_dur += (act_rec[x].startTime + act_rec[x].duration - now); - - - } - } // for - - if (recording->Info() != NULL) { - if ((recording->Info()->Description() != NULL) && add_desc) { - desc = cUrlEncode::doXmlSaveEncode(recording->Info()->Description()); - } - } - - if (writeXmlItem(cUrlEncode::doXmlSaveEncode(recording->Name()), link, "NA", desc, - cUrlEncode::doUrlSaveEncode(recording->FileName()).c_str(), - -1, - recording->Start(), rec_dur, recording->FramesPerSecond(), - (recording->IsPesRecording() ? 0: 1), (recording->IsNew() ? 0: 1)) == ERROR) - // Better Internal Server Error - return ERROR; - - } - - hdr = "\n"; - hdr += "\n"; - - *mResponseMessage += hdr; - - -#endif - return OKAY; -} - -bool cHttpResource::isTimeRequest(struct stat *statbuf) { - - vector avps; - parseQueryLine(&avps); - string time_str = ""; - string mode = ""; - float time = 0.0; - - if (getQueryAttributeValue(&avps, "time", time_str) != OKAY){ - return false; - } - - if (getQueryAttributeValue(&avps, "mode", mode) == OKAY){ - if (mode.compare ("streamtoend") ==0) { - mStreamToEnd = true; - } - } - - if (mIsRecording) - mStreamToEnd = true; - - time = atof(time_str.c_str()); - *(mLog->log())<< DEBUGPREFIX - << " Found a Time Parameter: " << time - << " streamToEnd= " << ((mStreamToEnd) ? "true" : "false") - << endl; - - mDir = mPath; - cRecording *rec = Recordings.GetByName(mPath.c_str()); - if (rec == NULL) { - *(mLog->log())<< DEBUGPREFIX - << " Error: Did not find recording= " << mPath << endl; - sendError(404, "Not Found", NULL, "File not found."); - return true; - } - - double fps = rec->FramesPerSecond(); - double dur = rec->NumFrames() * fps; - bool is_pes = rec->IsPesRecording(); - if (dur < time) { - sendError(400, "Bad Request", NULL, "Time to large."); - return true; - } - - int start_frame = int(time * fps) -25; - - FILE* idx_file= NULL; - - if (is_pes){ - idx_file = fopen((mDir +"/index.vdr").c_str(), "r"); - // sendError(400, "Bad Request", NULL, "PES not yet supported."); - // return true; - } - else { - idx_file = fopen((mDir +"/index").c_str(), "r"); - } - - if (idx_file == NULL){ - *(mLog->log()) << DEBUGPREFIX - << " failed to open idx file = "<< (mDir +"/index").c_str() - << endl; - sendError(404, "Not Found", NULL, "Failed to open Index file"); - return OKAY; - } - - int buffered_frames = 50; - char *index_buf = new char[8 *buffered_frames]; // 50 indexes - - *(mLog->log()) << DEBUGPREFIX - << " seeking to start_frame= " << start_frame - << " fps= " << fps << endl; - fseek(idx_file, start_frame * 8, SEEK_SET); - - int buffered_indexes = fread(index_buf, 8, (buffered_frames), idx_file); - - fclose(idx_file); - - if(buffered_indexes <= 0 ) { - *(mLog->log())<log()) << DEBUGPREFIX - << " Finding I-Frame now" << endl; - - - bool found_it = false; - int i = 0; - - uint32_t offset = 0; - int idx =0; - int type =0; - - for (i= 0; i < buffered_indexes; i++){ - if (is_pes) { - tIndexPes in_read_pes; - memcpy (&in_read_pes, &(index_buf[i*8]), 8); - type = in_read_pes.type == 1; - idx = in_read_pes.number; - offset = in_read_pes.offset; - } - else{ - tIndexTs in_read_ts; - memcpy (&in_read_ts, &(index_buf[i*8]), 8); - type = in_read_ts.independent; - idx = in_read_ts.number; - offset = in_read_ts.offset; - } - - *(mLog->log()) << DEBUGPREFIX - << " Frame= " << i - << " idx= "<< idx - << " offset= " << offset - << " type= " << type - << endl; - if (type){ - found_it = true; - break; - } - } - - if (!found_it) { - delete[] index_buf; - sendError(404, "Not Found", NULL, "Failed to read Index file"); - return OKAY; - } - - mVdrIdx = idx; - - *(mLog->log()) << DEBUGPREFIX - << " idx= "<< mVdrIdx - << " offset= " << offset - << endl; - - delete[] index_buf; - - char pathbuf[4096]; - snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), mPath.c_str(), mVdrIdx); - - *(mLog->log()) << DEBUGPREFIX - << " Opening Path= " - << pathbuf << endl; - if (openFile(pathbuf) != OKAY) { - sendError(403, "Forbidden", NULL, "Access denied."); - return true; - } - - fseek(mFile, offset, SEEK_SET); - - if (mStreamToEnd) { - sendHeaders(200, "OK", NULL, "video/mpeg", -1, -1); - return true; - } - - uint64_t file_size = 0; - bool more_to_go = true; - int vdr_idx = mVdrIdx; - while (more_to_go) { - snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), mPath.c_str(), vdr_idx); - if (stat(pathbuf, statbuf) >= 0) { - *(mLog->log())<< " found for " << pathbuf << endl; - file_size += statbuf->st_size; - } - else { - more_to_go = false; - } - vdr_idx ++; - } - mRemLength = file_size - offset; - - *(mLog->log()) << DEBUGPREFIX - << " Done. Start Streaming " - << endl; - - if (rangeHdr.isRangeRequest) { - snprintf(pathbuf, sizeof(pathbuf), "Content-Range: bytes 0-%lld/%lld", (mRemLength -1), mRemLength); - sendHeaders(206, "Partial Content", pathbuf, "video/mpeg", mRemLength, statbuf->st_mtime); - } - else { - sendHeaders(200, "OK", NULL, "video/mpeg", mRemLength, -1); - } - return true; -} - -int cHttpResource::sendVdrDir(struct stat *statbuf) { - -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX << " *** sendVdrDir mPath= " << mPath << endl; -#endif - char pathbuf[4096]; - char f[400]; - int vdr_idx = 0; - uint64_t total_file_size = 0; - // int ret = OKAY; - string vdr_dir = mPath; - vector file_sizes; - bool more_to_go = true; - - checkRecording(); - - mVdrIdx = 1; - mFileStructure = "%s/%03d.vdr"; - - snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), mPath.c_str(), 1); - if (stat(pathbuf, statbuf) < 0) { - mFileStructure = "%s/%05d.ts"; -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX << " using dir format: " << mFileStructure.c_str() << endl; -#endif - } - - // The range request functions are activated, when a time header is detected - if (isTimeRequest(statbuf)) { - *(mLog->log())<< DEBUGPREFIX - << " isTimeRequest is true" - << endl; - return OKAY; - } - - // --- looup all vdr files in the dir --- - while (more_to_go) { - vdr_idx ++; - snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), mPath.c_str(), vdr_idx); - if (stat(pathbuf, statbuf) >= 0) { -#ifndef DEBUG - *(mLog->log())<< " found for " << pathbuf << endl; -#endif - file_sizes.push_back(sVdrFileEntry(statbuf->st_size, total_file_size, vdr_idx)); - total_file_size += statbuf->st_size; - } - else { - more_to_go = false; - } - } - if (file_sizes.size() < 1) { - // There seems to be vdr video file in the directory - *(mLog->log())<< DEBUGPREFIX - << " No video file in the directory" - << DEBUGHDR << endl; - sendError(404, "Not Found", NULL, "File not found."); - return OKAY; - } - -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX - << " vdr filesize list " - << DEBUGHDR << endl; - for (uint i = 0; i < file_sizes.size(); i++) - *(mLog->log())<< " i= " << i << " size= " << file_sizes[i].sSize << " firstOffset= " << file_sizes[i].sFirstOffset << endl; - *(mLog->log())<< " total_file_size= " << total_file_size << endl << endl; -#endif - - // total_file_size (on disk) - - // ---------------- file sizes --------------------- - - uint cur_idx = 0; - -#ifndef DEBUG - if (mIsRecording) { - snprintf(f, sizeof(f), " CurFileSize= %lld mRecProgress= %f ExtFileSize= %lld", total_file_size, mRecProgress, (long long int)(mRecProgress * total_file_size)); - *(mLog->log()) << DEBUGPREFIX - << endl << " isRecording: " << f - << endl; - } -#endif - - if (!rangeHdr.isRangeRequest) { - snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), mPath.c_str(), file_sizes[cur_idx].sIdx); - - if (openFile(pathbuf) != OKAY) { - sendError(403, "Forbidden", NULL, "Access denied."); - return OKAY; - } - - mRemLength = total_file_size; - - sendHeaders(200, "OK", NULL, "video/mpeg", ((mIsRecording) ? (mRecProgress * total_file_size): total_file_size ), statbuf->st_mtime); - } - else { // Range request - // idenify the first file -#ifndef DEBUG - *(mLog->log()) << DEBUGPREFIX - << endl <<" --- Range Request Handling ---" - << DEBUGHDR << endl; -#endif - if (mIsRecording && (rangeHdr.begin > total_file_size)) { - *(mLog->log()) << DEBUGPREFIX - << " ERROR: Not yet available" << endl; - sendError(404, "Not Found", NULL, "File not found."); - return OKAY; - } - cur_idx = file_sizes.size() -1; - for (uint i = 1; i < file_sizes.size(); i++) { - if (rangeHdr.begin < file_sizes[i].sFirstOffset ) { - cur_idx = i -1; - break; - } - } - -#ifndef DEBUG - *(mLog->log())<< " Identified Record i= " << cur_idx << " file_sizes[i].sFirstOffset= " - << file_sizes[cur_idx].sFirstOffset << " rangeHdr.begin= " << rangeHdr.begin - << " vdr_no= " << file_sizes[cur_idx].sIdx << endl; -#endif - - mVdrIdx = file_sizes[cur_idx].sIdx; - snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), mPath.c_str(), file_sizes[cur_idx].sIdx); -#ifndef DEBUG - *(mLog->log())<< " file identified= " << pathbuf << endl; -#endif - if (openFile(pathbuf) != OKAY) { - *(mLog->log())<< "----- fopen failed dump ----------" << endl; - *(mLog->log())<< DEBUGPREFIX - << " vdr filesize list " - << DEBUGHDR << endl; - for (uint i = 0; i < file_sizes.size(); i++) - *(mLog->log())<< " i= " << i << " size= " << file_sizes[i].sSize << " firstOffset= " << file_sizes[i].sFirstOffset << endl; - *(mLog->log())<< " total_file_size= " << total_file_size << endl << endl; - - *(mLog->log())<< " Identified Record i= " << cur_idx << " file_sizes[i].sFirstOffset= " - << file_sizes[cur_idx].sFirstOffset << " rangeHdr.begin= " << rangeHdr.begin - << " vdr_no= " << file_sizes[cur_idx].sIdx << endl; - *(mLog->log())<< "---------------" << endl; - sendError(403, "Forbidden", NULL, "Access denied."); - return OKAY; - } - mDir = mPath; - mPath = pathbuf; -#ifndef DEBUG - *(mLog->log())<< " Seeking into file= " << (rangeHdr.begin - file_sizes[cur_idx].sFirstOffset) - << " cur_idx= " << cur_idx - << " file_sizes[cur_idx].sFirstOffset= " << file_sizes[cur_idx].sFirstOffset - << endl; -#endif - fseek(mFile, (rangeHdr.begin - file_sizes[cur_idx].sFirstOffset), SEEK_SET); - if (rangeHdr.end == 0) - rangeHdr.end = ((mIsRecording) ? (mRecProgress * total_file_size): total_file_size); - - mRemLength = (rangeHdr.end-rangeHdr.begin); - - snprintf(f, sizeof(f), "Content-Range: bytes %lld-%lld/%lld", rangeHdr.begin, (rangeHdr.end -1), - ((mIsRecording) ? (long long int)(mRecProgress * total_file_size): total_file_size)); - - sendHeaders(206, "Partial Content", f, "video/mpeg", (rangeHdr.end-rangeHdr.begin), statbuf->st_mtime); - } - -#ifndef DEBUG - *(mLog->log())<< " ***** Yes, vdr dir found ***** mPath= " << mPath<< endl; -#endif - - return OKAY; // handleRead() done -} - -void cHttpResource::sendHeaders(int status, const char *title, const char *extra, const char *mime, - long long int length, time_t date) { - - time_t now; - char timebuf[128]; - char f[400]; - - string hdr = ""; - snprintf(f, sizeof(f), "%s %d %s\r\n", PROTOCOL, status, title); - hdr += f; - snprintf(f, sizeof(f), "Server: %s\r\n", SERVER); - hdr += f; - now = time(NULL); - strftime(timebuf, sizeof(timebuf), RFC1123FMT, gmtime(&now)); - snprintf(f, sizeof(f), "Date: %s\r\n", timebuf); - hdr += f; - if (extra) { - snprintf(f, sizeof(f), "%s\r\n", extra); - *(mLog->log())<< DEBUGPREFIX << " " << f; - hdr += f; - } - if (mime) { - snprintf(f, sizeof(f), "Content-Type: %s\r\n", mime); - hdr += f; - } - if (length >= 0) { - snprintf(f, sizeof(f), "Content-Length: %lld\r\n", length); - // *(mLog->log())<< DEBUGPREFIX << " " << f << endl; - hdr += f; - } - if (date != -1) { - strftime(timebuf, sizeof(timebuf), RFC1123FMT, gmtime(&date)); - snprintf(f, sizeof(f), "Last-Modified: %s\r\n", timebuf); - hdr += f; - } - if (mAcceptRanges) { - snprintf(f, sizeof(f), "Accept-Ranges: bytes\r\n"); - hdr += f; - } - snprintf(f, sizeof(f), "Connection: close\r\n"); - hdr += f; - - snprintf(f, sizeof(f), "\r\n"); - hdr += f; - - - if (mBlkLen != 0) { - *(mLog->log())<< DEBUGPREFIX - << " ERROR in SendHeader: mBlkLen != 0!!! --> Overwriting" << endl; - } - mBlkLen = hdr.size(); - strcpy(mBlkData, hdr.c_str()); - -} - -int cHttpResource::sendFile(struct stat *statbuf32) { - // Send the First Datachunk, incl all headers - struct stat64 statbuf; - - if (stat64(mPath.c_str(), &statbuf) < 0) { - sendError(404, "Not Found", NULL, "File not found."); - return OKAY; - } - *(mLog->log())<< DEBUGPREFIX - << mReqId << " SendFile mPath= " << mPath - << endl; - - char f[400]; - - if (openFile(mPath.c_str()) == ERROR) { - sendError(403, "Forbidden", NULL, "Access denied."); - return OKAY; - } - mFile = fopen(mPath.c_str(), "r"); - // int ret = OKAY; - - if (!mFile) { - sendError(403, "Forbidden", NULL, "Access denied."); - return OKAY; - } - - mFileSize = S_ISREG(statbuf.st_mode) ? statbuf.st_size : -1; - - *(mLog->log())<< "fd= " << mFd << " mReqId= "<< mReqId - << " mFileSize= " <log())<< "fd= " << mFd << " mReqId= "<< mReqId - << " rangeHdr.begin= " <log())<< "fd= " << mFd << " mReqId= "<< mReqId - << ": Done mRemLength= "<< mRemLength << " mContentType= " << mContentType - << endl; -#endif - mConnState = SERVING; - - return OKAY; - -} - -const char *cHttpResource::getMimeType(const char *name) { - char *ext = strrchr((char*)name, '.'); - if (!ext) - return NULL; - // if (ext.compare(".html") || ext.compare(".htm")) return "text/html"; - if (strcmp(ext, ".html") == 0 || strcmp(ext, ".htm") == 0) return "text/html"; - if (strcmp(ext, ".jpg") == 0 || strcmp(ext, ".jpeg") == 0) return "image/jpeg"; - if (strcmp(ext, ".gif") == 0) return "image/gif"; - if (strcmp(ext, ".png") == 0) return "image/png"; - if (strcmp(ext, ".xml") == 0) return "application/xml"; - if (strcmp(ext, ".css") == 0) return "text/css"; - if (strcmp(ext, ".js") == 0) return "text/javascript"; - if (strcmp(ext, ".au") == 0) return "audio/basic"; - if (strcmp(ext, ".wav") == 0) return "audio/wav"; - if (strcmp(ext, ".avi") == 0) return "video/x-msvideo"; - if (strcmp(ext, ".mp4") == 0) return "video/mp4"; - if (strcmp(ext, ".vdr") == 0) return "video/mpeg"; - if (strcmp(ext, ".ts") == 0) return "video/mpeg"; - if (strcmp(ext, ".mpeg") == 0 || strcmp(ext, ".mpg") == 0) return "video/mpeg"; - if (strcmp(ext, ".mp3") == 0) return "audio/mpeg"; - if (strcmp(ext, ".mpd") == 0) return "application/dash+xml"; - if (strcmp(ext, ".m3u8") == 0) return "application/x-mpegURL"; - - return NULL; -} - - -string cHttpResource::getConnStateName() { - string state_string; - switch (mConnState) { - case WAITING: - state_string = "WAITING"; - break; - case READHDR: - state_string = "READ Req HDR"; - break; - case READPAYLOAD: - state_string = "READ Req Payload"; - break; - case SERVING: - state_string = "SERVING"; - break; - case TOCLOSE: - state_string = "TOCLOSE"; - break; - default: - state_string = "UNKNOWN"; - break; - } - return state_string; -} - - -string cHttpResource::getOwnIp(int fd) { - struct sockaddr_in sock; - socklen_t len_inet = sizeof(sock); - int ret = getsockname(fd, (struct sockaddr *)&sock, &len_inet); - if ( ret == -1 ) { - *(mLog->log()) << "Error: Cannot obtain own ip address" << endl; - return string("0.0.0.0"); - // exit(1); /* Failed */ - } - return string (inet_ntoa(sock.sin_addr)); -} - -uint64_t cHttpResource::getVdrFileSize() { - // iter over all vdr files and get file size - struct stat statbuf; - string file_structure = "%s/%05d.ts"; // Only ts supported for HLS and HAS - char pathbuf[4096]; - int vdr_idx = 0; - uint64_t total_file_size = 0; - bool more_to_go = true; - - while (more_to_go) { - vdr_idx ++; - snprintf(pathbuf, sizeof(pathbuf), file_structure.c_str(), mDir.c_str(), vdr_idx); - if (stat(pathbuf, &statbuf) >= 0) { - total_file_size += statbuf.st_size; - } - else { - more_to_go = false; - } - } - return total_file_size; -} - -int cHttpResource::parseHttpRequestLine(string line) { - mMethod = line.substr(0, line.find_first_of(" ")); - mRequest = line.substr(line.find_first_of(" ") +1, (line.find_last_of(" ") - line.find_first_of(" ") -1)); - mVersion = line.substr(line.find_last_of(" ") +1); -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX - << " ReqLine= " << line << endl; -#endif - if (mVersion.compare(0, 4, "HTTP") != 0) { -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX - << " ERROR: No HTTP request -> Closing Connection" << line << endl; -#endif - return ERROR; - } - - size_t pos = mRequest.find('?'); - if (pos != string::npos) - mQuery = mRequest.substr (pos+1, string::npos); - mPath = cUrlEncode::doUrlSaveDecode(mRequest.substr(0, mRequest.find('?'))); - *(mLog->log())<< DEBUGPREFIX - << " mMethod= " << mMethod - << " mPath= " << mPath - << " mVer= " << mVersion - << " mQuery= " << mQuery - // << " HexDump= " << endl << cUrlEncode::hexDump(mPath) << endl - << endl; - return OKAY; -} - -int cHttpResource::parseHttpHeaderLine (string line) { - string hdr_name = line.substr(0, line.find_first_of(":")); - string hdr_val = line.substr(line.find_first_of(":") +2); - - if (hdr_name.compare("Range") == 0) { - parseRangeHeaderValue(hdr_val); - *(mLog->log()) << DEBUGPREFIX - << " Range: Begin= " << rangeHdr.begin - << " End= " << rangeHdr.end - << endl; - } - if (hdr_name.compare("User-Agent") == 0) { - mUserAgent = hdr_val; - *(mLog->log())<< " User-Agent: " << hdr_val - << endl; - } - if (hdr_name.compare("Content-Length") == 0) { - mReqContentLength = atoll(hdr_val.c_str()); - //#ifndef DEBUG - *(mLog->log())<< " Content-Length: " << mReqContentLength - << endl; - //#endif - } - return 0; -} - -void cHttpResource::checkRecording() { - // sets mIsRecording to true when the recording is still on-going - mIsRecording = false; - time_t now = time(NULL); - -#ifndef STANDALONE - - // cRecordings* recordings = mFactory->getRecordings(); - cRecordings* recordings = &Recordings; -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX - << " GetByName(" <GetByName(mPath.c_str()); - if (rec != NULL) { - const cEvent *ev = rec->Info()->GetEvent(); - if (ev != NULL) { - if (now < ev->EndTime()) { - // still recording - mIsRecording = true; - - // mRecProgress * curFileSize = estimated File Size - mRecProgress = (ev->EndTime() - ev->StartTime()) *1.1 / (rec->NumFrames() / rec->FramesPerSecond()); - // mRecProgress = (ev->EndTime() - ev->StartTime()) *1.0/ (now - ev->StartTime()); -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX - << " **** is still recording for mIsRecording= " - << mIsRecording - << " mRecProgress= " << mRecProgress << endl; -#endif - } - } - -#ifndef DEBUG - *(mLog->log())<< DEBUGPREFIX - << " checking, whether recording is on-going" - << " now= " << now << " start= " << rec->Start() - << " curDur= " << rec->LengthInSeconds() - << endl; -#endif - } -#ifndef DEBUG - else { - *(mLog->log())<< DEBUGPREFIX - << " **** Recording Entry Not found **** " << endl; - } -#endif -#endif -} - -int cHttpResource::parseRangeHeaderValue(string val) { - rangeHdr.isRangeRequest = true; - size_t pos_equal = val.find_first_of('='); - size_t pos_minus = val.find_first_of('-'); - - string range_type = val.substr(0, pos_equal); - string first_val= val.substr(pos_equal+1, pos_minus -(pos_equal+1)); - rangeHdr.begin = atoll(first_val.c_str()); - - string sec_val = ""; - if ((pos_minus +1)< val.size()){ - sec_val = val.substr(pos_minus+1); - rangeHdr.end = atoll(sec_val.c_str()); - } - return 0; -} - -int cHttpResource::openFile(const char *name) { - mFile = fopen(name, "r"); - if (!mFile) { - *(mLog->log())<< DEBUGPREFIX - << " fopen failed pathbuf= " << name - << endl; - // sendError(403, "Forbidden", NULL, "Access denied."); - return ERROR; - } - return OKAY; -} - diff --git a/vdr-smarttvweb/httpresource.h b/vdr-smarttvweb/httpresource.h deleted file mode 100755 index fbbab4d..0000000 --- a/vdr-smarttvweb/httpresource.h +++ /dev/null @@ -1,187 +0,0 @@ -/* - * httpresource.h: VDR on Smart TV plugin - * - * Copyright (C) 2012, 2013 T. Lohmar - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * - */ - -#ifndef __HTTPREQUEST_H__ -#define __HTTPREQUEST_H__ - -#include -#include -#include -#include "log.h" - -using namespace std; - -struct cRange { -cRange(): isRangeRequest(false), begin(0), end(0) {}; - bool isRangeRequest; - unsigned long long begin; - unsigned long long end; -}; - -struct sQueryAVP { - string attribute; - string value; -sQueryAVP(string a, string v) : attribute (a), value(v) {}; -}; - - -enum eConnState { - WAITING, - READHDR, - READPAYLOAD, - SERVING, - TOCLOSE -}; - -enum eContentType { - NYD, // Not Yet Defined - VDRDIR, - SINGLEFILE, - MEMBLOCK -}; - -struct sFileEntry { - string sName; - string sPath; - int sStart; - -sFileEntry(string n, string l, int s) : sName(n), sPath(l), sStart(s) { - }; -}; - -class SmartTvServer; -class cResumeEntry; - -class cHttpResource { - - public: - cHttpResource(int, int, int, SmartTvServer*); - virtual ~cHttpResource(); - - int handleRead(); - int handleWrite(); - - int checkStatus(); - - int readFromClient(); - void threadLoop(); - int run(); - - private: - SmartTvServer* mFactory; - Log* mLog; - - int mServerPort; - int mFd; - int mReqId; - - time_t mConnTime; - int mHandleReadCount; - bool mConnected; - eConnState mConnState; - eContentType mContentType; - - string mReadBuffer; - string mMethod; - - string *mResponseMessage; - int mResponseMessagePos; - char* mBlkData; - int mBlkPos; - int mBlkLen; - - string mRequest; - string mQuery; - string mPath; - string mDir; - string mVersion; - string protocol; - unsigned long long mReqContentLength; - string mPayload; - string mUserAgent; - - bool mAcceptRanges; - cRange rangeHdr; - unsigned long long mFileSize; - bool mStreamToEnd; - uint64_t mRemLength; - FILE *mFile; - int mVdrIdx; - string mFileStructure; - bool mIsRecording; - float mRecProgress; - - void setNonBlocking(); - int fillDataBlk(); - - int handlePost(); - int handleHeadRequest(); - int processRequest(); - int processHttpHeaderNew(); - - int readRequestPayload(); - void sendError(int status, const char *title, const char *extra, const char *text); - int sendDir(struct stat *statbuf); - int sendVdrDir(struct stat *statbuf); - int sendRecordingsXml (struct stat *statbuf); - int sendChannelsXml (struct stat *statbuf); - int sendResumeXml (); - int sendVdrStatusXml (struct stat *statbuf); - int sendEpgXml (struct stat *statbuf); - int sendMediaXml (struct stat *statbuf); - - int sendManifest (struct stat *statbuf, bool is_hls = true); - - int receiveResume(); - int deleteRecording(); - - void writeM3U8(double duration, int bitrate, float seg_dur, int end_seg); - void writeMPD(double duration, int bitrate, float seg_dur, int end_seg); - - - int sendMediaSegment (struct stat *statbuf); - - void sendHeaders(int status, const char *title, const char *extra, const char *mime, - long long int length, time_t date); - - int sendFile(struct stat *statbuf); - - // Helper Functions - const char *getMimeType(const char *name); - string getConnStateName(); - string getOwnIp(int fd); - uint64_t getVdrFileSize(); - void checkRecording(); - bool isTimeRequest(struct stat *statbuf); - int parseRangeHeaderValue(string); - int parseHttpRequestLine(string); - int parseHttpHeaderLine (string); - int parseQueryLine (vector *avps); - int parseResume(cResumeEntry &entry, string &id); - - int parseFiles(vector *entries, string prefix, string dir_base, string dir_name, struct stat *statbuf); - - int getQueryAttributeValue(vector *avps, string id, string &val); - int openFile(const char *name); - int writeXmlItem(string title, string link, string programme, string desc, string guid, int no, time_t start, int dur, double fps, int is_pes, int is_new); -}; -#endif diff --git a/vdr-smarttvweb/log.c b/vdr-smarttvweb/log.c deleted file mode 100644 index 685bce3..0000000 --- a/vdr-smarttvweb/log.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * log.c: VDR on Smart TV plugin - * - * Copyright (C) 2012 Thorsten Lohmar - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * - */ - - -#include "log.h" -#include -#include - -Log* Log::instance = NULL; - -Log::Log() { - if (instance) - return; - instance = this; - mLogFile = NULL; -} - -Log::~Log() { - instance = NULL; -} - -Log* Log::getInstance() { - return instance; -} -int Log::init(string fileName) { - char timebuf[128]; - time_t now = time(NULL); - strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now)); - - if (fileName != "") { - mLogFile = new ofstream(); - - mLogFile->open(fileName.c_str(), ios::out ); - *mLogFile << "Log Created: " << timebuf << endl; - } - else - mLogFile = new ofstream("/dev/null"); - return 0; -} - -int Log::init(char* fileName) { - - char timebuf[128]; - time_t now = time(NULL); - strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now)); - - if (strcmp(fileName, "") !=0) { - mLogFile = new ofstream(); - mLogFile->open(fileName, ios::out ); - *mLogFile << "Log Created: " << timebuf << endl; - } - else - mLogFile = new ofstream("/dev/null"); - return 0; -} - -int Log::shutdown() { - if (mLogFile) - mLogFile->close(); - return 1; -} - -ofstream* Log::log() { - return mLogFile; -} - diff --git a/vdr-smarttvweb/log.h b/vdr-smarttvweb/log.h deleted file mode 100644 index 8a09fe3..0000000 --- a/vdr-smarttvweb/log.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * log.h.h: VDR on Smart TV plugin - * - * Copyright (C) 2012 Thorsten Lohmar - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * - */ - - -#ifndef LOG_H -#define LOG_H - -#include -#include - -using namespace std; - -class Log -{ - public: - Log(); - ~Log(); - static Log* getInstance(); - - int init(char* fileName); - int init(string fileName); - int shutdown(); - ofstream* log(); - - private: - static Log* instance; - - ofstream *mLogFile; -}; - -#endif diff --git a/vdr-smarttvweb/smarttvfactory.c b/vdr-smarttvweb/smarttvfactory.c deleted file mode 100755 index 3478e9d..0000000 --- a/vdr-smarttvweb/smarttvfactory.c +++ /dev/null @@ -1,408 +0,0 @@ -/* - * smarttvfactory.h: VDR on Smart TV plugin - * - * Copyright (C) 2012 T. Lohmar - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * - */ - -#ifndef STANDALONE -#include -#include -#endif - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - - -#include "smarttvfactory.h" - -#ifndef STANDALONE -#define PORT 8000 -#else -#define PORT 9000 -#endif - -#define OKAY 0 -#define ERROR (-1) - -#define DEBUG - - -using namespace std; - -void SmartTvServerStartThread(void* arg) { - SmartTvServer* m = (SmartTvServer*)arg; - m->threadLoop(); - delete m; - pthread_exit(NULL); -} - -SmartTvServer::SmartTvServer(): mRequestCount(0), isInited(false), serverPort(PORT), mServerFd(-1), - mSegmentDuration(10), mHasMinBufferTime(40), mLiveChannels(20), - clientList(), mActiveSessions(0), mConfig(NULL) { -} - - -SmartTvServer::~SmartTvServer() { - - if (mConfig != NULL) - delete mConfig; -} - -void SmartTvServer::cleanUp() { - // close listening ports - for (uint idx= 0; idx < clientList.size(); idx++) { - if (clientList[idx] != NULL) { - close(idx); - delete clientList[idx]; - clientList[idx] = NULL; - } - } - - // close server port - close(mServerFd); - - // Leave thread - pthread_cancel(mThreadId); - pthread_join(mThreadId, NULL); - - mLog.shutdown(); -} - -int SmartTvServer::runAsThread() { - int res = pthread_create(&mThreadId, NULL, (void*(*)(void*))SmartTvServerStartThread, (void *)this); - if (res != 0) { - *(mLog.log()) << " Error creating thread. res= " << res - << endl; - return 0; - } - return 1; -} - -void SmartTvServer::threadLoop() { - *(mLog.log()) << " SmartTvServer Thread Started " << endl; - - loop(); - - *(mLog.log()) << " SmartTvServer Thread Stopped " << endl; -} - - -void SmartTvServer::loop() { - socklen_t addr_size = 0; - int rfd; - sockaddr_in sadr; - int req_id = 0; - int ret = 0; - struct timeval timeout; - - int maxfd; - - fd_set read_set; - fd_set write_set; - - FD_ZERO(&read_set); - FD_ZERO(&write_set); - - FD_ZERO(&mReadState); - FD_ZERO(&mWriteState); - - FD_SET(mServerFd, &mReadState); - maxfd = mServerFd; - - *(mLog.log()) << "mServerFd= " << mServerFd << endl; - - int handeled_fds = 0; - - for (;;) { - FD_ZERO(&read_set); - FD_ZERO(&write_set); - read_set = mReadState; - write_set = mWriteState; - - if (ret != handeled_fds) { - // Only ok, when the server has closed a handing HTTP connection - *(mLog.log()) << "WARNING: Select-ret= " << ret - << " != handeled_fds= " << handeled_fds << endl; - /* FD_ZERO(&mReadState); - FD_ZERO(&mWriteState); - FD_SET(mServerFd, &mReadState); - maxfd = mServerFd; - - read_set = mReadState; - write_set = mWriteState; - for (uint idx= 0; idx < clientList.size(); idx++) { - if (clientList[idx] != NULL) { - close(idx); - delete clientList[idx]; - clientList[idx] = NULL; - } - } - mActiveSessions = 0; -*/ - } - - handeled_fds = 0; - timeout.tv_sec = 5; - timeout.tv_usec = 0; - - ret = select(maxfd + 1, &read_set, &write_set, NULL, &timeout); - - if (ret == 0) { - // timeout: Check for dead TCP connections - for (uint idx= 0; idx < clientList.size(); idx++) { - if (clientList[idx] != NULL) - if (clientList[idx]->checkStatus() == ERROR) { - close(idx); - delete clientList[idx]; - clientList[idx] = NULL; - mActiveSessions--; - FD_CLR(idx, &mReadState); /* dead client */ - FD_CLR(idx, &mWriteState); - *(mLog.log()) << "WARNING: Timeout - Dead Client fd=" << idx << endl; - } - } - continue; - } // timeout - - if (ret < 0){ - *(mLog.log()) << "ERROR: select error " << errno << endl; - continue; - } // Error - - // new accept - if (FD_ISSET(mServerFd, &read_set)) { - handeled_fds ++; - if((rfd = accept(mServerFd, (sockaddr*)&sadr, &addr_size))!= -1){ - req_id ++; - -#ifndef DEBUG - *(mLog.log()) << "fd= " << rfd - << " --------------------- Received connection ---------------------" << endl; -#endif - - FD_SET(rfd, &mReadState); - FD_SET(rfd, &mWriteState); - - if (rfd > maxfd) { - maxfd = rfd; - } - - if (clientList.size() < (rfd+1)) { - clientList.resize(rfd+1, NULL); // Check. - } - clientList[rfd] = new cHttpResource(rfd, req_id, serverPort, this); - mActiveSessions ++; - *(mLog.log()) << " + mActiveSessions= " << mActiveSessions << endl; - } - else{ - *(mLog.log()) << "Error accepting " << errno << endl; - } - } - - // Check for data on already accepted connections - for (rfd = 0; rfd < clientList.size(); rfd++) { - if (clientList[rfd] == NULL) - continue; - if (FD_ISSET(rfd, &read_set)) { - handeled_fds ++; - // HandleRead - if (clientList[rfd] == NULL) { - *(mLog.log()) << "ERROR in Check Read: oops - no cHttpResource anymore fd= " << rfd << endl; - close(rfd); - FD_CLR(rfd, &mReadState); /* remove dead client */ - FD_CLR(rfd, &mWriteState); - continue; - } - if ( clientList[rfd]->handleRead() < 0){ -#ifndef DEBUG - *(mLog.log()) << "fd= " << rfd << " --------------------- Check Read: Closing ---------------------" << endl; -#endif - close(rfd); - delete clientList[rfd]; - clientList[rfd] = NULL; - mActiveSessions--; - *(mLog.log()) << " - Check Read: mActiveSessions= " << mActiveSessions << endl; - FD_CLR(rfd, &mReadState); /* dead client */ - FD_CLR(rfd, &mWriteState); - } - } - } - - // Check for write - for (rfd = 0; rfd < clientList.size(); rfd++) { - if (clientList[rfd] == NULL) - continue; - if (FD_ISSET(rfd, &write_set)) { - handeled_fds++; - // HandleWrite - if (clientList[rfd] == NULL) { - close(rfd); - FD_CLR(rfd, &mReadState); - FD_CLR(rfd, &mWriteState); - continue; - } - if ( clientList[rfd]->handleWrite() < 0){ -#ifndef DEBUG - *(mLog.log()) << "fd= " << rfd << " --------------------- Check Write: Closing ---------------------" << endl; -#endif - close(rfd); - delete clientList[rfd]; - clientList[rfd] = NULL; - mActiveSessions--; - *(mLog.log()) << " - Check Write: mActiveSessions= " << mActiveSessions << endl; - FD_CLR(rfd, &mReadState); - FD_CLR(rfd, &mWriteState); - } - } - } - - // selfcheck - /* *(mLog.log()) << "Select Summary: ret= " << ret - << " handeled_fds=" << handeled_fds - << " mActiveSessions= " << mActiveSessions - << " clientList.size()= " << clientList.size() - << endl; -*/ - //Check for active sessions - /* - *(mLog.log()) << "checking number of active sessions clientList.size()= " << clientList.size() << endl; - - int act_ses = 0; - for (uint idx= 0; idx < clientList.size(); idx++) { - if (clientList[idx] != NULL) - act_ses++; - } - if (act_ses != mActiveSessions) { - *(mLog.log()) << "ERROR: Lost somewhere a session: " - << "mActiveSessions= " << mActiveSessions - << "act_ses= " << act_ses - << endl; - mActiveSessions = act_ses; - } - *(mLog.log()) << "checking number of active sessions - done mActiveSessions= " << mActiveSessions << endl; -*/ - } // for (;;) -} // org bracket - -int SmartTvServer::isServing() { - return (mActiveSessions != 0 ? true : false); -} - -void SmartTvServer::initServer(string dir) { - /* This function initialtes the listening socket for the server - * and sets isInited to true - */ - mConfigDir = dir; - int ret; - struct sockaddr_in sock; - int yes = 1; - - -#ifndef STANDALONE - mConfig = new cSmartTvConfig(dir); - mLog.init(mConfig->getLogFile()); - esyslog("SmartTvWeb: Logfile created"); - - *(mLog.log()) << mConfig->getLogFile() << endl; - -#else - mConfig = new cSmartTvConfig("."); - mLog.init(mConfig->getLogFile()); - cout << "SmartTvWeb: Logfile created" << endl; - cout << "SmartTvWeb: Listening on port= " << PORT << endl; - -#endif - - // mConfig->printConfig(); - - mSegmentDuration= mConfig->getSegmentDuration(); - mHasMinBufferTime= mConfig->getHasMinBufferTime(); - mLiveChannels = mConfig->getLiveChannels(); - - *(mLog.log()) <<"HTTP server listening on port " << serverPort << endl; - - mServerFd = socket(PF_INET, SOCK_STREAM, 0); - if (mServerFd <0) { - *(mLog.log()) << "Error: Cannot create serving socket, exit" << endl; - exit(1); - } - - ret = setsockopt(mServerFd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); - if (ret <0) { - *(mLog.log()) << "Error: Cannot set sockopts on serving socket, exit" << endl; - exit(1); - } - - memset((char *) &sock, 0, sizeof(sock)); - sock.sin_family = AF_INET; - - if (mConfig->getServerAddress() == "") - sock.sin_addr.s_addr = htonl(INADDR_ANY); - else { - *(mLog.log()) << "Binding Server to " << mConfig->getServerAddress() << endl; - sock.sin_addr.s_addr = inet_addr(mConfig->getServerAddress().c_str()); - } - sock.sin_port = htons(serverPort); - - ret = bind(mServerFd, (struct sockaddr *) &sock, sizeof(sock)); - if (ret !=0) { - *(mLog.log()) << "Error: Cannot bind serving socket, exit" << endl; - exit(1); - } - - /* - struct ifreq ifr; - - ifr.ifr_addr.sa_family = AF_INET; - strncpy(ifr.ifr_name, "eth0", IFNAMSIZ-1); - ioctl(mServerFd, SIOCGIFADDR, &ifr); - string own_ip = inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr); - *(mLog.log()) << " own if ip= " << own_ip << endl; -*/ - - ret = listen(mServerFd, 5); - if (ret <0) { - *(mLog.log()) << "Error: Cannot set listening on serving socket, exit" << endl; - exit(1); - } - - isInited = true; -} - - - - diff --git a/vdr-smarttvweb/smarttvfactory.h b/vdr-smarttvweb/smarttvfactory.h deleted file mode 100644 index 1073ab0..0000000 --- a/vdr-smarttvweb/smarttvfactory.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * smarttvfactory.h: VDR on Smart TV plugin - * - * Copyright (C) 2012 T. Lohmar - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * - */ - - -#ifndef __SMARTTVSERVER_H__ -#define __SMARTTVSERVER_H__ - -#include -#include -#include -#include -#include "httpresource.h" -#include "log.h" -#include "stvw_cfg.h" - -#ifndef STANDALONE -#include -#endif - -using namespace std; - -#define PLG_VERSION "0.9.6" -#define SERVER "SmartTvWeb/0.9.6" - -class SmartTvServer { - public: - SmartTvServer(); - virtual ~SmartTvServer(); - - void initServer(string c_dir); - void loop(); - void cleanUp(); - int runAsThread(); - void threadLoop(); - - Log mLog; - - void readRecordings(); - int isServing(); - - string getConfigDir() { return mConfigDir; }; - cSmartTvConfig* getConfig() { return mConfig; }; - - private: - pthread_t mThreadId; - int mRequestCount; - bool isInited; - int serverPort; - int mServerFd; - unsigned int mSegmentDuration; - int mHasMinBufferTime; - int mLiveChannels; - - vector clientList; - int mActiveSessions; - string mConfigDir; - cSmartTvConfig *mConfig; - - int mMaxFd; - fd_set mReadState; - fd_set mWriteState; -}; - - -#endif diff --git a/vdr-smarttvweb/smarttvweb.c b/vdr-smarttvweb/smarttvweb.c deleted file mode 100644 index 9e3269e..0000000 --- a/vdr-smarttvweb/smarttvweb.c +++ /dev/null @@ -1,152 +0,0 @@ -/* - * smarttvweb.c: VDR on Smart TV plugin - * - * Copyright (C) 2012 T. Lohmar - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * - */ - - -#ifndef STANDALONE -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "smarttvfactory.h" - - -static const char *VERSION = PLG_VERSION; -static const char *DESCRIPTION = "SmartTV Web Server"; - - -using namespace std; - -#ifndef STANDALONE -class cPluginSmartTvWeb : public cPlugin -{ -public: - cPluginSmartTvWeb(void); - virtual ~cPluginSmartTvWeb(); - virtual const char *Version(void) { return VERSION; } - virtual const char *Description(void) { return DESCRIPTION; } - virtual const char *CommandLineHelp(void); - virtual bool ProcessArgs(int argc, char *argv[]); - virtual bool Initialize(void); - virtual bool Start(void); - virtual bool SetupParse(const char *Name, const char *Value); -#if VDRVERSNUM > 10300 - virtual cString Active(void); -#endif - -private: - SmartTvServer mServer; - string mConfigDir; -}; - -cPluginSmartTvWeb::cPluginSmartTvWeb(void) { - // Initialize any member variables here. - // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL - // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT! - mConfigDir = ""; -} - -bool cPluginSmartTvWeb::Start(void) { - // Start any background activities the plugin shall perform. - - if (mConfigDir.compare("") == 0) { - const char* dir_name = cPlugin::ConfigDirectory(Name()); - if (!dir_name) { - dsyslog("SmartTvWeb: Could not get config dir from VDR"); - } - else - mConfigDir = string(dir_name); - } - - mServer.initServer(mConfigDir); - int success = mServer.runAsThread(); - - esyslog("SmartTvWeb: started %s", (success == 1) ? "sucessfully" : "failed!!!!"); - - return ((success == 1) ? true: false); -} - -cPluginSmartTvWeb::~cPluginSmartTvWeb() { - // Clean up after yourself! - mServer.cleanUp(); -} - -const char *cPluginSmartTvWeb::CommandLineHelp(void) -{ - // Return a string that describes all known command line options. - return " \n"; -} - -bool cPluginSmartTvWeb::ProcessArgs(int argc, char *argv[]) { - // Implement command line argument processing here if applicable. - return true; -} - -bool cPluginSmartTvWeb::Initialize(void) { - // Initialize any background activities the plugin shall perform. - esyslog("SmartTvWeb: Initialize called"); - - return true; -} - -bool cPluginSmartTvWeb::SetupParse(const char *Name, const char *Value) -{ - // Parse your own setup parameters and store their values. - return false; -} - -#if VDRVERSNUM > 10300 - -cString cPluginSmartTvWeb::Active(void) { - esyslog("SmartTvWeb: Active called Checkme"); - if (mServer.isServing()) - return tr("SmartTV client(s) serving"); - else - return NULL; -} - -#endif - -VDRPLUGINCREATOR(cPluginSmartTvWeb); // Don't touch this! - -#else //VOMPSTANDALONE - - -int main(int argc, char *argv[]) { - printf ("Starting up\n"); - - SmartTvServer server; - server.initServer("."); - server.loop(); -} -#endif diff --git a/vdr-smarttvweb/smarttvweb.conf b/vdr-smarttvweb/smarttvweb.conf deleted file mode 100644 index 07920dc..0000000 --- a/vdr-smarttvweb/smarttvweb.conf +++ /dev/null @@ -1,28 +0,0 @@ -# - -LogFile /tmp/smarttvweb.txt - -MediaFolder /multimedia/video - -# Media Segment Duration for HLS/ DASH -SegmentDuration 10 - -# minBufferTime value for the DASH MPD -HasMinBufferTime 30 - -# Bitrate correction parameter for Hls and DASH MPD -HasBitrateCorrection 1.1 - -# Default number of Live Channel entries included in the channels.xml, when not requested specifically. -LiveChannels 30 - -# Influence behavior, when you channels.conf contain group separators -# Valid Values (case sensitive): -# - Ignore : Ignore group separators) -# - EmptyIgnore : Ignore empty group separators (which might be set to assign channel numbers) -# - EmptyFolderDown : Interpretes an empty group channel as "cd ..", so that subsequent channels are not part of the group. -GroupSeparators Ignore - -# Bind the web server to a specific IP address. Otherwise, the Web Server is listening on ALL interfaces. -#ServerAddress 127.0.0.1 - diff --git a/vdr-smarttvweb/stvw_cfg.c b/vdr-smarttvweb/stvw_cfg.c deleted file mode 100755 index 75b8de6..0000000 --- a/vdr-smarttvweb/stvw_cfg.c +++ /dev/null @@ -1,196 +0,0 @@ -/* - * stvw_cfg.h: VDR on Smart TV plugin - * - * Copyright (C) 2012, 2013 T. Lohmar - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * - */ - -#include "stvw_cfg.h" - -#ifndef STANDALONE -#include -#endif - -#include -#include -#include -#include - -cSmartTvConfig::cSmartTvConfig(string d): mConfigDir(d), mLog(NULL), mCfgFile(NULL), - mLogFile(), mMediaFolder(), mSegmentDuration(), mHasMinBufferTime(), mHasBitrateCorrection(), - mLiveChannels(), mGroupSep(IGNORE), mServerAddress("") { - -#ifndef STANDALONE - mLogFile= ""; -#else - mLogFile= "./smartvvweblog-standalone.txt"; -#endif - - // Defaults - mMediaFolder= "/hd2/mpeg"; - mSegmentDuration = 10; - mHasMinBufferTime = 40; - mHasBitrateCorrection = 1.1; - mLiveChannels = 30; - - readConfig(); -} - -cSmartTvConfig::~cSmartTvConfig() { -} - -void cSmartTvConfig::printConfig() { - mLog = Log::getInstance(); - - - *(mLog->log()) << "printConfig: " << endl; - *(mLog->log()) << " ConfigDir: " << mConfigDir << endl; - *(mLog->log()) << " LogFile: " << mLogFile << endl; - *(mLog->log()) << " MediaFolder:" << mMediaFolder << endl; - *(mLog->log()) << " SegmentDuration: " << mSegmentDuration << endl; - *(mLog->log()) << " HasMinBufferTime: " << mHasMinBufferTime << endl; - *(mLog->log()) << " HasBitrateCorrection: " << mHasBitrateCorrection << endl; - *(mLog->log()) << " LiveChannels: " << mLiveChannels << endl; - *(mLog->log()) << " GroupSeparators: " << ((mGroupSep==IGNORE)? "Ignore" : ((mGroupSep==EMPTYIGNORE)? "EmptyIgnore": "EmptyFolderDown")) << endl; - *(mLog->log()) << " ServerAddress: " << mServerAddress << endl; -} - - -void cSmartTvConfig::readConfig() { - string line; - char attr[200]; - char value[200]; - - ifstream myfile ((mConfigDir +"/smarttvweb.conf").c_str()); - - if (!myfile.is_open()) { -#ifndef STANDALONE - esyslog ("ERROR in SmartTvWeb: Cannot open config file. Expecting %s", (mConfigDir +"/smarttvweb.conf").c_str() ); -#else - cout << "ERROR: Cannot open config file. Expecting "<< (mConfigDir +"/smarttvweb.conf") << endl; -#endif - return; - } - - while ( myfile.good() ) { - getline (myfile, line); - - if ((line == "") or (line[0] == '#')) - continue; - - sscanf(line.c_str(), "%s %s", attr, value); - - if (strcmp(attr, "LogFile")==0) { - mLogFile = string(value); - // cout << " Found mLogFile= " << mLogFile << endl; - continue; - } - - if (strcmp(attr, "MediaFolder") == 0) { - mMediaFolder = string (value); - // cout << " Found mMediaFolder= " << mMediaFolder << endl; - continue; - } - if (strcmp(attr, "SegmentDuration") == 0) { - mSegmentDuration = atoi(value); - // cout << " Found mSegmentDuration= " << mSegmentDuration << endl; - continue; - } - - if (strcmp(attr, "HasMinBufferTime") == 0) { - mHasMinBufferTime = atoi(value); - // cout << " Found mHasMinBufferTime= " << mHasMinBufferTime << endl; - continue; - } - if (strcmp(attr, "HasBitrateCorrection") == 0) { - mHasBitrateCorrection = atof(value); - // cout << " Found mHasBitrate= " <log()) << " readConfig: " << line << endl; - if (line == "") - continue; - - string t; - time_t st; - int r; - time_t lv; - int count = scanf (line.c_str(), "%s %lld %d %lld", &t, &st, &r, &lv); - if (count == 4) { - *(mLog->log()) << " read: " << t << " st= " << st << " r= " << r << " lv= " << lv << endl; - } - // first title - - - } - myfile.close(); - } - - else { - *(mLog->log()) << " readConfig: Cannot open file " << f << endl; - return NULL; - } - - - // open the file - // read the lines - return NULL; -} -*/ diff --git a/vdr-smarttvweb/stvw_cfg.h b/vdr-smarttvweb/stvw_cfg.h deleted file mode 100755 index d894820..0000000 --- a/vdr-smarttvweb/stvw_cfg.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * stvw_cfg.h: VDR on Smart TV plugin - * - * Copyright (C) 2012, 2013 T. Lohmar - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * - */ - - -#ifndef __SMARTTV_CONFIG_H__ -#define __SMARTTV_CONFIG_H__ - -#include -#include -#include -#include -#include "log.h" - -using namespace std; - - -enum eGroupSep { - IGNORE, - EMPTYIGNORE, - EMPTYFOLDERDOWN -}; - -class cSmartTvConfig { - private: - string mConfigDir; - Log* mLog; - FILE *mCfgFile; - - string mLogFile; - string mMediaFolder; - unsigned int mSegmentDuration; - int mHasMinBufferTime; - float mHasBitrateCorrection; - int mLiveChannels; - - eGroupSep mGroupSep; - string mServerAddress; - - public: - cSmartTvConfig(string dir); - ~cSmartTvConfig(); - - void readConfig(); - void printConfig(); - - string getLogFile() { return mLogFile; }; - string getMediaFolder() { return mMediaFolder; }; - unsigned int getSegmentDuration() {return mSegmentDuration; }; - int getHasMinBufferTime() { return mHasMinBufferTime; }; - float getHasBitrateCorrection() { return mHasBitrateCorrection; }; - int getLiveChannels() {return mLiveChannels; }; - eGroupSep getGroupSep() { return mGroupSep; }; - string getServerAddress() { return mServerAddress; }; -}; - -#endif diff --git a/vdr-smarttvweb/url.c b/vdr-smarttvweb/url.c deleted file mode 100644 index 6446654..0000000 --- a/vdr-smarttvweb/url.c +++ /dev/null @@ -1,340 +0,0 @@ -/* - * url.c: VDR on Smart TV plugin - * - * Copyright (C) 2012, 2013 T. Lohmar - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * - */ - -#include -#include"url.h" - - -//http://www.blooberry.com/indexdot/html/topics/urlencoding.htm -string cUrlEncode::doUrlSaveEncode(string in) { - string res = ""; - unsigned char num = 0; - char buf[5]; - - bool done = false; - unsigned int idx = 0; - while (!done) { - if (idx == in.size()) { - done = true; - continue; - } - num = in[idx]; - switch (num) { - case '&': - res += "%26"; - break; - case '%': - res += "%25"; - break; - case '#': - // break; - if (in.compare(idx, 3, "#3F") == 0) { - res += "%3F"; - idx +=3; - continue; - } - if (in.compare(idx, 3, "#3A") == 0) { - res += "%3A"; - idx += 3; - continue; - } - if (in.compare(idx, 3, "#2F") == 0) { - res += "%2F"; - idx += 3; - continue; - } - res += "%23"; // just a '#' char - break; - default: - // Copy the normal chars - if (num < 128) - res += char(num); - else { - sprintf (buf, "%02hhX", num); - res += "%"; - res += buf; - } - break; - } // switch - - idx ++; - } - return res; -} - - -string cUrlEncode::doUrlSaveDecode(string input) { - string res = ""; - unsigned int idx = 0; - int x; - while (idx < input.size()){ - if (input[idx] == '%') { - string num = input.substr(idx+1, 2); - sscanf(num.c_str(), "%X", &x); - idx+= 3; - switch (x) { - case 0x23: // '#' - res += "#"; - break; - case 0x25: // '%' - res += "%"; - break; - case 0x2f: // '/' - res += "#2F"; - break; - case 0x3a: // ':' - res += "#3A"; - break; - case 63: // '?' - res += "#3F"; - break; - default: - res += char(x); - break; - } - } - else { - res += input[idx]; - idx ++; - } - } - return res; -} - -string cUrlEncode::doXmlSaveEncode(string in) { - string res = ""; - unsigned char num = 0; - // char buf[5]; - - bool done = false; - unsigned int idx = 0; - while (!done) { - if (idx == in.size()) { - done = true; - continue; - } - num = in[idx]; - switch (num) { - case 0x26: // '&': - res += "&"; - break; - case 0x27: // '\'': - res += "'"; - break; - case 0x3c: // '<': - res += "<"; - break; - case 0x3e: // '>': - res += ">"; - break; - case 0x22: // '\"': - res += """; - break; - - /* case 0xc4: // Ä - res += "Ä"; - break; - case 0xe4: //'ä': - res += "ä"; - break; - case 0xd6: // Ö - res += "Ö"; - break; - case 0xf6: // 'ö': - res += "ö"; - break; - case 0xdc: //'Ü': - res += "Ü"; - break; - case 0xfc: //'ü': - res += "ü"; - break; - case 0xdf: //'ß': - res += "ß"; - break; -*/ - default: - // Copy the normal chars - res += char(num); - break; - } // switch - - idx ++; - } - return res; -} - -string cUrlEncode::doXmlSaveDecode(string input) { - string res = ""; - unsigned int idx = 0; - while (idx < input.size()){ - if (input[idx] == '&') { - if (input.compare(idx, 4, "<") == 0){ - res += "<"; - idx += 4; - } - else if (input.compare(idx, 4, ">") == 0){ - res += ">"; - idx += 4; - } - else if (input.compare(idx, 5, "&") == 0){ - res += "&"; - idx += 5; - } - else if (input.compare(idx, 6, """) == 0){ - res += "\""; - idx += 6; - } - else if (input.compare(idx, 6, "'") == 0){ - res += "\'"; - idx += 6; - } - else { - // ERROR - idx = input.size(); - res = ""; - } - } - else { - res += input[idx]; - idx ++; - } - - } - return res; -} - -string cUrlEncode::removeEtChar(string line, bool xml) { - bool done = false; - size_t cur_pos = 0; - size_t pos = 0; - string res = ""; - - int end_after_done = 0; - - while (!done) { - pos = line.find('&', cur_pos); - if (pos == string::npos) { - done = true; - res += line.substr(cur_pos); - break; - } - if (pos >= 0) { - if (xml) - res += line.substr(cur_pos, (pos-cur_pos)) + "&"; // xml save encoding - else - res += line.substr(cur_pos, (pos-cur_pos)) + "%26"; // url save encoding - // cur_pos = cur_pos+ pos +1; - cur_pos = pos +1; - end_after_done ++; - } - } - return res; -} - -string cUrlEncode::hexDump(char *line, int line_len) { - string res = ""; - string ascii = ""; - char buf[10]; - - int line_count = 0; - for (unsigned int i = 0; i < line_len; i++) { - unsigned char num = line[i]; - sprintf (buf, "%02hhX", num); - res += buf; - if ((num >= 32) && (num < 127)) { - ascii += char(num); - } - else - ascii += '.'; - - line_count++; - switch (line_count) { - case 8: - res += " "; - ascii += " "; - break; - case 17: - res += " " + ascii; - res += "\r\n"; - ascii = ""; - line_count = 0; - break; - default: - res += " "; - break; - } - } - if (line_count != 0) { - for (int i = 0; i < ((17 - line_count) * 3 ); i++) - res += " "; - if (line_count >= 8) - res += " "; - - res += " "; - res += ascii; - } - return res; -} - -string cUrlEncode::hexDump(string in) { - string res = ""; - string ascii = ""; - char buf[10]; - - int line_count = 0; - for (unsigned int i = 0; i < in.size(); i++) { - unsigned char num = in[i]; - sprintf (buf, "%02hhX", num); - res += buf; - if ((num >= 32) && (num < 127)) { - ascii += char(num); - } - else - ascii += '.'; - - line_count++; - switch (line_count) { - case 8: - res += " "; - ascii += " "; - break; - case 16: - res += " " + ascii; - res += "\r\n"; - ascii = ""; - line_count = 0; - break; - default: - res += " "; - break; - } - } - if (line_count != 0) { - for (int i = 0; i < ((16 - line_count) * 3 ); i++) - res += " "; - if (line_count >= 8) - res += " "; - res += ascii; - } - return res; -} diff --git a/vdr-smarttvweb/url.h b/vdr-smarttvweb/url.h deleted file mode 100644 index 4656a2a..0000000 --- a/vdr-smarttvweb/url.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * url.h: VDR on Smart TV plugin - * - * Copyright (C) 2012 T. Lohmar - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * - */ - - -#ifndef __URL_H__ -#define __URL_H__ - -#include - -using namespace std; - -class cUrlEncode { - - public: - cUrlEncode() {}; - cUrlEncode(string &) {}; - - static string doUrlSaveEncode (string); - static string doUrlSaveDecode (string); - - static string doXmlSaveEncode (string); - static string doXmlSaveDecode (string); - static string removeEtChar(string line, bool xml=true); - static string hexDump(char *line, int line_len); - static string hexDump(string); - - private: - -}; - -#endif diff --git a/vdr-smarttvweb/web/Data.js b/vdr-smarttvweb/web/Data.js deleted file mode 100755 index 49e9026..0000000 --- a/vdr-smarttvweb/web/Data.js +++ /dev/null @@ -1,296 +0,0 @@ -//Diff: -// getNumString -// createJQMDomTree - -var Data = -{ - assets : new Item, - folderList : [], -}; - -Array.prototype.remove = function(from, to) { - var rest = this.slice((to || from) + 1 || this.length); - this.length = from < 0 ? this.length + from : from; - return this.push.apply(this, rest); -}; - -Data.reset = function() { - this.assets = null; - this.assets = new Item; - - this.folderList = []; - -// this.folderList.push({item : this.assets, id: 0}); -// Main.log("Data.reset: folderList.push. this.folderList.length= " + this.folderList.length); -}; - -Data.completed= function(sort) { - if (sort == true) - this.assets.sortPayload(); - - this.folderList.push({item : this.assets, id: 0}); -// Main.log("Data.completed: folderList.push. this.folderList.length= " + this.folderList.length); -// Main.log ("Data.completed()= " +this.folderList.length); - -}; - -Data.selectFolder = function (idx, first_idx) { - this.folderList.push({item : this.getCurrentItem().childs[idx], id: idx, first:first_idx}); -// Main.log("Data.selectFolder: folderList.push. this.folderList.length= " + this.folderList.length); -}; - -Data.folderUp = function () { - itm = this.folderList.pop(); -// Main.log("Data.folderUp: folderList.pop. this.folderList.length= " + this.folderList.length); - return itm; -// return itm.id; -}; - -Data.isRootFolder = function() { -// Main.log("Data.isRootFolder: this.folderList.length= " + this.folderList.length); - if (this.folderList.length == 1) - return true; - else - return false; -}; - -Data.addItem = function(t_list, pyld) { - this.assets.addChild(t_list, pyld, 0); -}; - -Data.dumpFolderStruct = function(){ - Main.log("---------- dumpFolderStruct ------------"); - this.assets.print(0); - Main.log("---------- dumpFolderStruct Done -------"); -}; - -Data.createJQMDomTree = function () { - return this.assets.createJQMDomTree(0); -}; - -Data.findEpgUpdateTime = function() { - return this.assets.findEpgUpdateTime(Display.GetEpochTime() + 10000, "", 0); - // min, guid, level -}; - -Data.updateEpg = function (guid, entry) { - this.assets.updateEpgEntry(guid, entry, 0); -}; - -Data.getCurrentItem = function () { - return this.folderList[this.folderList.length-1].item; -}; - -Data.getVideoCount = function() { - return this.folderList[this.folderList.length-1].item.childs.length; -}; - -Data.getNumString =function(num, fmt) { - var res = ""; - - if (num < 10) { - for (var i = 1; i < fmt; i ++) { - res += "0"; - }; - } else if (num < 100) { - for (var i = 2; i < fmt; i ++) { - res += "0"; - }; - } - - res = res + num; - - return res; -}; - -Data.deleteElm = function (pos) { - Data.getCurrentItem().childs.remove(pos); -}; -//----------------------------------------- -function Item() { - this.title = "root"; - this.isFolder = true; - this.childs = []; - this.payload = ""; // only set, if (isFolder == false) -} - -Item.prototype.isFolder = function() { - return this.isFolder; -}; - -Item.prototype.getTitle = function () { - return this.title; -}; - -Item.prototype.getPayload = function () { - if (this.isFolder == true) { - Main.log("WARNING: getting payload on a folder title=" +this.title); - } - return this.payload; -}; - -Item.prototype.getItem = function (title) { - for (var i = 0; i < this.childs.length; i++) { - if (this.childs[i].title == title) { - return this.childs[i]; - } - } - return 0; -}; - -Item.prototype.addChild = function (key, pyld, level) { - if (key.length == 1) { - var folder = new Item; -// folder.title = pyld.startstr + " - " + key; - folder.title = key[0]; - folder.payload = pyld; - folder.isFolder = false; - this.childs.push(folder); -// this.titles.push({title: pyld.startstr + " - " + key , pyld : pyld}); - } - else { - if (level > 10) { - Main.log(" too many levels"); - return; - } - var t = key.shift(); - var found = false; - for (var i = 0; i < this.childs.length; i++) { - if (this.childs[i].title == t) { - this.childs[i].addChild(key, pyld, level +1); - found = true; - break; - } - } - if (found == false) { - var folder = new Item; - folder.title = t; - folder.addChild(key, pyld, level+1); - this.childs.push(folder); - } - } -}; - -Item.prototype.findEpgUpdateTime = function (min, guid, level) { - var prefix= ""; - for (var i = 0; i < level; i++) - prefix += "-"; - - for (var i = 0; i < this.childs.length; i++) { - if (this.childs[i].isFolder == true) { - var res = this.childs[i].findEpgUpdateTime(min, guid, level+1); - min = res.min; - guid = res.guid; - } - else { - var digi =new Date(this.childs[i].payload['start'] * 1000); - var str = digi.getHours() + ":" + digi.getMinutes(); - -// Main.log(prefix + "min= " + min+ " start= " + this.childs[i].payload['start'] + " (" + str+ ") title= " + this.childs[i].title); - - if ((this.childs[i].payload['start'] != 0) && ((this.childs[i].payload['start'] + this.childs[i].payload['dur']) < min)) { - min = this.childs[i].payload['start'] + this.childs[i].payload['dur']; - guid = this.childs[i].payload['guid'] ; -// Main.log(prefix + "New Min= " + min + " new id= " + guid + " title= " + this.childs[i].title); -// Main.logToServer(prefix + "New Min= " + min + " new id= " + guid + " title= " + this.childs[i].title); - } - } - } - - return { "min": min, "guid" : guid}; -}; - -Item.prototype.updateEpgEntry = function (guid, entry, level) { - var prefix= ""; - for (var i = 0; i < level; i++) - prefix += "-"; - for (var i = 0; i < this.childs.length; i++) { - if (this.childs[i].isFolder == true) { - var res = this.childs[i].updateEpgEntry(guid, entry, level+1); - if (res == true) - return true; - } - else { - if (this.childs[i].payload['guid'] == guid) { - Main.log("updateEpgEntry: Found " + this.childs[i].title); - this.childs[i].payload.prog = entry.prog; - this.childs[i].payload.desc = entry.desc; - this.childs[i].payload.start = entry.start; - this.childs[i].payload.dur = entry.dur; - return true; - } - } - } - return false; -}; - -Item.prototype.print = function(level) { - var prefix= ""; - for (var i = 0; i < level; i++) - prefix += " "; - - for (var i = 0; i < this.childs.length; i++) { - Main.log(prefix + this.childs[i].title); - if (this.childs[i].isFolder == true) { - Main.log(prefix+"Childs:"); - this.childs[i].print(level +1); - } - } -}; - -Item.prototype.createJQMDomTree = function(level) { - var mydiv = $('