diff options
Diffstat (limited to 'webstore.c')
-rw-r--r-- | webstore.c | 786 |
1 files changed, 786 insertions, 0 deletions
diff --git a/webstore.c b/webstore.c new file mode 100644 index 0000000..d16513a --- /dev/null +++ b/webstore.c @@ -0,0 +1,786 @@ +/* + * webstore.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 "httpd.h" + +//*************************************************************************** +// Store Channels +//*************************************************************************** + +int cEpgHttpd::storeChannels(json_t* jInData, json_t* response) +{ + json_t* channels = json_object_get(jInData, "channels"); + + if (!channels) + return buildResponse(response, MHD_HTTP_BAD_REQUEST, "Error: Got unexpected json object"); + + connection->startTransaction(); + + for (void* iter = json_object_iter(channels); iter; iter = json_object_iter_next(channels, iter)) + { + int ord = na; + int visible = na; + const char* name = 0; + const char* channelid = json_object_iter_key(iter); + + for (void* iter = json_object_iter(json_object_get(channels, channelid)); iter; + iter = json_object_iter_next(json_object_get(channels, channelid), iter)) + { + const char* key = json_object_iter_key(iter); + json_t* _val = json_object_iter_value(iter); + + if (strcmp(key, "sources") == 0) + tell(3, " 'store ext sources and id' ... to be implemented ..."); // store ext sources and id -> TO BE IMPLEMENTED + else if (strcmp(key, "name") == 0) + name = json_string_value(_val); + else if (strcmp(key, "visible") == 0) + visible = json_integer_value(_val); + else if (strcmp(key, "order") == 0) + ord = json_integer_value(_val); + else + tell(0, "Error: Got unexpected json object '%s'", key); + } + + if (!isEmpty(channelid) && ord != na && visible != na) + { + tell(3, "Store: '%s' name=%s; ord=%d; visible=%d", channelid, name, ord, visible); + mapDb->clear(); + mapDb->setValue("ChannelId", channelid); + mapDb->setValue("ChannelName", name); + mapDb->setValue("Order", ord); + mapDb->setValue("Visible", visible); + + updateMap->execute(); // error handling needed? + updateMap->freeResult(); + } + } + + connection->commit(); + + return buildResponse(response, MHD_HTTP_OK, "stored"); +} + +//*************************************************************************** +// Delete Done Timer(s) +//*************************************************************************** + +int cEpgHttpd::deleteDoneTimers(json_t* jInData, json_t* response) +{ + json_t* idArray = json_object_get(jInData, "delete"); + + // --------------------------- + // delete done timers by id + + if (!idArray || !json_is_array(idArray)) + return buildResponse(response, MHD_HTTP_BAD_REQUEST, "Error: Got unexpected json object"); + + int length = json_array_size(idArray); + + for (int i = 0; i < length; ++i) + { + json_t* obj = json_array_get(idArray, i); + long id = json_integer_value(obj); + + // delete olny if not assumed in meanwhile! + + timersDoneDb->deleteWhere("id = %ld", id); + } + + return buildResponse(response, MHD_HTTP_OK, "done"); +} + +//*************************************************************************** +// Delete Timer Job(s) +// -> remove pending timer action from table (cleanup) +//*************************************************************************** + +int cEpgHttpd::deleteTimerJobs(json_t* jInData, json_t* response) +{ + json_t* idArray = json_object_get(jInData, "delete"); + + // --------------------------- + // delete timer jobs + + if (!idArray || !json_is_array(idArray)) + return buildResponse(response, MHD_HTTP_BAD_REQUEST, "Error: Got unexpected json object"); + + int length = json_array_size(idArray); + + for (int i = 0; i < length; ++i) + { + json_t* obj = json_array_get(idArray, i); + long id = json_integer_value(obj); + + // delete olny if not assumed in meanwhile! + + timerDb->deleteWhere("id = %ld and action != 'A'", id); // taAssumed + } + + return buildResponse(response, MHD_HTTP_OK, "done"); +} + +//*************************************************************************** +// Store Timer Job +// -> store timer job for distribution to the VDR(s) +// even 'delete-jobs' to distribution the delete request to the VDR(s) +//*************************************************************************** + +int cEpgHttpd::storeTimerJob(json_t* jInData, json_t* response) +{ + int hasEvent = no; + long doneid = na; + + json_t* idArray = json_object_get(jInData, "delete"); + + // --------------------------- + // delete some timers + + if (idArray && json_is_array(idArray)) + { + int length = json_array_size(idArray); + + for (int i = 0; i < length; ++i) + { + json_t* obj = json_array_get(idArray, i); + long timerid = json_integer_value(obj); + + // lookup timer to get doneid + + timerDb->clear(); + timerDb->setValue("ID", timerid); + + if (!selectTimerById->find()) + { + selectTimerById->freeResult(); + tell(0, "Timer (%ld) not found, maybe deleted from other user, skipping", timerid); + continue; + } + + selectTimerById->freeResult(); + + timerDb->setCharValue("ACTION", taDelete); + timerDb->setValue("SOURCE", "webif"); + timerDb->update(); + } + } + + // ---------------------------- + // store changes of ONE timer + + else + { + long int eventStartTime = 0; + cDbRow timerRow("timers"); + + timerRow.clear(); + + // first get and check event and channel (if set) + + int eventid = getIntFromJson(jInData, "eventid", na); + int timerid = getIntFromJson(jInData, "id", na); + time_t day = getIntFromJson(jInData, "day", na); + + if (eventid != na) + { + int unknown = yes; + const char* channelname = ""; + + useeventsDb->clear(); + useeventsDb->setValue("USEID", eventid); + + if (!selectEvent->find()) + { + selectEvent->freeResult(); + return buildResponse(response, MHD_HTTP_NOT_FOUND, "Event '%d' not found, " + "aborting request!", eventid); + } + + long int endTime = useeventsDb->getIntValue("STARTTIME") + useeventsDb->getIntValue("DURATION"); + eventStartTime = useeventsDb->getIntValue("STARTTIME"); + + if (endTime < time(0)) + { + selectEvent->freeResult(); + + return buildResponse(response, MHD_HTTP_BAD_REQUEST, "Event '%d' finished in the past, " + "aborting request!", eventid); + } + + hasEvent = yes; + + // --------------- + // lookup channel + + mapDb->clear(); + mapDb->setValue("CHANNELID", useeventsDb->getStrValue("CHANNELID")); + + if (selectChannelFromMap->find()) + { + unknown = mapDb->getIntValue("UNKNOWNATVDR"); + channelname = mapDb->getStrValue("CHANNELNAME"); + + if (isEmpty(channelname)) + channelname = useeventsDb->getStrValue("CHANNELID"); + } + + selectChannelFromMap->freeResult(); + + if (unknown) + { + return buildResponse(response, MHD_HTTP_NOT_FOUND, "Channel '%s' not found or unknown for at least one VDR" + ", aborting request!", + useeventsDb->getStrValue("CHANNELID")); + } + + // in case of a 'new' event based timer add it to timersdone table (if desired) + + long int manualTimer2Done; + + getParameter("epgd", "manualTimer2Done", manualTimer2Done); + + if (timerid == na && manualTimer2Done) + { + timersDoneDb->clear(); + timersDoneDb->setCharValue("STATE", tdsTimerRequested); + timersDoneDb->setValue("SOURCE", "webif"); + timersDoneDb->setValue("CHANNELID", useeventsDb->getStrValue("CHANNELID")); + timersDoneDb->setValue("STARTTIME", useeventsDb->getIntValue("STARTTIME")); + timersDoneDb->setValue("DURATION", useeventsDb->getIntValue("DURATION")); + timersDoneDb->setValue("TITLE", useeventsDb->getStrValue("TITLE")); + timersDoneDb->setValue("COMPTITLE", useeventsDb->getStrValue("COMPTITLE")); + + if (!useeventsDb->getValue("SHORTTEXT")->isEmpty()) + timersDoneDb->setValue("SHORTTEXT", useeventsDb->getStrValue("SHORTTEXT")); + if (!useeventsDb->getValue("COMPSHORTTEXT")->isEmpty()) + timersDoneDb->setValue("COMPSHORTTEXT", useeventsDb->getStrValue("COMPSHORTTEXT")); + + if (!useeventsDb->getValue("LONGDESCRIPTION")->isEmpty()) + timersDoneDb->setValue("LONGDESCRIPTION", useeventsDb->getStrValue("LONGDESCRIPTION")); + if (!useeventsDb->getValue("COMPLONGDESCRIPTION")->isEmpty()) + timersDoneDb->setValue("COMPLONGDESCRIPTION", useeventsDb->getStrValue("COMPLONGDESCRIPTION")); + + if (!useeventsDb->getValue("EPISODECOMPNAME")->isEmpty()) + timersDoneDb->setValue("EPISODECOMPNAME", useeventsDb->getStrValue("EPISODECOMPNAME")); + if (!useeventsDb->getValue("EPISODECOMPSHORTNAME")->isEmpty()) + timersDoneDb->setValue("EPISODECOMPSHORTNAME", useeventsDb->getStrValue("EPISODECOMPSHORTNAME")); + + if (!useeventsDb->getValue("EPISODECOMPPARTNAME")->isEmpty()) + timersDoneDb->setValue("EPISODECOMPPARTNAME", useeventsDb->getStrValue("EPISODECOMPPARTNAME")); + if (!useeventsDb->getValue("EPISODELANG")->isEmpty()) + timersDoneDb->setValue("EPISODELANG", useeventsDb->getStrValue("EPISODELANG")); + if (!useeventsDb->getValue("EPISODESEASON")->isEmpty()) + timersDoneDb->setValue("EPISODESEASON", useeventsDb->getIntValue("EPISODESEASON")); + if (!useeventsDb->getValue("EPISODEPART")->isEmpty()) + timersDoneDb->setValue("EPISODEPART", useeventsDb->getIntValue("EPISODEPART")); + + if (!isEmpty(channelname)) + timersDoneDb->setValue("CHANNELNAME", channelname); + + timersDoneDb->insert(); + doneid = timersDoneDb->getLastInsertId(); + } + } + + // ----------------------------------- + // get optional data + + timerRow.setValue("ID", timerid); + + if (doneid != na) + timerRow.setValue("DONEID", doneid); + + timerRow.setValue("SOURCE", "webif"); + timerRow.setValue("ACTIVE", yes); + getFieldFromJson(jInData, &timerRow, "ACTIVE"); + + int namingmode = getIntFromJson(jInData, "namingmode", tnmAuto); + const char* tmplExpression = getStringFromJson(jInData, "template", ""); + + if (namingmode != tnmDefault && hasEvent) + { + if (ptyRecName->execute(useeventsDb, namingmode, tmplExpression) == success) + { + tell(1, "Info: The recording name (mode=%d) calculated by 'recording.py' is '%s'", + namingmode, ptyRecName->getResult()); + + if (!isEmpty(ptyRecName->getResult())) + timerRow.setValue("FILE", ptyRecName->getResult()); + } + } + + timerRow.setValue("TEMPLATE", tmplExpression); + timerRow.setValue("NAMINGMODE", namingmode); + + if (day != na) + { + day = midnightOf(day); + timerRow.setValue("DAY", day); + } + + if (eventid != na) + { + timerRow.setValue("EVENTID", eventid); + timerRow.setValue("_STARTTIME", eventStartTime); + } + + getFieldFromJson(jInData, &timerRow, "VDRUUID"); + + if (timerRow.getValue("VDRUUID")->isEmpty()) + timerRow.setValue("VDRUUID", "any"); // the default + + getFieldFromJson(jInData, &timerRow, "STARTTIME"); + getFieldFromJson(jInData, &timerRow, "ENDTIME"); + getFieldFromJson(jInData, &timerRow, "WEEKDAYS"); + getFieldFromJson(jInData, &timerRow, "VPS"); + getFieldFromJson(jInData, &timerRow, "PRIORITY"); + getFieldFromJson(jInData, &timerRow, "LIFETIME"); + getFieldFromJson(jInData, &timerRow, "CHILDLOCK"); + getFieldFromJson(jInData, &timerRow, "CHANNELID"); + getFieldFromJson(jInData, &timerRow, "DIRECTORY"); + + modifyCreateTimer(&timerRow); + } + + triggerVdrs("TIMERJOB", "epg2vdr"); + + selectEvent->freeResult(); + + return buildResponse(response, MHD_HTTP_OK, "stored"); +} + +//*************************************************************************** +// Modify Timer (copy paste from cMenuDb of epg2vdr) +// +// - timerRow contains the destination vdrUuid +//*************************************************************************** + +int cEpgHttpd::modifyCreateTimer(cDbRow* timerRow) +{ + int status = success; + int timerid = timerRow->getIntValue("ID"); + int knownTimer = timerid != na; + int move = no; + + connection->startTransaction(); + + timerDb->clear(); + + // lookup known (existing) timer + + if (knownTimer) + { + timerDb->copyValues(timerRow, cDBS::ftPrimary); + + if (!timerDb->find()) + { + connection->commit(); + + tell(0, "Timer (%d) at vdr '%s' not found, aborting modify request!", + timerid, timerDb->getStrValue("VDRUUID")); + + return fail; + } + + // found and all values are loaded! + + // move to another vdr? + + if (!timerDb->hasValue("VDRUUID", timerRow->getStrValue("VDRUUID"))) + move = yes; + } + else + { + timerDb->setValue("VDRUUID", timerRow->getStrValue("VDRUUID")); + } + + if (move) + { + // request 'D'elete of 'old' timer + + timerDb->setCharValue("ACTION", taDelete); + timerDb->setValue("SOURCE", timerRow->getStrValue("SOURCE")); + status = timerDb->update(); + + // triggerVdrs("TIMERJOB", timerDb->getStrValue("VDRUUID")); + + // create new on other vdr + + timerDb->copyValues(timerRow, cDBS::ftData); // takeover all data (possibly modified by user) + timerDb->setValue("ID", 0); + timerDb->setCharValue("ACTION", taCreate); + status += timerDb->insert(); + + if (status == success) + tell(1, "Created 'move' request for timer (%d) at vdr '%s'", + timerid, timerDb->getStrValue("VDRUUID")); + } + else + { + // create 'C'reate oder 'M'odify request ... + + timerDb->copyValues(timerRow, cDBS::ftData); + timerDb->setCharValue("ACTION", knownTimer ? taModify : taCreate); + + if (knownTimer) + status = timerDb->update(); + else + status = timerDb->insert(); + + if (status == success) + tell(1, "Created '%s' request for timer (%d) at vdr '%s'", + knownTimer ? "modify" : "create", + timerid, timerDb->getStrValue("VDRUUID")); + } + + connection->commit(); + + // triggerVdrs("TIMERJOB", timerDb->getStrValue("VDRUUID")); + + return status; +} + +//*************************************************************************** +// Store Timer +//*************************************************************************** + +int cEpgHttpd::storeSearchTimer(json_t* jInData, json_t* response) +{ + cDbTableDef* def = searchtimerDb->getTableDef(); + int insert = yes; + + // get data + + json_t* idArray = json_object_get(jInData, "delete"); + + // --------------------------- + // delete some search timers + + if (idArray && json_is_array(idArray)) + { + int length = json_array_size(idArray); + int deleted = 0; + int failed = 0; + + for (int i = 0; i < length; ++i) + { + json_t* obj = json_array_get(idArray, i); + long id = json_integer_value(obj); + + searchtimerDb->clear(); + searchtimerDb->setValue("ID", id); + + if (!searchtimerDb->find()) + { + failed++; + // failedMsg += + tell(0, "Warning: Can't delete searchtimer %ld, not fount, ignoring", id); + continue; + } + + searchtimerDb->setValue("STATE", "D"); + searchtimerDb->update(); + deleted++; + } + + return buildResponse(response, failed ? MHD_HTTP_CONFLICT : MHD_HTTP_OK, + "deleted %d searchtimer, %d failed", deleted, failed); + } + + // get/check data + + int id = getIntFromJson(jInData, "id", na); + const char* expression = getStringFromJson(jInData, "expression", ""); + const char* name = getStringFromJson(jInData, "name", expression); + const char* state = getStringFromJson(jInData, "state", ""); + + tell(2, "%s searchtimer%s '%s' with expression '%s'", + *state == 'D' ? "Delete" : id == na ? "Append" : "Modify", + id == na ? "" : std::string(" " + num2Str(id)).c_str(), name , expression); + + // web deliver name = "" instead of null if not set + + if (isEmpty(name)) + name = expression; + + if (isEmpty(expression) && *state != 'D') + return buildResponse(response, MHD_HTTP_BAD_REQUEST, "Ignoring searchtimer request without an expression!"); + + searchtimerDb->clear(); + + if (id != na) + { + searchtimerDb->setValue("ID", id); + + if (!searchtimerDb->find()) + return buildResponse(response, MHD_HTTP_NOT_FOUND, "Ignoring change request for searchtimer %d, not found!", id); + + insert = no; + } + + if (*state == 'D') + { + searchtimerDb->setValue("STATE", "D"); + searchtimerDb->update(); + return buildResponse(response, MHD_HTTP_OK, "deleted"); + } + + const char* specialfields = "STATE:NAME:HITS:SOURCE:ACTIVE:SEARCHMODE:SEARCHFIELDS:NAMINGMODE:VDRUUID:MODSP:LASTRUN"; + + for (int i = 0; i < def->fieldCount(); i++) + { + if (def->getField(i)->hasType(cDBS::ftMeta) || def->getField(i)->hasType(cDBS::ftPrimary)) + continue; + + if (strstr(specialfields, def->getField(i)->getName())) + continue; + + getFieldFromJson(jInData, searchtimerDb->getRow(), def->getField(i)->getName()); + } + + int active = getIntFromJson(jInData, "active", yes); + int searchmode = getIntFromJson(jInData, "searchmode", smRegexp); + int searchfields = getIntFromJson(jInData, "searchfields", sfTitle); + int namingmode = getIntFromJson(jInData, "namingmode", tnmAuto); + const char* vdruuid = getStringFromJson(jInData, "vdruuid", "any"); + int nameChanged = !insert && searchtimerDb->hasValue("NAME", name); + + searchtimerDb->setValue("NAME", name); + searchtimerDb->setValue("STATE", ""); + searchtimerDb->setValue("SOURCE", "webif"); + searchtimerDb->setValue("ACTIVE", active); + searchtimerDb->setValue("SEARCHMODE", searchmode); + searchtimerDb->setValue("SEARCHFIELDS", searchfields); + searchtimerDb->setValue("NAMINGMODE", namingmode); + searchtimerDb->setValue("VDRUUID", vdruuid); + searchtimerDb->setValue("MODSP", time(0)); + searchtimerDb->setValue("LASTRUN", 0); // reset last run to force search + + // store .... + + if (insert) + searchtimerDb->insert(); + else + searchtimerDb->update(); + + if (id != na && nameChanged) + { + // #TODO - update AUTOTIMERNAME in timers an timersdone ... + + timerDb->clear(); + timerDb->setValue("AUTOTIMERID", id); + timerDb->setValue("AUTOTIMERNAME", name); + updateTimerAName->execute(); + + timersDoneDb->clear(); + timersDoneDb->setValue("AUTOTIMERID", id); + timersDoneDb->setValue("AUTOTIMERNAME", name); + updateDoneAName->execute(); + } + + // result + + return buildResponse(response, MHD_HTTP_OK, "stored"); +} + +//*************************************************************************** +// Store Parameters +//*************************************************************************** + +int cEpgHttpd::storeParameters(json_t* jInData, json_t* response) +{ + int stored = 0; + int failed = 0; + int statusCode = MHD_HTTP_OK; + json_t* oFails = json_array(); + json_t* parameters = json_object_get(jInData, "parameters"); + + if (!parameters && !json_is_array(parameters)) + return buildResponse(response, MHD_HTTP_BAD_REQUEST, "Error: Got unexpected json object"); + + int length = json_array_size(parameters); + + for (int i = 0; i < length; ++i) + { + json_t* obj = json_array_get(parameters, i); + + const char* owner = getStringFromJson(obj, "owner"); + const char* name = getStringFromJson(obj, "name"); + const char* value = getStringFromJson(obj, "value"); + + if (!isEmpty(owner) && !isEmpty(name)) + { + // Owner "@" used for 'needLogin = no' + // If owner "@...." check user to avoid edit parameter of others ... + + if (owner[0] == '@' && strlen(owner) > 1) + { + if (!currentSession || strcmp(owner+1, currentSession->user.c_str()) != 0) + { + tell(0, "Warning: Modification of parameter '%s/%s' for other user '%s' rejected", + owner+1, name, currentSession ? currentSession->user.c_str() : "<null>"); + failed++; + + continue; + } + } + + if (!value) + { + tell(0, "Warning: Ignoring save request for parameter '%s/%s', missing value", owner+1, name); + json_array_append_new(oFails, json_integer(i)); + failed++; + + continue; + } + + if (setParameter(owner, name, value) == success) + stored++; + else + { + json_array_append_new(oFails, json_integer(i)); + failed++; + } + } + } + + // if at least one faild .. + + if (failed) + statusCode = MHD_HTTP_CONFLICT; + + int status = buildResponse(response, statusCode, "stored %d parameters, " + "%d failed - see log for details", stored, failed); + + json_t* oResult = json_object_get(response, "result"); + + if (failed && oResult) + json_object_set_new(oResult, "failed", oFails); + + return status; +} + +//*************************************************************************** +// Store Users +//*************************************************************************** + +int cEpgHttpd::storeUsers(json_t* jInData, json_t* response) +{ + json_t* users = json_object_get(jInData, "users"); + + if (!users && !json_is_array(users)) + return buildResponse(response, MHD_HTTP_NOT_FOUND, "Error: Got unexpected json object"); + + if (needLogin() && !currentSession) + return buildResponse(response, MHD_HTTP_FORBIDDEN, "Rejecting request, " + "insufficient rights for user management!"); + + int length = json_array_size(users); + + for (int i = 0; i < length; ++i) + { + int insert; + json_t* obj = json_array_get(users, i); + const char* user = getStringFromJson(obj, "user"); + const char* state = getStringFromJson(obj, "state"); + + // tell(0, "STATE '%s'; USER '%s'", state, user); + + if (!isEmpty(user) && !isEmpty(state)) + { + const char* passwd = getStringFromJson(obj, "passwd"); + int active = getIntFromJson(obj, "active"); + int rights = getIntFromJson(obj, "rights"); + + userDb->clear(); + userDb->setValue("USER", user); + + // search user to collect data .. if exist + + insert = !userDb->find(); + + if (!insert && state[0] == 'C') + { + tell(0, "Warning: Ignoring 'create' user request, user '%s' already exists", + user); + continue; + } + else if (insert && state[0] != 'C') + { + tell(0, "Warning: Ignoring '%s' user request, user '%s' not found", + state[0] == 'D' ? "delete" : "modify", user); + continue; + } + + if (state[0] == 'D') + { + userDb->reset(); + userDb->deleteWhere("user = '%s'", user); + + continue; + } + + // update values + + if (!isEmpty(passwd)) + userDb->setValue("PASSWD", passwd); + + if (active != na) + userDb->setValue("ACTIVE", active); + + if (rights != na) + { + if (!needLogin()) + userDb->setValue("RIGHTS", rights); + else if (currentSession->rights) + userDb->setValue("RIGHTS", rights & currentSession->rights); + } + + userDb->store(); + userDb->reset(); + + tell(1, "%s user '%s'", state[0] == 'D' ? "deleted" : insert ? "Inserted" : "Updated", user); + } + } + + return buildResponse(response, MHD_HTTP_OK, "stored"); +} + +//*************************************************************************** +// mark Messages +//*************************************************************************** + +int cEpgHttpd::markMessages(json_t* jInData, json_t* response) +{ + json_t* idArray = json_object_get(jInData, "messages"); + + // --------------------------- + // mark messages + + if (!idArray || !json_is_array(idArray)) + return buildResponse(response, MHD_HTTP_BAD_REQUEST, "Error: Got unexpected json object"); + + int length = json_array_size(idArray); + + for (int i = 0; i < length; ++i) + { + json_t* obj = json_array_get(idArray, i); + long id = getIntFromJson(obj, "id"); + const char* state = getStringFromJson(obj, "state"); + + messageDb->setValue("ID", id); + + if (messageDb->find()) + { + messageDb->setValue("STATE", state); + messageDb->store(); + } + } + + return buildResponse(response, MHD_HTTP_OK, "done"); +} |