void EventsResponder::reply(ostream& out, cxxtools::http::Request& request, cxxtools::http::Reply& reply) { if ( request.method() == "OPTIONS" ) { reply.addHeader("Allow", "GET, POST"); reply.httpReturn(200, "OK"); return; } QueryHandler::addHeader(reply); if ( (int)request.url().find("/events/image/") == 0 ) { replyImage(out, request, reply); } else if ( (int)request.url().find("/events/search") == 0 ){ replySearchResult(out, request, reply); } else if ( (int)request.url().find("/events/contentdescriptors") == 0 ){ replyContentDescriptors(out, request, reply); } else { replyEvents(out, request, reply); } }
void TimersResponder::createOrUpdateTimer(ostream& out, cxxtools::http::Request& request, cxxtools::http::Reply& reply, bool update) { QueryHandler q("/timers", request); if ( Timers.BeingEdited() ) { reply.httpReturn(502, "Timers are being edited - try again later"); return; } int error = false; string error_values = ""; static TimerValues v; int flags = v.ConvertFlags(q.getBodyAsString("flags")); string aux = v.ConvertAux(q.getBodyAsString("aux")); string file = v.ConvertFile(q.getBodyAsString("file")); int lifetime = v.ConvertLifetime(q.getBodyAsString("lifetime")); int priority = v.ConvertPriority(q.getBodyAsString("priority")); int stop = v.ConvertStop(q.getBodyAsString("stop")); int start = v.ConvertStart(q.getBodyAsString("start")); string weekdays = q.getBodyAsString("weekdays"); string day = v.ConvertDay(q.getBodyAsString("day")); cChannel* chan = v.ConvertChannel(q.getBodyAsString("channel")); cTimer* timer_orig = v.ConvertTimer(q.getBodyAsString("timer_id")); if ( update == false ) { //create int eventid = q.getBodyAsInt("eventid"); int minpre = q.getBodyAsInt("minpre"); int minpost = q.getBodyAsInt("minpost"); if (eventid >= 0 && chan != NULL) { cEvent* event = VdrExtension::GetEventById((tEventID)eventid, chan); if (event == NULL) { reply.httpReturn(407, "eventid invalid"); return; } else { if (minpre < 0) minpre = 0; if (minpost < 0) minpost = 0; if (!v.IsFlagsValid(flags)) flags = 1; if (!v.IsFileValid(file)) file = (string)event->Title(); if (!v.IsWeekdaysValid(weekdays)) weekdays = "-------"; if (!v.IsLifetimeValid(lifetime)) lifetime = 50; if (!v.IsPriorityValid(priority)) priority = 99; chan = VdrExtension::getChannel((const char*)event->ChannelID().ToString()); if (!v.IsStartValid(start) || !v.IsStopValid(stop) || !v.IsDayValid(day)) { time_t estart = event->StartTime(); time_t estop = event->EndTime(); struct tm *starttime = localtime(&estart); ostringstream daystream; daystream << StringExtension::addZeros((starttime->tm_year + 1900), 4) << "-" << StringExtension::addZeros((starttime->tm_mon + 1), 2) << "-" << StringExtension::addZeros((starttime->tm_mday), 2); day = daystream.str(); start = starttime->tm_hour * 100 + starttime->tm_min - ((int)(minpre/60))*100 - minpre%60; struct tm *stoptime = localtime(&estop); stop = stoptime->tm_hour * 100 + stoptime->tm_min + ((int)(minpost/60))*100 + minpost%60; } } } else { if ( !v.IsFlagsValid(flags) ) { flags = 1; } if ( !v.IsFileValid(file) ) { error = true; error_values += "file, "; } if ( !v.IsLifetimeValid(lifetime) ) { lifetime = 50; } if ( !v.IsPriorityValid(priority) ) { priority = 99; } if ( !v.IsStopValid(stop) ) { error = true; error_values += "stop, "; } if ( !v.IsStartValid(start) ) { error = true; error_values += "start, "; } if ( !v.IsWeekdaysValid(weekdays) ) { error = true; error_values += "weekdays, "; } if ( !v.IsDayValid(day)&& !day.empty() ) { error = true; error_values += "day, "; } if ( chan == NULL ) { error = true; error_values += "channel, "; } } } else { //update if ( timer_orig == NULL ) { error = true; error_values += "timer_id, "; } if ( !error ) { if ( !v.IsFlagsValid(flags) ) { flags = timer_orig->Flags(); } if ( !v.IsFileValid(file) ) { file = (string)timer_orig->File(); } if ( !v.IsLifetimeValid(lifetime) ) { lifetime = timer_orig->Lifetime(); } if ( !v.IsPriorityValid(priority) ) { priority = timer_orig->Priority(); } if ( !v.IsStopValid(stop) ) { stop = timer_orig->Stop(); } if ( !v.IsStartValid(start) ) { start = timer_orig->Start(); } if ( !v.IsWeekdaysValid(weekdays) ) { weekdays = v.ConvertWeekdays(timer_orig->WeekDays()); } if ( !v.IsDayValid(day) ) { day = v.ConvertDay(timer_orig->Day()); } if ( chan == NULL ) { chan = (cChannel*)timer_orig->Channel(); } } } if (error) { string error_message = (string)"The following parameters aren't valid: " + error_values.substr(0, error_values.length()-2) + (string)"!"; reply.httpReturn(403, error_message); return; } ostringstream builder; builder << flags << ":" << (const char*)chan->GetChannelID().ToString() << ":" << ( weekdays != "-------" ? weekdays : "" ) << ( weekdays == "-------" || day.empty() ? "" : "@" ) << day << ":" << start << ":" << stop << ":" << priority << ":" << lifetime << ":" << file << ":" << aux; dsyslog("restfulapi: /%s/ ", builder.str().c_str()); chan = NULL; if ( update == false ) { // create timer cTimer* timer = new cTimer(); if ( timer->Parse(builder.str().c_str()) ) { cTimer* checkTimer = Timers.GetTimer(timer); if ( checkTimer != NULL ) { delete timer; reply.httpReturn(403, "Timer already defined!"); esyslog("restfulapi: Timer already defined!"); } else { replyCreatedId(timer, request, reply, out); timer->SetEventFromSchedule(); Timers.Add(timer); Timers.SetModified(); esyslog("restfulapi: timer created!"); } } else { reply.httpReturn(403, "Creating timer failed!"); esyslog("restfulapi: timer creation failed!"); } } else { if ( timer_orig->Parse(builder.str().c_str()) ) { timer_orig->SetEventFromSchedule(); Timers.SetModified(); replyCreatedId(timer_orig, request, reply, out); esyslog("restfulapi: updating timer successful!"); } else { reply.httpReturn(403, "updating timer failed!"); esyslog("restfulapi: updating timer failed!"); } } }
void RecordingsResponder::reply(ostream& out, cxxtools::http::Request& request, cxxtools::http::Reply& reply) { QueryHandler::addHeader(reply); bool found = false; if (request.method() == "OPTIONS") { return; } if ((int)request.url().find("/recordings/play") == 0 ) { if ( request.method() == "GET" ) { playRecording(out, request, reply); reply.addHeader("Content-Type", "text/plain; charset=utf-8"); } else if (request.method() == "POST") { rewindRecording(out, request, reply); reply.addHeader("Content-Type", "text/plain; charset=utf-8"); } else { reply.httpReturn(501, "Only GET and POST method is supported by the /recordings/play service."); } found = true; } else if ((int)request.url().find("/recordings/cut") == 0 ) { if ( request.method() == "GET" ) { showCutterStatus(out, request, reply); } else if (request.method() == "POST") { cutRecording(out, request, reply); } else { reply.httpReturn(501, "Only GET and POST methods are supported by the /recordings/cut service."); } found = true; } else if ((int)request.url().find("/recordings/marks") == 0 ) { if ( request.method() == "DELETE" ) { deleteMarks(out, request, reply); } else if (request.method() == "POST" ) { saveMarks(out, request, reply); } else { reply.httpReturn(501, "Only DELETE and POST methods are supported by the /recordings/marks service."); } found = true; } // original /recordings service else if ((int) request.url().find("/recordings") == 0 ) { if ( request.method() == "GET" ) { showRecordings(out, request, reply); found = true; } else if (request.method() == "DELETE" ) { deleteRecording(out, request,reply); found = true; } else { reply.httpReturn(501, "Only GET and DELETE methods are supported by the /recordings service."); } found = true; } if (!found) { reply.httpReturn(403, "Service not found"); } }
void TimersResponder::replyBulkdelete(std::ostream& out, cxxtools::http::Request& request, cxxtools::http::Reply& reply) { QueryHandler q("/timers/bulkdelete", request); #if APIVERSNUM > 20300 LOCK_TIMERS_WRITE; cTimers& timers = *Timers; #else cTimers& timers = Timers; if ( timers.BeingEdited() ) { reply.httpReturn(502, "Timers are being edited - try again later"); return; } #endif TimerDeletedList* list; if ( q.isFormat(".json") ) { reply.addHeader("Content-Type", "application/json; charset=utf-8"); list = (TimerDeletedList*)new JsonTimerDeletedList(&out); } else if ( q.isFormat(".html") ) { reply.addHeader("Content-Type", "text/html; charset=utf-8"); list = (TimerDeletedList*)new HtmlTimerDeletedList(&out); } else if ( q.isFormat(".xml") ) { reply.addHeader("Content-Type", "text/xml; charset=utf-8"); list = (TimerDeletedList*)new XmlTimerDeletedList(&out); } else { reply.httpReturn(404, "Resources are not available for the selected format. (Use: .json, .html or .xml)"); return; } TimerValues v; cTimer* timer; vector< string > deleteTimers = q.getBodyAsStringArray("timers"); vector< SerBulkDeleted > results; SerBulkDeleted result; size_t i; list->init(); for ( i = 0; i < deleteTimers.size(); i++ ) { timer = v.ConvertTimer(deleteTimers[i]); result.id = deleteTimers[i]; if ( timer == NULL ) { result.deleted = false; } else { if ( timer->Recording() ) { timer->Skip(); #if APIVERSNUM > 20300 cRecordControls::Process(Timers, time(NULL)); #else cRecordControls::Process(time(NULL)); #endif } timers.Del(timer); timers.SetModified(); result.deleted = true; } list->addDeleted(result); } list->setTotal((int)deleteTimers.size()); list->finish(); delete list; };
void EventsResponder::replyContentDescriptors(std::ostream& out, cxxtools::http::Request& request, cxxtools::http::Reply& reply) { QueryHandler q("/events/contentdescriptors", request); if ( request.method() != "GET") { reply.httpReturn(403, "To retrieve content descriptors use the POST method!"); return; } StreamExtension se(&out); ContentDescriptorList* list; if ( q.isFormat(".json") ) { reply.addHeader("Content-Type", "application/json; charset=utf-8"); list = (ContentDescriptorList*)new JsonContentDescriptorList(&out); } else if ( q.isFormat(".html") ) { reply.addHeader("Content-Type", "text/html; charset=utf-8"); list = (ContentDescriptorList*)new HtmlContentDescriptorList(&out); } else if ( q.isFormat(".xml") ) { reply.addHeader("Content-Type", "text/xml; charset=utf-8"); list = (ContentDescriptorList*)new XmlContentDescriptorList(&out); } else { reply.httpReturn(403, "Resources are not available for the selected format. (Use: .json, .xml or .html)"); return; } list->init(); int total = 0; std::set<std::string> contentStrings; for(unsigned int i=0; i<CONTENT_DESCRIPTOR_MAX;i++) { const string contentDescr = cEvent::ContentToString(i); SerContentDescriptor cDescr; if (!contentDescr.empty() && contentStrings.find(contentDescr) == contentStrings.end()) { contentStrings.insert(contentDescr); cDescr.name = StringExtension::UTF8Decode(contentDescr); std::stringstream stream; stream << std::hex << i; std::string result( stream.str() ); switch (i) { case ecgArtsCulture: case ecgChildrenYouth: case ecgEducationalScience: case ecgLeisureHobbies: case ecgMovieDrama: case ecgMusicBalletDance: case ecgNewsCurrentAffairs: case ecgShow: case ecgSocialPoliticalEconomics: case ecgSpecial: case ecgSports: case ecgUserDefined: cDescr.isGroup = true; break; default: cDescr.isGroup = false; } cDescr.id = result; list->addDescr(cDescr); total++; } } list->setTotal(total); list->finish(); delete list; };
void EventsResponder::replyEvents(ostream& out, cxxtools::http::Request& request, cxxtools::http::Reply& reply) { QueryHandler q("/events", request); if ( request.method() != "GET") { reply.httpReturn(403, "To retrieve information use the GET method!"); return; } EventList* eventList; if ( q.isFormat(".json") ) { reply.addHeader("Content-Type", "application/json; charset=utf-8"); eventList = (EventList*)new JsonEventList(&out); } else if ( q.isFormat(".html") ) { reply.addHeader("Content-Type", "text/html; charset=utf-8"); eventList = (EventList*)new HtmlEventList(&out); } else if ( q.isFormat(".xml") ) { reply.addHeader("Content-Type", "text/xml; charset=utf-8"); eventList = (EventList*)new XmlEventList(&out); } else { reply.httpReturn(403, "Resources are not available for the selected format. (Use: .json or .html)"); return; } string channel_id = q.getParamAsString(0); int timespan = q.getOptionAsInt("timespan");//q.getParamAsInt(1); int from = q.getOptionAsInt("from");//q.getParamAsInt(2); int start_filter = q.getOptionAsInt("start"); int limit_filter = q.getOptionAsInt("limit"); int event_id = q.getParamAsInt(1);//q.getOptionAsInt("eventid"); string onlyCount = q.getOptionAsString("only_count"); #if APIVERSNUM > 20300 LOCK_CHANNELS_READ; const cChannels& channels = *Channels; #else cChannels& channels = Channels; #endif const cChannel* channel = VdrExtension::getChannel(channel_id); if ( channel == NULL ) { /*reply.addHeader("Content-Type", "application/octet-stream"); string error_message = (string)"Could not find channel with id: " + channel_id + (string)"!"; reply.httpReturn(404, error_message); return;*/ } int channel_limit = q.getOptionAsInt("chevents"); if ( channel_limit <= -1 ) channel_limit = 0; // default channel events is 0 -> all int channel_from = q.getOptionAsInt("chfrom"); if ( channel_from <= -1 || channel != NULL ) channel_from = 0; // default channel number is 0 int channel_to = q.getOptionAsInt("chto"); if ( channel_to <= 0 || channel != NULL ) channel_to = channels.Count(); if ( from <= -1 ) from = time(NULL); // default time is now if ( timespan <= -1 ) timespan = 0; // default timespan is 0, which means all entries will be returned int to = from + timespan; #if APIVERSNUM > 20300 LOCK_SCHEDULES_READ; #else cSchedulesLock MutexLock; const cSchedules *Schedules = cSchedules::Schedules(MutexLock); #endif if( !Schedules ) { reply.httpReturn(404, "Could not find schedules!"); return; } if ( start_filter >= 0 && limit_filter >= 1 ) { eventList->activateLimit(start_filter, limit_filter); } bool initialized = false; int total = 0; for(int i=0; i<channels.Count(); i++) { const cSchedule *Schedule = Schedules->GetSchedule(channels.Get(i)->GetChannelID()); if ((channel == NULL || strcmp(channel->GetChannelID().ToString(), channels.Get(i)->GetChannelID().ToString()) == 0) && (i >= channel_from && i <= channel_to)) { if (!Schedule) { if (channel != NULL) { reply.httpReturn(404, "Could not find schedule!"); return; } } else { if (!initialized) { eventList->init(); initialized = true; } int old = 0; int channel_events = 0; for(const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event)) { int ts = event->StartTime(); int te = ts + event->Duration(); if ((ts <= to && te > from) || (te > from && timespan == 0)) { if (channel_limit == 0 || channel_limit > channel_events) { if ((event_id < 0 || event_id == (int)event->EventID()) && onlyCount != "true") { eventList->addEvent(event); channel_events++; } } } else { if (ts > to) break; if (te <= from) old++; } } total += (Schedule->Events()->Count() - old); } } } eventList->setTotal(total); eventList->finish(); delete eventList; }
void EventsResponder::replySearchResult(ostream& out, cxxtools::http::Request& request, cxxtools::http::Reply& reply) { QueryHandler q("/events/search", request); if ( request.method() != "POST") { reply.httpReturn(403, "To search for information use the POST method!"); return; } StreamExtension se(&out); string query = q.getBodyAsString("query"); string search = q.getBodyAsString("search"); if ( query.length() == 0 && search.length() == 0 ) { reply.httpReturn(402, "Query required"); return; } EventList* eventList; if ( q.isFormat(".json") ) { reply.addHeader("Content-Type", "application/json; charset=utf-8"); eventList = (EventList*)new JsonEventList(&out); } else if ( q.isFormat(".html") ) { reply.addHeader("Content-Type", "text/html; charset=utf-8"); eventList = (EventList*)new HtmlEventList(&out); } else if ( q.isFormat(".xml") ) { reply.addHeader("Content-Type", "text/xml; charset=utf-8"); eventList = (EventList*)new XmlEventList(&out); } else { reply.httpReturn(403, "Resources are not available for the selected format. (Use: .json or .html)"); return; } eventList->init(); int start_filter = q.getOptionAsInt("start"); int limit_filter = q.getOptionAsInt("limit"); int date_filter = q.getOptionAsInt("date_limit"); if ( start_filter >= 0 && limit_filter >= 1 ) { eventList->activateLimit(start_filter, limit_filter); } if ( date_filter >= 0 ) { eventList->activateDateLimit(date_filter); } int total = 0; if ( search.length() > 0 ) { vdrlive::SearchTimer* searchtimer = new vdrlive::SearchTimer; searchtimer->SetId(0); string result = searchtimer->LoadCommonFromQuery(q); if (result.length() > 0) { reply.httpReturn(406, result.c_str()); return; } string query = searchtimer->ToText(); vdrlive::SearchResults* results = new vdrlive::SearchResults; results->GetByQuery(query); for (vdrlive::SearchResults::iterator result = results->begin(); result != results->end(); ++result) { eventList->addEvent(((cEvent*)result->GetEvent())); total++; } delete searchtimer; delete results; } else { int mode = q.getBodyAsInt("mode");// search mode (0=phrase, 1=and, 2=or, 3=exact, 4=regular expression, 5=fuzzy) string channelid = q.getBodyAsString("channelid"); //id !! bool use_title = q.getBodyAsString("use_title") == "true"; bool use_subtitle = q.getBodyAsString("use_subtitle") == "true"; bool use_description = q.getBodyAsString("use_description") == "true"; int channel = 0; const cChannel* channelInstance = VdrExtension::getChannel(channelid); if (channelInstance != NULL) { channel = channelInstance->Number(); } #if APIVERSNUM > 20300 LOCK_CHANNELS_READ; const cChannels& channels = *Channels; #else cChannels& channels = Channels; #endif if (!use_title && !use_subtitle && !use_description) use_title = true; if (mode < 0 || mode > 5) mode = 0; if (channel < 0 || channel > channels.Count()) channel = 0; if (query.length() > 100) query = query.substr(0,100); //don't allow more than 100 characters, NOTE: maybe I should add a limitation to the Responderclass? struct Epgsearch_searchresults_v1_0* epgquery = new struct Epgsearch_searchresults_v1_0; epgquery->query = (char*)query.c_str(); epgquery->mode = mode; epgquery->channelNr = channel; epgquery->useTitle = use_title; epgquery->useSubTitle = use_subtitle; epgquery->useDescription = use_description; cPlugin *Plugin = cPluginManager::GetPlugin("epgsearch"); if (Plugin) { if (Plugin->Service("Epgsearch-searchresults-v1.0", NULL)) { if (Plugin->Service("Epgsearch-searchresults-v1.0", epgquery)) { cList< Epgsearch_searchresults_v1_0::cServiceSearchResult>* result = epgquery->pResultList; Epgsearch_searchresults_v1_0::cServiceSearchResult* item = NULL; if (result != NULL) { for(int i=0;i<result->Count();i++) { item = result->Get(i); eventList->addEvent(((cEvent*)item->event)); total++; } } } else { reply.httpReturn(406, "Internal (epgsearch) error, check parameters."); } } else { reply.httpReturn(405, "Plugin-service not available."); } } else { reply.httpReturn(404, "Plugin not installed!"); } delete epgquery; } eventList->setTotal(total); eventList->finish(); delete eventList; }
bool KeyPairList::hitKey(cxxtools::http::Request& request, cxxtools::http::Reply& reply) { if ( (int)request.url().find("/remote/kbd") != -1) { QueryHandler q("/remote/kbd", request); cxxtools::String kbd = StringExtension::UTF8Decode(q.getBodyAsString("kbd")); if ( kbd == StringExtension::UTF8Decode("") ) { reply.httpReturn(400, "Key is empty."); } std::size_t n = 0; while (kbd[n]) { cRemote::Put(KBDKEY(kbd[n])); ++n; } return true; } else if ( (int)request.url().find("/remote/seq") != -1) { QueryHandler q("/remote/seq", request); JsonArray* seq = q.getBodyAsArray("seq"); if ( seq == NULL ) { reply.httpReturn(400, "Sequence is empty."); return false; } for (int i = 0; i < seq->CountItem(); i++) { JsonBase* jsonBase = seq->GetItem(i); if (jsonBase->IsBasicValue()) { JsonBasicValue* jsonBasicValue = (JsonBasicValue*)jsonBase; if (jsonBasicValue->IsString()) { string key = jsonBasicValue->ValueAsString(); for (int n=0;n<(int)key.length();n++ ) { key[n] = tolower(key[n]); } for (int x=0;x<(int)keys.size();x++) { if (string(keys[x].str) == key) { cRemote::Put(keys[x].key); } } } } } return true; } else { QueryHandler q("/remote", request); string key = q.getParamAsString(0); if (key.length() == 0) { reply.httpReturn(404, "Please add a key to the parameter list, see API-file for more details."); return false; } for (int i=0;i<(int)key.length();i++) { key[i] = tolower(key[i]); } for (int i=0;i<(int)keys.size();i++) { if (string(keys[i].str) == key) { cRemote::Put(keys[i].key); return true; } } } return false; }
void ChannelsResponder::replyChannels(std::ostream& out, cxxtools::http::Request& request, cxxtools::http::Reply& reply) { QueryHandler q("/channels", request); ChannelList* channelList; if ( q.isFormat(".json") ) { reply.addHeader("Content-Type", "application/json; charset=utf-8"); channelList = (ChannelList*)new JsonChannelList(&out); } else if ( q.isFormat(".html") ) { reply.addHeader("Content-Type", "text/html; charset=utf-8"); channelList = (ChannelList*)new HtmlChannelList(&out); } else if ( q.isFormat(".xml") ) { reply.addHeader("Content-Type", "text/xml; charset=utf-8"); channelList = (ChannelList*)new XmlChannelList(&out); } else { reply.httpReturn(403, "Resources are not available for the selected format. (Use: .json, .html or .xml)"); return; } std::string channel_details = q.getParamAsString(0); int start_filter = q.getOptionAsInt("start"); int limit_filter = q.getOptionAsInt("limit"); std::string group_filter = q.getOptionAsString("group"); if (channel_details.length() > 0) { cChannel* channel = VdrExtension::getChannel(channel_details); if (channel == NULL || channel->GroupSep()) { reply.httpReturn(403, "The requested channel is not available."); delete channelList; return; } else { channelList->init(); std::string group = ""; int total = 0; for (cChannel *channelIt = Channels.First(); channelIt; channelIt = Channels.Next(channelIt)) { if (!channelIt->GroupSep()) total++; else if ( total < channel->Number()) group = channelIt->Name(); } channelList->setTotal(total); std::string image = FileCaches::get()->searchChannelLogo(channel); channelList->addChannel(channel, group, image.length() == 0); } } else { if ( start_filter >= 0 && limit_filter >= 1 ) { channelList->activateLimit(start_filter, limit_filter); } channelList->init(); int total = 0; std::string group = ""; for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) { if (!channel->GroupSep()) { if ( group_filter.length() == 0 || group == group_filter ) { std::string image = FileCaches::get()->searchChannelLogo(channel); channelList->addChannel(channel, group, image.length() != 0); total++; } } else { group = channel->Name(); } } channelList->setTotal(total); } channelList->finish(); delete channelList; }
/* move or copy recording */ void RecordingsResponder::moveRecording(ostream& out, cxxtools::http::Request& request, cxxtools::http::Reply& reply) { QueryHandler q("/recordings/move", request); string source = q.getBodyAsString("source"); string target = q.getBodyAsString("target"); bool copy_only = q.getBodyAsBool("copy_only"); #if APIVERSNUM > 20300 LOCK_RECORDINGS_WRITE; cRecordings& recordings = *Recordings; #else cThreadLock RecordingsLock(&Recordings); cRecordings& recordings = Recordings; #endif if (source.length() <= 0 || target.length() <= 0) { reply.httpReturn(404, "Missing file name!"); return; } else if (access(source.c_str(), F_OK) != 0) { reply.httpReturn(504, "Path is invalid!"); return; } const cRecording* recording = recordings.GetByName(source.c_str()); if (!recording) { reply.httpReturn(504, "Recording not found!"); return; } //string newname = VdrExtension::MoveRecording(recording, VdrExtension::FileSystemExchangeChars(target.c_str(), true), copy_only); string oldname = recording->FileName(); size_t found = oldname.find_last_of("/"); if (found == string::npos) { LOG_ERROR_STR(source.c_str()); reply.httpReturn(503, "File copy failed!"); return; } #if APIVERSNUM > 20101 string newname = string(cVideoDirectory::Name()) + "/" + VdrExtension::FileSystemExchangeChars(target.c_str(), true) + oldname.substr(found); #else string newname = string(VideoDirectory) + "/" + VdrExtension::FileSystemExchangeChars(target.c_str(), true) + oldname.substr(found); #endif if (!VdrExtension::MoveDirectory(oldname.c_str(), newname.c_str(), copy_only)) { esyslog("[Restfulapi]: renaming failed from '%s' to '%s'", oldname.c_str(), newname.c_str()); reply.httpReturn(503, "File copy failed!"); return; } if (!copy_only) recordings.DelByName(oldname.c_str()); recordings.AddByName(newname.c_str()); cRecordingUserCommand::InvokeCommand(*cString::sprintf("rename \"%s\"", *strescape(oldname.c_str(), "\\\"$'")), newname.c_str()); const cRecording* new_recording = recordings.GetByName(newname.c_str()); if (!new_recording) { LOG_ERROR_STR(newname.c_str()); reply.httpReturn(504, "Recording not found, after moving!"); return; } esyslog("restfulapi: %s, %d", new_recording->FileName(), new_recording->Index()); RecordingList* recordingList = getRecordingList(out, q, reply); if (recordingList == NULL) { return; } recordingList->addRecording(new_recording, new_recording->Index(), NULL, ""); recordingList->setTotal(recordings.Count()); recordingList->finish(); delete recordingList; }