diff options
author | horchi <vdr@jwendel.de> | 2017-03-05 16:39:28 +0100 |
---|---|---|
committer | horchi <vdr@jwendel.de> | 2017-03-05 16:39:28 +0100 |
commit | e2a48d8701f91b8e24fbe9e99e91eb72a87bb749 (patch) | |
tree | 726f70554b4ca985a09ef6e30a7fdc8df089993c /httpd.c | |
download | vdr-epg-daemon-e2a48d8701f91b8e24fbe9e99e91eb72a87bb749.tar.gz vdr-epg-daemon-e2a48d8701f91b8e24fbe9e99e91eb72a87bb749.tar.bz2 |
git init1.1.103
Diffstat (limited to 'httpd.c')
-rw-r--r-- | httpd.c | 2287 |
1 files changed, 2287 insertions, 0 deletions
@@ -0,0 +1,2287 @@ +/* + * httpd.c + * + * See the README file for copyright information and how to reach the author. + * + */ + +#include "lib/python.h" // at first du to symbol conflict with older python headers + +#include <sys/signal.h> +#include <sys/select.h> + +#include <vector> + +#include "lib/common.h" +#include "lib/epgservice.h" +#include "lib/curl.h" +#include "lib/wol.h" + +#include "svdrpclient.h" +#include "httpd.h" + +const char* logPrefix = LOG_PREFIX; +class Python; + +const char* realm = "Maintenance"; + +//*************************************************************************** +// EPG Http Daemon +//*************************************************************************** + +int cEpgHttpd::shutdown = no; +cEpgHttpd* cEpgHttpd::singleton = 0; +const char* confDir = (char*)confDirDefault; + +//*************************************************************************** +// Rights Management +//*************************************************************************** + +cEpgHttpd::UserRight cEpgHttpd::userrights[] = +{ + { "/data/parameters", umAll|umNologin }, + { "/data/channels", umAll|umNologin }, + { "/data/vdrs", umAll|umNologin }, + { "/data/login", umAll|umNologin }, + + { "/data/channellogo", umAll|umNologin }, + { "/data/eventimg", umAll|umNologin }, + { "/data/moviemedia", umAll|umNologin }, + { "/data/seriesmedia", umAll|umNologin }, + { "/data/genres", umAll|umNologin }, + { "/data/categories", umAll|umNologin }, + + { "/data/events", umAll }, + { "/data/event", umAll }, + { "/data/timers", umAll }, + { "/data/recordings", umAll }, + { "/data/recording", umAll }, + { "/data/recordingdirs", umAll }, + { "/data/pendingtimerjobs", umAll }, + { "/data/deltimerjob", umTimerEdit }, + { "/data/donetimers", umAll }, + { "/data/donetimer", umAll }, + { "/data/searchtimers", umAll }, + { "/data/updatesearchtimer", umAll }, + { "/data/updaterecordings", umAll }, + { "/data/replayrecording", umRecordings }, + { "/data/channelswitch", umAll }, + { "/data/hitkey", umAll }, + { "/data/log", umAll|umNologin }, + { "/data/debug", umAll }, + { "/data/proxy", umAll }, + { "/data/wakeupvdr", umAll }, + { "/data/messages", umAll }, + + // needed for store of needLogin :o ! + + { "/data/save-parameters", umAll }, + + // data request + + { "/data/users", umConfig }, + + // data store + + { "/data/sendmail", umAll }, + { "/data/save-channels", umConfigEdit }, + { "/data/delete-timerjobs", umTimerEdit }, + { "/data/store-donetimers", umTimerEdit }, + { "/data/save-timer", umTimerEdit }, + { "/data/save-searchtimer", umSearchTimerEdit }, + { "/data/search", umSearchTimer }, + { "/data/save-users", umConfigEdit }, + { "/data/renamerecording", umRecordingsEdit }, + { "/data/deleterecording", umRecordingsEdit }, + { "/data/markmessages", umAll }, + + { 0, umNone } +}; + +UserMask cEpgHttpd::toRightMask(const char* url) +{ + for (int i = 0; userrights[i].url; i++) + { + if (strcmp(userrights[i].url, url) == 0) + return (UserMask)userrights[i].mask; + } + + return umNone; +} + +//*************************************************************************** +// Object +//*************************************************************************** + +cEpgHttpd::cEpgHttpd() +{ + daemon = 0; + connection = 0; + useeventsDb = 0; + mapDb = 0; + imageDb = 0; + imageRefDb = 0; + vdrDb = 0; + timerDb = 0; + timersDoneDb = 0; + searchtimerDb = 0; + movieDb = 0; + movieActorDb = 0; + movieActorsDb = 0; + movieMediaDb = 0; + seriesDb = 0; + seriesEpisodeDb = 0; + seriesActorsDb = 0; + seriesMediaDb = 0; + recordingDirDb = 0; + recordingListDb = 0; + userDb = 0; + messageDb = 0; + + currentSession = 0; + loginWithSession = no; + lastSdWatchdogAt = time(0); + + selectEventsAt = 0; + selectEventsNext = 0; + selectEventsStartInRange = 0; + selectGenres = 0; + selectCategories = 0; + selectEvent = 0; + selectEventByTime = 0; + selectAllMap = 0; + selectPendingTimerActions = 0; + selectDoneTimers = 0; + selectMapById = 0; + selectVdrs = 0; + selectUsers = 0; + updateMap = 0; + updateTimerAName = 0; + updateDoneAName = 0; + selectAllTimer = 0; + selectTimerById = 0; + selectTimerByEventId = 0; + selectAllSearchTimer = 0; + selectMovie = 0; + selectMovieActors = 0; + selectMovieMedia = 0; + selectSerie = 0; + selectSeriesEpisode = 0; + selectSeriesMedia = 0; + selectActiveVdrs = 0; + selectRecordingDirs = 0; + selectRecordingByPath = 0; + selectChannelFromMap = 0; + selectUserByMd5 = 0; + selectAllRecordings = 0; + selectRecordingForEventByLv = 0; + selectPendingMessages = 0; + selectWebUsers = 0; + + withutf8 = no; + + search = new cSearchTimer(this); + ptyRecName = new Python("recording", "name"); + + setlocale(LC_CTYPE, ""); + const char* lang = setlocale(LC_CTYPE, 0); + + if (lang) + { + tell(0, "Set locale to '%s'", lang); + + if ((strcasestr(lang, "UTF-8") != 0) || (strcasestr(lang, "UTF8") != 0)) + { + tell(0, "detected UTF-8"); + withutf8 = yes; + } + } + else + { + tell(0, "Warning: Detecting locale setting for LC_CTYPE failed"); + } + + tzset(); // init timezone environment +} + +cEpgHttpd::~cEpgHttpd() +{ + cSystemNotification::notify(evStopping); + + delete search; + delete ptyRecName; +} + +//*************************************************************************** +// Init / Exit +//*************************************************************************** + +int cEpgHttpd::init() +{ + char* dictPath = 0; +#if MHD_VERSION >= 0x00095102 + int mhdFlags = MHD_USE_EPOLL_INTERNALLY; +#else + int mhdFlags = MHD_USE_SELECT_INTERNALLY; +#endif + singleton = this; + + if (readConfig() != success) + return fail; + + tell(0, "Log level is set to (%d)", EpgdConfig.loglevel); + + if (search->init(confDir) != success) + return fail; + + if (ptyRecName->init(confDir) != success) + return fail; + + // initialize the dictionary + + asprintf(&dictPath, "%s/epg.dat", confDir); + + if (dbDict.in(dictPath) != success) + { + tell(0, "Fatal: Dictionary not loaded, aborting!"); + return 1; + } + + tell(0, "Dictionary '%s' loaded", dictPath); + free(dictPath); + + // init database ... + + cDbConnection::init(); + cDbConnection::setEncoding(withutf8 ? "utf8": "latin1"); // mysql use latin1 for ISO8851-1 + cDbConnection::setHost(EpgdConfig.dbHost); + cDbConnection::setPort(EpgdConfig.dbPort); + cDbConnection::setName(EpgdConfig.dbName); + cDbConnection::setUser(EpgdConfig.dbUser); + cDbConnection::setPass(EpgdConfig.dbPass); + cDbConnection::setConfPath(confDir); + + initDb(); + + if (EpgdConfig.httpUseTls) + { + int status; + char* path = 0; + +#if MHD_VERSION >= 0x00095102 + mhdFlags |= MHD_USE_TLS; +#else + mhdFlags |= MHD_USE_SSL; +#endif + asprintf(&path, "%s/server.key", confDir); + status = loadFromFile(path, &keyPem); + free(path); + keyPem.append("\0", 1); + asprintf(&path, "%s/server.pem", confDir); + status += loadFromFile(path, &certPem); + free(path); + certPem.append("\0", 1); + + if (status != success) + { + tell(0, "The key/certificate files could not be loaded"); + return fail; + } + + tell(0, "Loading key and certificate files succeeded"); + } + + // bind to net device - if configured + + if (!isEmpty(EpgdConfig.httpDevice)) + { + struct sockaddr_in localSockAddr; + const char* bindIp = getIpOf(EpgdConfig.httpDevice); + long localAddr; + + memset((char*)&localSockAddr, 0, sizeof(localSockAddr)); + localSockAddr.sin_family = AF_INET; + + if ((localAddr = inet_addr(bindIp)) == INADDR_NONE) + return fail; + + // set local endpoint + + memcpy(&localSockAddr.sin_addr, &localAddr, sizeof(struct in_addr)); + localSockAddr.sin_port = htons(EpgdConfig.httpPort); + + tell(0, "Binding listener to '%s' at '%s'", bindIp, EpgdConfig.httpDevice); + + // establish listener + + tell(0, "Starting http server ..."); + + if (!EpgdConfig.httpUseTls) + { + daemon = MHD_start_daemon(mhdFlags, + EpgdConfig.httpPort, + 0, 0, // accept policy callback + &dispatcher, 0, // access handler callback + MHD_OPTION_SOCK_ADDR, (struct sockaddr*)&localSockAddr, + MHD_OPTION_END); + } + else + { + daemon = MHD_start_daemon(mhdFlags, + EpgdConfig.httpPort, + 0, 0, // accept policy callback + &dispatcher, 0, // access handler callback + MHD_OPTION_SOCK_ADDR, (struct sockaddr*)&localSockAddr, + MHD_OPTION_HTTPS_MEM_KEY, keyPem.memory, + MHD_OPTION_HTTPS_MEM_CERT, certPem.memory, + MHD_OPTION_END); + } + } + else + { + // establish listener + + tell(0, "Starting http server ..."); + + if (!EpgdConfig.httpUseTls) + { + daemon = MHD_start_daemon(mhdFlags, + EpgdConfig.httpPort, + 0, 0, // accept policy callback + &dispatcher, 0, // access handler callback + MHD_OPTION_END); + } + else + { + daemon = MHD_start_daemon(mhdFlags, + EpgdConfig.httpPort, + 0, 0, // accept policy callback + &dispatcher, 0, // access handler callback + MHD_OPTION_HTTPS_MEM_KEY, keyPem.memory, + MHD_OPTION_HTTPS_MEM_CERT, certPem.memory, + MHD_OPTION_END); + } + } + + if (!daemon) + { + tell(0, "Error: Start of http server failed"); + return fail; + } + + tell(0, "Listener at port %d established, waiting for connections", EpgdConfig.httpPort); + + cSystemNotification::notify(evReady); + cSystemNotification::getWatchdogState(10); + + return success; +} + +int cEpgHttpd::exit() +{ + singleton = 0; + + if (daemon) + MHD_stop_daemon(daemon); + + exitDb(); + + cDbConnection::exit(); + + return done; +} + +//*************************************************************************** +// Init DB +//*************************************************************************** + +cDbFieldDef endTimeDef("ENDTIME", "cnt_starttime+cnt_duration", cDBS::ffInt, 10, cDBS::ftData); +cDbFieldDef startTimeDef("STARTTIME", "cnt_starttime", cDBS::ffInt, 10, cDBS::ftData); +cDbFieldDef mergeDef("MERGE", "merge", cDBS::ffAscii, 50, cDBS::ftData); +cDbFieldDef imageidDef("IMAGEID", "imageid", cDBS::ffUBigInt, 0, cDBS::ftData); +cDbFieldDef matchDensityTitleDef("MATCHDENSITYTITLE", "matchdensitytitle", cDBS::ffInt, 0, cDBS::ftData); +cDbFieldDef matchDensityShorttextDef("MATCHDENSITYSHORTTEXT", "matchdensityshorttext", cDBS::ffInt, 0, cDBS::ftData); +//cDbFieldDef timerStateDef("STATE", "state", cDBS::ffAscii, 100, cDBS::ftData); +//cDbFieldDef timerActionDef("ACTION", "action", cDBS::ffAscii, 100, cDBS::ftData); + +//*************************************************************************** + +int cEpgHttpd::initDb() +{ + int status = success; + + // db connection + + tell(0, "Connecting to database at '%s:%d'", cDbConnection::getHost(), cDbConnection::getPort()); + + if (!connection) + connection = new cDbConnection(); + + // open tables + + vdrDb = new cDbTable(connection, "vdrs"); + if (vdrDb->open() != success) return fail; + + useeventsDb = new cDbTable(connection, "useevents"); + if (useeventsDb->open() != success) return fail; + + mapDb = new cDbTable(connection, "channelmap"); + if (mapDb->open() != success) return fail; + + imageDb = new cDbTable(connection, "images"); + if (imageDb->open() != success) return fail; + + imageRefDb = new cDbTable(connection, "imagerefs"); + if (imageRefDb->open() != success) return fail; + + timerDb = new cDbTable(connection, "timers"); + if (timerDb->open() != success) return fail; + + timersDoneDb = new cDbTable(connection, "timersdone"); + if (timersDoneDb->open(yes) != success) return fail; + + searchtimerDb = new cDbTable(connection, "searchtimers"); + if (searchtimerDb->open() != success) return fail; + + movieDb = new cDbTable(connection, "movie"); + if (movieDb->open() != success) return fail; + + movieActorDb = new cDbTable(connection, "movie_actor"); + if (movieActorDb->open() != success) return fail; + + movieActorsDb = new cDbTable(connection, "movie_actors"); + if (movieActorsDb->open() != success) return fail; + + movieMediaDb = new cDbTable(connection, "movie_media"); + if (movieMediaDb->open() != success) return fail; + + seriesDb = new cDbTable(connection, "series"); + if (seriesDb->open() != success) return fail; + + seriesEpisodeDb = new cDbTable(connection, "series_episode"); + if (seriesEpisodeDb->open() != success) return fail; + + seriesMediaDb = new cDbTable(connection, "series_media"); + if (seriesMediaDb->open() != success) return fail; + + seriesActorsDb = new cDbTable(connection, "series_actor"); + if (seriesActorsDb->open() != success) return fail; + + recordingDirDb = new cDbTable(connection, "recordingdirs"); + if (recordingDirDb->open() != success) return fail; + + recordingListDb = new cDbTable(connection, "recordinglist"); + if ((status = recordingListDb->open()) != success) return status; + + userDb = new cDbTable(connection, "users"); + if ((status = userDb->open()) != success) return status; + + messageDb = new cDbTable(connection, "messages"); + if (messageDb->open() != success) return fail; + + if ((status = cParameters::initDb(connection)) != success) + return status; + + // ---------- + // select * + // from vdrs + + selectVdrs = new cDbStatement(vdrDb); + + selectVdrs->build("select "); + selectVdrs->bindAllOut(); + selectVdrs->build(" from %s", vdrDb->TableName()); + + status += selectVdrs->prepare(); + + // ---------- + // select ip, svdrp from vdrs + // where state = 'attached' + + selectActiveVdrs = new cDbStatement(vdrDb); + + selectActiveVdrs->build("select "); + selectActiveVdrs->bind("IP", cDBS::bndOut); + selectActiveVdrs->bind("SVDRP", cDBS::bndOut, ", "); + selectActiveVdrs->bind("SHAREINWEB", cDBS::bndOut, ", "); + selectActiveVdrs->build(" from %s where state = 'attached' and svdrp > 0", vdrDb->TableName()); + + status += selectActiveVdrs->prepare(); + + // ---------- + // select * + // from users + + selectUsers = new cDbStatement(userDb); + + selectUsers->build("select "); + selectUsers->bindAllOut(); + selectUsers->build(" from %s", userDb->TableName()); + + status += selectUsers->prepare(); + + // select distinct(genre) + // from events + + selectGenres = new cDbStatement(useeventsDb); + + selectGenres->build("select distinct("); + selectGenres->bind("Genre", cDBS::bndOut); + selectGenres->build(") from %s", useeventsDb->TableName()); + + status += selectGenres->prepare(); + + // select distinct(category) + // from events + + selectCategories = new cDbStatement(useeventsDb); + + selectCategories->build("select distinct("); + selectCategories->bind("Category", cDBS::bndOut); + selectCategories->build(") from %s", useeventsDb->TableName()); + + status += selectCategories->prepare(); + + // -> select events running at specific time + // + // select e.useid, e.channelid, + // e.imagecount, e.title, e.shorttext, e.shortdescription, + // e.starttime, e.duration, e.category, e.genre, e.tipp, e.numrating + // from eventsviewplain e, (select distinct channelid,channelname,ord,visible from channelmap) c + // where + // e.channelid = c.channelid + // and c.visible & 1 + // and e.starttime <= ? + // and e.cnt_starttime+cnt_duration >= ? + // and e.channelid like ? + // and e.updflg in (...) + // order by c.ord, e.cnt_starttime + + selectEventsAt = new cDbStatement(useeventsDb); + + endTime.setField(&endTimeDef); + + selectEventsAt->build("select "); + selectEventsAt->setBindPrefix("e."); + selectEventsAt->bind("UseId", cDBS::bndOut); + selectEventsAt->bind("ChannelId", cDBS::bndOut, ", "); + // selectEventsAt->bind("UpdFlg", cDBS::bndOut, ", "); + selectEventsAt->bind("ImageCount", cDBS::bndOut, ", "); + selectEventsAt->bind("Title", cDBS::bndOut, ", "); + selectEventsAt->bind("ShortText", cDBS::bndOut, ", "); + selectEventsAt->bind("ShortDescription", cDBS::bndOut, ", "); + selectEventsAt->bind("StartTime", cDBS::bndOut, ", "); + selectEventsAt->bind("Duration", cDBS::bndOut, ", "); + selectEventsAt->bind("Category", cDBS::bndOut, ", "); + selectEventsAt->bind("Genre", cDBS::bndOut, ", "); + selectEventsAt->bind("Tipp", cDBS::bndOut, ", "); + selectEventsAt->bind("Numrating", cDBS::bndOut, ", "); + selectEventsAt->build(" from eventsviewplain e, (select distinct channelid,channelname,ord,visible from %s) c where ", mapDb->TableName()); + selectEventsAt->build("e.%s = c.%s and c.visible & 1", + useeventsDb->getField("ChannelId")->getDbName(), + mapDb->getField("ChannelId")->getDbName()); + selectEventsAt->bindCmp(0, "StartTime", 0, "<=", " and "); + selectEventsAt->bindCmp(0, &endTime, ">", " and "); + selectEventsAt->bindCmp(0, "ChannelId", 0, "like", " and "); + selectEventsAt->build(" and e.updflg in (%s) order by c.ord, e.cnt_starttime", cEventState::getVisible()); + + status += selectEventsAt->prepare(); + + // -> select events starting in specified range + // + // select e.useid, e.channelid, + // e.imagecount, e.title, e.shorttext, e.shortdescription, + // e.starttime, e.duration, e.category, e.genre, e.tipp, e.numrating + // from eventsviewplain e, (select distinct channelid, channelname, ord, visible from channelmap) c + // where + // e.channelid = c.channelid + // and c.visible & 1 + // and e.cnt_starttime >= ? + // and e.cnt_starttime <= ? + // and e.channelid like ? + // and e.updflg in (...) + // order by c.ord, e.cnt_starttime + + selectEventsStartInRange = new cDbStatement(useeventsDb); + + startTime.setField(&startTimeDef); + + selectEventsStartInRange->build("select "); + selectEventsStartInRange->setBindPrefix("e."); + selectEventsStartInRange->bind("UseId", cDBS::bndOut); + selectEventsStartInRange->bind("ChannelId", cDBS::bndOut, ", "); + // selectEventsStartInRange->bind("UpdFlg", cDBS::bndOut, ", "); + selectEventsStartInRange->bind("ImageCount", cDBS::bndOut, ", "); + selectEventsStartInRange->bind("Title", cDBS::bndOut, ", "); + selectEventsStartInRange->bind("ShortText", cDBS::bndOut, ", "); + selectEventsStartInRange->bind("ShortDescription", cDBS::bndOut, ", "); + selectEventsStartInRange->bind("StartTime", cDBS::bndOut, ", "); + selectEventsStartInRange->bind("Duration", cDBS::bndOut, ", "); + selectEventsStartInRange->bind("Category", cDBS::bndOut, ", "); + selectEventsStartInRange->bind("Genre", cDBS::bndOut, ", "); + selectEventsStartInRange->bind("Tipp", cDBS::bndOut, ", "); + selectEventsStartInRange->bind("Numrating", cDBS::bndOut, ", "); + selectEventsStartInRange->build(" from eventsviewplain e, (select distinct channelid,channelname,ord,visible from %s) c where ", mapDb->TableName()); + selectEventsStartInRange->build("e.%s = c.%s and c.visible & 1", + useeventsDb->getField("ChannelId")->getDbName(), + mapDb->getField("ChannelId")->getDbName()); + selectEventsStartInRange->bindCmp(0, "StartTime", 0, ">=", " and "); + selectEventsStartInRange->bindCmp(0, &startTime, "<=", " and "); + selectEventsStartInRange->bindCmp(0, "ChannelId", 0, "like", " and "); + selectEventsStartInRange->build(" and e.updflg in (%s) order by c.ord, e.cnt_starttime", cEventState::getVisible()); + + status += selectEventsStartInRange->prepare(); + + // -> select events running after current event + // + // select e.useid, e.channelid, + // e.imagecount, e.title, e.shorttext, e.shortdescription, e.starttime, + // e.duration, e.category, e.genre, e.tipp, e.numrating + // from + // (select + // min(concat(e.starttime,e.channelid,e.source)) PK, + // e.channelid, + // c.ord + // from eventsviewplain e, (select distinct channelid,channelname,ord,visible from channelmap) c + // where + // e.channelid = c.channelid and + // e.updflg in ('A','L','P') and + // c.visible & 1 and + // e.starttime between unix_timestamp() and unix_timestamp() + 127800 + // group by e.channelid, c.ord + // ) s, + // eventsviewplain e + // where + // s.PK = concat(e.starttime, e.channelid, e.source) and + // e.starttime between unix_timestamp() and unix_timestamp() + 127800 + // order by s.ord, e.starttime; + + selectEventsNext = new cDbStatement(useeventsDb); + + selectEventsNext->build("select "); + selectEventsNext->setBindPrefix("e."); + selectEventsNext->bind("UseId", cDBS::bndOut); + selectEventsNext->bind("ChannelId", cDBS::bndOut, ", "); + selectEventsNext->bind("ImageCount", cDBS::bndOut, ", "); + selectEventsNext->bind("Title", cDBS::bndOut, ", "); + selectEventsNext->bind("ShortText", cDBS::bndOut, ", "); + selectEventsNext->bind("ShortDescription", cDBS::bndOut, ", "); + selectEventsNext->bind("StartTime", cDBS::bndOut, ", "); + selectEventsNext->bind("Duration", cDBS::bndOut, ", "); + selectEventsNext->bind("Category", cDBS::bndOut, ", "); + selectEventsNext->bind("Genre", cDBS::bndOut, ", "); + selectEventsNext->bind("Tipp", cDBS::bndOut, ", "); + selectEventsNext->bind("Numrating", cDBS::bndOut, ", "); + selectEventsNext->build(" from (select min(concat(e.%s, e.%s, e.%s)) PK, e.%s, c.%s", + useeventsDb->getField("STARTTIME")->getDbName(), + useeventsDb->getField("CHANNELID")->getDbName(), + useeventsDb->getField("CNTSOURCE")->getDbName(), + useeventsDb->getField("CHANNELID")->getDbName(), + mapDb->getField("ORDER")->getDbName()); + selectEventsNext->build(" from eventsviewplain e, (select distinct channelid,channelname,ord,visible from %s) c where ", mapDb->TableName()); + + selectEventsNext->build("e.%s = c.%s and c.visible & 1 and e.updflg in (%s)", + useeventsDb->getField("ChannelId")->getDbName(), + mapDb->getField("ChannelId")->getDbName(), + cEventState::getVisible()); + selectEventsNext->bindCmp(0, "STARTTIME", 0, ">", " and "); + selectEventsNext->bindCmp(0, "STARTTIME", 0, "< 127800 +", " and "); + selectEventsNext->build(" group by e.%s, c.%s) s,", useeventsDb->getField("CHANNELID")->getDbName(), mapDb->getField("ORDER")->getDbName()); + selectEventsNext->build(" eventsviewplain e where "); + selectEventsNext->build("s.PK = concat(e.%s, e.%s, e.%s)", + useeventsDb->getField("STARTTIME")->getDbName(), + useeventsDb->getField("CHANNELID")->getDbName(), + useeventsDb->getField("CNTSOURCE")->getDbName()); + selectEventsNext->bindCmp(0, "STARTTIME", 0, ">", " and "); + selectEventsNext->bindCmp(0, "STARTTIME", 0, "< 127800 +", " and "); + selectEventsNext->build(" order by s.%s, e.%s", + mapDb->getField("ORDER")->getDbName(), + useeventsDb->getField("STARTTIME")->getDbName()); + + status += selectEventsNext->prepare(); + + // prepare fields for selects + + merge.setField(&mergeDef); + imageid.setField(&imageidDef); + + // select imageid, merge, * + // from eventsviewplain where + // useid = ? + + selectEvent = new cDbStatement(useeventsDb); + + selectEvent->build("select "); + selectEvent->bind(&imageid, cDBS::bndOut); + selectEvent->bind(&merge, cDBS::bndOut, ", "); + selectEvent->bindAllOut(", "); + selectEvent->build(" from eventsviewplain where "); + selectEvent->bind("USEID", cDBS::bndIn | cDBS::bndSet); + + status += selectEvent->prepare(); + + // select imageid, merge, * + // from eventsviewplain where + // channleid = ? + // and cnt_starttime <= ? + // and cnt_starttime+cnt_duration >= ? + + selectEventByTime = new cDbStatement(useeventsDb); + + selectEventByTime->build("select "); + selectEventByTime->bind(&imageid, cDBS::bndOut); + selectEventByTime->bind(&merge, cDBS::bndOut, ", "); + selectEventByTime->bindAllOut(", "); + selectEventByTime->build(" from eventsviewplain where "); + selectEventByTime->bind("CHANNELID", cDBS::bndIn | cDBS::bndSet); + selectEventByTime->bindCmp(0, "STARTTIME", 0, "<=", " and "); + selectEventByTime->bindCmp(0, &endTime, ">=", " and "); + + status += selectEventByTime->prepare(); + + // select * from movie + // where movie_id = ? + + selectMovie = new cDbStatement(movieDb); + + selectMovie->build("select "); + selectMovie->bindAllOut(); + selectMovie->build(" from %s where ", movieDb->TableName()); + selectMovie->bind("MOVIEID", cDBS::bndIn | cDBS::bndSet); + + status += selectMovie->prepare(); + + // select act.actor_id, act.actor_name, actors.actor_role, media.media_url, media.media_type, + // length(media.media_content) + // from movie_actor act, movie_actors actors + // left outer join movie_media media on (actors.actor_id = media.actor_id) + // where act.actor_id = actors.actor_id and actors.movie_id = ? + + // imageSize.setField(&imageSizeDef); + selectMovieActors = new cDbStatement(movieActorDb); + selectMovieActors->build("select "); + selectMovieActors->setBindPrefix("act."); + selectMovieActors->bind("ActorId", cDBS::bndOut); + selectMovieActors->bind("ActorName", cDBS::bndOut, ", "); + selectMovieActors->setBindPrefix("actors."); + selectMovieActors->bind(movieActorsDb, "Role", cDBS::bndOut, ", "); + selectMovieActors->setBindPrefix("media."); + selectMovieActors->bind(movieMediaDb, "MediaUrl", cDBS::bndOut, ", "); + selectMovieActors->bind(movieMediaDb, "MediaType", cDBS::bndOut, ", "); + // selectMovieActors->build(", length("); + // selectMovieActors->bind(&imageSize, cDBS::bndOut); + // selectMovieActors->build(")"); + selectMovieActors->clrBindPrefix(); + selectMovieActors->build(" from %s act, %s actors left outer join %s media on (actors.%s = media.%s) where ", + movieActorDb->TableName(), movieActorsDb->TableName(), movieMediaDb->TableName(), + movieActorsDb->getField("ActorId")->getDbName(), + movieMediaDb->getField("ActorId")->getDbName()); + selectMovieActors->build("act.%s = actors.%s ", + movieActorDb->getField("ActorId")->getDbName(), + movieActorsDb->getField("ActorId")->getDbName()); + selectMovieActors->setBindPrefix("actors."); + selectMovieActors->bind(movieActorsDb, "MovieId", cDBS::bndIn | cDBS::bndSet, " and "); + + status += selectMovieActors->prepare(); + + // select media_type, media.media_url, length(media_content) + // from movie_media + // where + // media.movie_id = ? + + selectMovieMedia = new cDbStatement(movieMediaDb); + + selectMovieMedia->build("select "); + selectMovieMedia->bind("MediaUrl", cDBS::bndOut); + selectMovieMedia->bind("MediaType", cDBS::bndOut, ", "); + // selectMovieMedia->build(", length("); + // selectMovieMedia->bind(&imageSize, cDBS::bndOut); + // selectMovieMedia->build(")"); + selectMovieMedia->build(" from %s where ", movieMediaDb->TableName()); + selectMovieMedia->bind("MovieId", cDBS::bndIn | cDBS::bndSet); + + status += selectMovieMedia->prepare(); + + // select .... from series + // where series_id = ? + + selectSerie = new cDbStatement(seriesDb); + + selectSerie->build("select "); + selectSerie->bindAllOut(); + selectSerie->build(" from %s where ", seriesDb->TableName()); + selectSerie->bind("SeriesId", cDBS::bndIn | cDBS::bndSet); + + status += selectSerie->prepare(); + + // select .... from series_episode + // where episode_id = ? + + selectSeriesEpisode = new cDbStatement(seriesEpisodeDb); + + selectSeriesEpisode->build("select "); + selectSeriesEpisode->bindAllOut(); + selectSeriesEpisode->build(" from %s where ", seriesEpisodeDb->TableName()); + selectSeriesEpisode->bind("EPISODEID", cDBS::bndIn | cDBS::bndSet); + + status += selectSeriesEpisode->prepare(); + + // select + // m.episode_id, m.season_number, m.actor_id, m.media_type, m.media_url, m.media_rating, + // a.actor_name, a.actor_role, a.actor_sortorder + // from series_media m + // left outer join series_actor a on (a.actor_id = m.actor_id) + // where m.series_id = ? + // and (m.episode_id = 0 or m.episode_id = ?) + // and (m.season_number = 0 or m.season_number = ?) + + selectSeriesMedia = new cDbStatement(seriesMediaDb); + + selectSeriesMedia->build("select "); + selectSeriesMedia->setBindPrefix("m."); + selectSeriesMedia->bind("EpisodeId", cDBS::bndOut); + selectSeriesMedia->bind("SeasonNumber", cDBS::bndOut, ", "); + selectSeriesMedia->bind("ActorId", cDBS::bndOut, ", "); + selectSeriesMedia->bind("MediaType", cDBS::bndOut, ", "); + selectSeriesMedia->bind("MediaUrl", cDBS::bndOut, ", "); + selectSeriesMedia->bind("MediaRating", cDBS::bndOut, ", "); + selectSeriesMedia->setBindPrefix("a."); + selectSeriesMedia->bind(seriesActorsDb, "ActorName", cDBS::bndOut, ", "); + selectSeriesMedia->bind(seriesActorsDb, "ActorRole", cDBS::bndOut, ", "); + selectSeriesMedia->bind(seriesActorsDb, "SortOrder", cDBS::bndOut, ", "); + selectSeriesMedia->clrBindPrefix(); + selectSeriesMedia->build(" from %s m left outer join %s a on (m.%s = a.%s) where ", + seriesMediaDb->TableName(), seriesActorsDb->TableName(), + movieActorsDb->getField("ActorId")->getDbName(), + movieMediaDb->getField("ActorId")->getDbName()); + selectSeriesMedia->setBindPrefix("m."); + selectSeriesMedia->bind("SeriesId", cDBS::bndIn | cDBS::bndSet); + selectSeriesMedia->build(" and (m.episode_id = 0 or "); + selectSeriesMedia->bind("EpisodeId", cDBS::bndIn | cDBS::bndSet); + selectSeriesMedia->build(") and (m.season_number = 0 or "); + selectSeriesMedia->bind("SeasonNumber", cDBS::bndIn | cDBS::bndSet); + selectSeriesMedia->build(")"); + + status += selectSeriesMedia->prepare(); + + // select * + // from channelmap order by ord; + + selectAllMap = new cDbStatement(mapDb); + + selectAllMap->build("select "); + selectAllMap->bindAllOut(); + selectAllMap->build(" from %s order by %s", mapDb->TableName(), + mapDb->getField("ORDER")->getDbName()); + + status += selectAllMap->prepare(); + + // select t.*, + // e.eventid, e.channelid, e.title, e.shorttext, e.shortdescription, e.category, e.genre, e.tipp, e.numrating + // from timers t left outer join events e + // on (t.eventid = e.masterid and e.updflg in (...)) + // where + // t.state in (?) + + // timerIncState.setField(&timerStateDef); + // timerExcState.setField(&timerStateDef); + // timerIncAction.setField(&timerActionDef); + // timerExcAction.setField(&timerActionDef); + + selectAllTimer = new cDbStatement(timerDb); + + selectAllTimer->build("select "); + selectAllTimer->setBindPrefix("t."); + selectAllTimer->bindAllOut(); + selectAllTimer->setBindPrefix("e."); + selectAllTimer->bind(useeventsDb, "USEID", cDBS::bndOut, ", "); + selectAllTimer->bind(useeventsDb, "CHANNELID", cDBS::bndOut, ", "); + selectAllTimer->bind(useeventsDb, "CNTSOURCE", cDBS::bndOut, ", "); + selectAllTimer->bind(useeventsDb, "CNTEVENTID", cDBS::bndOut, ", "); + selectAllTimer->bind(useeventsDb, "TITLE", cDBS::bndOut, ", "); + selectAllTimer->bind(useeventsDb, "SHORTTEXT", cDBS::bndOut, ", "); + selectAllTimer->bind(useeventsDb, "SHORTDESCRIPTION", cDBS::bndOut, ", "); + selectAllTimer->bind(useeventsDb, "CATEGORY", cDBS::bndOut, ", "); + selectAllTimer->bind(useeventsDb, "GENRE", cDBS::bndOut, ", "); + selectAllTimer->bind(useeventsDb, "TIPP", cDBS::bndOut, ", "); + selectAllTimer->bind(useeventsDb, "NUMRATING", cDBS::bndOut, ", "); + selectAllTimer->clrBindPrefix(); + selectAllTimer->build(" from %s t left outer join %s e", + timerDb->TableName(), "eventsviewplain"); + selectAllTimer->build(" on (t.eventid = e.cnt_useid) and e.updflg in (%s)", cEventState::getVisible()); + // selectAllTimer->build(" where "); + // selectAllTimer->bindInChar("t", "STATE", &timerIncState); + // selectAllTimer->bindInChar("t", "STATE", &timerExcState, " and not "); + // selectAllTimer->bindInChar("t", "ACTION", &timerIncAction, " and "); + // selectAllTimer->bindInChar("t", "ACTION", &timerExcAction, " and not "); + + status += selectAllTimer->prepare(); + + // select * + // from timers where + // id = ? + + selectTimerById = new cDbStatement(timerDb); + + selectTimerById->build("select "); + selectTimerById->bindAllOut(); + selectTimerById->build(" from %s where ", timerDb->TableName()); + selectTimerById->bind("ID", cDBS::bndIn | cDBS::bndSet); + + status += selectTimerById->prepare(); + + // select * + // from timers where + // state not in ('D','E') + // eventid = ? and channelid = ? + + selectTimerByEventId = new cDbStatement(timerDb); + + selectTimerByEventId->build("select "); + selectTimerByEventId->bindAllOut(); + selectTimerByEventId->build(" from %s where ", timerDb->TableName()); + selectTimerByEventId->build(" %s not in ('D', 'E', '-')", timerDb->getField("STATE")->getDbName()); + selectTimerByEventId->bind("EVENTID", cDBS::bndIn | cDBS::bndSet, " and "); + selectTimerByEventId->bind("CHANNELID", cDBS::bndIn | cDBS::bndSet, " and "); + + status += selectTimerByEventId->prepare(); + + // select * + // from searchtimers + // where state <> 'D' + + selectAllSearchTimer = new cDbStatement(searchtimerDb); + + selectAllSearchTimer->build("select "); + selectAllSearchTimer->bindAllOut(); + selectAllSearchTimer->build(" from %s where ", searchtimerDb->TableName()); + selectAllSearchTimer->build(" %s <> 'D'", + searchtimerDb->getField("STATE")->getDbName()); + + status += selectAllSearchTimer->prepare(); + + // update searchtimers set autotimername = ? + // whrer autotimerid = ? + + updateTimerAName = new cDbStatement(timerDb); + + updateTimerAName->build("update %s set ", timerDb->TableName()); + updateTimerAName->bind("AUTOTIMERNAME", cDBS::bndIn | cDBS::bndSet); + updateTimerAName->build(" where "); + updateTimerAName->bind("AUTOTIMERID", cDBS::bndIn | cDBS::bndSet); + + status += updateTimerAName->prepare(); + + // update timersdonedb set autotimername = ? + // whrer autotimerid = ? + + updateDoneAName = new cDbStatement(timersDoneDb); + + updateDoneAName->build("update %s set ", timersDoneDb->TableName()); + updateDoneAName->bind("AUTOTIMERNAME", cDBS::bndIn | cDBS::bndSet); + updateDoneAName->build(" where "); + updateDoneAName->bind("AUTOTIMERID", cDBS::bndIn | cDBS::bndSet); + + status += updateDoneAName->prepare(); + + // select *, inssp, updsp + // from timers + // where ACTION != 'A' + + selectPendingTimerActions = new cDbStatement(timerDb); + + selectPendingTimerActions->build("select "); + selectPendingTimerActions->bindAllOut(); + selectPendingTimerActions->bind("INSSP", cDBS::bndOut, ", "); + selectPendingTimerActions->bind("UPDSP", cDBS::bndOut, ", "); + selectPendingTimerActions->build(" from %s where %s != 'A'", // taAssumed + timerDb->TableName(), + timerDb->getField("ACTION")->getDbName()); + + status += selectPendingTimerActions->prepare(); + + // select *, inssp, updsp + // from timersdone + + selectDoneTimers = new cDbStatement(timersDoneDb); + + selectDoneTimers->build("select "); + selectDoneTimers->bindAllOut(); + selectDoneTimers->bind("INSSP", cDBS::bndOut, ", "); + selectDoneTimers->bind("UPDSP", cDBS::bndOut, ", "); + selectDoneTimers->build(" from %s", timersDoneDb->TableName()); + + status += selectDoneTimers->prepare(); + + // select channelname + // from channelmap + // where channelid = ? + // and channelname is not null + + selectMapById = new cDbStatement(mapDb); + + selectMapById->build("select "); + selectMapById->bind("ChannelName", cDBS::bndOut); + selectMapById->build(" from %s where ", mapDb->TableName()); + selectMapById->bind("ChannelId", cDBS::bndIn | cDBS::bndSet); + selectMapById->build(" and channelname is not null"); + + status += selectMapById->prepare(); + + // update channelmap set ord = ?, visible = ?, channelname = ? + // where channelid = ? + + updateMap = new cDbStatement(mapDb); + + updateMap->build("update %s set ", mapDb->TableName()); + updateMap->bind("ORDER", cDBS::bndIn | cDBS::bndSet); + updateMap->bind("VISIBLE", cDBS::bndIn | cDBS::bndSet, ", "); + updateMap->bind("CHANNELNAME", cDBS::bndIn | cDBS::bndSet, ", "); + updateMap->build(" where "); + updateMap->bind("CHANNELID", cDBS::bndIn | cDBS::bndSet); + + status += updateMap->prepare(); + + // ---------- + // select channelname + // from channelmap + // where channelid = ? + + selectChannelFromMap = new cDbStatement(mapDb); + + selectChannelFromMap->build("select "); + selectChannelFromMap->bind("CHANNELNAME", cDBS::bndOut); + selectChannelFromMap->bind("UNKNOWNATVDR", cDBS::bndOut, ", "); + selectChannelFromMap->build(" from %s where ", mapDb->TableName()); + selectChannelFromMap->bind("CHANNELID", cDBS::bndIn | cDBS::bndSet); + + status += selectChannelFromMap->prepare(); + + // ---------- + // select * from recordingdirs + // where owner = ? + + selectRecordingDirs = new cDbStatement(recordingDirDb); + + selectRecordingDirs->build("select "); + selectRecordingDirs->bindAllOut(); + selectRecordingDirs->build(" from %s order by vdruuid, directory", recordingDirDb->TableName()); + + status += selectRecordingDirs->prepare(); + + // ---------- + // select * from users + // where passwd = ? + + selectUserByMd5 = new cDbStatement(userDb); + + selectUserByMd5->build("select "); + selectUserByMd5->bindAllOut(); + selectUserByMd5->build(" from %s where ", userDb->TableName()); + selectUserByMd5->bind("PASSWD", cDBS::bndIn | cDBS::bndSet); + // selectUserByMd5->bindText("md5(passwd)", userDb->getValue("passwd"), "="); + + status += selectUserByMd5->prepare(); + + // ---------- + // select * + // from recordinglist + + selectAllRecordings = new cDbStatement(recordingListDb); + + selectAllRecordings->build("select "); + selectAllRecordings->setBindPrefix("r."); + selectAllRecordings->bindAllOut(); + selectAllRecordings->setBindPrefix("v."); + selectAllRecordings->bind(vdrDb, "SHAREINWEB", cDBS::bndOut, ", "); + selectAllRecordings->clrBindPrefix(); + selectAllRecordings->build(" from %s r, %s v where", + recordingListDb->TableName(), + vdrDb->TableName()); + selectAllRecordings->build(" r.%s = v.%s", + recordingListDb->getField("VDRUUID")->getDbName(), + vdrDb->getField("UUID")->getDbName()); + selectAllRecordings->build(" and (r.%s <> 'D' or r.%s is null)", + recordingListDb->getField("STATE")->getDbName(), + vdrDb->getField("STATE")->getDbName()); + + status += selectAllRecordings->prepare(); + + // select *, + // epglvr(title, ?) + // epglvr(shorttext, ?) + // from recordinglist where + // (state <> 'D' or state is null) + // and epglvr(title, ?) < 50 +// // order by lv + + matchDensityTitle.setField(&matchDensityTitleDef); + matchDensityShorttext.setField(&matchDensityShorttextDef); + + selectRecordingForEventByLv = new cDbStatement(recordingListDb); + + selectRecordingForEventByLv->build("select "); + selectRecordingForEventByLv->bindAllOut(); + selectRecordingForEventByLv->bindTextFree(", 100 - ifNull(epglvr(title, ?), 100)", &matchDensityTitle, cDBS::bndOut); + selectRecordingForEventByLv->appendBinding(recordingListDb->getValue("TITLE"), cDBS::bndIn); + selectRecordingForEventByLv->bindTextFree(", 100 - ifNull(epglvr(shorttext, ?), 100)", &matchDensityShorttext, cDBS::bndOut); + selectRecordingForEventByLv->appendBinding(recordingListDb->getValue("SHORTTEXT"), cDBS::bndIn); + selectRecordingForEventByLv->build(" from %s where ", recordingListDb->TableName()); + selectRecordingForEventByLv->build(" (%s <> 'D' or %s is null)", + recordingListDb->getField("STATE")->getDbName(), + recordingListDb->getField("STATE")->getDbName()); + selectRecordingForEventByLv->bindTextFree("and epglvr(title, ?) < 47", recordingListDb->getValue("TITLE"), cDBS::bndIn); + + status += selectRecordingForEventByLv->prepare(); + + // select * + // from recordinglist where + // state <> 'D' + // snd path = ? + + selectRecordingByPath = new cDbStatement(recordingListDb); + + selectRecordingByPath->build("select "); + selectRecordingByPath->bindAllOut(); + selectRecordingByPath->build(" from %s where ", recordingListDb->TableName()); + selectRecordingByPath->build(" (%s <> 'D' or %s is null)", + recordingListDb->getField("STATE")->getDbName(), + recordingListDb->getField("STATE")->getDbName()); + selectRecordingByPath->bind("PATH", cDBS::bndIn | cDBS::bndSet, " and "); + + status += selectRecordingByPath->prepare(); + + // select *, inssp, updsp + // from messages + // where STATE != 'D' + + selectPendingMessages = new cDbStatement(messageDb); + + selectPendingMessages->build("select "); + selectPendingMessages->bindAllOut(); + selectPendingMessages->bind("INSSP", cDBS::bndOut, ", "); + selectPendingMessages->bind("UPDSP", cDBS::bndOut, ", "); + selectPendingMessages->build(" from %s where %s != 'D'", + messageDb->TableName(), + messageDb->getField("STATE")->getDbName()); + + status += selectPendingMessages->prepare(); + + // ---------- + // select distinct(owner) from parameters + // where owner like '@%'; + + selectWebUsers = new cDbStatement(parametersDb); + + selectWebUsers->build("select distinct("); + selectWebUsers->bind("OWNER", cDBS::bndOut); + selectWebUsers->build(") from %s where %s like '@%%'", + parametersDb->TableName(), + parametersDb->getField("OWNER")->getDbName()); + + status += selectWebUsers->prepare(); + + // --------- + // .... + + status += search->initDb(); + + // init some DB values for faster and easier access + + status += vdrDb->init(vdrState, "State") + + vdrDb->init(vdrIp, "Ip") + + vdrDb->init(vdrSvdrp, "Svdrp") + + vdrDb->init(vdrUuid, "UUID") + + + useeventsDb->init(eventsUpdSp, "UpdSp") + + useeventsDb->init(eventsGenre, "Genre") + + useeventsDb->init(eventsCategory, "Category") + + + mapDb->init(mapChannelName, "ChannelName") + + mapDb->init(mapChannelId, "ChannelId") + + mapDb->init(mapSource, "Source") + + + imageRefDb->init(imagerefImgName, "ImgName") + + imageDb->init(imageUpdSp, "UpdSp") + + imageDb->init(imageImage, "Image") + + + movieActorDb->init(movieactorActorId, "ActorId") + + movieMediaDb->init(moviemediaMediaContent, "MediaContent") + + movieMediaDb->init(moviemediaMediaType, "MediaType") + + seriesEpisodeDb->init(seriesepisodeSeasonNumber, "SeasonNumber") + + seriesEpisodeDb->init(seriesepisodeEpisodeId, "EpisodeId") + + + seriesMediaDb->init(seriesmediaMediaContent, "MediaContent") + + seriesMediaDb->init(seriesmediaMediaType, "MediaType") + + seriesMediaDb->init(seriesmediaActorId, "ActorId"); + + return status; +} + +//*************************************************************************** +// ExitDb +//*************************************************************************** + +int cEpgHttpd::exitDb() +{ + search->exitDb(); + cParameters::exitDb(); + + delete selectEventsAt; selectEventsAt = 0; + delete selectEventsNext; selectEventsNext = 0; + delete selectEventsStartInRange; selectEventsStartInRange = 0; + delete selectGenres; selectGenres = 0; + delete selectCategories; selectCategories = 0; + delete selectEvent; selectEvent = 0; + delete selectEventByTime; selectEventByTime = 0; + delete selectAllMap; selectAllMap = 0; + delete selectMapById; selectMapById = 0; + delete selectVdrs; selectVdrs = 0; + delete selectUsers; selectUsers = 0; + delete updateMap; updateMap = 0; + delete updateTimerAName; updateTimerAName = 0; + delete updateDoneAName; updateDoneAName = 0; + + delete selectAllTimer; selectAllTimer = 0; + delete selectTimerById; selectTimerById = 0; + delete selectTimerByEventId; selectTimerByEventId = 0; + delete selectAllSearchTimer; selectAllSearchTimer = 0; + delete selectMovie; selectMovie = 0; + delete selectMovieActors; selectMovieActors = 0; + delete selectMovieMedia; selectMovieMedia = 0; + delete selectSerie; selectSerie = 0; + delete selectSeriesEpisode; selectSeriesEpisode = 0; + delete selectSeriesMedia; selectSeriesMedia = 0; + delete selectPendingTimerActions; selectPendingTimerActions = 0; + delete selectDoneTimers; selectDoneTimers = 0; + delete selectActiveVdrs; selectActiveVdrs = 0; + delete selectRecordingDirs; selectRecordingDirs = 0; + delete selectRecordingByPath; selectRecordingByPath = 0; + delete selectRecordingForEventByLv; selectRecordingForEventByLv = 0; + delete selectChannelFromMap; selectChannelFromMap = 0; + delete selectPendingMessages; selectPendingMessages = 0; + delete selectWebUsers; selectWebUsers = 0; + + delete timerDb; timerDb = 0; + delete timersDoneDb; timersDoneDb = 0; + delete searchtimerDb; searchtimerDb = 0; + delete vdrDb; vdrDb = 0; + delete useeventsDb; useeventsDb = 0; + delete mapDb; mapDb = 0; + delete imageDb; imageDb = 0; + delete imageRefDb; imageRefDb = 0; + delete movieDb; movieDb = 0; + delete movieActorsDb; movieActorsDb = 0; + delete movieActorDb; movieActorDb = 0; + delete movieMediaDb; movieMediaDb = 0; + delete seriesDb; seriesDb = 0; + delete seriesEpisodeDb; seriesEpisodeDb = 0; + delete seriesMediaDb; seriesMediaDb= 0; + delete seriesActorsDb; seriesActorsDb = 0; + delete recordingDirDb; recordingDirDb = 0; + delete recordingListDb; recordingListDb = 0; + delete userDb; userDb = 0; + delete messageDb; messageDb = 0; + + delete connection; connection = 0; + + return done; +} + +//*************************************************************************** +// Loop +//*************************************************************************** + +int cEpgHttpd::loop() +{ + while (!doShutDown()) + { + cSystemNotification::check(); + sleep(1); + } + + return done; +} + +//*************************************************************************** +// Check Connection +//*************************************************************************** + +int cEpgHttpd::checkConnection() +{ + static int retry = 0; + + // check connection + + if (!dbConnected(yes)) + { + // try to connect + + tell(0, "Trying to re-connect to database!"); + retry++; + + if (initDb() != success) + { + tell(0, "Retry #%d failed!", retry); + exitDb(); + + return fail; + } + + retry = 0; + tell(0, "Connection established successfull!"); + } + + return success; +} + +//*************************************************************************** +// Check Session +//*************************************************************************** + +int cEpgHttpd::setSession(const char* sessionId) +{ + std::map<std::string,Session>::iterator it; + Session* s; + + currentSession = 0; + loginWithSession = yes; + + if ((it = sessions.find(std::string(sessionId))) != sessions.end()) + { + s = &it->second; + + if (time(0) > s->last + tmeSecondsPerHour) + { + tell(0, "Session '%s' for user '%s' expired, removing it", + s->id.c_str(), s->user.c_str()); + + sessions.erase(it); + + return fail; + } + + tell(2, "Session now '%s' for user '%s'", + s->id.c_str(), s->user.c_str()); + + currentSession = s; + currentSession->last = time(0); + + return success; + } + + return fail; +} + +//*************************************************************************** +// Need Login +//*************************************************************************** + +int cEpgHttpd::needLogin() +{ + long int need = no; + + getParameter("webif", "needLogin", need); + + return need; +} + +//*************************************************************************** +// Has Rights +//*************************************************************************** + +int cEpgHttpd::hasRights(const char* url, json_t* response, int& statusCode) +{ + UserMask mask = toRightMask(url); + + statusCode = MHD_HTTP_OK; + + if (!needLogin()) + return yes; + + tell(2, "Checking rights of '%s' for user '%s' with%s session", url, + currentSession ? currentSession->user.c_str() : "<null>", + currentSession ? "" : "out"); + + if (!currentSession || !hasUserMask(currentSession->rights, mask)) + { + if (currentSession) + { + statusCode = buildResponse(response, MHD_HTTP_FORBIDDEN, "Rejecting '%s' request of user " + "'%s' due to insufficient rights!", + url, currentSession->user.c_str()); + return no; + } + else if (!hasUserMask(umNologin, mask)) + { + statusCode = buildResponse(response, MHD_HTTP_UNAUTHORIZED, "Rejecting '%s' request, missing login!", url); + return no; + } + } + + // special case, request has invalid or expired session + // -> reject even in umNologin case + + if (!currentSession && loginWithSession) + { + statusCode = buildResponse(response, MHD_HTTP_UNAUTHORIZED, "Rejecting '%s' request " + "due to invalid session!", url); + return no; + } + + return yes; +} + +//*************************************************************************** +// Build Response +//*************************************************************************** + +int cEpgHttpd::buildResponse(json_t* obj, int state, const char* format, ...) +{ + va_list ap; + json_t* oResult = json_object(); + char* message; + + // {"result": {"state": 200, "message": "success"}} + + va_start(ap, format); + vasprintf(&message, format, ap); + va_end(ap); + + if (state != MHD_HTTP_OK) + tell(0, "Error: %s", message); + + json_object_set_new(oResult, "state", json_integer(state)); + json_object_set_new(oResult, "message", json_string(message)); + json_object_set_new(obj, "result", oResult); + + free(message); + + return state; +} + +//*************************************************************************** +// Method Of +//*************************************************************************** + +const char* cEpgHttpd::methodOf(const char* url) +{ + const char* p; + + if (url && (p = strchr(url+1, '/'))) + return p+1; + + return ""; +} + +//*************************************************************************** +// Perform Data Request +//*************************************************************************** + +int cEpgHttpd::performDataRequest(MHD_Connection* tcp, const char* url, MemoryStruct* data) +{ + const char* method = methodOf(url); + const char* encoding = MHD_lookup_connection_value(tcp, MHD_HEADER_KIND, "Accept-Encoding"); + json_t* response = json_object(); + int statusCode = MHD_HTTP_OK; + int jsonResponse = yes; + + if (checkConnection() != success) // check connection .. + { + statusCode = buildResponse(response, MHD_HTTP_SERVICE_UNAVAILABLE, "Lost database connection, retry later"); + } + + else if (!hasRights(url, response, statusCode)) // check rights .. + { + // ... + } + + else + { + data->modTime = time(0); + + if (strcmp(method, "events") == 0) + statusCode = doEvents(tcp, response); + else if (strcmp(method, "timers") == 0) + statusCode = doTimers(tcp, response); + else if (strcmp(method, "recordings") == 0) + statusCode = doRecordings(tcp, response); + else if (strcmp(method, "recording") == 0) + statusCode = doRecording(tcp, response); + else if (strcmp(method, "parameters") == 0) + statusCode = doParameters(tcp, response); + else if (strcmp(method, "recordingdirs") == 0) + statusCode = doRecDirs(tcp, response); + else if (strcmp(method, "pendingtimerjobs") == 0) + statusCode = doTimerJobs(tcp, response); + else if (strcmp(method, "messages") == 0) + statusCode = doMessages(tcp, response); + else if (strcmp(method, "donetimers") == 0) + statusCode = doDoneTimers(tcp, response); + else if (strcmp(method, "donetimer") == 0) + statusCode = doDoneTimer(tcp, response); + else if (strcmp(method, "searchtimers") == 0) + statusCode = doSearchtimers(tcp, response); + else if (strcmp(method, "event") == 0) + statusCode = doEvent(tcp, response, data); + else if (strcmp(method, "channels") == 0) + statusCode = doChannels(tcp, response); + else if (strcmp(method, "genres") == 0) + statusCode = doGenres(tcp, response); + else if (strcmp(method, "categories") == 0) + statusCode = doCategories(tcp, response); + else if (strcmp(method, "vdrs") == 0) + statusCode = doVdrs(tcp, response); + else if (strcmp(method, "users") == 0) + statusCode = doUsers(tcp, response); + else if (strcmp(method, "updatesearchtimer") == 0) + statusCode = doUpdateSearchtimer(tcp, response); + else if (strcmp(method, "updaterecordings") == 0) + statusCode = doUpdateRecordingTable(tcp, response); + else if (strcmp(method, "wakeupvdr") == 0) + statusCode = doWakeupVdr(tcp, response); + else if (strcmp(method, "renamerecording") == 0) + statusCode = doRenameRecording(tcp, response); + else if (strcmp(method, "replayrecording") == 0) + statusCode = doReplayRecording(tcp, response); + else if (strcmp(method, "deleterecording") == 0) + statusCode = doDeleteRecording(tcp, response); + else if (strcmp(method, "channelswitch") == 0) + statusCode = doChannelSwitch(tcp, response); + else if (strcmp(method, "hitkey") == 0) + statusCode = doHitKey(tcp, response); + else if (strcmp(method, "log") == 0) + statusCode = doLog(tcp, response); + else if (strcmp(method, "debug") == 0) + statusCode = doDebug(tcp, response); + else + { + // requests with *non* json response (data buffer is ready - nothing to prepare later) + + jsonResponse = no; + + if (strcmp(method, "channellogo") == 0) + statusCode = doChannelLogo(tcp, response, data); + else if (strcmp(method, "eventimg") == 0) + statusCode = doEpgImage(tcp, response, data); + else if (strcmp(method, "moviemedia") == 0) + statusCode = doMovieMedia(tcp, response, data); + else if (strcmp(method, "seriesmedia") == 0) + statusCode = doSeriesMedia(tcp, response, data); + else if (strcmp(method, "proxy") == 0) + statusCode = doProxy(tcp, response, data); + else + { + statusCode = buildResponse(response, MHD_HTTP_NOT_FOUND, "Unexpected method '%s' requested, ignoring", method); + jsonResponse = yes; + } + } + } + + // if json response convert to data + + if (jsonResponse) + { + json2Data(response, data, encoding); + + if (EpgdConfig.loglevel >= 2) + json_dump_file(response, "debug-dump.json", JSON_PRESERVE_ORDER); + } + + json_decref(response); // free the json object + + return statusCode; +} + +//*************************************************************************** +// Perform Http Get +//*************************************************************************** + +int cEpgHttpd::performHttpGet(MHD_Connection* tcp, const char* inurl, MemoryStruct* data) +{ + const char* url; + int statusCode = MHD_HTTP_OK; + + if (strcmp(inurl, "/") == 0) + url = "index.html"; + else + url = inurl; + + data->modTime = getModTimeOf(url, ""); + + tell(3, "file: %s; expire: %s", l2pTime(data->modTime).c_str(), l2pTime(data->expireAt).c_str()); + + if (!data->expireAt || data->modTime > data->expireAt) + { + if (loadFromFs(data, url, "") == success) + { + const char* encoding = MHD_lookup_connection_value(tcp, MHD_HEADER_KIND, "Accept-Encoding"); + + sprintf(data->name, "%.*s", (int)sizeof(data->name), url); + + if (encoding && strstr(encoding, "gzip")) + { + if (data->toGzip() != success) + statusCode = 500; + } + } + else + { + statusCode = MHD_HTTP_NOT_FOUND; + } + } + else + { + statusCode = MHD_HTTP_NOT_MODIFIED; + } + + return statusCode; +} + +//*************************************************************************** +// Perform Post Data +//*************************************************************************** + +int cEpgHttpd::performPostData(const char* url, MemoryStruct* data) +{ + int statusCode = MHD_HTTP_OK;; + json_t* response = json_object(); + json_error_t error; + json_t* jInData = json_loads(data->memory, 0, &error); + + tell(2, "<- post (%s) '%s'", url, data->memory); + + if (!jInData) + { + statusCode = buildResponse(response, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE, "Ignoring invalid post object [%s]", data->memory); + } + + else if (checkConnection() != success) // check db connection .. + { + statusCode = buildResponse(response, MHD_HTTP_SERVICE_UNAVAILABLE, "Lost database connection, retry later"); + } + + else if (!hasRights(url, response, statusCode)) // check rights .. + { + // ... + } + + else + { + // dispatch post requests + + if (strcmp(url, "/data/save-channels") == 0) + statusCode = storeChannels(jInData, response); + else if (strcmp(url, "/data/delete-timerjobs") == 0) + statusCode = deleteTimerJobs(jInData, response); + else if (strcmp(url, "/data/store-donetimers") == 0) + statusCode = deleteDoneTimers(jInData, response); + else if (strcmp(url, "/data/save-timer") == 0) + statusCode = storeTimerJob(jInData, response); + else if (strcmp(url, "/data/save-searchtimer") == 0) + statusCode = storeSearchTimer(jInData, response); + else if (strcmp(url, "/data/save-parameters") == 0) + statusCode = storeParameters(jInData, response); + else if (strcmp(url, "/data/search") == 0) + statusCode = doSearch(jInData, response); + else if (strcmp(url, "/data/sendmail") == 0) + statusCode = doSendMail(jInData, response); + else if (strcmp(url, "/data/save-users") == 0) + statusCode = storeUsers(jInData, response); + else if (strcmp(url, "/data/login") == 0) + statusCode = doLogin(jInData, response); + else if (strcmp(url, "/data/markmessages") == 0) + statusCode = markMessages(jInData, response); + + else + statusCode = buildResponse(response, MHD_HTTP_NOT_FOUND, "Unexpected post request '%s', ignoring", url); + } + + json_decref(jInData); // free the json object + data->clear(); + + // prepare response data + + json2Data(response, data); + json_decref(response); // free the json object + + return statusCode; +} + +//*************************************************************************** +// Debug ... +//*************************************************************************** + +int debugPrint(void *cls, enum MHD_ValueKind kind, const char *key, const char *value) +{ + if (kind == MHD_GET_ARGUMENT_KIND) + tell(0, "Parameter: '%s' - '%s'", key, value); + else if (kind == MHD_HEADER_KIND) + tell(0, "Header: '%s' - '%s'", key, value); + + return MHD_YES; +} + +int parameterInfo(void* cls, enum MHD_ValueKind kind, const char* key, const char* value) +{ + if (strlen((char*)cls) < 1000 - strlen(notNull(value)) - strlen(notNull(key)) - 10) + sprintf(eos((char*)cls), "%s:%s;", notNull(key), notNull(value)); + + return MHD_YES; +} + +//*************************************************************************** +// Dispatcher +//*************************************************************************** + +int cEpgHttpd::dispatcher(void* cls, MHD_Connection* tcp, + const char* url, const char* method, + const char* version, const char* upload_data, + size_t* upload_data_size, void** con_cls) +{ + const char* contentNotFound = "<html><body>Page not found</body></html>"; + unsigned int statusCode = MHD_HTTP_OK; + MHD_Response* response; + MemoryStruct data; + double requestStartAt = cMyTimeMs::Now(); + + // int acceptRequest = no; + int state; + + // reset session + + singleton->currentSession = 0; + singleton->loginWithSession = no; + + // get header infos + + const char* id = getStrParameter(tcp, "id"); + data.expireAt = getTimeHeader(tcp, "If-Modified-Since", ""); // Sat, 08 May 2010 11:25:27 GMT. + const char* cacheTag = getStrHeader(tcp, "If-None-Match", ""); // "2a28c-73a1-6eb997c0" + const char* cacheControl = getStrHeader(tcp, "Cache-Control", "") ; // max-age=0 + int checkCache = strstr(cacheControl, "max-age=0") != 0; + + if (!checkCache) + data.expireAt = 0; + + // debug + + if (EpgdConfig.loglevel >= 3) + { + // MHD_get_connection_values(tcp, MHD_GET_ARGUMENT_KIND, debugPrint, 0); + MHD_get_connection_values(tcp, MHD_HEADER_KIND, debugPrint, 0); + } + + // check session in url + // "/sid3cf33d41332e3e2702e7a8e39429f848/data/parameters" + + if (strncmp(url, "/sid", 4) == 0) + { + char* session = 0; + + asprintf(&session, "%.*s", (int)(strchr(url+4, '/') - url - 4), url + 4); + url = strchr(url+4, '/'); + + tell(3, "SESSION: '%s'", session); + singleton->setSession(session); + free(session); + } + + tell(3, "Cache info: cacheExpire = %s; cacheTag = %s; cacheControl = %s", l2pTime(data.expireAt).c_str(), cacheTag, cacheControl); + + // dispatch ... + + if (strcmp(method, "POST") == 0) + { + if (EpgdConfig.loglevel >= 2) + tell(3, "<- '%s' / %s%s [%s](%d)", url, id ? "id=" : "", id ? id : "", upload_data, (int)(*upload_data_size)); + + if (!(*con_cls)) // start of new incoming data + { + *con_cls = (void*) new MemoryStruct; + return MHD_YES; + } + + if (EpgdConfig.httpUseTls && !isEmpty(EpgdConfig.httpUser)) + { + if (!isAuthenticated(tcp, EpgdConfig.httpUser, EpgdConfig.httpPass)) + return askForAuthentication(tcp, realm); + } + + MemoryStruct* postData = (MemoryStruct*)*con_cls; + + if (*upload_data_size) // chunk of POST data? + { + // processing of POST data + + postData->append(upload_data, (int)*upload_data_size); + *upload_data_size = 0; // set 0 to indicate 'successfully processed' + + return MHD_YES; + } + else // end of incoming data + { + postData->memory = (char*)realloc(postData->memory, postData->size+1); + postData->memory[postData->size] = 0; + postData->size++; + + data.size = postData->size; + data.memory = (char*)malloc(data.size); + urlUnescape(data.memory, postData->memory); + delete postData; + + statusCode = singleton->performPostData(url, &data); + } + } + + else if (strcmp(method, "GET") == 0) + { + if (EpgdConfig.httpUseTls && !isEmpty(EpgdConfig.httpUser)) + { + if (!isAuthenticated(tcp, EpgdConfig.httpUser, EpgdConfig.httpPass)) + return askForAuthentication(tcp, realm); + } + + if (EpgdConfig.loglevel >= 2) + { + char parmDebug[1000+TB] = ""; + + MHD_get_connection_values(tcp, MHD_GET_ARGUMENT_KIND, parameterInfo, parmDebug); + tell(2, "<- %s with [%s] (expire at %s)", url, parmDebug, l2pTime(data.expireAt).c_str()); + tell(3, "data: %s; expire: %s", l2pTime(data.modTime).c_str(), l2pTime(data.expireAt).c_str()); + } + + if (strstr(url, "/data") == url) // data requested ... + statusCode = cEpgHttpd::singleton->performDataRequest(tcp, url, &data); + else // file request ... + statusCode = cEpgHttpd::singleton->performHttpGet(tcp, url, &data); + + if (statusCode == MHD_HTTP_OK) + { + if (data.modTime <= data.expireAt) + { + statusCode = MHD_HTTP_NOT_MODIFIED; + tell(2, "-> %d 'not modified'", statusCode); + } + else if (!data.size) + { + data.memory = strdup(contentNotFound); + data.size = strlen(data.memory); + + statusCode = MHD_HTTP_NOT_FOUND; + tell(1, "-> %d 'file not found'", statusCode); + } + } + } + + // --------------------- + // prepare response ... + + response = createHttpResponse(&data); + + // header + + char* server; + asprintf(&server, "epghttpd/%s from %s\n", VERSION, VERSION_DATE); + addHeaderItem(response, "Server", "server"); + + if (strcmp(method, "POST") == 0) + addHeaderItem(response, "Cache-Control", "max-age=0, must-revalidate"); + else + addHeaderItem(response, "Cache-Control", "max-age=300, must-revalidate"); + + free(server); + + if (statusCode == MHD_HTTP_OK) + { + addHeaderItem(response, "Last-Modified", l2HttpTime(data.modTime).c_str()); + addHeaderItem(response, "Content-Type", data.contentType); + } + + if (!isEmpty(data.contentEncoding)) + addHeaderItem(response, "Content-Encoding", data.contentEncoding); + + // response + + state = MHD_queue_response(tcp, statusCode, response); + MHD_destroy_response(response); + + // log and debug ... + { +// const uint dspWidth = 6000; // debug, later around 50 or 100 ...? + const char* dump = "..."; +// uint dumpSize = 3; + + if ((strncmp(data.contentType, "test", 4) == 0 || strstr(data.contentType, "json"))) + { + dump = data.memory; + // dumpSize = data.size; + } + else if (!isEmpty(data.name)) + { + dump = data.name; + // dumpSize = strlen(data.name); + } + + tell(2, "-> %s (%ld) (%s); Content-Type: %s; %s%s [%s]", + !isEmpty(data.name) ? "file" : "data", + data.size, + ms2Dur(cMyTimeMs::Now()-requestStartAt).c_str(), + data.contentType, + !isEmpty(data.contentEncoding) ? "Content-Encoding: " : "", data.contentEncoding, + dump); + +// tell(2, "-> %s (%ld); Content-Type: %s; %s%s [%.*s%s]", +// !isEmpty(data.name) ? "file" : "data", +// data.size, data.contentType, +// !isEmpty(data.contentEncoding) ? "Content-Encoding: " : "", data.contentEncoding, +// dspWidth,dump, dumpSize > dspWidth ? "..." : ""); + } + + return state; +} + +//*************************************************************************** +// Trigger Epgd +//*************************************************************************** + +int cEpgHttpd::triggerEpgd() +{ + int pid = na; + + vdrDb->clear(); + vdrDb->setValue("UUID", EPGDNAME); + + if (!vdrDb->find() || vdrDb->getValue("PID")->isNull()) + { + tell(0, "Error: Can't lookup epgd information, abort trigger"); + return fail; + } + + vdrDb->reset(); + + pid = vdrDb->getIntValue("PID"); + + if (pid > 0) + { + if (kill(pid, SIGUSR1) == 0) + tell(1, "Triggered searchtimer update at %s", EPGDNAME); + } + + return success; +} + +//*************************************************************************** +// Trigger VDRs +//*************************************************************************** + +int cEpgHttpd::triggerVdrs(const char* trg, const char* plug, const char* options) +{ + if (!selectActiveVdrs) // wait for dbInit ;) + return done; + + vdrDb->clear(); + + for (int f = selectActiveVdrs->find(); f; f = selectActiveVdrs->fetch()) + { + const char* ip = vdrDb->getStrValue("IP"); + unsigned int port = vdrDb->getIntValue("SVDRP"); + + if (vdrDb->getIntValue("SHAREINWEB")) + triggerVdr(ip, port, trg, plug, options); + } + + selectActiveVdrs->freeResult(); + + return success; +} + +//*************************************************************************** +// Trigger VDRs +//*************************************************************************** + +int cEpgHttpd::triggerVdr(const char* ip, unsigned int port, const char* trg, + const char* plug, const char* options, char* res) +{ + int status = success; + char* command = 0; + cList<cLine> result; + + cSvdrpClient cl(ip, port); + + // open tcp connection + + if (cl.open() != success) + return fail; + + if (!isEmpty(plug)) + asprintf(&command, "PLUG %s %s %s", plug, trg, !isEmpty(options) ? options : ""); + else + asprintf(&command, "%s %s", trg, !isEmpty(options) ? options : ""); + + tell(1, "Send '%s' to '%s:%d'", command, ip, port); + + if (!cl.send(command)) + { + status = fail; + tell(0, "Error: Send '%s' at '%s:%d' failed!", command, ip, port); + } + else + { + int code = cl.receive(&result); + + if (code != 900) + { + status = fail; + + if (res && result.First()) + sprintf(res, "%.*s", 512, result.First()->Text()); + } + } + + free(command); + cl.close(no); + + return status; +} + +//*************************************************************************** +// IP of VDR +//*************************************************************************** + +int cEpgHttpd::ipOfVdr(const char* uuid, const char*& ip, int& port) +{ + int alive = no; + + port = 0; + ip = 0; + + vdrDb->clear(); + vdrDb->setValue("UUID", uuid); + + if (vdrDb->find()) + { + ip = vdrDb->getStrValue("IP"); + port = vdrDb->getIntValue("SVDRP"); + alive = vdrDb->hasValue("STATE", "attached"); + } + + vdrDb->reset(); + + return alive; +} + +//*************************************************************************** +// Wakekup VDR +//*************************************************************************** + +int cEpgHttpd::wakeupVdr(const char* uuid) +{ + int status = fail; + + vdrDb->clear(); + vdrDb->setValue("UUID", uuid); + + if (vdrDb->find()) + { + const char* mac = vdrDb->getStrValue("MAC"); + + if (!isEmpty(mac)) + status = sendWol(mac, EpgdConfig.netDevice); + } + + vdrDb->reset(); + + return status; +} + +//*************************************************************************** +// Message +//*************************************************************************** + +int cEpgHttpd::message(int level, char type, const char* title, const char* format, ...) +{ + va_list ap; + char* message; + std::string receivers; + + va_start(ap, format); + vasprintf(&message, format, ap); + va_end(ap); + + messageDb->setCharValue("TYPE", type); + messageDb->setValue("TITLE", title); + messageDb->setValue("STATE", "N"); + messageDb->setValue("TEXT", message); + messageDb->insert(); + + tell(level, "%s: %s", title, message); + + // loop over web users + + parametersDb->clear(); + + for (int found = selectWebUsers->find(); found; found = selectWebUsers->fetch()) + { + char receiver[255+TB] = ""; + char typesToMail[10+TB] = ""; + const char* owner = parametersDb->getStrValue("OWNER"); + + getParameter(owner, "messageMailTypes", typesToMail); + + if (!strchr(typesToMail, type)) + continue; + + getParameter(owner, "mailReceiver", receiver); + + if (isEmpty(receiver)) + tell(0, "Warning: Missing mail receiver, can't send mail to '%s'", owner+1); + else + receivers += receiver + std::string(","); + } + + sendMail("text/plain", receivers.c_str(), title, message); + + free(message); + + return done; +} + +//*************************************************************************** +// Configuration +//*************************************************************************** + +int cEpgHttpd::atConfigItem(const char* Name, const char* Value) +{ + // Parse setup parameters and store values. + + if (!strcasecmp(Name, "DbHost")) sstrcpy(EpgdConfig.dbHost, Value, sizeof(EpgdConfig.dbHost)); + else if (!strcasecmp(Name, "DbPort")) EpgdConfig.dbPort = atoi(Value); + else if (!strcasecmp(Name, "DbName")) sstrcpy(EpgdConfig.dbName, Value, sizeof(EpgdConfig.dbName)); + else if (!strcasecmp(Name, "DbUser")) sstrcpy(EpgdConfig.dbUser, Value, sizeof(EpgdConfig.dbUser)); + else if (!strcasecmp(Name, "DbPass")) sstrcpy(EpgdConfig.dbPass, Value, sizeof(EpgdConfig.dbPass)); + + else if (!strcasecmp(Name, "NetDevice")) sstrcpy(EpgdConfig.netDevice, Value, sizeof(EpgdConfig.netDevice)); + + else if (!strcasecmp(Name, "CachePath")) sstrcpy(EpgdConfig.cachePath, Value, sizeof(EpgdConfig.cachePath)); + else if (!strcasecmp(Name, "LogLevel")) EpgdConfig.loglevel = EpgdConfig.argLoglevel == na ? atoi(Value) : EpgdConfig.argLoglevel; + else if (!strcasecmp(Name, "HttpDevice")) sstrcpy(EpgdConfig.httpDevice, Value, sizeof(EpgdConfig.httpDevice)); + else if (!strcasecmp(Name, "HttpPort")) EpgdConfig.httpPort = atoi(Value); + else if (!strcasecmp(Name, "HttpTls")) EpgdConfig.httpUseTls = atoi(Value); + else if (!strcasecmp(Name, "HttpUser")) sstrcpy(EpgdConfig.httpUser, Value, sizeof(EpgdConfig.httpUser)); + else if (!strcasecmp(Name, "HttpPass")) sstrcpy(EpgdConfig.httpPass, Value, sizeof(EpgdConfig.httpPass)); + + return success; +} + +//*************************************************************************** +// Usage +//*************************************************************************** + +void showUsage() +{ + printf("Usage: epghttpd [-n][-c <config-dir>][-l <log-level>][-t]\n"); + printf(" -v show version and exit\n"); + printf(" -n don't daemonize\n"); + printf(" -t log to stdout\n"); + printf(" -c <config-dir> use config in <config-dir>\n"); + printf(" -l <log-level> set log level\n"); + printf(" -i <pidfile>\n"); +} + +//*************************************************************************** +// Main +//*************************************************************************** + +int main(int argc, char** argv) +{ + cEpgHttpd* job; + int nofork = no; + int pid; + int logstdout = na; + int loglevel = na; + + // Usage .. + + if (argc > 1 && (argv[1][0] == '?' || (strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0))) + { + showUsage(); + return 0; + } + + // Parse command line + + for (int i = 0; argv[i]; i++) + { + if (argv[i][0] != '-' || strlen(argv[i]) != 2) + continue; + + switch (argv[i][1]) + { + case 'v': printf("epghttpd version %s from %s\n", VERSION, VERSION_DATE); return 0; + case 't': logstdout = yes; break; + case 'n': nofork = yes; break; + case 'l': if (argv[i+1]) loglevel = atoi(argv[++i]); break; + case 'c': if (argv[i+1]) confDir = argv[++i]; break; + case 'i': if (argv[i+1]) + { + cSystemNotification::setPidFile(argv[++i]); + break; + } + + default: + { + showUsage(); + return 0; + } + } + } + + if (logstdout != na) EpgdConfig.logstdout = logstdout; + if (loglevel != na) EpgdConfig.loglevel = loglevel; + if (loglevel != na) EpgdConfig.argLoglevel = loglevel; + + EpgdConfig.logName = "epghttpd"; + EpgdConfig.logFacility = Syslog::toCode("user"); + + // fork daemon + + if (!nofork) + { + if ((pid = fork()) < 0) + { + printf("Can't fork daemon, %s\n", strerror(errno)); + return 1; + } + + if (pid != 0) + return 0; + } + + job = new cEpgHttpd; + + if (job->init() != success) + { + job->exit(); + delete job; + return 1; + } + + // register signal handler + + ::signal(SIGINT, cEpgHttpd::downF); + ::signal(SIGTERM, cEpgHttpd::downF); + + // do work ... + + job->loop(); + + // shutdown + + job->exit(); + tell(0, "normal exit"); + + delete job; + + return 0; +} |