summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorthlo <t.lohmar@gmx.de>2013-04-21 16:20:52 +0200
committerthlo <t.lohmar@gmx.de>2013-04-21 16:20:52 +0200
commit7fe1953d5bb204ec97e8f5032b0f7ec5d0e1cbda (patch)
tree97d66165ef8bde60e9008026222742f2447d465a
parentcc00a9c44bde4527dafc0143cb3055404f102da4 (diff)
downloadvdr-plugin-smarttvweb-7fe1953d5bb204ec97e8f5032b0f7ec5d0e1cbda.tar.gz
vdr-plugin-smarttvweb-7fe1953d5bb204ec97e8f5032b0f7ec5d0e1cbda.tar.bz2
Plugin code refactoring.
-rw-r--r--Makefile10
-rwxr-xr-xhttpresource.c2863
-rwxr-xr-xhttpresource.h98
-rw-r--r--httpresource_base.h2
-rw-r--r--mngurls.c22
-rw-r--r--mngurls.h8
-rw-r--r--responsebase.c162
-rw-r--r--responsebase.h77
-rw-r--r--responsefile.c174
-rw-r--r--responsefile.h51
-rw-r--r--responsememblk.c1689
-rw-r--r--responsememblk.h92
-rw-r--r--responsevdrdir.c781
-rw-r--r--responsevdrdir.h64
14 files changed, 3332 insertions, 2761 deletions
diff --git a/Makefile b/Makefile
index 3798c6c..f957795 100644
--- a/Makefile
+++ b/Makefile
@@ -16,12 +16,12 @@ VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ pri
CXX ?= g++
-ifdef DEBUG
+#ifdef DEBUG
CXXFLAGS ?= -g -O0 -fPIC -Wall -Woverloaded-virtual #-Werror
-else
-CXXFLAGS ?= -fPIC -Wall -Woverloaded-virtual #-Werror
+#else
+#CXXFLAGS ?= -fPIC -Wall -Woverloaded-virtual #-Werror
#CXXFLAGS ?= -O2 -fPIC -Wall -Woverloaded-virtual #-Werror
-endif
+#endif
### The directory environment:
@@ -55,7 +55,7 @@ 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 httpclient.o mngurls.o log.o url.o stvw_cfg.o
+OBJS = $(PLUGIN).o smarttvfactory.o httpresource.o httpclient.o mngurls.o log.o url.o stvw_cfg.o responsebase.o responsefile.o responsevdrdir.o responsememblk.o
OBJS2 =
diff --git a/httpresource.c b/httpresource.c
index 2da4345..e20bf00 100755
--- a/httpresource.c
+++ b/httpresource.c
@@ -42,6 +42,10 @@
#include "stvw_cfg.h"
#include "mngurls.h"
#include "url.h"
+#include "responsebase.h"
+#include "responsefile.h"
+#include "responsevdrdir.h"
+#include "responsememblk.h"
#ifndef STANDALONE
#include <vdr/recording.h>
@@ -56,10 +60,10 @@
#include <arpa/inet.h>
#endif
-#define PROTOCOL "HTTP/1.1"
-#define RFC1123FMT "%a, %d %b %Y %H:%M:%S GMT"
-#define MAXLEN 4096
+//TODO: Should I increase the MAXLEN from 4k to 64k?
+//#define MAXLEN 4096
+//#define MAXLEN 32768
#define OKAY 0
#define ERROR (-1)
#define DEBUG_REGHEADERS
@@ -72,71 +76,21 @@
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;
- uint8_t type; // standalone
- uint8_t number; // standalone
- 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
- };
-
-//cHttpResource::cHttpResource(int f, int id, int port, SmartTvServer* factory): mFactory(factory), mLog(),
-// mServerPort(port), mFd(f), mReqId(id), mConnTime(0), mHandleReadCount(0),
+
cHttpResource::cHttpResource(int f, int id, int port, SmartTvServer* factory): cHttpResourceBase(f, id, port, factory),
mLog(), mConnTime(0), mHandleReadCount(0),
- mConnected(true), mConnState(WAITING), mContentType(NYD), mReadBuffer(),
- mMethod(), mResponseMessagePos(0), mBlkData(NULL), mBlkPos(0), mBlkLen(0),
+ mConnected(true), mConnState(WAITING), mReadBuffer(), mMethod(),
+ // 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) {
+ mAcceptRanges(true), rangeHdr(), mResponse(NULL) {
mLog = Log::getInstance();
mPath = "";
mConnTime = time(NULL);
setNonBlocking();
- mBlkData = new char[MAXLEN];
+ // mBlkData = new char[MAXLEN];
//#ifndef DEBUG
*(mLog->log()) << DEBUGPREFIX
@@ -146,19 +100,14 @@ cHttpResource::cHttpResource(int f, int id, int port, SmartTvServer* factory): c
cHttpResource::~cHttpResource() {
- //#ifndef DEBUG
+#ifndef DEBUG
*(mLog->log()) << DEBUGPREFIX
- << " Destructor of cHttpResource called"
+ << " cHttpResource destroyed"
<< endl;
- //#endif
- delete[] mBlkData;
- if (mFile != NULL) {
- *(mLog->log())<< DEBUGPREFIX
- << " ERROR: mFile still open. Closing now..." << endl;
- fclose(mFile);
- mFile = NULL;
- }
-
+#endif
+ // delete[] mBlkData;
+ if (mResponse != NULL)
+ delete mResponse;
}
int cHttpResource::checkStatus() {
@@ -320,6 +269,9 @@ int cHttpResource::handleRead() {
return OKAY;
}
+
+
+
int cHttpResource::processRequest() {
// do stuff based on the request and the query
#ifndef DEBUG
@@ -333,89 +285,71 @@ int cHttpResource::processRequest() {
}
if (!((strcasecmp(mMethod.c_str(), "GET") == 0) or (strcasecmp(mMethod.c_str(), "HEAD") == 0))) {
- sendError(501, "Not supported", NULL, "Method is not supported.");
+ mResponse = new cResponseError(this, 501, "Not supported", NULL, "Method is not supported.");
+ // ((cResponseError*)mResponse)->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);
+ mResponse = new cResponseMemBlk(this);
+ ((cResponseMemBlk*)mResponse)->sendRecordingsXml( &statbuf);
return OKAY;
}
if (mPath.compare("/channels.xml") == 0) {
- if (handleHeadRequest() != 0)
- return OKAY;
-
- sendChannelsXml( &statbuf);
+ mResponse = new cResponseMemBlk(this);
+ ((cResponseMemBlk*)mResponse)->sendChannelsXml( &statbuf);
return OKAY;
}
if (mPath.compare("/epg.xml") == 0) {
- if (handleHeadRequest() != 0)
- return OKAY;
-
- sendEpgXml( &statbuf);
+ mResponse = new cResponseMemBlk(this);
+ ((cResponseMemBlk*)mResponse)->sendEpgXml( &statbuf);
return OKAY;
}
if (mPath.compare("/setResume.xml") == 0) {
- if (handleHeadRequest() != 0)
- return OKAY;
-
- receiveResume();
+ mResponse = new cResponseMemBlk(this);
+ ((cResponseMemBlk*)mResponse)->receiveResume();
return OKAY;
}
if (mPath.compare("/getResume.xml") == 0) {
- if (handleHeadRequest() != 0)
- return OKAY;
-
- sendResumeXml();
+ mResponse = new cResponseMemBlk(this);
+ ((cResponseMemBlk*)mResponse)->sendResumeXml();
return OKAY;
}
if (mPath.compare("/vdrstatus.xml") == 0) {
- if (handleHeadRequest() != 0)
- return OKAY;
-
- sendVdrStatusXml( &statbuf);
+ mResponse = new cResponseMemBlk(this);
+ ((cResponseMemBlk*)mResponse)->sendVdrStatusXml( &statbuf);
return OKAY;
}
#endif
if (mPath.compare("/yt-bookmarklet.js") == 0) {
- if (handleHeadRequest() != 0)
- return OKAY;
-
- sendYtBookmarkletJs();
+ mResponse = new cResponseMemBlk(this);
+ ((cResponseMemBlk*)mResponse)->sendYtBookmarkletJs();
return OKAY;
}
if (mPath.compare("/bmlet-inst.html") == 0) {
- if (handleHeadRequest() != 0)
- return OKAY;
-
- sendBmlInstHtml();
+ mResponse = new cResponseMemBlk(this);
+ ((cResponseMemBlk*)mResponse)->sendBmlInstHtml();
return OKAY;
}
-
if (mPath.compare("/media.xml") == 0) {
- if (handleHeadRequest() != 0)
- return OKAY;
- sendMediaXml( &statbuf);
+ mResponse = new cResponseMemBlk(this);
+ ((cResponseMemBlk*)mResponse)->sendMediaXml( &statbuf);
return OKAY;
}
if (mPath.compare("/clients") == 0) {
- if (handleHeadRequest() != 0)
- return OKAY;
- handleClients();
+ mResponse = new cResponseMemBlk(this);
+ ((cResponseMemBlk*)mResponse)->receiveClientInfo();
return OKAY;
}
@@ -423,64 +357,61 @@ int cHttpResource::processRequest() {
mPath = mFactory->getConfigDir() + "/widget.conf";
if (stat(mPath.c_str(), &statbuf) < 0) {
- sendError(404, "Not Found", NULL, "File not found.");
+ mResponse = new cResponseError(this, 404, "Not Found", NULL, "File not found.");
+ // ((cResponseError*)mResponse)->sendError(404, "Not Found", NULL, "File not found.");
return OKAY;
}
- if (handleHeadRequest() != 0)
- return OKAY;
- mFileSize = statbuf.st_size;
- mContentType = SINGLEFILE;
- return sendFile(&statbuf);
+ mResponse = new cResponseFile(this);
+ return ((cResponseFile*)mResponse)->sendFile();
+ // mContentType = SINGLEFILE;
+ // mFileSize = statbuf.st_size;
+ // 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.");
+ mResponse = new cResponseError(this, 404, "Not Found", NULL, "File not found.");
+ // ((cResponseError*)mResponse)->sendError(404, "Not Found", NULL, "File not found.");
return OKAY;
}
- if (handleHeadRequest() != 0)
- return OKAY;
-
- mFileSize = statbuf.st_size;
- mContentType = SINGLEFILE;
- return sendFile(&statbuf);
+ mResponse = new cResponseFile(this);
+ return ((cResponseFile*)mResponse)->sendFile();
+ // mFileSize = statbuf.st_size;
+ // mContentType = SINGLEFILE;
+ // return sendFile(&statbuf);
}
if (mPath.compare("/urls.xml") == 0) {
- if (handleHeadRequest() != 0)
- return OKAY;
- sendUrlsXml();
+
+ mResponse = new cResponseMemBlk(this);
+ ((cResponseMemBlk*)mResponse)->sendUrlsXml();
return OKAY;
}
if (mPath.size() > 8) {
if (mPath.compare(mPath.size() -8, 8, "-seg.mpd") == 0) {
- if (handleHeadRequest() != 0)
- return OKAY;
- sendManifest( &statbuf, false);
+ mResponse = new cResponseMemBlk(this);
+ ((cResponseMemBlk*)mResponse)->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);
+ mResponse = new cResponseMemBlk(this);
+ ((cResponseMemBlk*)mResponse)->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);
+ mResponse = new cResponseVdrDir(this);
+ ((cResponseVdrDir*)mResponse)->sendMediaSegment( &statbuf);
return OKAY;
}
}
@@ -492,12 +423,12 @@ int cHttpResource::processRequest() {
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.");
+ mResponse = new cResponseError(this, 404, "Not Found", NULL, "File not found.");
+ // ((cResponseError*)mResponse)->sendError(404, "Not Found", NULL, "File not found.");
return OKAY;
}
@@ -512,25 +443,35 @@ int cHttpResource::processRequest() {
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);
+ mResponse = new cResponseVdrDir(this);
+ return ((cResponseVdrDir*)mResponse)->sendVdrDir( &statbuf);
+ // 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.");
+ mResponse = new cResponseError(this, 404, "Not Found", NULL, "File not found.");
+ // ((cResponseError*)mResponse)->sendError(404, "Not Found", NULL, "File not found.");
return OKAY;
}
- if (handleHeadRequest() != 0)
- return OKAY;
- mContentType = MEMBLOCK;
- sendDir( &statbuf);
- return OKAY;
+ // mContentType = MEMBLOCK;
+ string tmp = mPath + "index.html";
+ // snprintf(pathbuf, sizeof(pathbuf), "%sindex.html", mRequest->mPath.c_str());
+ // if (stat(pathbuf, statbuf) >= 0) {
+ if (stat(tmp.c_str(), &statbuf) >= 0) {
+ mPath = tmp;
+ mResponse = new cResponseFile(this);
+ return ((cResponseFile*)mResponse)->sendFile();
+ }
+ else {
+ mResponse = new cResponseMemBlk(this);
+ ((cResponseMemBlk*)mResponse)->sendDir( &statbuf);
+ return OKAY;
+ }
}
else {
// mPath is not a folder, thus it is a file
@@ -541,24 +482,28 @@ int cHttpResource::processRequest() {
// 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.");
+ mResponse = new cResponseError(this, 404, "Not Found", NULL, "File not found.");
+ // ((cResponseError*)mResponse)->sendError(404, "Not Found", NULL, "File not found.");
return OKAY;
}
- if (handleHeadRequest() != 0)
- return OKAY;
- mFileSize = statbuf.st_size;
- mContentType = SINGLEFILE;
- return sendFile(&statbuf);
+
+ mResponse = new cResponseFile(this);
+ return ((cResponseFile*)mResponse)->sendFile();
+ // mFileSize = statbuf.st_size;
+ //mContentType = SINGLEFILE;
+ //return sendFile(&statbuf);
}
#ifndef DEBUG
*(mLog->log())<< DEBUGPREFIX
- << " processRequest: Not Handled SHOULD not be here\n";
+ << " processRequest: Not Handled SHOULD not be here\n";
#endif
return ERROR;
}
+
+
int cHttpResource::handleWrite() {
if (mConnState == TOCLOSE) {
@@ -571,268 +516,74 @@ int cHttpResource::handleWrite() {
if (mConnState != SERVING) {
return OKAY;
}
-
- if (mBlkLen == mBlkPos) {
+
+ // if (mResponse->mBlkLen == mResponse->mBlkPos) {
+ if (mResponse->isBlkWritten()) {
// note the mBlk may be filled with header info first.
- if (fillDataBlk() != OKAY) {
+ if (mResponse->fillDataBlk() != OKAY) {
return ERROR;
}
}
- int this_write = write(mFd, &mBlkData[mBlkPos], mBlkLen - mBlkPos);
- if (this_write <=0) {
+ // int this_write = write(mFd, &(mResponse->mBlkData[mBlkPos]), mResponse->mBlkLen - mResponse->mBlkPos);
+ // int this_write = mResponse->writeData(mFd);
+ // if (this_write <=0) {
+ if (mResponse->writeData(mFd) <=0) {
#ifndef DEBUG
*(mLog->log())<< DEBUGPREFIX
<< " ERROR after write: Stopped (Client terminated Connection)"
- << " mBlkPos= " << mBlkPos << " mBlkLen= " << mBlkLen
+ << " mBlkPos= " << mResponse->mBlkPos << " mBlkLen= " << mResponse->mBlkLen
<< DEBUGHDR << endl;
#endif
mConnState = TOCLOSE;
mConnected = false;
return ERROR;
}
- mBlkPos += this_write;
+ // mResponse->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);
+ mResponse = new cResponseOk(this, 200, "OK", NULL, NULL, -1, -1);
+ // ((cResponseError*)mResponse)->sendHeaders(200, "OK", NULL, NULL, -1, -1);
return OKAY;
}
if (mPath.compare("/getResume.xml") == 0) {
- if (handleHeadRequest() != 0)
- return OKAY;
- // sendResumeXml( &statbuf);
- sendResumeXml( );
+ mResponse = new cResponseMemBlk(this);
+ ((cResponseMemBlk*)mResponse)->sendResumeXml( );
return OKAY;
}
if (mPath.compare("/setResume.xml") == 0) {
- if (handleHeadRequest() != 0)
- return OKAY;
- receiveResume();
+ mResponse = new cResponseMemBlk(this);
+ ((cResponseMemBlk*)mResponse)->receiveResume();
return OKAY;
}
if (mPath.compare("/setYtUrl") == 0) {
- if (handleHeadRequest() != 0)
- return OKAY;
- receiveYtUrl();
+ mResponse = new cResponseMemBlk(this);
+ ((cResponseMemBlk*)mResponse)->receiveYtUrl();
return OKAY;
}
if (mPath.compare("/deleteRecording.xml") == 0) {
- if (handleHeadRequest() != 0)
- return OKAY;
- deleteRecording();
+ mResponse = new cResponseMemBlk(this);
+ ((cResponseMemBlk*)mResponse)->receiveDelRecReq();
return OKAY;
}
@@ -841,2181 +592,80 @@ int cHttpResource::handlePost() {
}
-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), "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>\r\n", status, title);
- hdr += f;
- snprintf(f, sizeof(f), "<BODY><H4>%d %s</H4>\r\n", status, title);
- hdr += f;
-*/
- snprintf(f, sizeof(f), "%s\r\n", text);
- hdr += f;
- /* snprintf(f, sizeof(f), "</BODY></HTML>\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), "<HTML><HEAD><TITLE>Index of %s</TITLE></HEAD>\r\n<BODY>", mPath.c_str());
- hdr += f;
- snprintf(f, sizeof(f), "<H4>Index of %s</H4>\r\n<PRE>\n", mPath.c_str());
- hdr += f;
- snprintf(f, sizeof(f), "Name Last Modified Size\r\n");
- hdr += f;
- snprintf(f, sizeof(f), "<HR>\r\n");
- hdr += f;
-
- *mResponseMessage += hdr;
- hdr = "";
-
- if (len > 1) {
- snprintf(f, sizeof(f), "<A HREF=\"..\">..</A>\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), "<A HREF=\"%s%s\">", de->d_name, S_ISDIR(statbuf->st_mode) ? "/" : "");
- hdr += f;
-
- snprintf(f, sizeof(f), "%s%s", de->d_name, S_ISDIR(statbuf->st_mode) ? "/</A>" : "</A> ");
- 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), "</PRE>\r\n<HR>\r\n<ADDRESS>%s</ADDRESS>\r\n</BODY></HTML>\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 += "<item>\n";
- // snprintf(f, sizeof(f), "%s - %s", );
- hdr += "<title>" + name +"</title>\n";
- hdr += "<link>" +link + "</link>\n";
- hdr += "<enclosure url=\"" +link + "\" type=\"video/mpeg\" />\n";
-
- hdr += "<guid>" + guid + "</guid>\n";
-
- snprintf(f, sizeof(f), "%d", no);
- hdr += "<number>";
- hdr += f;
- hdr += "</number>\n";
-
- hdr += "<programme>" + programme +"</programme>\n";
- hdr += "<description>" + desc + "</description>\n";
-
- snprintf(f, sizeof(f), "%ld", start);
- hdr += "<start>";
- hdr += f;
- hdr += "</start>\n";
-
- snprintf(f, sizeof(f), "%d", dur);
- hdr += "<duration>";
- hdr += f;
- hdr += "</duration>\n";
-
- if (fps != -1)
- snprintf(f, sizeof(f), "<fps>%.2f</fps>\n", fps);
- else
- snprintf(f, sizeof(f), "<fps>unknown</fps>\n");
- hdr += f;
-
- switch (is_pes){
- case -1:
- // unknown
- hdr += "<ispes>unknown</ispes>\n";
- break;
- case 0:
- // true
- hdr += "<ispes>true</ispes>\n";
- break;
- case 1:
- // false
- hdr += "<ispes>false</ispes>\n";
- break;
- default:
- break;
- }
-
- switch (is_new){
- case -1:
- // unknown
- hdr += "<isnew>unknown</isnew>\n";
- break;
- case 0:
- // true
- hdr += "<isnew>true</isnew>\n";
- break;
- case 1:
- // false
- hdr += "<isnew>false</isnew>\n";
- break;
- default:
- break;
- }
-
- hdr += "</item>\n";
-
- *mResponseMessage += hdr;
-
-
-
-
- // return writeToClient(hdr.c_str(), hdr.size());
- return OKAY;
-}
-
-int cHttpResource::parseQueryLine (vector<sQueryAVP> *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<sQueryAVP> *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<sFileEntry> *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;
-}
-
-int cHttpResource::receiveYtUrl() {
- vector<sQueryAVP> avps;
- parseQueryLine(&avps);
- string line;
- string store_str;
- bool store = true;
-
- /* *(mLog->log()) << DEBUGPREFIX
- << " receiveYtUrl: Query= " << mQuery
- << endl;
-*/
- if (getQueryAttributeValue(&avps, "store", store_str) == OKAY){
- if (store_str.compare("false")==0) {
- store = false;
- *(mLog->log()) << DEBUGPREFIX
- << " receiveYtUrl: set store to false "
- << endl;
-
- }
- }
- if (getQueryAttributeValue(&avps, "line", line) == OKAY){
- if (line.compare ("") == 0) {
- *(mLog->log())<< DEBUGPREFIX
- << " receiveYtUrl: Nothing to push "
- << endl;
-
- sendHeaders(200, "OK", NULL, NULL, 0, -1);
- return OKAY;
- }
-
- mFactory->pushYtVideoId(line, store);
-
- if (store)
- mFactory->storeYtVideoId(line);
-
- sendHeaders(200, "OK", NULL, NULL, 0, -1);
- return OKAY;
-
- }
-
-
- sendError(400, "Bad Reqiest", NULL, "Mandatory Line attribute not present.");
- 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 += "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
-
- snprintf(line, sizeof(line), "<MPD type=\"OnDemand\" minBufferTime=\"PT%dS\" mediaPresentationDuration=\"PT%.1fS\"",
- mFactory->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 += "<ProgramInformation>\n";
- *mResponseMessage += "<ChapterDataURL/>\n";
- *mResponseMessage += "</ProgramInformation>\n";
- *mResponseMessage += "<Period start=\"PT0S\" segmentAlignmentFlag=\"True\">\n";
- // SD: 720x 576
- // HD: 1280x 720
- // snprintf(line, sizeof(line), "<Representation id=\"0\" mimeType=\"video/mpeg\" bandwidth=\"%d\" startWithRAP=\"True\" width=\"1280\" height=\"720\" group=\"0\">\n", mFactory->getConfig()->getHasBitrate());
- snprintf(line, sizeof(line), "<Representation id=\"0\" mimeType=\"video/mpeg\" bandwidth=\"%d\" startWithRAP=\"True\" %s group=\"0\">\n",
- bitrate, ((bitrate < 10000000)? "width=\"720\" height=\"576\"" : "width=\"1280\" height=\"720\""));
- *mResponseMessage = *mResponseMessage + line;
-
- hdr = "<SegmentInfo duration=";
- snprintf(buf, sizeof(buf), "\"PT%.1fS\"", (seg_dur*1.0));
- hdr = hdr + buf + " >\n";
- *mResponseMessage += hdr;
-
- snprintf(buf, sizeof(buf), "\"%d\"", end_seg);
- *mResponseMessage += "<UrlTemplate sourceURL=\"$Index$-seg.ts\" startIndex=\"1\" endIndex=";
- hdr = buf ;
- *mResponseMessage += hdr + " />\n";
-
- *mResponseMessage += "</SegmentInfo>\n";
- *mResponseMessage += "</Representation>\n";
- *mResponseMessage += "</Period>\n";
- *mResponseMessage += "</MPD>";
-
- 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())<<DEBUGPREFIX
- << " issue while reading" << endl;
- delete[] index_buf;
- sendError(404, "Not Found", NULL, "Failed to read Index file");
- return OKAY;
- }
-
- // Reading the segment
- mFileStructure = "%s/%05d.ts";
- int start_offset = -1;
- int start_idx = -1;
-
- tIndexTs in_read_ts;
- memcpy (&in_read_ts, &(index_buf[0]), 8);
-
- start_offset = in_read_ts.offset;
- start_idx = in_read_ts.number;
-
- char seg_fn[200];
- mVdrIdx = start_idx;
-
- snprintf(seg_fn, sizeof(seg_fn), mFileStructure.c_str(), mDir.c_str(), mVdrIdx);
- mPath = seg_fn;
-
- /*
- * Now we determine the end of the segment
- */
- memcpy (&in_read_ts, &(index_buf[(frames_per_seg)*8]), 8);
-
- int end_idx = in_read_ts.number;
- int end_offset = in_read_ts.offset;
-
- /*#ifndef DEBUG*/
- *(mLog->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= " <<mRemLength
- << endl;
-#endif
- }
- else {
-#ifndef DEBUG
- *(mLog->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= " <<seg_fn << " does not exist"
- << endl;
- error= true;
- // issue:
- }
- rem_len = statbuf->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= " <<seg_fn << " does not exist"
- << endl;
- error = true;
- break;
- // issue:
- }
- rem_len += statbuf->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= " <<mRemLength
- << endl;
-#endif
- }
-
- if (error){
- sendError(404, "Not Found", NULL, "Not all inputs exists");
- return OKAY;
- }
-
- mContentType = VDRDIR;
-
- if (openFile(seg_fn) != OKAY) {
- *(mLog->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::sendUrlsXml () {
- // read urls file and generate XML
- string type;
- string value;
- string line;
-
- mResponseMessage = new string();
- mResponseMessagePos = 0;
- *mResponseMessage = "";
- mContentType = MEMBLOCK;
-
- mConnState = SERVING;
-
- cManageUrls* urls = mFactory->getUrlsObj();
-
- // ifstream myfile ((mFactory->getConfigDir() +"/urls.txt").c_str());
- // An empty xml is provided, if the file does not exist.
-
- //thlo: here to continue
- *mResponseMessage = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
- *mResponseMessage += "<rss version=\"2.0\">\n";
- *mResponseMessage += "<channel>\n";
-
- for (uint i = 0; i < urls->size(); i++) {
- *mResponseMessage += "<item>\n<guid>";
- *mResponseMessage += (urls->getEntry(i))->mEntry;
- *mResponseMessage += "</guid>\n</item>\n";
- }
-
- /*
- while ( myfile.good() ) {
- getline (myfile, line);
-
- if ((line == "") or (line[0] == '#'))
- continue;
-
- size_t pos = line.find('|');
- type = line.substr(0, pos);
- value = line.substr(pos+1);
-
- if (type.compare("YT")==0) {
- *mResponseMessage += "<item>\n<guid>";
- *mResponseMessage += value;
- *mResponseMessage += "</guid>\n</item>\n";
- continue;
- }
-
- *mResponseMessage += "<item>\n<title>";
- *mResponseMessage += "Unknown: " + line + " type=" + type;
-
- *mResponseMessage += "</title>\n</item>\n";
- }
- myfile.close();
-*/
- *mResponseMessage += "</channel>\n";
- *mResponseMessage += "</rss>\n";
-
- sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), -1);
- 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<sFileEntry> entries;
-
- if (parseFiles(&entries, "", media_folder, "", statbuf) == ERROR) {
- sendError(404, "Not Found", NULL, "Media Folder likely not configured.");
- return OKAY;
- }
-
- string hdr = "";
- hdr += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
- hdr += "<rss version=\"2.0\">\n";
- hdr+= "<channel>\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 = "</channel>\n";
- hdr += "</rss>\n";
- *mResponseMessage += hdr;
- sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), statbuf->st_mtime);
-
- return OKAY;
-}
-
-
-void cHttpResource::handleClients() {
- vector<sQueryAVP> avps;
- parseQueryLine(&avps);
-
- string mac = "";
- string ip = "";
- string state = "";
-
- getQueryAttributeValue(&avps, "mac", mac) ;
- getQueryAttributeValue(&avps, "ip", ip);
- getQueryAttributeValue(&avps, "state", state);
-
- /* if (getQueryAttributeValue(&avps, "mac", mac) == OKAY){
- }
-
- if (getQueryAttributeValue(&avps, "ip", ip) == OKAY){
- }
-
- if (getQueryAttributeValue(&avps, "state", state) == OKAY){
- }
-*/
- // state: started, running, stopped
- *(mLog->log())<< DEBUGPREFIX
- << " handleClients mac= " << mac << " ip= " << ip << " state= " << state
- << endl;
- if (mac.compare ("") == 0) {
- *(mLog->log())<< DEBUGPREFIX
- << " mac is empty. Ignoring"
- << endl;
- sendHeaders(200, "OK", NULL, NULL, 0, -1);
- return;
- }
- if (state.compare("stopped") == 0) {
- mFactory->removeTvClient(ip, mac, time(NULL));
- }
- else {
- mFactory->updateTvClient(ip, mac, time(NULL));
- }
- sendHeaders(200, "OK", NULL, NULL, 0, -1);
-}
-
-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 += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
- *mResponseMessage += "<vdrstatus>\n";
-
- *mResponseMessage += "<diskspace>\n";
- snprintf(f, sizeof(f), "<free>%d</free>", free);
- *mResponseMessage += f;
-
- snprintf(f, sizeof(f), "<used>%d</used>", used);
- *mResponseMessage += f;
- snprintf(f, sizeof(f), "<percent>%d</percent>", percent);
- *mResponseMessage += f;
- *mResponseMessage += "</diskspace>\n";
-
- *mResponseMessage += "</vdrstatus>\n";
-
- sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), -1);
-
-#endif
- return OKAY;
-}
-
-int cHttpResource::sendYtBookmarkletJs() {
- *(mLog->log()) << DEBUGPREFIX
- << " sendYtBookmarkletJs" << endl;
-
- vector<sQueryAVP> avps;
- parseQueryLine(&avps);
- string store_str = "";
- bool store= true;
-
- if (getQueryAttributeValue(&avps, "store", store_str) == OKAY) {
- if (store_str.compare("false") == 0) {
- store= false;
- *(mLog->log()) << DEBUGPREFIX
- << " store= false " << endl;
- }
- }
-
- mResponseMessage = new string();
- *mResponseMessage = "";
- mResponseMessagePos = 0;
- mContentType = MEMBLOCK;
-
- mConnState = SERVING;
-
- stringstream own_host ;
- own_host << "http://"
- << getOwnIp(mFd)
- << ":" << mServerPort;
-
- // string own_host = "http://"+getOwnIp(mFd)+ ":" + str;
-
- *(mLog->log()) << " Ownhost= " << own_host.str() << endl;
-
-
- *mResponseMessage = "function get_query_var (querystring, name) { "
- "var filter = new RegExp( name + \"=([^&]+)\" ); "
- "var res = null;"
- "if (querystring != null)"
- " res = querystring.match(filter);"
- " if (res != null) return unescape( res[1] );"
- " else return \"\";"
- "}"
- "var vid_id= get_query_var(document.URL, \"v\");"
-
- "var iframe = document.createElement(\"iframe\");"
- "iframe.setAttribute(\"name\",\"myiframe\");"
- "iframe.setAttribute(\"frameborder\",\"0\");"
- "iframe.setAttribute(\"scrolling\",\"no\");"
- "iframe.setAttribute(\"src\",\"about:blank\");"
- "iframe.setAttribute(\"width\",\"1\");"
- "iframe.setAttribute(\"height\",\"1\");"
- "document.body.appendChild(iframe);"
-
- "var form = document.createElement(\"form\");"
- "form.setAttribute(\"method\", \"POST\");"
- "form.setAttribute(\"target\", \"myiframe\");"
- "form.setAttribute(\"action\", \"" + own_host.str() + "/setYtUrl?line=\"+vid_id"+((!store)?"+\"&store=false\"":"")+");"
- "var hiddenField = document.createElement(\"input\");"
- "form.appendChild(hiddenField);"
- "hiddenField.setAttribute(\"type\", \"hidden\");"
- "hiddenField.setAttribute(\"name\", \"line\");"
- "hiddenField.setAttribute(\"value\", vid_id);"
- "document.body.appendChild(form);"
- "form.submit();"
- ;
-
- sendHeaders(200, "OK", NULL, "text/javascript", mResponseMessage->size(), -1);
- return OKAY;
-}
-
-int cHttpResource::sendBmlInstHtml() {
- *(mLog->log()) << DEBUGPREFIX
- << " sendBmlInstHtml" << endl;
-
- mResponseMessage = new string();
- *mResponseMessage = "";
- mResponseMessagePos = 0;
- mContentType = MEMBLOCK;
-
- mConnState = SERVING;
-
- stringstream own_host ;
- own_host << "http://"
- << getOwnIp(mFd)
- << ":" << mServerPort;
-
- *(mLog->log()) << " Ownhost= " << own_host << endl;
-
- *mResponseMessage = "<html><head>"
- "<title>SmartTVWeb Bookmarklets</title>"
- "</head><body>"
- "<br>"
- "<h2>Bookmarklet for collecting YouTube Pages</h2>"
- "<hr width=\"80%\">"
- "<br>"
- "<h3>Installation</h3>"
- "Drag the link below to your Bookmarks toolbar"
- "<p>or</p>"
- "<p>Right click and select &#8220;Bookmark This Link&#8221;</p>"
- "<p><a href='javascript:document.body.appendChild(document.createElement(&quot;script&quot;)).src=&quot;"+own_host.str()+"/yt-bookmarklet.js&quot;;void(0)'>YT SaveNPlay</a>: Save the video and also Play it.</p>"
- "<p><a href='javascript:document.body.appendChild(document.createElement(&quot;script&quot;)).src=&quot;"+own_host.str()+"/yt-bookmarklet.js?store=false&quot;;void(0)'>YT Play</a>: Play the video without saving.</p>"
- "<br>"
- "<hr width=\"80%\">"
- "<h3>Usage</h3>"
- "<p>Browse to your favorite YouTube page and click the bookmark to the bookmarklet (link above). The YouTube video is then provided to the VDR smarttvweb plugin, stored there and pushed to the TV screen for immediate playback. Tested with Firefox.</p>"
- "<br>"
- "<hr width=\"80%\">"
- "<p>Have fun...<br></p>"
- "</body>";
-
- sendHeaders(200, "OK", NULL, "text/html", mResponseMessage->size(), -1);
- return OKAY;
-}
-
-int cHttpResource::sendEpgXml (struct stat *statbuf) {
-#ifndef STANDALONE
-
- char f[400];
- mResponseMessage = new string();
- *mResponseMessage = "";
- mResponseMessagePos = 0;
- mContentType = MEMBLOCK;
-
- mConnState = SERVING;
+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
- << " generating /epg.xml"
- << DEBUGHDR << endl;
-#endif
- vector<sQueryAVP> 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 += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
- hdr += "<tv version=\"2.0\">\n";
- hdr+= "<programme>\n";
-
- *mResponseMessage += hdr;
- // Payload here
-
- hdr = "";
- if (ev->Title() != NULL) {
- string title = cUrlEncode::doXmlSaveEncode(ev->Title());
- hdr += "<title>" + title +"</title>\n";
- }
- else {
- *(mLog->log())<< DEBUGPREFIX
- << " ERROR: title is zero for guid= " << id << endl;
- hdr += "<title>Empty</title>\n";
-
- delete mResponseMessage;
- sendError(500, "Internal Server Error", NULL, "Title is zero.");
- return OKAY;
- }
-
- hdr += "<guid>" + id + "</guid>\n";
-
- *(mLog->log())<< DEBUGPREFIX
- << " guid= " << id
- << " title= " << ev->Title()
- << " start= " << ev->StartTime()
- << " end= " << ev->EndTime()
- << " now= " << now
- << endl;
- if (add_desc) {
- hdr += "<desc>";
- 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 += "</desc>\n";
- }
- else {
- hdr += "<desc>No Description Available</desc>\n";
- }
- snprintf(f, sizeof(f), "<start>%ld</start>\n", ev->StartTime());
- hdr += f ;
-
- snprintf(f, sizeof(f), "<end>%ld</end>\n", ev->EndTime());
- hdr += f;
-
- snprintf(f, sizeof(f), "<duration>%d</duration>\n", ev->Duration());
- hdr += f;
- *mResponseMessage += hdr;
-
- hdr = "</programme>\n";
- hdr += "</tv>\n";
-
- *mResponseMessage += hdr;
-
- delete lock;
-
- sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), -1);
-
+ << " ReqLine= " << line << endl;
#endif
- return OKAY;
-}
-
-int cHttpResource::sendChannelsXml (struct stat *statbuf) {
-#ifndef STANDALONE
-
- char f[400];
- mResponseMessage = new string();
- *mResponseMessage = "";
- mResponseMessagePos = 0;
- mContentType = MEMBLOCK;
-
- mConnState = SERVING;
-
+ if (mVersion.compare(0, 4, "HTTP") != 0) {
#ifndef DEBUG
- *(mLog->log())<< DEBUGPREFIX
- << " generating /channels.xml"
- << DEBUGHDR << endl;
-#endif
- string own_ip = getOwnIp(mFd);
- *(mLog->log()) << " OwnIP= " << own_ip << endl;
-
- vector<sQueryAVP> 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 += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
- hdr += "<rss version=\"2.0\">\n";
- hdr+= "<channel>\n";
- hdr+= "<title>VDR Channels List</title>\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 = "</channel>\n";
- hdr += "</rss>\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<sQueryAVP> avps;
- parseQueryLine(&avps);
- string guid;
- string resume_str;
-
- if (getQueryAttributeValue(&avps, "guid", guid) == OKAY){
- // entry.mFilename = guid;
- entry.mFilename = cUrlEncode::doUrlSaveDecode(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= " <<entry.mResume
- << " fpr= " << rec->FramesPerSecond()
- << endl;
-
- resume.Save(int(entry.mResume * rec->FramesPerSecond() ));
-#endif
-
- sendHeaders(200, "OK", NULL, NULL, -1, -1);
- return OKAY;
-}
-
-//int cHttpResource::sendResumeXml (struct stat *statbuf) {
-int cHttpResource::sendResumeXml () {
-#ifndef STANDALONE
-
- mResponseMessage = new string();
- *mResponseMessage = "";
- mResponseMessagePos = 0;
- mContentType = MEMBLOCK;
-
- mConnState = SERVING;
-
- char f[400];
-
- cResumeEntry entry;
- string id;
-
- parseResume(entry, id);
-
- vector<sQueryAVP> avps;
- parseQueryLine(&avps);
- string guid;
-
- if (getQueryAttributeValue(&avps, "guid", guid) == OKAY){
- // entry.mFilename = guid;
- entry.mFilename = cUrlEncode::doUrlSaveDecode(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 += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
- *mResponseMessage += "<resume>";
- snprintf(f, sizeof(f), "%.02f", resume.Read() *1.0 / rec->FramesPerSecond());
- *mResponseMessage += f;
- *mResponseMessage += "</resume>\n";
-
-
- sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), -1);
-#endif
-
- return OKAY;
-}
-
-int cHttpResource::deleteRecording() {
-#ifndef STANDALONE
-
- vector<sQueryAVP> 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);
- #endif
- 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<sQueryAVP> 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;
+ << " ERROR: No HTTP request -> Closing Connection" << line << endl;
#endif
- sendHeaders(200, "OK", NULL, "application/xml", -1, statbuf->st_mtime);
-
- string hdr = "";
- hdr += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
- hdr += "<rss version=\"2.0\">\n";
- hdr+= "<channel>\n";
- hdr+= "<title>VDR Recordings List</title>\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<sTimerEntry> 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
+ 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
- << " Found " << act_rec.size()
- << " running timers"
+ << " mMethod= " << mMethod
+ << " mPath= " << mPath
+ << " mVer= " << mVersion
+ << " mQuery= " << mQuery
+ // << " HexDump= " << endl << cUrlEncode::hexDump(mPath) << endl
<< 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 = "</channel>\n";
- hdr += "</rss>\n";
-
- *mResponseMessage += hdr;
-
-
-#endif
return OKAY;
}
-bool cHttpResource::isTimeRequest(struct stat *statbuf) {
-
-#ifndef STANDALONE
- vector<sQueryAVP> 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())<<DEBUGPREFIX
- << " issue while reading, buffered_indexes <=0" << endl;
- delete[] index_buf;
- sendError(404, "Not Found", NULL, "Failed to read Index file");
- return OKAY;
- }
-
- *(mLog->log()) << DEBUGPREFIX
- << " Finding I-Frame now" << endl;
-
-
- bool found_it = false;
- int i = 0;
+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);
- 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;
- }
-
+ if (hdr_name.compare("Range") == 0) {
+ parseRangeHeaderValue(hdr_val);
*(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;
-#else
- return false;
-#endif
-}
-
-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<sVdrFileEntry> 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);
+ << " Range: Begin= " << rangeHdr.begin
+ << " End= " << rangeHdr.end
+ << endl;
}
- 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);
+ 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())<< " ***** Yes, vdr dir found ***** mPath= " << mPath<< endl;
+ *(mLog->log())<< " Content-Length: " << mReqContentLength
+ << 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());
-
+ return 0;
}
-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;
+int cHttpResource::parseRangeHeaderValue(string val) {
+ rangeHdr.isRangeRequest = true;
+ size_t pos_equal = val.find_first_of('=');
+ size_t pos_minus = val.find_first_of('-');
- *(mLog->log())<< "fd= " << mFd << " mReqId= "<< mReqId
- << " mFileSize= " <<mFileSize << endl;
+ 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());
- if (!rangeHdr.isRangeRequest) {
- mRemLength = mFileSize;
- sendHeaders(200, "OK", NULL, getMimeType(mPath.c_str()), mFileSize, statbuf.st_mtime);
- }
- else { // Range request
- fseeko64(mFile, rangeHdr.begin, SEEK_SET);
- if (rangeHdr.end == 0)
- rangeHdr.end = mFileSize;
- mRemLength = (rangeHdr.end-rangeHdr.begin);
- *(mLog->log())<< "fd= " << mFd << " mReqId= "<< mReqId
- << " rangeHdr.begin= " <<rangeHdr.begin << " rangeHdr.end= " << rangeHdr.end
- << " Content-Length= " << mRemLength << endl;
-
- snprintf(f, sizeof(f), "Content-Range: bytes %lld-%lld/%lld", rangeHdr.begin, (rangeHdr.end -1), mFileSize);
- sendHeaders(206, "Partial Content", f, getMimeType(mPath.c_str()), (rangeHdr.end-rangeHdr.begin), statbuf.st_mtime);
+ string sec_val = "";
+ if ((pos_minus +1)< val.size()){
+ sec_val = val.substr(pos_minus+1);
+ rangeHdr.end = atoll(sec_val.c_str());
}
-
-#ifndef DEBUG
- *(mLog->log())<< "fd= " << mFd << " mReqId= "<< mReqId
- << ": Done mRemLength= "<< mRemLength << " mContentType= " << mContentType
- << endl;
-#endif
- mConnState = SERVING;
-
- return OKAY;
-
+ return 0;
}
-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() {
@@ -3044,173 +694,58 @@ string cHttpResource::getConnStateName() {
}
-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::parseQueryLine (vector<sQueryAVP> *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);
-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;
+ 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;
}
- 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;
-#ifndef STANDALONE
- time_t now = time(NULL);
- // cRecordings* recordings = mFactory->getRecordings();
- cRecordings* recordings = &Recordings;
-#ifndef DEBUG
- *(mLog->log())<< DEBUGPREFIX
- << " GetByName(" <<mPath.c_str() << ")"
- << endl;
-#endif
- cRecording* rec = recordings->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
- }
+int cHttpResource::getQueryAttributeValue(vector<sQueryAVP> *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
- << " checking, whether recording is on-going"
- << " now= " << now << " start= " << rec->Start()
- << " curDur= " << rec->LengthInSeconds()
+ *(mLog->log())<< DEBUGPREFIX
+ << " a= "
+ << (*avps)[i].attribute
+ << " v= " << (*avps)[i].value
<< endl;
#endif
}
-#ifndef DEBUG
- else {
- *(mLog->log())<< DEBUGPREFIX
- << " **** Recording Entry Not found **** " << endl;
- }
-#endif
-#endif
+ return found;
}
-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;
+// common for all mem functions
+//string cHttpResource::getOwnIp(int fd) {
+string cHttpResource::getOwnIp() {
+ struct sockaddr_in sock;
+ socklen_t len_inet = sizeof(sock);
+ int ret = getsockname(mFd, (struct sockaddr *)&sock, &len_inet);
+ if ( ret == -1 ) {
+ *(mLog->log()) << "Error: Cannot obtain own ip address" << endl;
+ return string("0.0.0.0");
+ }
+ return string (inet_ntoa(sock.sin_addr));
}
diff --git a/httpresource.h b/httpresource.h
index c563b3c..e170b4f 100755
--- a/httpresource.h
+++ b/httpresource.h
@@ -25,10 +25,14 @@
#include <string>
#include <cstring>
+#include <vector>
#include <pthread.h>
+
#include "log.h"
#include "httpresource_base.h"
+#define MAXLEN 32768
+
using namespace std;
struct cRange {
@@ -53,24 +57,9 @@ enum eConnState {
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 cResponseBase;
+
class cHttpResource : public cHttpResourceBase {
@@ -82,34 +71,24 @@ class cHttpResource : public cHttpResourceBase {
int handleWrite();
int checkStatus();
- int readFromClient();
- void threadLoop();
- int run();
+ public:
- private:
- // SmartTvServer* mFactory;
Log* mLog;
- // int mServerPort;
- // int mFd;
- // int mReqId;
-
time_t mConnTime;
int mHandleReadCount;
bool mConnected;
- eConnState mConnState;
- eContentType mContentType;
+ eConnState mConnState;
string mReadBuffer;
string mMethod;
- string *mResponseMessage;
- int mResponseMessagePos;
+ /*
char* mBlkData;
int mBlkPos;
int mBlkLen;
-
+*/
string mRequest;
string mQuery;
string mPath;
@@ -122,17 +101,10 @@ class cHttpResource : public cHttpResourceBase {
bool mAcceptRanges;
cRange rangeHdr;
- unsigned long long mFileSize;
- bool mStreamToEnd;
- uint64_t mRemLength;
- FILE *mFile;
- int mVdrIdx;
- string mFileStructure;
- bool mIsRecording;
- float mRecProgress;
+
+ cResponseBase* mResponse;
void setNonBlocking();
- int fillDataBlk();
int handlePost();
int handleHeadRequest();
@@ -140,56 +112,14 @@ class cHttpResource : public cHttpResourceBase {
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 sendYtBookmarkletJs();
- int sendBmlInstHtml();
-
- int sendEpgXml (struct stat *statbuf);
- int sendUrlsXml ();
- int sendMediaXml (struct stat *statbuf);
-
- void handleClients();
-
- int sendManifest (struct stat *statbuf, bool is_hls = true);
-
- int receiveResume();
- int deleteRecording();
-
- int receiveYtUrl();
- 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<sQueryAVP> *avps);
- int parseResume(cResumeEntry &entry, string &id);
-
- int parseFiles(vector<sFileEntry> *entries, string prefix, string dir_base, string dir_name, struct stat *statbuf);
-
int getQueryAttributeValue(vector<sQueryAVP> *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);
+
+ string getOwnIp();
};
#endif
diff --git a/httpresource_base.h b/httpresource_base.h
index 81a62c6..6494443 100644
--- a/httpresource_base.h
+++ b/httpresource_base.h
@@ -35,12 +35,10 @@ class cHttpResourceBase {
virtual int handleWrite() = 0;
virtual int checkStatus() =0;
- protected:
int mFd;
int mReqId;
SmartTvServer* mFactory;
int mServerPort;
-
};
#endif
diff --git a/mngurls.c b/mngurls.c
index e9f18d0..dabcfe7 100644
--- a/mngurls.c
+++ b/mngurls.c
@@ -39,6 +39,7 @@ cManageUrls::~cManageUrls() {
//TODO: delete entries
};
+
//called from outside to add an entry
void cManageUrls::appendEntry(string type, string url) {
// iter through entries
@@ -61,6 +62,27 @@ void cManageUrls::appendEntry(string type, string url) {
}
}
+
+void cManageUrls::deleteEntry(string type, string url) {
+ *(mLog->log()) << " cManageUrls::deleteEntry: type= " << type << "guid= " << url << endl;
+
+ bool found = false;
+ if (type.compare("YT") !=0) {
+ return;
+ }
+ for (int i = 0; i < mEntries.size(); i ++) {
+ if (url.compare(mEntries[i]->mEntry) == 0) {
+ // delete the entry here
+ *(mLog->log()) << " cManageUrls::deleteEntry ... " << endl;
+ found = true;
+ break;
+ }
+ }
+
+}
+
+
+
size_t cManageUrls::size() {
return mEntries.size();
}
diff --git a/mngurls.h b/mngurls.h
index b247808..d500f61 100644
--- a/mngurls.h
+++ b/mngurls.h
@@ -22,12 +22,6 @@
//Manage Urls
-/*
-the object is kept by the factory.
-The object is loaded with the first url request (get or post)
-The object is then kept.
- The file is updated with every new url entry, so that it does not need to wrte when closing
-*/
#ifndef __MANAGEURLS_H__
#define __MANAGEURLS_H__
@@ -47,6 +41,8 @@ class cManageUrls {
virtual ~cManageUrls();
void appendEntry (string type, string guid);
+ void deleteEntry(string type, string guid);
+
size_t size();
sUrlEntry* getEntry(int index);
diff --git a/responsebase.c b/responsebase.c
new file mode 100644
index 0000000..77bca0a
--- /dev/null
+++ b/responsebase.c
@@ -0,0 +1,162 @@
+/*
+ * responsebase.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 "responsebase.h"
+#include "httpresource.h"
+#include "smarttvfactory.h"
+#include "log.h"
+
+#define DEBUGPREFIX "mReqId= " << mRequest->mReqId << " fd= " << mRequest->mFd
+#define OKAY 0
+#define ERROR (-1)
+#define DEBUG
+
+#define PROTOCOL "HTTP/1.1"
+#define RFC1123FMT "%a, %d %b %Y %H:%M:%S GMT"
+
+
+
+cResponseBase::cResponseBase(cHttpResource* req): mBlkData(NULL), mBlkPos(0), mBlkLen(0), mLog(NULL), mRequest(req), mRemLength(0), mError (false) {
+ mLog = Log::getInstance();
+
+ mBlkData = new char[MAXLEN];
+
+}
+
+cResponseBase::~cResponseBase() {
+ delete[] mBlkData;
+}
+
+bool cResponseBase::isHeadRequest() {
+ if (mRequest->mMethod.compare("HEAD") == 0) {
+ *(mLog->log())<< DEBUGPREFIX << " HEAD Request" << endl;
+ mError= true;
+ sendHeaders(200, "OK", NULL, NULL, -1, -1);
+ return true;
+ }
+ return false;
+}
+
+void cResponseBase::sendError(int status, const char *title, const char *extra, const char *text) {
+ char f[400];
+
+ mError = true;
+ string hdr = "";
+ sendHeaders(status, title, extra, "text/plain", -1, -1);
+
+ snprintf(f, sizeof(f), "%s\r\n", text);
+ hdr += f;
+
+ // strcpy(&(mRequest->mBlkData[mRequest->mBlkLen]), hdr.c_str());
+ strcpy(&mBlkData[mBlkLen], hdr.c_str());
+ mBlkLen += hdr.size();
+}
+
+void cResponseBase::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);
+ hdr += f;
+ }
+ if (date != -1) {
+ strftime(timebuf, sizeof(timebuf), RFC1123FMT, gmtime(&date));
+ snprintf(f, sizeof(f), "Last-Modified: %s\r\n", timebuf);
+ hdr += f;
+ }
+ 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 cResponseBase::fillDataBlk() {
+
+ if (mError) {
+ mRequest->mConnState = TOCLOSE;
+ return ERROR;
+ }
+}
+
+bool cResponseBase::isBlkWritten() {
+ return ((mBlkLen == mBlkPos) ? true : false);
+}
+
+int cResponseBase::writeData(int fd) {
+ int this_write = write(fd, &mBlkData[mBlkPos], mBlkLen - mBlkPos);
+ mBlkPos += this_write;
+ return this_write;
+}
+
+
+cResponseOk::cResponseOk (cHttpResource* req, int status, const char *title, const char *extra, const char *mime, long long int length, time_t date) : cResponseBase(req) {
+
+ if (isHeadRequest())
+ return;
+ sendHeaders(status, title, extra, mime, length, date);
+}
+
+cResponseOk::~cResponseOk() {
+}
+
+
+cResponseError::cResponseError(cHttpResource* req, int status, const char *title, const char *extra, const char *text) : cResponseBase(req) {
+
+ sendError(status, title, extra, text);
+}
+
+cResponseError::~cResponseError() {
+}
diff --git a/responsebase.h b/responsebase.h
new file mode 100644
index 0000000..18ea439
--- /dev/null
+++ b/responsebase.h
@@ -0,0 +1,77 @@
+/*
+ * responsebase.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 __HTTPRESPONSEBASE_H__
+#define __HTTPRESPONSEBASE_H__
+
+#include <ctime>
+#include <stdint.h>
+
+class cHttpResource;
+class Log;
+
+class cResponseBase {
+ public:
+ cResponseBase(cHttpResource* req);
+ virtual ~cResponseBase();
+
+ virtual int fillDataBlk();
+
+ bool isBlkWritten();
+ int writeData(int fd);
+
+ char* mBlkData;
+ int mBlkPos;
+ int mBlkLen;
+
+ protected:
+ bool isHeadRequest();
+
+ void sendError(int status, const char *title, const char *extra, const char *text);
+ void sendHeaders(int status, const char *title, const char *extra, const char *mime,
+ long long int length, time_t date);
+
+ Log* mLog;
+ cHttpResource* mRequest;
+ uint64_t mRemLength;
+ bool mError;
+};
+
+
+class cResponseOk : public cResponseBase {
+ public:
+ cResponseOk(cHttpResource*, int status, const char *title, const char *extra, const char *mime,
+ long long int length, time_t date );
+ virtual ~cResponseOk();
+
+ // int fillDataBlk();
+};
+
+class cResponseError : public cResponseBase {
+ public:
+ cResponseError(cHttpResource* req, int status, const char *title, const char *extra, const char *text );
+ virtual ~cResponseError();
+
+ // int fillDataBlk();
+};
+
+#endif
diff --git a/responsefile.c b/responsefile.c
new file mode 100644
index 0000000..9724957
--- /dev/null
+++ b/responsefile.c
@@ -0,0 +1,174 @@
+/*
+ * responsefile.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 "responsefile.h"
+#include "httpresource.h"
+
+#include <vector>
+#include <sys/stat.h>
+
+//#define MAXLEN 4096
+#define DEBUGPREFIX "mReqId= " << mRequest->mReqId << " fd= " << mRequest->mFd
+#define OKAY 0
+#define ERROR (-1)
+#define DEBUG
+
+
+cResponseFile::cResponseFile(cHttpResource* req) : cResponseBase(req), mFile(NULL), mFileSize(0) {
+}
+
+cResponseFile::~cResponseFile() {
+ if (mFile != NULL) {
+ *(mLog->log())<< DEBUGPREFIX
+ << " ERROR: mFile still open. Closing now..." << endl;
+ fclose(mFile);
+ mFile = NULL;
+ }
+}
+
+
+const char *cResponseFile::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;
+}
+
+
+int cResponseFile::sendFile() {
+ // Send the First Datachunk, incl all headers
+
+ if (isHeadRequest())
+ return OKAY;
+
+ struct stat64 statbuf;
+
+ if (stat64((mRequest->mPath).c_str(), &statbuf) < 0) {
+ sendError(404, "Not Found", NULL, "File not found.");
+ return OKAY;
+ }
+ *(mLog->log())<< DEBUGPREFIX
+ << " SendFile mPath= " << mRequest->mPath
+ << endl;
+
+ char f[400];
+
+ if (openFile((mRequest->mPath).c_str()) == ERROR) {
+ sendError(403, "Forbidden", NULL, "Access denied.");
+ return OKAY;
+ }
+
+ mFile = fopen((mRequest->mPath).c_str(), "r");
+
+ if (!mFile) {
+ sendError(403, "Forbidden", NULL, "Access denied.");
+ return OKAY;
+ }
+
+ mFileSize = S_ISREG(statbuf.st_mode) ? statbuf.st_size : -1;
+
+ *(mLog->log())<< "fd= " << mRequest->mFd << " mReqId= "<< mRequest->mReqId
+ << " mFileSize= " <<mFileSize << endl;
+
+ if (!(mRequest->rangeHdr).isRangeRequest) {
+ mRemLength = mFileSize;
+ sendHeaders(200, "OK", NULL, getMimeType((mRequest->mPath).c_str()), mFileSize, statbuf.st_mtime);
+ }
+ else { // Range request
+ fseeko64(mFile, (mRequest->rangeHdr).begin, SEEK_SET);
+ if ((mRequest->rangeHdr).end == 0)
+ (mRequest->rangeHdr).end = mFileSize;
+ mRemLength = ((mRequest->rangeHdr).end-(mRequest->rangeHdr).begin);
+ *(mLog->log())<< "fd= " << mRequest->mFd << " mReqId= "<< mRequest->mReqId
+ << " rangeHdr.begin= " <<(mRequest->rangeHdr).begin << " rangeHdr.end= " << (mRequest->rangeHdr).end
+ << " Content-Length= " << mRemLength << endl;
+
+ snprintf(f, sizeof(f), "Content-Range: bytes %lld-%lld/%lld", (mRequest->rangeHdr).begin, ((mRequest->rangeHdr).end -1), mFileSize);
+ sendHeaders(206, "Partial Content", f, getMimeType((mRequest->mPath).c_str()), ((mRequest->rangeHdr).end-(mRequest->rangeHdr).begin), statbuf.st_mtime);
+ }
+
+#ifndef DEBUG
+ *(mLog->log())<< "fd= " << mRequest->mFd << " mReqId= "<< mRequest->mReqId
+ << ": Done mRemLength= "<< mRemLength
+ << endl;
+#endif
+ mRequest->mConnState = SERVING;
+
+ return OKAY;
+
+}
+
+
+int cResponseFile::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;
+}
+
+
+int cResponseFile::fillDataBlk() {
+ // char pathbuf[4096];
+ mBlkPos = 0;
+ int to_read = 0;
+
+ if (mError)
+ return ERROR;
+
+ 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;
+ }
+ return OKAY;
+}
+
diff --git a/responsefile.h b/responsefile.h
new file mode 100644
index 0000000..b4a1b36
--- /dev/null
+++ b/responsefile.h
@@ -0,0 +1,51 @@
+/*
+ * responsefile.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 __RESPONSE_FILE_H__
+#define __RESPONSE_FILE_H__
+
+#include <cstdio>
+#include "responsebase.h"
+
+class cHttpResource;
+
+// create the complete response for the file request
+// info on range request should be provided.
+class cResponseFile : public cResponseBase {
+ public:
+ cResponseFile(cHttpResource* );
+ virtual ~cResponseFile(); // same as sendFile
+
+ int fillDataBlk();
+
+ int sendFile();
+
+ private:
+ const char *getMimeType(const char *name) ;
+
+ int openFile(const char *name);
+
+ FILE *mFile;
+ unsigned long long mFileSize;
+};
+
+#endif
diff --git a/responsememblk.c b/responsememblk.c
new file mode 100644
index 0000000..a695340
--- /dev/null
+++ b/responsememblk.c
@@ -0,0 +1,1689 @@
+/*
+ * responsememblk.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 "responsememblk.h"
+#include "httpresource.h"
+#include "url.h"
+#include "smarttvfactory.h"
+
+#include <sstream>
+
+#ifndef STANDALONE
+#include <vdr/recording.h>
+#include <vdr/channels.h>
+#include <vdr/timers.h>
+#include <vdr/videodir.h>
+#include <vdr/epg.h>
+
+#else
+//standalone
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <cstdlib>
+//#include <stdio.h>
+//#include <sys/stat.h>
+#include <dirent.h>
+
+#endif
+
+
+//#define MAXLEN 32768
+#define DEBUGPREFIX "mReqId= " << mRequest->mReqId << " fd= " << mRequest->mFd
+#define OKAY 0
+#define ERROR (-1)
+#define DEBUG
+
+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 sTimerEntry {
+ string name;
+ time_t startTime;
+ int duration;
+sTimerEntry(string t, time_t s, int d) : name(t), startTime(s), duration(d) {};
+};
+
+
+cResponseMemBlk::cResponseMemBlk(cHttpResource* req) : cResponseBase(req), mResponseMessage(NULL), mResponseMessagePos(0) {
+
+ gettimeofday(&mResponseStart,0);
+
+}
+
+cResponseMemBlk::~cResponseMemBlk() {
+
+ timeval now;
+ gettimeofday(&now, 0);
+
+ // long diff; // in ms
+ // diff = (now.tv_sec - mResponseStart.tv_sec) *1000;
+ // diff += (now.tv_usec - mResponseStart.tv_usec) /1000;
+
+ long long diff; // in us
+ diff = (now.tv_sec - mResponseStart.tv_sec) *1000000;
+ diff += (now.tv_usec - mResponseStart.tv_usec) ;
+
+ *(mLog->log())<< DEBUGPREFIX
+ << " cResponseMemBlk: Response duration= " << diff/1000.0 << " ms"
+ << endl;
+
+}
+
+int cResponseMemBlk::receiveYtUrl() {
+ vector<sQueryAVP> avps;
+ mRequest->parseQueryLine(&avps);
+ string line;
+ string store_str;
+ bool store = true;
+
+ if (isHeadRequest())
+ return OKAY;
+
+ if (mRequest->getQueryAttributeValue(&avps, "store", store_str) == OKAY){
+ if (store_str.compare("false")==0) {
+ store = false;
+ *(mLog->log()) << DEBUGPREFIX
+ << " receiveYtUrl: set store to false "
+ << endl;
+
+ }
+ }
+ if (mRequest->getQueryAttributeValue(&avps, "line", line) == OKAY){
+ if (line.compare ("") == 0) {
+ *(mLog->log())<< DEBUGPREFIX
+ << " receiveYtUrl: Nothing to push "
+ << endl;
+
+ sendHeaders(200, "OK", NULL, NULL, 0, -1);
+ return OKAY;
+ }
+
+ mRequest->mFactory->pushYtVideoId(line, store);
+
+ if (store)
+ mRequest->mFactory->storeYtVideoId(line);
+
+ sendHeaders(200, "OK", NULL, NULL, 0, -1);
+ return OKAY;
+
+ }
+
+ sendError(400, "Bad Request", NULL, "Mandatory Line attribute not present.");
+ return OKAY;
+
+}
+
+void cResponseMemBlk::receiveClientInfo() {
+ vector<sQueryAVP> avps;
+ mRequest->parseQueryLine(&avps);
+
+ string mac = "";
+ string ip = "";
+ string state = "";
+
+ if (isHeadRequest())
+ return;
+
+ mRequest->getQueryAttributeValue(&avps, "mac", mac) ;
+ mRequest->getQueryAttributeValue(&avps, "ip", ip);
+ mRequest->getQueryAttributeValue(&avps, "state", state);
+
+ // state: started, running, stopped
+ *(mLog->log())<< DEBUGPREFIX
+ << " receiveClientInfo mac= " << mac << " ip= " << ip << " state= " << state
+ << endl;
+ if (mac.compare ("") == 0) {
+ *(mLog->log())<< DEBUGPREFIX
+ << " mac is empty. Ignoring"
+ << endl;
+ sendHeaders(200, "OK", NULL, NULL, 0, -1);
+ return;
+ }
+ if (state.compare("stopped") == 0) {
+ mRequest->mFactory->removeTvClient(ip, mac, time(NULL));
+ }
+ else {
+ mRequest->mFactory->updateTvClient(ip, mac, time(NULL));
+ }
+ sendHeaders(200, "OK", NULL, NULL, 0, -1);
+ *(mLog->log())<< DEBUGPREFIX
+ << " receiveClientInfo -done "
+ << endl;
+
+}
+
+
+
+int cResponseMemBlk::parseResume(cResumeEntry &entry, string &id) {
+ bool done = false;
+ size_t cur_pos = 0;
+
+ bool have_filename = false;
+ bool have_resume = false;
+
+ if (isHeadRequest())
+ return OKAY;
+
+ while (!done) {
+ size_t pos = mRequest->mPayload.find('\n', cur_pos);
+ if (pos == string::npos) {
+ done = true;
+ continue;
+ }
+ size_t pos_col = mRequest->mPayload.find(':', cur_pos);
+ string attr= mRequest->mPayload.substr(cur_pos, (pos_col- cur_pos));
+ string val = mRequest->mPayload.substr(pos_col +1, (pos - pos_col-1));
+
+ if (attr == "filename") {
+ have_filename = true;
+ entry.mFilename = cUrlEncode::doUrlSaveDecode(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 >= mRequest->mPayload.size())
+ done= true;
+ }
+ if (have_resume && have_filename )
+ return OKAY;
+ else
+ return ERROR;
+}
+
+int cResponseMemBlk::receiveResume() {
+ string dev_id;
+ cResumeEntry entry;
+
+ if (isHeadRequest())
+ return OKAY;
+
+ if (parseResume(entry, dev_id) == ERROR) {
+ *(mLog->log())<< DEBUGPREFIX
+ << " ERROR parsing resume"
+ << endl;
+ }
+
+ *(mLog->log())<< DEBUGPREFIX
+ << " Resume: id= " << dev_id
+ << " resume= " << entry << endl;
+
+ vector<sQueryAVP> avps;
+ mRequest->parseQueryLine(&avps);
+ string guid;
+ string resume_str;
+
+ if (mRequest->getQueryAttributeValue(&avps, "guid", guid) == OKAY){
+ entry.mFilename = cUrlEncode::doUrlSaveDecode(guid);
+
+ *(mLog->log())<< DEBUGPREFIX
+ << " Found a id Parameter: " << guid
+ << endl;
+ }
+ if (mRequest->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= " <<entry.mResume
+ << " fpr= " << rec->FramesPerSecond()
+ << endl;
+
+ resume.Save(int(entry.mResume * rec->FramesPerSecond() ));
+#endif
+
+ sendHeaders(200, "OK", NULL, NULL, -1, -1);
+ return OKAY;
+}
+
+int cResponseMemBlk::receiveDelRecReq() {
+ if (isHeadRequest())
+ return OKAY;
+
+#ifndef STANDALONE
+
+ vector<sQueryAVP> avps;
+ mRequest->parseQueryLine(&avps);
+ string id = "";
+
+ if (mRequest->getQueryAttributeValue(&avps, "id", id) == ERROR){
+ *(mLog->log())<< DEBUGPREFIX
+ << " ERROR: id not found in query."
+ << endl;
+ sendError(400, "Bad Request", NULL, "no id in query line");
+ return OKAY;
+ }
+ mRequest->mPath = cUrlEncode::doUrlSaveDecode(id);
+
+ cRecording* rec = Recordings.GetByName(mRequest->mPath.c_str());
+ if (rec == NULL) {
+ *(mLog->log())<< DEBUGPREFIX
+ << " ERROR: Recording not found. Deletion failed: mPath= " << mRequest->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= " << mRequest->mPath
+ << endl;
+ sendError(500, "Internal Server Error", NULL, "deletion failed!");
+ return OKAY;
+ }
+
+ *(mLog->log())<< DEBUGPREFIX
+ << " Deleted."
+ << endl;
+ sendHeaders(200, "OK", NULL, NULL, -1, -1);
+ #endif
+ return OKAY;
+}
+
+
+//***************************
+//**** Creade index.html ****
+//***************************
+int cResponseMemBlk::sendDir(struct stat *statbuf) {
+ char pathbuf[4096];
+ char f[400];
+ int len;
+
+ if (isHeadRequest())
+ return OKAY;
+
+ mRequest->mConnState = SERVING;
+
+#ifndef DEBUG
+ *(mLog->log()) << DEBUGPREFIX << " sendDir: mPath= " << mRequest->mPath << endl;
+#endif
+ len = mRequest->mPath.length();
+ // int ret = OKAY;
+
+ if (len == 0 || mRequest->mPath[len - 1] != '/') {
+ snprintf(pathbuf, sizeof(pathbuf), "Location: %s/", mRequest->mPath.c_str());
+ sendError(302, "Found", pathbuf, "Directories must end with a slash.");
+ return OKAY;
+ }
+
+ /* thlo: TODO
+ snprintf(pathbuf, sizeof(pathbuf), "%sindex.html", mRequest->mPath.c_str());
+ if (stat(pathbuf, statbuf) >= 0) {
+ mRequest->mPath = pathbuf;
+ // mFileSize = statbuf->st_size;
+ mRemLength = statbuf->st_size;
+ mRequest->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), "<HTML><HEAD><TITLE>Index of %s</TITLE></HEAD>\r\n<BODY>", mRequest->mPath.c_str());
+ hdr += f;
+ snprintf(f, sizeof(f), "<H4>Index of %s</H4>\r\n<PRE>\n", mRequest->mPath.c_str());
+ hdr += f;
+ snprintf(f, sizeof(f), "Name Last Modified Size\r\n");
+ hdr += f;
+ snprintf(f, sizeof(f), "<HR>\r\n");
+ hdr += f;
+
+ *mResponseMessage += hdr;
+ hdr = "";
+
+ if (len > 1) {
+ snprintf(f, sizeof(f), "<A HREF=\"..\">..</A>\r\n");
+ hdr += f;
+ }
+ *mResponseMessage += hdr;
+
+ dir = opendir(mRequest->mPath.c_str());
+ while ((de = readdir(dir)) != NULL) {
+ char timebuf[32];
+ struct tm *tm;
+ strcpy(pathbuf, mRequest->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), "<A HREF=\"%s%s\">", de->d_name, S_ISDIR(statbuf->st_mode) ? "/" : "");
+ hdr += f;
+
+ snprintf(f, sizeof(f), "%s%s", de->d_name, S_ISDIR(statbuf->st_mode) ? "/</A>" : "</A> ");
+ 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), "</PRE>\r\n<HR>\r\n<ADDRESS>%s</ADDRESS>\r\n</BODY></HTML>\r\n", SERVER);
+ *mResponseMessage += f;
+
+ sendHeaders(200, "OK", NULL, "text/html", mResponseMessage->size(), statbuf->st_mtime);
+
+ mRemLength = 0;
+ return OKAY;
+}
+
+
+
+
+// -------------------------------------------
+// Manifest
+// -------------------------------------------
+
+
+int cResponseMemBlk::sendManifest (struct stat *statbuf, bool is_hls) {
+ if (isHeadRequest())
+ return OKAY;
+
+#ifndef STANDALONE
+
+ size_t pos = mRequest->mPath.find_last_of ("/");
+
+ mRequest->mDir = mRequest->mPath.substr(0, pos);
+ string mpd_name = mRequest->mPath.substr(pos+1);
+
+ float seg_dur = mRequest->mFactory->getConfig()->getSegmentDuration() *1.0;
+
+ cRecordings* recordings = &Recordings;
+ cRecording* rec = recordings->GetByName(mRequest->mDir.c_str());
+ double duration = rec->NumFrames() / rec->FramesPerSecond();
+
+ int bitrate = (int)((getVdrFileSize() *8.0 * mRequest->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 mRequest->mDir= " << mRequest->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 cResponseMemBlk::writeM3U8(double duration, int bitrate, float seg_dur, int end_seg) {
+ mResponseMessage = new string();
+ mResponseMessagePos = 0;
+ *mResponseMessage = "";
+
+ // mRequest->mContentType = MEMBLOCK;
+
+ mRequest->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 cResponseMemBlk::writeMPD(double duration, int bitrate, float seg_dur, int end_seg) {
+ mResponseMessage = new string();
+ mResponseMessagePos = 0;
+ *mResponseMessage = "";
+
+ // mRequest->mContentType = MEMBLOCK;
+
+ mRequest->mConnState = SERVING;
+ char buf[30];
+ char line[400];
+
+ string hdr = "";
+
+ *mResponseMessage += "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
+
+ snprintf(line, sizeof(line), "<MPD type=\"OnDemand\" minBufferTime=\"PT%dS\" mediaPresentationDuration=\"PT%.1fS\"",
+ mRequest->mFactory->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 += "<ProgramInformation>\n";
+ *mResponseMessage += "<ChapterDataURL/>\n";
+ *mResponseMessage += "</ProgramInformation>\n";
+ *mResponseMessage += "<Period start=\"PT0S\" segmentAlignmentFlag=\"True\">\n";
+ // SD: 720x 576
+ // HD: 1280x 720
+ // snprintf(line, sizeof(line), "<Representation id=\"0\" mimeType=\"video/mpeg\" bandwidth=\"%d\" startWithRAP=\"True\" width=\"1280\" height=\"720\" group=\"0\">\n", mRequest->mFactory->getConfig()->getHasBitrate());
+ snprintf(line, sizeof(line), "<Representation id=\"0\" mimeType=\"video/mpeg\" bandwidth=\"%d\" startWithRAP=\"True\" %s group=\"0\">\n",
+ bitrate, ((bitrate < 10000000)? "width=\"720\" height=\"576\"" : "width=\"1280\" height=\"720\""));
+ *mResponseMessage = *mResponseMessage + line;
+
+ hdr = "<SegmentInfo duration=";
+ snprintf(buf, sizeof(buf), "\"PT%.1fS\"", (seg_dur*1.0));
+ hdr = hdr + buf + " >\n";
+ *mResponseMessage += hdr;
+
+ snprintf(buf, sizeof(buf), "\"%d\"", end_seg);
+ *mResponseMessage += "<UrlTemplate sourceURL=\"$Index$-seg.ts\" startIndex=\"1\" endIndex=";
+ hdr = buf ;
+ *mResponseMessage += hdr + " />\n";
+
+ *mResponseMessage += "</SegmentInfo>\n";
+ *mResponseMessage += "</Representation>\n";
+ *mResponseMessage += "</Period>\n";
+ *mResponseMessage += "</MPD>";
+
+ sendHeaders(200, "OK", NULL, "application/x-mpegURL", mResponseMessage->size(), -1);
+}
+
+
+uint64_t cResponseMemBlk::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(), mRequest->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;
+}
+
+// - Manifest End
+
+
+
+// --------------------
+// GET Resources
+// --------------------
+
+
+// common for all create xml file modules
+int cResponseMemBlk::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 += "<item>\n";
+ // snprintf(f, sizeof(f), "%s - %s", );
+ hdr += "<title>" + name +"</title>\n";
+ hdr += "<link>" +link + "</link>\n";
+ hdr += "<enclosure url=\"" +link + "\" type=\"video/mpeg\" />\n";
+
+ hdr += "<guid>" + guid + "</guid>\n";
+
+ snprintf(f, sizeof(f), "%d", no);
+ hdr += "<number>";
+ hdr += f;
+ hdr += "</number>\n";
+
+ hdr += "<programme>" + programme +"</programme>\n";
+ hdr += "<description>" + desc + "</description>\n";
+
+ snprintf(f, sizeof(f), "%ld", start);
+ hdr += "<start>";
+ hdr += f;
+ hdr += "</start>\n";
+
+ snprintf(f, sizeof(f), "%d", dur);
+ hdr += "<duration>";
+ hdr += f;
+ hdr += "</duration>\n";
+
+ if (fps != -1)
+ snprintf(f, sizeof(f), "<fps>%.2f</fps>\n", fps);
+ else
+ snprintf(f, sizeof(f), "<fps>unknown</fps>\n");
+ hdr += f;
+
+ switch (is_pes){
+ case -1:
+ // unknown
+ hdr += "<ispes>unknown</ispes>\n";
+ break;
+ case 0:
+ // true
+ hdr += "<ispes>true</ispes>\n";
+ break;
+ case 1:
+ // false
+ hdr += "<ispes>false</ispes>\n";
+ break;
+ default:
+ break;
+ }
+
+ switch (is_new){
+ case -1:
+ // unknown
+ hdr += "<isnew>unknown</isnew>\n";
+ break;
+ case 0:
+ // true
+ hdr += "<isnew>true</isnew>\n";
+ break;
+ case 1:
+ // false
+ hdr += "<isnew>false</isnew>\n";
+ break;
+ default:
+ break;
+ }
+
+ hdr += "</item>\n";
+
+ *mResponseMessage += hdr;
+
+
+ // return writeToClient(hdr.c_str(), hdr.size());
+ return OKAY;
+}
+
+
+
+
+
+int cResponseMemBlk::sendUrlsXml () {
+ // read urls file and generate XML
+ string type;
+ string value;
+ string line;
+ if (isHeadRequest())
+ return OKAY;
+
+ mResponseMessage = new string();
+ mResponseMessagePos = 0;
+ *mResponseMessage = "";
+ // mRequest->mContentType = MEMBLOCK;
+
+ mRequest->mConnState = SERVING;
+
+ cManageUrls* urls = mRequest->mFactory->getUrlsObj();
+
+ // ifstream myfile ((mRequest->mFactory->getConfigDir() +"/urls.txt").c_str());
+ // An empty xml is provided, if the file does not exist.
+
+ //thlo: here to continue
+ *mResponseMessage = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+ *mResponseMessage += "<rss version=\"2.0\">\n";
+ *mResponseMessage += "<channel>\n";
+
+ for (uint i = 0; i < urls->size(); i++) {
+ *mResponseMessage += "<item>\n<guid>";
+ *mResponseMessage += (urls->getEntry(i))->mEntry;
+ *mResponseMessage += "</guid>\n</item>\n";
+ }
+
+ /*
+ while ( myfile.good() ) {
+ getline (myfile, line);
+
+ if ((line == "") or (line[0] == '#'))
+ continue;
+
+ size_t pos = line.find('|');
+ type = line.substr(0, pos);
+ value = line.substr(pos+1);
+
+ if (type.compare("YT")==0) {
+ *mResponseMessage += "<item>\n<guid>";
+ *mResponseMessage += value;
+ *mResponseMessage += "</guid>\n</item>\n";
+ continue;
+ }
+
+ *mResponseMessage += "<item>\n<title>";
+ *mResponseMessage += "Unknown: " + line + " type=" + type;
+
+ *mResponseMessage += "</title>\n</item>\n";
+ }
+ myfile.close();
+*/
+ *mResponseMessage += "</channel>\n";
+ *mResponseMessage += "</rss>\n";
+
+ sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), -1);
+ return OKAY;
+}
+
+
+
+
+// mediaXML
+int cResponseMemBlk::parseFiles(vector<sFileEntry> *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 cResponseMemBlk::sendMediaXml (struct stat *statbuf) {
+ char pathbuf[4096];
+ string link;
+ string media_folder = mRequest->mFactory->getConfig()->getMediaFolder();
+
+ if (isHeadRequest())
+ return OKAY;
+
+
+ mResponseMessage = new string();
+ mResponseMessagePos = 0;
+
+ *mResponseMessage = "";
+
+ mRequest->mConnState = SERVING;
+
+#ifndef DEBUG
+ *(mLog->log()) << DEBUGPREFIX << " sendMedia " << endl;
+#endif
+
+ string own_ip = mRequest->getOwnIp();
+ *(mLog->log()) << " OwnIP= " << own_ip << endl;
+
+ vector<sFileEntry> entries;
+
+ if (parseFiles(&entries, "", media_folder, "", statbuf) == ERROR) {
+ sendError(404, "Not Found", NULL, "Media Folder likely not configured.");
+ return OKAY;
+ }
+
+ string hdr = "";
+ hdr += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+ hdr += "<rss version=\"2.0\">\n";
+ hdr+= "<channel>\n";
+
+ *mResponseMessage += hdr;
+
+ hdr = "";
+
+ for (uint i=0; i < entries.size(); i++) {
+
+ snprintf(pathbuf, sizeof(pathbuf), "http://%s:%d%s", own_ip.c_str(), mRequest->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 = "</channel>\n";
+ hdr += "</rss>\n";
+ *mResponseMessage += hdr;
+ sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), statbuf->st_mtime);
+
+ return OKAY;
+}
+
+
+
+
+
+int cResponseMemBlk::sendVdrStatusXml (struct stat *statbuf) {
+ if (isHeadRequest())
+ return OKAY;
+
+#ifndef STANDALONE
+
+ char f[400];
+ mResponseMessage = new string();
+ *mResponseMessage = "";
+
+ mResponseMessagePos = 0;
+
+ mRequest->mConnState = SERVING;
+
+ int free;
+ int used;
+ int percent;
+
+ percent = VideoDiskSpace(&free, &used);
+
+ *mResponseMessage += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+ *mResponseMessage += "<vdrstatus>\n";
+
+ *mResponseMessage += "<diskspace>\n";
+ snprintf(f, sizeof(f), "<free>%d</free>", free);
+ *mResponseMessage += f;
+
+ snprintf(f, sizeof(f), "<used>%d</used>", used);
+ *mResponseMessage += f;
+ snprintf(f, sizeof(f), "<percent>%d</percent>", percent);
+ *mResponseMessage += f;
+ *mResponseMessage += "</diskspace>\n";
+
+ *mResponseMessage += "</vdrstatus>\n";
+
+ sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), -1);
+#else
+ sendHeaders(200, "OK", NULL, NULL, 0, -1);
+#endif
+ return OKAY;
+}
+
+int cResponseMemBlk::sendYtBookmarkletJs() {
+ *(mLog->log()) << DEBUGPREFIX
+ << " sendYtBookmarkletJs" << endl;
+
+ vector<sQueryAVP> avps;
+ mRequest->parseQueryLine(&avps);
+ string store_str = "";
+ bool store= true;
+
+ if (isHeadRequest())
+ return OKAY;
+
+ if (mRequest->getQueryAttributeValue(&avps, "store", store_str) == OKAY) {
+ if (store_str.compare("false") == 0) {
+ store= false;
+ *(mLog->log()) << DEBUGPREFIX
+ << " store= false " << endl;
+ }
+ }
+
+ mResponseMessage = new string();
+ *mResponseMessage = "";
+ mResponseMessagePos = 0;
+ // mRequest->mContentType = MEMBLOCK;
+
+ mRequest->mConnState = SERVING;
+
+ stringstream own_host ;
+ own_host << "http://"
+ << mRequest->getOwnIp()
+ << ":" << mRequest->mServerPort;
+
+ // string own_host = "http://"+mRequest->getOwnIp()+ ":" + str;
+
+ *(mLog->log()) << " Ownhost= " << own_host.str() << endl;
+
+
+ *mResponseMessage = "function get_query_var (querystring, name) { "
+ "var filter = new RegExp( name + \"=([^&]+)\" ); "
+ "var res = null;"
+ "if (querystring != null)"
+ " res = querystring.match(filter);"
+ " if (res != null) return unescape( res[1] );"
+ " else return \"\";"
+ "}"
+ "var vid_id= get_query_var(document.URL, \"v\");"
+
+ "var iframe = document.createElement(\"iframe\");"
+ "iframe.setAttribute(\"name\",\"myiframe\");"
+ "iframe.setAttribute(\"frameborder\",\"0\");"
+ "iframe.setAttribute(\"scrolling\",\"no\");"
+ "iframe.setAttribute(\"src\",\"about:blank\");"
+ "iframe.setAttribute(\"width\",\"1\");"
+ "iframe.setAttribute(\"height\",\"1\");"
+ "document.body.appendChild(iframe);"
+
+ "var form = document.createElement(\"form\");"
+ "form.setAttribute(\"method\", \"POST\");"
+ "form.setAttribute(\"target\", \"myiframe\");"
+ "form.setAttribute(\"action\", \"" + own_host.str() + "/setYtUrl?line=\"+vid_id"+((!store)?"+\"&store=false\"":"")+");"
+ "var hiddenField = document.createElement(\"input\");"
+ "form.appendChild(hiddenField);"
+ "hiddenField.setAttribute(\"type\", \"hidden\");"
+ "hiddenField.setAttribute(\"name\", \"line\");"
+ "hiddenField.setAttribute(\"value\", vid_id);"
+ "document.body.appendChild(form);"
+ "form.submit();"
+ ;
+
+ sendHeaders(200, "OK", NULL, "text/javascript", mResponseMessage->size(), -1);
+ return OKAY;
+}
+
+int cResponseMemBlk::sendBmlInstHtml() {
+ *(mLog->log()) << DEBUGPREFIX
+ << " sendBmlInstHtml" << endl;
+
+ if (isHeadRequest())
+ return OKAY;
+
+ mResponseMessage = new string();
+ *mResponseMessage = "";
+ mResponseMessagePos = 0;
+ // mRequest->mContentType = MEMBLOCK;
+
+ mRequest->mConnState = SERVING;
+
+ stringstream own_host ;
+ own_host << "http://"
+ << mRequest->getOwnIp()
+ << ":" << mRequest->mServerPort;
+
+ *(mLog->log()) << " Ownhost= " << own_host << endl;
+
+ *mResponseMessage = "<html><head>"
+ "<title>SmartTVWeb Bookmarklets</title>"
+ "</head><body>"
+ "<br>"
+ "<h2>Bookmarklet for collecting YouTube Pages</h2>"
+ "<hr width=\"80%\">"
+ "<br>"
+ "<h3>Installation</h3>"
+ "Drag the link below to your Bookmarks toolbar"
+ "<p>or</p>"
+ "<p>Right click and select &#8220;Bookmark This Link&#8221;</p>"
+ "<p><a href='javascript:document.body.appendChild(document.createElement(&quot;script&quot;)).src=&quot;"+own_host.str()+"/yt-bookmarklet.js&quot;;void(0)'>YT SaveNPlay</a>: Save the video and also Play it.</p>"
+ "<p><a href='javascript:document.body.appendChild(document.createElement(&quot;script&quot;)).src=&quot;"+own_host.str()+"/yt-bookmarklet.js?store=false&quot;;void(0)'>YT Play</a>: Play the video without saving.</p>"
+ "<br>"
+ "<hr width=\"80%\">"
+ "<h3>Usage</h3>"
+ "<p>Browse to your favorite YouTube page and click the bookmark to the bookmarklet (link above). The YouTube video is then provided to the VDR smarttvweb plugin, stored there and pushed to the TV screen for immediate playback. Tested with Firefox.</p>"
+ "<br>"
+ "<hr width=\"80%\">"
+ "<p>Have fun...<br></p>"
+ "</body>";
+
+ sendHeaders(200, "OK", NULL, "text/html", mResponseMessage->size(), -1);
+ return OKAY;
+}
+
+
+
+int cResponseMemBlk::sendChannelsXml (struct stat *statbuf) {
+ if (isHeadRequest())
+ return OKAY;
+
+#ifndef STANDALONE
+
+ char f[400];
+ mResponseMessage = new string();
+ *mResponseMessage = "";
+ mResponseMessagePos = 0;
+ // mRequest->mContentType = MEMBLOCK;
+
+ mRequest->mConnState = SERVING;
+
+#ifndef DEBUG
+ *(mLog->log())<< DEBUGPREFIX
+ << " generating /channels.xml"
+ << endl;
+#endif
+ string own_ip = mRequest->getOwnIp();
+ *(mLog->log()) << " OwnIP= " << own_ip << endl;
+
+ vector<sQueryAVP> avps;
+ mRequest->parseQueryLine(&avps);
+ string mode = "";
+ bool add_desc = true;
+
+ string no_channels_str = "";
+ int no_channels = -1;
+ string group_sep = "";
+
+ if (mRequest->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 (mRequest->getQueryAttributeValue(&avps, "channels", no_channels_str) == OKAY){
+ no_channels = atoi(no_channels_str.c_str()) ;
+ }
+
+
+ string hdr = "";
+ hdr += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+ hdr += "<rss version=\"2.0\">\n";
+ hdr+= "<channel>\n";
+ hdr+= "<title>VDR Channels List</title>\n";
+
+
+ *mResponseMessage += hdr;
+
+ int count = mRequest->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 (mRequest->mFactory->getConfig()->getGroupSep() != IGNORE) {
+ // if emtpyFolderDown, always.
+ // otherwise, only when not empty
+ if (!((strcmp(channel->Name(), "") == 0) && (mRequest->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 = "</channel>\n";
+ hdr += "</rss>\n";
+
+ *mResponseMessage += hdr;
+ delete lock;
+ sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), statbuf->st_mtime);
+
+#endif
+ return OKAY;
+}
+
+int cResponseMemBlk::sendEpgXml (struct stat *statbuf) {
+ if (isHeadRequest())
+ return OKAY;
+
+#ifndef STANDALONE
+
+ char f[400];
+ mResponseMessage = new string();
+ *mResponseMessage = "";
+ mResponseMessagePos = 0;
+ // mRequest->mContentType = MEMBLOCK;
+
+ mRequest->mConnState = SERVING;
+
+#ifndef DEBUG
+ *(mLog->log())<< DEBUGPREFIX
+ << " generating /epg.xml"
+ << endl;
+#endif
+ vector<sQueryAVP> avps;
+ mRequest->parseQueryLine(&avps);
+ string id = "S19.2E-1-1107-17500";
+ string mode = "";
+ bool add_desc = true;
+
+ if (mRequest->getQueryAttributeValue(&avps, "id", id) == ERROR){
+ *(mLog->log())<< DEBUGPREFIX
+ << " ERROR: id not found"
+ << endl;
+ delete mResponseMessage;
+ mResponseMessage = NULL;
+ sendError(400, "Bad Request", NULL, "no id in query line");
+ return OKAY;
+ }
+
+ if (mRequest->getQueryAttributeValue(&avps, "mode", mode) == OKAY){
+ if (mode == "nodesc") {
+ add_desc = false;
+ *(mLog->log())<< DEBUGPREFIX
+ << " **** Mode: No Description ****"
+ << 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"
+ << endl;
+ delete mResponseMessage;
+ mResponseMessage = NULL;
+ 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;
+ mResponseMessage = NULL;
+ 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;
+ mResponseMessage = NULL;
+ sendError(500, "Internal Server Error", NULL, "Event is zero.");
+ return OKAY;
+ }
+
+ string hdr = "";
+ hdr += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+ hdr += "<tv version=\"2.0\">\n";
+ hdr+= "<programme>\n";
+
+ *mResponseMessage += hdr;
+ // Payload here
+
+ hdr = "";
+ if (ev->Title() != NULL) {
+ string title = cUrlEncode::doXmlSaveEncode(ev->Title());
+ hdr += "<title>" + title +"</title>\n";
+ }
+ else {
+ *(mLog->log())<< DEBUGPREFIX
+ << " ERROR: title is zero for guid= " << id << endl;
+ hdr += "<title>Empty</title>\n";
+
+ delete mResponseMessage;
+ mResponseMessage = NULL;
+ sendError(500, "Internal Server Error", NULL, "Title is zero.");
+ return OKAY;
+ }
+
+ hdr += "<guid>" + id + "</guid>\n";
+
+ *(mLog->log())<< DEBUGPREFIX
+ << " guid= " << id
+ << " title= " << ev->Title()
+ << " start= " << ev->StartTime()
+ << " end= " << ev->EndTime()
+ << " now= " << now
+ << endl;
+ if (add_desc) {
+ hdr += "<desc>";
+ if (ev->Description() != NULL) {
+ hdr += cUrlEncode::doXmlSaveEncode(ev->Description());
+ }
+ else {
+ *(mLog->log())<< DEBUGPREFIX
+ << " ERROR: description is zero for guid= " << id << endl;
+
+ delete mResponseMessage;
+ mResponseMessage = NULL;
+ sendError(500, "Internal Server Error", NULL, "Description is zero.");
+ return OKAY;
+ }
+ hdr += "</desc>\n";
+ }
+ else {
+ hdr += "<desc>No Description Available</desc>\n";
+ }
+ snprintf(f, sizeof(f), "<start>%ld</start>\n", ev->StartTime());
+ hdr += f ;
+
+ snprintf(f, sizeof(f), "<end>%ld</end>\n", ev->EndTime());
+ hdr += f;
+
+ snprintf(f, sizeof(f), "<duration>%d</duration>\n", ev->Duration());
+ hdr += f;
+ *mResponseMessage += hdr;
+
+ hdr = "</programme>\n";
+ hdr += "</tv>\n";
+
+ *mResponseMessage += hdr;
+
+ delete lock;
+
+ sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), -1);
+
+#endif
+ return OKAY;
+}
+
+
+int cResponseMemBlk::sendResumeXml () {
+ if (isHeadRequest())
+ return OKAY;
+#ifndef STANDALONE
+
+ mResponseMessage = new string();
+ *mResponseMessage = "";
+ mResponseMessagePos = 0;
+ // mRequest->mContentType = MEMBLOCK;
+
+ mRequest->mConnState = SERVING;
+
+ char f[400];
+
+ cResumeEntry entry;
+ string id;
+
+ parseResume(entry, id);
+
+ vector<sQueryAVP> avps;
+ mRequest->parseQueryLine(&avps);
+ string guid;
+
+ if (mRequest->getQueryAttributeValue(&avps, "guid", guid) == OKAY){
+ // entry.mFilename = guid;
+ entry.mFilename = cUrlEncode::doUrlSaveDecode(guid);
+
+ *(mLog->log() )<< DEBUGPREFIX
+ << " Found guid: " << guid
+ << " filename: " << entry.mFilename
+ << 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 += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+ *mResponseMessage += "<resume>";
+ snprintf(f, sizeof(f), "%.02f", resume.Read() *1.0 / rec->FramesPerSecond());
+ *mResponseMessage += f;
+ *mResponseMessage += "</resume>\n";
+
+
+ sendHeaders(200, "OK", NULL, "application/xml", mResponseMessage->size(), -1);
+#endif
+
+ return OKAY;
+}
+
+int cResponseMemBlk::sendRecordingsXml(struct stat *statbuf) {
+ if (isHeadRequest())
+ return OKAY;
+#ifndef STANDALONE
+
+ mResponseMessage = new string();
+ *mResponseMessage = "";
+ mResponseMessagePos = 0;
+ // mRequest->mContentType = MEMBLOCK;
+
+ mRequest->mConnState = SERVING;
+
+ string own_ip = mRequest->getOwnIp();
+ *(mLog->log()) << " OwnIP= " << own_ip << endl;
+
+ vector<sQueryAVP> avps;
+ mRequest->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 (mRequest->getQueryAttributeValue(&avps, "model", model) == OKAY){
+ *(mLog->log())<< DEBUGPREFIX
+ << " Found a Model Parameter: " << model
+ << endl;
+ }
+
+ if (mRequest->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 (mRequest->getQueryAttributeValue(&avps, "mode", mode) == OKAY){
+ if (mode == "nodesc") {
+ add_desc = false;
+ *(mLog->log())<< DEBUGPREFIX
+ << " Mode: No Description"
+ << endl;
+ }
+ }
+
+ if (mRequest->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"
+ << endl;
+#endif
+ sendHeaders(200, "OK", NULL, "application/xml", -1, statbuf->st_mtime);
+
+ string hdr = "";
+ hdr += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+ hdr += "<rss version=\"2.0\">\n";
+ hdr+= "<channel>\n";
+ hdr+= "<title>VDR Recordings List</title>\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()
+ << endl;
+#endif
+
+ // List of recording timer
+ time_t now = time(NULL);
+
+ vector<sTimerEntry> act_rec;
+ /*#ifndef DEBUG*/
+ *(mLog->log())<< DEBUGPREFIX
+ << " checking avtive timer"
+ << endl;
+ /*#endif*/
+ for (cTimer * ti = Timers.First(); ti; ti = Timers.Next(ti)){
+ ti->Matches();
+
+ if (ti->HasFlags(tfRecording) ) {
+ *(mLog->log()) << DEBUGPREFIX
+ << " Active Timer: " << ti->File()
+ << " Start= " << ti->StartTime()
+ << " Duration= " << (ti->StopTime() - ti->StartTime())
+ << endl;
+ 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(), mRequest->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(), mRequest->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 = "</channel>\n";
+ hdr += "</rss>\n";
+
+ *mResponseMessage += hdr;
+
+
+#endif
+ return OKAY;
+}
+
+
+int cResponseMemBlk::fillDataBlk() {
+ mBlkPos = 0;
+
+ if (mError) {
+ *(mLog->log())<< DEBUGPREFIX << " mError == true -> Done" << endl;
+ mRequest->mConnState = TOCLOSE;
+ return ERROR;
+ }
+
+ if (mResponseMessage == NULL) {
+ *(mLog->log())<< DEBUGPREFIX << " mResponseMessage == NULL -> Done" << endl;
+ mRequest->mConnState = TOCLOSE;
+ return ERROR;
+ }
+
+ int rem_len = mResponseMessage->size() - mResponseMessagePos;
+ if (rem_len == 0) {
+
+#ifndef DEBUG
+ *(mLog->log())<< DEBUGPREFIX << " fillDataBlock: MEMBLOCK done" << endl;
+#endif
+ delete mResponseMessage;
+ mResponseMessage = NULL;
+ mResponseMessagePos = 0;
+ mRequest->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);
+
+ return OKAY;
+}
diff --git a/responsememblk.h b/responsememblk.h
new file mode 100644
index 0000000..84491e2
--- /dev/null
+++ b/responsememblk.h
@@ -0,0 +1,92 @@
+/*
+ * responsememblk.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 __RESPONSE_MEMBLK_H__
+#define __RESPONSE_MEMBLK_H__
+
+#include <cstdio>
+#include <sys/stat.h>
+#include <string>
+#include <vector>
+#include <sys/time.h>
+#include "responsebase.h"
+
+using namespace std;
+
+struct sFileEntry {
+ string sName;
+ string sPath;
+ int sStart;
+
+sFileEntry(string n, string l, int s) : sName(n), sPath(l), sStart(s) {
+ };
+};
+
+
+class cResumeEntry;
+class cHttpResource;
+
+// create the complete response for the file request
+// info on range request should be provided.
+class cResponseMemBlk : public cResponseBase {
+ public:
+ cResponseMemBlk(cHttpResource* );
+ virtual ~cResponseMemBlk(); // same as sendFile
+
+ int fillDataBlk();
+
+ int sendRecordingsXml (struct stat *statbuf);
+ int sendChannelsXml (struct stat *statbuf);
+ int sendResumeXml ();
+ int sendVdrStatusXml (struct stat *statbuf);
+ int sendYtBookmarkletJs();
+ int sendBmlInstHtml();
+
+ int sendEpgXml (struct stat *statbuf);
+ int sendUrlsXml ();
+ int sendMediaXml (struct stat *statbuf);
+ int sendManifest (struct stat *statbuf, bool is_hls = true);
+
+ void receiveClientInfo();
+
+ int receiveResume();
+ int receiveDelRecReq();
+
+ int receiveYtUrl();
+
+ 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 parseResume(cResumeEntry &entry, string &id);
+ int parseFiles(vector<sFileEntry> *entries, string prefix, string dir_base, string dir_name, struct stat *statbuf);
+ int sendDir(struct stat *statbuf);
+ 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);
+ uint64_t getVdrFileSize();
+
+ private:
+ string *mResponseMessage;
+ int mResponseMessagePos;
+
+ timeval mResponseStart;
+};
+
+#endif
diff --git a/responsevdrdir.c b/responsevdrdir.c
new file mode 100644
index 0000000..50949d5
--- /dev/null
+++ b/responsevdrdir.c
@@ -0,0 +1,781 @@
+/*
+ * responsevdrdir.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 "responsevdrdir.h"
+#include "httpresource.h"
+#include "smarttvfactory.h"
+#include <vector>
+#include <cstdlib>
+
+#ifndef STANDALONE
+#include <vdr/recording.h>
+//#include <vdr/channels.h>
+//#include <vdr/timers.h>
+//#include <vdr/videodir.h>
+//#include <vdr/epg.h>
+#else
+#include <sys/stat.h>
+
+#endif
+
+//#define MAXLEN 4096
+#define DEBUGPREFIX "mReqId= " << mRequest->mReqId << " fd= " << mRequest->mFd
+#define OKAY 0
+#define ERROR (-1)
+#define DEBUG
+
+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) {};
+};
+
+
+
+// 8 Byte Per Entry
+struct tIndexPes {
+ uint32_t offset;
+ uint8_t type; // standalone
+ uint8_t number; // standalone
+ 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
+ };
+
+
+cResponseVdrDir::cResponseVdrDir(cHttpResource* req) : cResponseBase(req), mIsRecording(false),
+ mStreamToEnd(false), mRecProgress(0.0), mVdrIdx(0), mFile(NULL), mFileStructure() {
+}
+
+cResponseVdrDir::~cResponseVdrDir() {
+ if (mFile != NULL) {
+ *(mLog->log())<< DEBUGPREFIX
+ << " ERROR: mFile still open. Closing now..." << endl;
+ fclose(mFile);
+ mFile = NULL;
+ }
+}
+
+
+bool cResponseVdrDir::isTimeRequest(struct stat *statbuf) {
+
+#ifndef STANDALONE
+ vector<sQueryAVP> avps;
+ mRequest->parseQueryLine(&avps);
+ string time_str = "";
+ string mode = "";
+ float time = 0.0;
+
+ if (mRequest->getQueryAttributeValue(&avps, "time", time_str) != OKAY){
+ return false;
+ }
+
+ if (mRequest->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;
+
+ mRequest->mDir = mRequest->mPath;
+ cRecording *rec = Recordings.GetByName(mRequest->mPath.c_str());
+ if (rec == NULL) {
+ *(mLog->log())<< DEBUGPREFIX
+ << " Error: Did not find recording= " << mRequest->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(((mRequest->mDir) +"/index.vdr").c_str(), "r");
+ // sendError(400, "Bad Request", NULL, "PES not yet supported.");
+ // return true;
+ }
+ else {
+ idx_file = fopen(((mRequest->mDir) +"/index").c_str(), "r");
+ }
+
+ if (idx_file == NULL){
+ *(mLog->log()) << DEBUGPREFIX
+ << " failed to open idx file = "<< (mRequest->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())<<DEBUGPREFIX
+ << " issue while reading, buffered_indexes <=0" << endl;
+ delete[] index_buf;
+ sendError(404, "Not Found", NULL, "Failed to read Index file");
+ return OKAY;
+ }
+
+ *(mLog->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(), (mRequest->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(), (mRequest->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 ((mRequest->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;
+#else
+ return false;
+#endif
+}
+
+// Needed by createRecordings.xml and byCreateManifest
+void cResponseVdrDir::checkRecording() {
+ // sets mIsRecording to true when the recording is still on-going
+ mIsRecording = false;
+
+#ifndef STANDALONE
+ time_t now = time(NULL);
+
+ // cRecordings* recordings = mFactory->getRecordings();
+ cRecordings* recordings = &Recordings;
+#ifndef DEBUG
+ *(mLog->log())<< DEBUGPREFIX
+ << " GetByName(" <<(mRequest->mPath).c_str() << ")"
+ << endl;
+#endif
+ cRecording* rec = recordings->GetByName((mRequest->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 cResponseVdrDir::sendVdrDir(struct stat *statbuf) {
+
+#ifndef DEBUG
+ *(mLog->log())<< DEBUGPREFIX << " *** sendVdrDir mPath= " << mRequest->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 = mRequest->mPath;
+ vector<sVdrFileEntry> file_sizes;
+ bool more_to_go = true;
+
+ if (isHeadRequest())
+ return OKAY;
+
+ checkRecording();
+
+ mVdrIdx = 1;
+ mFileStructure = "%s/%03d.vdr";
+
+ snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), (mRequest->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(), (mRequest->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"
+ << 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 (!(mRequest->rangeHdr).isRangeRequest) {
+ snprintf(pathbuf, sizeof(pathbuf), mFileStructure.c_str(), (mRequest->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 && ((mRequest->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 ((mRequest->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(), (mRequest->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 "
+ << 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= " << (mRequest->rangeHdr).begin
+ << " vdr_no= " << file_sizes[cur_idx].sIdx << endl;
+ *(mLog->log())<< "---------------" << endl;
+ sendError(403, "Forbidden", NULL, "Access denied.");
+ return OKAY;
+ }
+ mRequest->mDir = mRequest->mPath;
+ mRequest->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, ((mRequest->rangeHdr).begin - file_sizes[cur_idx].sFirstOffset), SEEK_SET);
+ if ((mRequest->rangeHdr).end == 0)
+ (mRequest->rangeHdr).end = ((mIsRecording) ? (mRecProgress * total_file_size): total_file_size);
+
+ mRemLength = ((mRequest->rangeHdr).end-(mRequest->rangeHdr).begin);
+
+ snprintf(f, sizeof(f), "Content-Range: bytes %lld-%lld/%lld", (mRequest->rangeHdr).begin, ((mRequest->rangeHdr).end -1),
+ ((mIsRecording) ? (long long int)(mRecProgress * total_file_size): total_file_size));
+
+ sendHeaders(206, "Partial Content", f, "video/mpeg", ((mRequest->rangeHdr).end-(mRequest->rangeHdr).begin), statbuf->st_mtime);
+ }
+
+#ifndef DEBUG
+ *(mLog->log())<< " ***** Yes, vdr dir found ***** mPath= " << mRequest->mPath << endl;
+#endif
+
+ return OKAY; // handleRead() done
+}
+
+
+
+
+//-----------------------------------------------------------------
+// ----- Send Segment -----
+//-----------------------------------------------------------------
+
+int cResponseVdrDir::sendMediaSegment (struct stat *statbuf) {
+ if (isHeadRequest())
+ return OKAY;
+
+#ifndef STANDALONE
+
+ *(mLog->log()) << DEBUGPREFIX << " sendMediaSegment " << mRequest->mPath << endl;
+ size_t pos = (mRequest->mPath).find_last_of ("/");
+
+ mRequest->mDir = (mRequest->mPath).substr(0, pos);
+ string seg_name = (mRequest->mPath).substr(pos+1);
+ int seg_number;
+
+ int seg_dur = mRequest->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((mRequest->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= " << mRequest->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((mRequest->mDir +"/index").c_str(), "r");
+ if (idx_file == NULL){
+ *(mLog->log()) << DEBUGPREFIX
+ << " failed to open idx file = "<< (mRequest->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())<<DEBUGPREFIX
+ << " issue while reading" << endl;
+ delete[] index_buf;
+ sendError(404, "Not Found", NULL, "Failed to read Index file");
+ return OKAY;
+ }
+
+ // Reading the segment
+ mFileStructure = "%s/%05d.ts";
+ int start_offset = -1;
+ int start_idx = -1;
+
+ tIndexTs in_read_ts;
+ memcpy (&in_read_ts, &(index_buf[0]), 8);
+
+ start_offset = in_read_ts.offset;
+ start_idx = in_read_ts.number;
+
+ char seg_fn[200];
+ mVdrIdx = start_idx;
+
+ snprintf(seg_fn, sizeof(seg_fn), mFileStructure.c_str(), (mRequest->mDir).c_str(), mVdrIdx);
+ (mRequest->mPath) = seg_fn;
+
+ /*
+ * Now we determine the end of the segment
+ */
+ memcpy (&in_read_ts, &(index_buf[(frames_per_seg)*8]), 8);
+
+ int end_idx = in_read_ts.number;
+ int end_offset = in_read_ts.offset;
+
+ /*#ifndef DEBUG*/
+ *(mLog->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= " <<mRemLength
+ << endl;
+#endif
+ }
+ else {
+#ifndef DEBUG
+ *(mLog->log()) << DEBUGPREFIX
+ << " start_idx < end_idx "
+ << endl;
+#endif
+ snprintf(seg_fn, sizeof(seg_fn), mFileStructure.c_str(), (mRequest->mDir).c_str(), mVdrIdx);
+ if (stat(seg_fn, statbuf) < 0) {
+ *(mLog->log()) << DEBUGPREFIX
+ << " file= " <<seg_fn << " does not exist"
+ << endl;
+ error= true;
+ // issue:
+ }
+ rem_len = statbuf->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(), (mRequest->mDir).c_str(), idx);
+ if (stat(seg_fn, statbuf) < 0) {
+ *(mLog->log()) << DEBUGPREFIX
+ << " for loop file= " <<seg_fn << " does not exist"
+ << endl;
+ error = true;
+ break;
+ // issue:
+ }
+ rem_len += statbuf->st_size; // remaining length of the first segment
+ }
+ rem_len += end_offset; //
+ mRemLength = rem_len;
+ snprintf(seg_fn, sizeof(seg_fn), mFileStructure.c_str(), (mRequest->mDir).c_str(), mVdrIdx);
+
+#ifndef DEBUG
+ *(mLog->log()) << DEBUGPREFIX
+ << " start_idx= " << start_idx << " != end_idx= "<< end_idx <<": mRemLength= " <<mRemLength
+ << endl;
+#endif
+ }
+
+ if (error){
+ sendError(404, "Not Found", NULL, "Not all inputs exists");
+ return OKAY;
+ }
+
+ // mContentType = VDRDIR;
+
+ if (openFile(seg_fn) != OKAY) {
+ *(mLog->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 cResponseVdrDir::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;
+}
+
+
+int cResponseVdrDir::fillDataBlk() {
+ char pathbuf[4096];
+
+ if (mError)
+ return ERROR;
+
+ mBlkPos = 0;
+ int to_read = 0;
+
+
+ // 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(), (mRequest->mDir).c_str(), mVdrIdx);
+ mRequest->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 ;
+ }
+ }
+ return OKAY;
+}
diff --git a/responsevdrdir.h b/responsevdrdir.h
new file mode 100644
index 0000000..9fba86f
--- /dev/null
+++ b/responsevdrdir.h
@@ -0,0 +1,64 @@
+/*
+ * responsevdrdir.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 __HTTPRESPONSE_VDRDIR_H__
+#define __HTTPRESPONSE_VDRDIR_H__
+
+#include <cstdio>
+#include <string>
+#include <cstring>
+#include "responsebase.h"
+
+using namespace std;
+
+//class cHttpResource;
+
+class cResponseVdrDir : public cResponseBase{
+ public:
+ cResponseVdrDir(cHttpResource*);
+ virtual ~cResponseVdrDir();
+
+ int fillDataBlk();
+
+ int sendVdrDir(struct stat *statbuf);
+ int sendMediaSegment (struct stat *statbuf);
+ protected:
+
+ // cHttpResource* mRequest;
+
+ //range
+ bool mIsRecording;
+ bool mStreamToEnd;
+ float mRecProgress;
+
+ int mVdrIdx;
+ FILE* mFile;
+ string mFileStructure;
+
+ bool isTimeRequest(struct stat *statbuf);
+ void checkRecording() ;
+
+ int openFile(const char *name);
+
+};
+
+#endif