CDateTime CPVRTimers::GetNextEventTime(void) const { const CStdString wakeupcmd = g_guiSettings.GetString("pvrpowermanagement.setwakeupcmd", false); const bool dailywakup = g_guiSettings.GetBool("pvrpowermanagement.dailywakeup"); const CDateTime now = CDateTime::GetUTCDateTime(); const CDateTimeSpan prewakeup(0, 0, g_guiSettings.GetInt("pvrpowermanagement.prewakeup"), 0); const CDateTimeSpan idle(0, 0, g_guiSettings.GetInt("pvrpowermanagement.backendidletime"), 0); CDateTime wakeuptime; /* Check next active time */ CFileItemPtr item = GetNextActiveTimer(); if (item && item->HasPVRTimerInfoTag()) { const CDateTime start = item->GetPVRTimerInfoTag()->StartAsUTC(); if ((start - idle) > now) { wakeuptime = start - prewakeup; } else { wakeuptime = now + idle; } } /* check daily wake up */ if (dailywakup) { CDateTime dailywakeuptime; dailywakeuptime.SetFromDBTime(g_guiSettings.GetString("pvrpowermanagement.dailywakeuptime", false)); dailywakeuptime = dailywakeuptime.GetAsUTCDateTime(); dailywakeuptime.SetDateTime( now.GetYear(), now.GetMonth(), now.GetDay(), dailywakeuptime.GetHour(), dailywakeuptime.GetMinute(), dailywakeuptime.GetSecond() ); if ((dailywakeuptime - idle) < now) { const CDateTimeSpan oneDay(1,0,0,0); dailywakeuptime += oneDay; } if (dailywakeuptime < wakeuptime) wakeuptime = dailywakeuptime; } const CDateTime retVal(wakeuptime); return retVal; }
CDateTime CPVRTimers::GetNextEventTime(void) const { const bool dailywakup = CSettings::GetInstance().GetBool(CSettings::SETTING_PVRPOWERMANAGEMENT_DAILYWAKEUP); const CDateTime now = CDateTime::GetUTCDateTime(); const CDateTimeSpan prewakeup(0, 0, CSettings::GetInstance().GetInt(CSettings::SETTING_PVRPOWERMANAGEMENT_PREWAKEUP), 0); const CDateTimeSpan idle(0, 0, CSettings::GetInstance().GetInt(CSettings::SETTING_PVRPOWERMANAGEMENT_BACKENDIDLETIME), 0); CDateTime wakeuptime; /* Check next active time */ CFileItemPtr item = GetNextActiveTimer(); if (item && item->HasPVRTimerInfoTag()) { const CDateTimeSpan prestart(0, 0, item->GetPVRTimerInfoTag()->MarginStart(), 0); const CDateTime start = item->GetPVRTimerInfoTag()->StartAsUTC(); wakeuptime = ((start - prestart - prewakeup - idle) > now) ? start - prestart - prewakeup : now + idle; } /* check daily wake up */ if (dailywakup) { CDateTime dailywakeuptime; dailywakeuptime.SetFromDBTime(CSettings::GetInstance().GetString(CSettings::SETTING_PVRPOWERMANAGEMENT_DAILYWAKEUPTIME)); dailywakeuptime = dailywakeuptime.GetAsUTCDateTime(); dailywakeuptime.SetDateTime( now.GetYear(), now.GetMonth(), now.GetDay(), dailywakeuptime.GetHour(), dailywakeuptime.GetMinute(), dailywakeuptime.GetSecond() ); if ((dailywakeuptime - idle) < now) { const CDateTimeSpan oneDay(1,0,0,0); dailywakeuptime += oneDay; } if (!wakeuptime.IsValid() || dailywakeuptime < wakeuptime) wakeuptime = dailywakeuptime; } const CDateTime retVal(wakeuptime); return retVal; }
CDateTime CPVRTimers::GetNextEventTime(void) const { const bool dailywakup = m_settings.GetBoolValue(CSettings::SETTING_PVRPOWERMANAGEMENT_DAILYWAKEUP); const CDateTime now = CDateTime::GetUTCDateTime(); const CDateTimeSpan prewakeup(0, 0, m_settings.GetIntValue(CSettings::SETTING_PVRPOWERMANAGEMENT_PREWAKEUP), 0); const CDateTimeSpan idle(0, 0, m_settings.GetIntValue(CSettings::SETTING_PVRPOWERMANAGEMENT_BACKENDIDLETIME), 0); CDateTime wakeuptime; /* Check next active time */ const std::shared_ptr<CPVRTimerInfoTag> timer = GetNextActiveTimer(); if (timer) { const CDateTimeSpan prestart(0, 0, timer->MarginStart(), 0); const CDateTime start = timer->StartAsUTC(); wakeuptime = ((start - prestart - prewakeup - idle) > now) ? start - prestart - prewakeup : now + idle; } /* check daily wake up */ if (dailywakup) { CDateTime dailywakeuptime; dailywakeuptime.SetFromDBTime(m_settings.GetStringValue(CSettings::SETTING_PVRPOWERMANAGEMENT_DAILYWAKEUPTIME)); dailywakeuptime = dailywakeuptime.GetAsUTCDateTime(); dailywakeuptime.SetDateTime( now.GetYear(), now.GetMonth(), now.GetDay(), dailywakeuptime.GetHour(), dailywakeuptime.GetMinute(), dailywakeuptime.GetSecond() ); if ((dailywakeuptime - idle) < now) { const CDateTimeSpan oneDay(1,0,0,0); dailywakeuptime += oneDay; } if (!wakeuptime.IsValid() || dailywakeuptime < wakeuptime) wakeuptime = dailywakeuptime; } const CDateTime retVal(wakeuptime); return retVal; }
CDateTime CPVRTimers::GetNextEventTime(void) const { const bool dailywakup = CSettings::Get().GetBool("pvrpowermanagement.dailywakeup"); const CDateTime now = CDateTime::GetUTCDateTime(); const CDateTimeSpan prewakeup(0, 0, CSettings::Get().GetInt("pvrpowermanagement.prewakeup"), 0); const CDateTimeSpan idle(0, 0, CSettings::Get().GetInt("pvrpowermanagement.backendidletime"), 0); CDateTime wakeuptime; /* Check next active time */ CFileItemPtr item = GetNextActiveTimer(); if (item && item->HasPVRTimerInfoTag()) { const CDateTimeSpan prestart(0, 0, item->GetPVRTimerInfoTag()->MarginStart(), 0); const CDateTime start = item->GetPVRTimerInfoTag()->StartAsUTC(); wakeuptime = ((start - prestart - prewakeup - idle) > now) ? start - prestart - prewakeup : now + idle; } /* check daily wake up */ if (dailywakup) { CDateTime dailywakeuptime; dailywakeuptime.SetFromDBTime(CSettings::Get().GetString("pvrpowermanagement.dailywakeuptime")); dailywakeuptime = dailywakeuptime.GetAsUTCDateTime(); dailywakeuptime.SetDateTime( now.GetYear(), now.GetMonth(), now.GetDay(), dailywakeuptime.GetHour(), dailywakeuptime.GetMinute(), dailywakeuptime.GetSecond() ); if ((dailywakeuptime - idle) < now) { const CDateTimeSpan oneDay(1,0,0,0); dailywakeuptime += oneDay; } if (!wakeuptime.IsValid() || dailywakeuptime < wakeuptime) wakeuptime = dailywakeuptime; } const CDateTime retVal(wakeuptime); return retVal; }
void PVR::CPVREpg::Cleanup(const CDateTime &Time) { CSingleLock lock(m_critSection); CDateTime firstDate = Time.GetAsUTCDateTime() - CDateTimeSpan(0, g_advancedSettings.m_iEpgLingerTime / 60, g_advancedSettings.m_iEpgLingerTime % 60, 0); unsigned int iSize = size(); for (unsigned int iTagPtr = 0; iTagPtr < iSize; iTagPtr++) { CPVREpgInfoTag *tag = (CPVREpgInfoTag *) at(iTagPtr); if ( tag && /* valid tag */ !tag->HasTimer() && /* no timer set */ tag->EndAsLocalTime() < firstDate) { DeleteInfoTag(tag); iTagPtr--; iSize--; } } }
bool CWebServer::IsRequestRanged(const HTTPRequest& request, const CDateTime &lastModified) const { // parse the Range header and store it in the request object CHttpRanges ranges; bool ranged = ranges.Parse(HTTPRequestHandlerUtils::GetRequestHeaderValue(request.connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_RANGE)); // handle If-Range header but only if the Range header is present if (ranged && lastModified.IsValid()) { std::string ifRange = HTTPRequestHandlerUtils::GetRequestHeaderValue(request.connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_IF_RANGE); if (!ifRange.empty() && lastModified.IsValid()) { CDateTime ifRangeDate; ifRangeDate.SetFromRFC1123DateTime(ifRange); // check if the last modification is newer than the If-Range date // if so we have to server the whole file instead if (lastModified.GetAsUTCDateTime() > ifRangeDate) ranges.Clear(); } } return !ranges.IsEmpty(); }
void CEpgInfoTag::SetFirstAiredFromLocalTime(const CDateTime &firstAired) { CDateTime tmp = firstAired.GetAsUTCDateTime(); SetStartFromUTC(tmp); }
void CEpgInfoTag::SetEndFromLocalTime(const CDateTime &end) { CDateTime tmp = end.GetAsUTCDateTime(); SetEndFromUTC(tmp); }
void CEpgInfoTag::SetStartFromLocalTime(const CDateTime &start) { CDateTime tmp = start.GetAsUTCDateTime(); SetStartFromUTC(tmp); }
int CWebServer::AnswerToConnection(void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, unsigned int *upload_data_size, void **con_cls) #endif { if (cls == NULL || con_cls == NULL || *con_cls == NULL) { CLog::Log(LOGERROR, "CWebServer: invalid request received"); return MHD_NO; } CWebServer *server = reinterpret_cast<CWebServer*>(cls); std::auto_ptr<ConnectionHandler> conHandler(reinterpret_cast<ConnectionHandler*>(*con_cls)); HTTPMethod methodType = GetMethod(method); HTTPRequest request = { server, connection, conHandler->fullUri, url, methodType, version }; // remember if the request was new bool isNewRequest = conHandler->isNew; // because now it isn't anymore conHandler->isNew = false; // reset con_cls and set it if still necessary *con_cls = NULL; #ifdef WEBSERVER_DEBUG if (isNewRequest) { std::multimap<std::string, std::string> headerValues; GetRequestHeaderValues(connection, MHD_HEADER_KIND, headerValues); std::multimap<std::string, std::string> getValues; GetRequestHeaderValues(connection, MHD_GET_ARGUMENT_KIND, getValues); CLog::Log(LOGDEBUG, "webserver [IN] %s %s %s", version, method, request.pathUrlFull.c_str()); if (!getValues.empty()) { std::string tmp; for (std::multimap<std::string, std::string>::const_iterator get = getValues.begin(); get != getValues.end(); ++get) { if (get != getValues.begin()) tmp += "; "; tmp += get->first + " = " + get->second; } CLog::Log(LOGDEBUG, "webserver [IN] Query arguments: %s", tmp.c_str()); } for (std::multimap<std::string, std::string>::const_iterator header = headerValues.begin(); header != headerValues.end(); ++header) CLog::Log(LOGDEBUG, "webserver [IN] %s: %s", header->first.c_str(), header->second.c_str()); } #endif if (!IsAuthenticated(server, connection)) return AskForAuthentication(connection); // check if this is the first call to AnswerToConnection for this request if (isNewRequest) { // parse the Range header and store it in the request object CHttpRanges ranges; bool ranged = ranges.Parse(GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_RANGE)); // look for a IHTTPRequestHandler which can take care of the current request for (std::vector<IHTTPRequestHandler *>::const_iterator it = m_requestHandlers.begin(); it != m_requestHandlers.end(); ++it) { IHTTPRequestHandler *requestHandler = *it; if (requestHandler->CanHandleRequest(request)) { // we found a matching IHTTPRequestHandler so let's get a new instance for this request IHTTPRequestHandler *handler = requestHandler->Create(request); // if we got a GET request we need to check if it should be cached if (methodType == GET) { if (handler->CanBeCached()) { bool cacheable = true; // handle Cache-Control std::string cacheControl = GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CACHE_CONTROL); if (!cacheControl.empty()) { std::vector<std::string> cacheControls = StringUtils::Split(cacheControl, ","); for (std::vector<std::string>::const_iterator it = cacheControls.begin(); it != cacheControls.end(); ++it) { std::string control = *it; control = StringUtils::Trim(control); // handle no-cache if (control.compare(HEADER_VALUE_NO_CACHE) == 0) cacheable = false; } } if (cacheable) { // handle Pragma (but only if "Cache-Control: no-cache" hasn't been set) std::string pragma = GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_PRAGMA); if (pragma.compare(HEADER_VALUE_NO_CACHE) == 0) cacheable = false; } CDateTime lastModified; if (handler->GetLastModifiedDate(lastModified) && lastModified.IsValid()) { // handle If-Modified-Since or If-Unmodified-Since std::string ifModifiedSince = GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_IF_MODIFIED_SINCE); std::string ifUnmodifiedSince = GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_IF_UNMODIFIED_SINCE); CDateTime ifModifiedSinceDate; CDateTime ifUnmodifiedSinceDate; // handle If-Modified-Since (but only if the response is cacheable) if (cacheable && ifModifiedSinceDate.SetFromRFC1123DateTime(ifModifiedSince) && lastModified.GetAsUTCDateTime() <= ifModifiedSinceDate) { struct MHD_Response *response = MHD_create_response_from_data(0, NULL, MHD_NO, MHD_NO); if (response == NULL) { CLog::Log(LOGERROR, "CWebServer: failed to create a HTTP 304 response"); return MHD_NO; } return FinalizeRequest(handler, MHD_HTTP_NOT_MODIFIED, response); } // handle If-Unmodified-Since else if (ifUnmodifiedSinceDate.SetFromRFC1123DateTime(ifUnmodifiedSince) && lastModified.GetAsUTCDateTime() > ifUnmodifiedSinceDate) return SendErrorResponse(connection, MHD_HTTP_PRECONDITION_FAILED, methodType); } // handle If-Range header but only if the Range header is present if (ranged && lastModified.IsValid()) { std::string ifRange = GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_IF_RANGE); if (!ifRange.empty() && lastModified.IsValid()) { CDateTime ifRangeDate; ifRangeDate.SetFromRFC1123DateTime(ifRange); // check if the last modification is newer than the If-Range date // if so we have to server the whole file instead if (lastModified.GetAsUTCDateTime() > ifRangeDate) ranges.Clear(); } } // pass the requested ranges on to the request handler handler->SetRequestRanged(!ranges.IsEmpty()); } } // if we got a POST request we need to take care of the POST data else if (methodType == POST) { conHandler->requestHandler = handler; // get the content-type of the POST data std::string contentType = GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_TYPE); if (!contentType.empty()) { // if the content-type is application/x-ww-form-urlencoded or multipart/form-data we can use MHD's POST processor if (StringUtils::EqualsNoCase(contentType, MHD_HTTP_POST_ENCODING_FORM_URLENCODED) || StringUtils::EqualsNoCase(contentType, MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)) { // Get a new MHD_PostProcessor conHandler->postprocessor = MHD_create_post_processor(connection, MAX_POST_BUFFER_SIZE, &CWebServer::HandlePostField, (void*)conHandler.get()); // MHD doesn't seem to be able to handle this post request if (conHandler->postprocessor == NULL) { CLog::Log(LOGERROR, "CWebServer: unable to create HTTP POST processor for %s", url); delete conHandler->requestHandler; return SendErrorResponse(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, methodType); } } } // otherwise we need to handle the POST data ourselves which is done in the next call to AnswerToConnection // as ownership of the connection handler is passed to libmicrohttpd we must not destroy it *con_cls = conHandler.release(); return MHD_YES; } return HandleRequest(handler); } } } // this is a subsequent call to AnswerToConnection for this request else { // again we need to take special care of the POST data if (methodType == POST) { if (conHandler->requestHandler == NULL) { CLog::Log(LOGERROR, "CWebServer: cannot handle partial HTTP POST for %s request because there is no valid request handler available", url); return SendErrorResponse(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, methodType); } // we only need to handle POST data if there actually is data left to handle if (*upload_data_size > 0) { // either use MHD's POST processor if (conHandler->postprocessor != NULL) MHD_post_process(conHandler->postprocessor, upload_data, *upload_data_size); // or simply copy the data to the handler else conHandler->requestHandler->AddPostData(upload_data, *upload_data_size); // signal that we have handled the data *upload_data_size = 0; // we may need to handle more POST data which is done in the next call to AnswerToConnection // as ownership of the connection handler is passed to libmicrohttpd we must not destroy it *con_cls = conHandler.release(); return MHD_YES; } // we have handled all POST data so it's time to invoke the IHTTPRequestHandler else { if (conHandler->postprocessor != NULL) MHD_destroy_post_processor(conHandler->postprocessor); return HandleRequest(conHandler->requestHandler); } } // it's unusual to get more than one call to AnswerToConnection for none-POST requests, but let's handle it anyway else { for (std::vector<IHTTPRequestHandler *>::const_iterator it = m_requestHandlers.begin(); it != m_requestHandlers.end(); ++it) { IHTTPRequestHandler *requestHandler = *it; if (requestHandler->CanHandleRequest(request)) return HandleRequest(requestHandler->Create(request)); } } } CLog::Log(LOGERROR, "CWebServer: couldn't find any request handler for %s", url); return SendErrorResponse(connection, MHD_HTTP_NOT_FOUND, methodType); }
int CWebServer::CreateFileDownloadResponse(struct MHD_Connection *connection, const string &strURL, HTTPMethod methodType, struct MHD_Response *&response, int &responseCode) { CFile *file = new CFile(); #ifdef WEBSERVER_DEBUG CLog::Log(LOGDEBUG, "webserver [IN] %s", strURL.c_str()); multimap<string, string> headers; if (GetRequestHeaderValues(connection, MHD_HEADER_KIND, headers) > 0) { for (multimap<string, string>::const_iterator header = headers.begin(); header != headers.end(); header++) CLog::Log(LOGDEBUG, "webserver [IN] %s: %s", header->first.c_str(), header->second.c_str()); } #endif if (file->Open(strURL, READ_NO_CACHE)) { bool getData = true; bool ranged = false; int64_t fileLength = file->GetLength(); // try to get the file's last modified date CDateTime lastModified; if (!GetLastModifiedDateTime(file, lastModified)) lastModified.Reset(); // get the MIME type for the Content-Type header CStdString ext = URIUtils::GetExtension(strURL); ext = ext.ToLower(); string mimeType = CreateMimeTypeFromExtension(ext.c_str()); if (methodType != HEAD) { int64_t firstPosition = 0; int64_t lastPosition = fileLength - 1; uint64_t totalLength = 0; HttpFileDownloadContext *context = new HttpFileDownloadContext(); context->file = file; context->rangesLength = fileLength; context->contentType = mimeType; context->boundaryWritten = false; context->writePosition = 0; if (methodType == GET) { // handle If-Modified-Since string ifModifiedSince = GetRequestHeaderValue(connection, MHD_HEADER_KIND, "If-Modified-Since"); if (!ifModifiedSince.empty() && lastModified.IsValid()) { CDateTime ifModifiedSinceDate; ifModifiedSinceDate.SetFromRFC1123DateTime(ifModifiedSince); if (lastModified.GetAsUTCDateTime() <= ifModifiedSinceDate) { getData = false; response = MHD_create_response_from_data(0, NULL, MHD_NO, MHD_NO); responseCode = MHD_HTTP_NOT_MODIFIED; } } if (getData) { // handle Range header context->rangesLength = ParseRangeHeader(GetRequestHeaderValue(connection, MHD_HEADER_KIND, "Range"), fileLength, context->ranges, firstPosition, lastPosition); // handle If-Range header but only if the Range header is present if (!context->ranges.empty()) { string ifRange = GetRequestHeaderValue(connection, MHD_HEADER_KIND, "If-Range"); if (!ifRange.empty() && lastModified.IsValid()) { CDateTime ifRangeDate; ifRangeDate.SetFromRFC1123DateTime(ifRange); // check if the last modification is newer than the If-Range date // if so we have to server the whole file instead if (lastModified.GetAsUTCDateTime() > ifRangeDate) context->ranges.clear(); } } } } if (getData) { // if there are no ranges, add the whole range if (context->ranges.empty() || context->rangesLength == fileLength) { if (context->rangesLength == fileLength) context->ranges.clear(); context->ranges.push_back(HttpRange(0, fileLength - 1)); context->rangesLength = fileLength; firstPosition = 0; lastPosition = fileLength - 1; } else responseCode = MHD_HTTP_PARTIAL_CONTENT; // remember the total number of ranges context->rangeCount = context->ranges.size(); // remember the total length totalLength = context->rangesLength; // we need to remember whether we are ranged because the range length // might change and won't be reliable anymore for length comparisons ranged = context->rangeCount > 1 || context->rangesLength < fileLength; // adjust the MIME type and range length in case of multiple ranges // which requires multipart boundaries if (context->rangeCount > 1) { context->boundary = GenerateMultipartBoundary(); mimeType = "multipart/byteranges; boundary=" + context->boundary; // build part of the boundary with the optional Content-Type header // "--<boundary>\r\nContent-Type: <content-type>\r\n context->boundaryWithHeader = "\r\n--" + context->boundary + "\r\n"; if (!context->contentType.empty()) context->boundaryWithHeader += "Content-Type: " + context->contentType + "\r\n"; // for every range, we need to add a boundary with header for (HttpRanges::const_iterator range = context->ranges.begin(); range != context->ranges.end(); range++) { // we need to temporarily add the Content-Range header to the // boundary to be able to determine the length string completeBoundaryWithHeader = context->boundaryWithHeader; completeBoundaryWithHeader += StringUtils::Format("Content-Range: " CONTENT_RANGE_FORMAT, range->first, range->second, range->second - range->first + 1); completeBoundaryWithHeader += "\r\n\r\n"; totalLength += completeBoundaryWithHeader.size(); } // and at the very end a special end-boundary "\r\n--<boundary>--" totalLength += 4 + context->boundary.size() + 2; } // set the initial write position context->writePosition = context->ranges.begin()->first; // create the response object response = MHD_create_response_from_callback(totalLength, 2048, &CWebServer::ContentReaderCallback, context, &CWebServer::ContentReaderFreeCallback); } if (response == NULL) { file->Close(); delete file; delete context; return MHD_NO; } // add Content-Range header if (ranged) AddHeader(response, "Content-Range", StringUtils::Format(CONTENT_RANGE_FORMAT, firstPosition, lastPosition, fileLength).c_str()); } else { getData = false; CStdString contentLength; contentLength.Format("%" PRId64, fileLength); response = MHD_create_response_from_data(0, NULL, MHD_NO, MHD_NO); if (response == NULL) { file->Close(); delete file; return MHD_NO; } AddHeader(response, "Content-Length", contentLength); } // add "Accept-Ranges: bytes" header AddHeader(response, "Accept-Ranges", "bytes"); // set the Content-Type header if (!mimeType.empty()) AddHeader(response, "Content-Type", mimeType.c_str()); // set the Last-Modified header if (lastModified.IsValid()) AddHeader(response, "Last-Modified", lastModified.GetAsRFC1123DateTime()); // set the Expires header CDateTime expiryTime = CDateTime::GetCurrentDateTime(); if (StringUtils::EqualsNoCase(mimeType, "text/html") || StringUtils::EqualsNoCase(mimeType, "text/css") || StringUtils::EqualsNoCase(mimeType, "application/javascript")) expiryTime += CDateTimeSpan(1, 0, 0, 0); else expiryTime += CDateTimeSpan(365, 0, 0, 0); AddHeader(response, "Expires", expiryTime.GetAsRFC1123DateTime()); // only close the CFile instance if libmicrohttpd doesn't have to grab the data of the file if (!getData) { file->Close(); delete file; } } else { delete file; CLog::Log(LOGERROR, "WebServer: Failed to open %s", strURL.c_str()); return SendErrorResponse(connection, MHD_HTTP_NOT_FOUND, methodType); } return MHD_YES; }
bool CPVRManager::CanSystemPowerdown(bool bAskUser /*= true*/) const { bool bReturn(true); if (IsStarted()) { CPVRTimerInfoTagPtr cause; if (!AllLocalBackendsIdle(cause)) { if (bAskUser) { std::string text; if (cause) { if (cause->IsRecording()) { text = StringUtils::Format(g_localizeStrings.Get(19691).c_str(), // "PVR is currently recording...." cause->Title().c_str(), cause->ChannelName().c_str()); } else { // Next event is due to a local recording. const CDateTime now(CDateTime::GetUTCDateTime()); const CDateTime start(cause->StartAsUTC()); const CDateTimeSpan prestart(0, 0, cause->MarginStart(), 0); CDateTimeSpan diff(start - now); diff -= prestart; int mins = diff.GetSecondsTotal() / 60; std::string dueStr; if (mins > 1) { // "%d minutes" dueStr = StringUtils::Format(g_localizeStrings.Get(19694).c_str(), mins); } else { // "about a minute" dueStr = g_localizeStrings.Get(19695); } text = StringUtils::Format(g_localizeStrings.Get(19692).c_str(), // "PVR will start recording...." cause->Title().c_str(), cause->ChannelName().c_str(), dueStr.c_str()); } } else { // Next event is due to automatic daily wakeup of PVR. const CDateTime now(CDateTime::GetUTCDateTime()); CDateTime dailywakeuptime; dailywakeuptime.SetFromDBTime(CSettings::GetInstance().GetString(CSettings::SETTING_PVRPOWERMANAGEMENT_DAILYWAKEUPTIME)); dailywakeuptime = dailywakeuptime.GetAsUTCDateTime(); const CDateTimeSpan diff(dailywakeuptime - now); int mins = diff.GetSecondsTotal() / 60; std::string dueStr; if (mins > 1) { // "%d minutes" dueStr = StringUtils::Format(g_localizeStrings.Get(19694).c_str(), mins); } else { // "about a minute" dueStr = g_localizeStrings.Get(19695); } text = StringUtils::Format(g_localizeStrings.Get(19693).c_str(), // "Daily wakeup is due in...." dueStr.c_str()); } // Inform user about PVR being busy. Ask if user wants to powerdown anyway. bReturn = HELPERS::DialogResponse::YES == HELPERS::ShowYesNoDialogText(CVariant{19685}, // "Confirm shutdown" CVariant{text}, CVariant{222}, // "Shutdown anyway", CVariant{19696}, // "Cancel" 10000); // timeout value before closing } else bReturn = false; // do not powerdown (busy, but no user interaction requested). } } return bReturn; }
int CWebServer::CreateFileDownloadResponse(struct MHD_Connection *connection, const string &strURL, HTTPMethod methodType, struct MHD_Response *&response, int &responseCode) { CFile *file = new CFile(); if (file->Open(strURL, READ_NO_CACHE)) { bool getData = true; if (methodType != HEAD) { if (methodType == GET) { string ifModifiedSince = GetRequestHeaderValue(connection, MHD_HEADER_KIND, "If-Modified-Since"); if (!ifModifiedSince.empty()) { CDateTime ifModifiedSinceDate; ifModifiedSinceDate.SetFromRFC1123DateTime(ifModifiedSince); struct __stat64 statBuffer; if (file->Stat(&statBuffer) == 0) { struct tm *time = localtime((time_t *)&statBuffer.st_mtime); if (time != NULL) { CDateTime lastModified = *time; if (lastModified.GetAsUTCDateTime() <= ifModifiedSinceDate) { getData = false; response = MHD_create_response_from_data (0, NULL, MHD_NO, MHD_NO); responseCode = MHD_HTTP_NOT_MODIFIED; } } } } } if (getData) response = MHD_create_response_from_callback(file->GetLength(), 2048, &CWebServer::ContentReaderCallback, file, &CWebServer::ContentReaderFreeCallback); if (response == NULL) { file->Close(); delete file; return MHD_NO; } } else { getData = false; CStdString contentLength; contentLength.Format("%I64d", file->GetLength()); response = MHD_create_response_from_data (0, NULL, MHD_NO, MHD_NO); if (response == NULL) { file->Close(); delete file; return MHD_NO; } MHD_add_response_header(response, "Content-Length", contentLength); } // set the Content-Type header CStdString ext = URIUtils::GetExtension(strURL); ext = ext.ToLower(); const char *mime = CreateMimeTypeFromExtension(ext.c_str()); if (mime) MHD_add_response_header(response, "Content-Type", mime); // set the Last-Modified header struct __stat64 statBuffer; if (file->Stat(&statBuffer) == 0) { struct tm *time = localtime((time_t *)&statBuffer.st_mtime); if (time != NULL) { CDateTime lastModified = *time; MHD_add_response_header(response, "Last-Modified", lastModified.GetAsRFC1123DateTime()); } } // set the Expires header CDateTime expiryTime = CDateTime::GetCurrentDateTime(); if (mime && strncmp(mime, "text/html", 9) == 0) expiryTime += CDateTimeSpan(1, 0, 0, 0); else expiryTime += CDateTimeSpan(365, 0, 0, 0); MHD_add_response_header(response, "Expires", expiryTime.GetAsRFC1123DateTime()); // only close the CFile instance if libmicrohttpd doesn't have to grab the data of the file if (!getData) { file->Close(); delete file; } } else { delete file; CLog::Log(LOGERROR, "WebServer: Failed to open %s", strURL.c_str()); return SendErrorResponse(connection, MHD_HTTP_NOT_FOUND, GET); /* GET Assumed Temporarily */ } return MHD_YES; }
int CWebServer::HandlePartialRequest(struct MHD_Connection *connection, ConnectionHandler* connectionHandler, const HTTPRequest& request, const char *upload_data, size_t *upload_data_size, void **con_cls) { std::unique_ptr<ConnectionHandler> conHandler(connectionHandler); // remember if the request was new bool isNewRequest = conHandler->isNew; // because now it isn't anymore conHandler->isNew = false; // reset con_cls and set it if still necessary *con_cls = nullptr; if (!IsAuthenticated(request)) return AskForAuthentication(request); // check if this is the first call to AnswerToConnection for this request if (isNewRequest) { // look for a IHTTPRequestHandler which can take care of the current request auto handler = FindRequestHandler(request); if (handler != nullptr) { // if we got a GET request we need to check if it should be cached if (request.method == GET) { if (handler->CanBeCached()) { bool cacheable = IsRequestCacheable(request); CDateTime lastModified; if (handler->GetLastModifiedDate(lastModified) && lastModified.IsValid()) { // handle If-Modified-Since or If-Unmodified-Since std::string ifModifiedSince = HTTPRequestHandlerUtils::GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_IF_MODIFIED_SINCE); std::string ifUnmodifiedSince = HTTPRequestHandlerUtils::GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_IF_UNMODIFIED_SINCE); CDateTime ifModifiedSinceDate; CDateTime ifUnmodifiedSinceDate; // handle If-Modified-Since (but only if the response is cacheable) if (cacheable && ifModifiedSinceDate.SetFromRFC1123DateTime(ifModifiedSince) && lastModified.GetAsUTCDateTime() <= ifModifiedSinceDate) { struct MHD_Response *response = create_response(0, nullptr, MHD_NO, MHD_NO); if (response == nullptr) { CLog::Log(LOGERROR, "CWebServer[%hu]: failed to create a HTTP 304 response", m_port); return MHD_NO; } return FinalizeRequest(handler, MHD_HTTP_NOT_MODIFIED, response); } // handle If-Unmodified-Since else if (ifUnmodifiedSinceDate.SetFromRFC1123DateTime(ifUnmodifiedSince) && lastModified.GetAsUTCDateTime() > ifUnmodifiedSinceDate) return SendErrorResponse(request, MHD_HTTP_PRECONDITION_FAILED, request.method); } // pass the requested ranges on to the request handler handler->SetRequestRanged(IsRequestRanged(request, lastModified)); } } // if we got a POST request we need to take care of the POST data else if (request.method == POST) { // as ownership of the connection handler is passed to libmicrohttpd we must not destroy it SetupPostDataProcessing(request, conHandler.get(), handler, con_cls); // as ownership of the connection handler has been passed to libmicrohttpd we must not destroy it conHandler.release(); return MHD_YES; } return HandleRequest(handler); } } // this is a subsequent call to AnswerToConnection for this request else { // again we need to take special care of the POST data if (request.method == POST) { // process additional / remaining POST data if (ProcessPostData(request, conHandler.get(), upload_data, upload_data_size, con_cls)) { // as ownership of the connection handler has been passed to libmicrohttpd we must not destroy it conHandler.release(); return MHD_YES; } // finalize POST data processing FinalizePostDataProcessing(conHandler.get()); // check if something went wrong while handling the POST data if (conHandler->errorStatus != MHD_HTTP_OK) return SendErrorResponse(request, conHandler->errorStatus, request.method); // we have handled all POST data so it's time to invoke the IHTTPRequestHandler return HandleRequest(conHandler->requestHandler); } // it's unusual to get more than one call to AnswerToConnection for none-POST requests, but let's handle it anyway auto requestHandler = FindRequestHandler(request); if (requestHandler != nullptr) return HandleRequest(requestHandler); } CLog::Log(LOGERROR, "CWebServer[%hu]: couldn't find any request handler for %s", m_port, request.pathUrl.c_str()); return SendErrorResponse(request, MHD_HTTP_NOT_FOUND, request.method); }
int CWebServer::CreateFileDownloadResponse(struct MHD_Connection *connection, const string &strURL, HTTPMethod methodType, struct MHD_Response *&response, int &responseCode) { boost::shared_ptr<CFile> file = boost::make_shared<CFile>(); #ifdef WEBSERVER_DEBUG CLog::Log(LOGDEBUG, "webserver [IN] %s", strURL.c_str()); multimap<string, string> headers; if (GetRequestHeaderValues(connection, MHD_HEADER_KIND, headers) > 0) { for (multimap<string, string>::const_iterator header = headers.begin(); header != headers.end(); header++) CLog::Log(LOGDEBUG, "webserver [IN] %s: %s", header->first.c_str(), header->second.c_str()); } #endif if (!file->Open(strURL, READ_NO_CACHE)) { CLog::Log(LOGERROR, "WebServer: Failed to open %s", strURL.c_str()); return SendErrorResponse(connection, MHD_HTTP_NOT_FOUND, methodType); } bool getData = true; bool ranged = false; int64_t fileLength = file->GetLength(); // try to get the file's last modified date CDateTime lastModified; if (!GetLastModifiedDateTime(file.get(), lastModified)) lastModified.Reset(); // get the MIME type for the Content-Type header std::string ext = URIUtils::GetExtension(strURL); StringUtils::ToLower(ext); string mimeType = CreateMimeTypeFromExtension(ext.c_str()); if (methodType != HEAD) { int64_t firstPosition = 0; int64_t lastPosition = fileLength - 1; uint64_t totalLength = 0; std::auto_ptr<HttpFileDownloadContext> context(new HttpFileDownloadContext()); context->file = file; context->rangesLength = fileLength; context->contentType = mimeType; context->boundaryWritten = false; context->writePosition = 0; if (methodType == GET) { bool cacheable = true; // handle Cache-Control string cacheControl = GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CACHE_CONTROL); if (!cacheControl.empty()) { vector<string> cacheControls = StringUtils::Split(cacheControl, ","); for (vector<string>::const_iterator it = cacheControls.begin(); it != cacheControls.end(); ++it) { string control = *it; control = StringUtils::Trim(control); // handle no-cache if (control.compare(HEADER_VALUE_NO_CACHE) == 0) cacheable = false; } } if (cacheable) { // handle Pragma (but only if "Cache-Control: no-cache" hasn't been set) string pragma = GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_PRAGMA); if (pragma.compare(HEADER_VALUE_NO_CACHE) == 0) cacheable = false; } if (lastModified.IsValid()) { // handle If-Modified-Since or If-Unmodified-Since string ifModifiedSince = GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_IF_MODIFIED_SINCE); string ifUnmodifiedSince = GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_IF_UNMODIFIED_SINCE); CDateTime ifModifiedSinceDate; CDateTime ifUnmodifiedSinceDate; // handle If-Modified-Since (but only if the response is cacheable) if (cacheable && ifModifiedSinceDate.SetFromRFC1123DateTime(ifModifiedSince) && lastModified.GetAsUTCDateTime() <= ifModifiedSinceDate) { getData = false; response = MHD_create_response_from_data(0, NULL, MHD_NO, MHD_NO); if (response == NULL) return MHD_NO; responseCode = MHD_HTTP_NOT_MODIFIED; } // handle If-Unmodified-Since else if (ifUnmodifiedSinceDate.SetFromRFC1123DateTime(ifUnmodifiedSince) && lastModified.GetAsUTCDateTime() > ifUnmodifiedSinceDate) return SendErrorResponse(connection, MHD_HTTP_PRECONDITION_FAILED, methodType); } if (getData) { // handle Range header context->rangesLength = ParseRangeHeader(GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_RANGE), fileLength, context->ranges, firstPosition, lastPosition); // handle If-Range header but only if the Range header is present if (!context->ranges.empty()) { string ifRange = GetRequestHeaderValue(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_IF_RANGE); if (!ifRange.empty() && lastModified.IsValid()) { CDateTime ifRangeDate; ifRangeDate.SetFromRFC1123DateTime(ifRange); // check if the last modification is newer than the If-Range date // if so we have to server the whole file instead if (lastModified.GetAsUTCDateTime() > ifRangeDate) context->ranges.clear(); } } } } if (getData) { // if there are no ranges, add the whole range if (context->ranges.empty() || context->rangesLength == fileLength) { if (context->rangesLength == fileLength) context->ranges.clear(); context->ranges.push_back(HttpRange(0, fileLength - 1)); context->rangesLength = fileLength; firstPosition = 0; lastPosition = fileLength - 1; } else responseCode = MHD_HTTP_PARTIAL_CONTENT; // remember the total number of ranges context->rangeCount = context->ranges.size(); // remember the total length totalLength = context->rangesLength; // we need to remember whether we are ranged because the range length // might change and won't be reliable anymore for length comparisons ranged = context->rangeCount > 1 || context->rangesLength < fileLength; // adjust the MIME type and range length in case of multiple ranges // which requires multipart boundaries if (context->rangeCount > 1) { context->boundary = GenerateMultipartBoundary(); mimeType = "multipart/byteranges; boundary=" + context->boundary; // build part of the boundary with the optional Content-Type header // "--<boundary>\r\nContent-Type: <content-type>\r\n context->boundaryWithHeader = HEADER_NEWLINE HEADER_BOUNDARY + context->boundary + HEADER_NEWLINE; if (!context->contentType.empty()) context->boundaryWithHeader += MHD_HTTP_HEADER_CONTENT_TYPE ": " + context->contentType + HEADER_NEWLINE; // for every range, we need to add a boundary with header for (HttpRanges::const_iterator range = context->ranges.begin(); range != context->ranges.end(); range++) { // we need to temporarily add the Content-Range header to the // boundary to be able to determine the length string completeBoundaryWithHeader = context->boundaryWithHeader; completeBoundaryWithHeader += StringUtils::Format(MHD_HTTP_HEADER_CONTENT_RANGE ": " CONTENT_RANGE_FORMAT, range->first, range->second, range->second - range->first + 1); completeBoundaryWithHeader += HEADER_SEPARATOR; totalLength += completeBoundaryWithHeader.size(); } // and at the very end a special end-boundary "\r\n--<boundary>--" totalLength += strlen(HEADER_SEPARATOR) + strlen(HEADER_BOUNDARY) + context->boundary.size() + strlen(HEADER_BOUNDARY); } // set the initial write position context->writePosition = context->ranges.begin()->first; // create the response object response = MHD_create_response_from_callback(totalLength, 2048, &CWebServer::ContentReaderCallback, context.get(), &CWebServer::ContentReaderFreeCallback); if (response == NULL) return MHD_NO; context.release(); // ownership was passed to mhd } // add Content-Range header if (ranged) AddHeader(response, MHD_HTTP_HEADER_CONTENT_RANGE, StringUtils::Format(CONTENT_RANGE_FORMAT, firstPosition, lastPosition, fileLength)); } else { getData = false; std::string contentLength = StringUtils::Format("%" PRId64, fileLength); response = MHD_create_response_from_data(0, NULL, MHD_NO, MHD_NO); if (response == NULL) return MHD_NO; AddHeader(response, MHD_HTTP_HEADER_CONTENT_LENGTH, contentLength); } // add "Accept-Ranges: bytes" header AddHeader(response, MHD_HTTP_HEADER_ACCEPT_RANGES, "bytes"); // set the Content-Type header if (!mimeType.empty()) AddHeader(response, MHD_HTTP_HEADER_CONTENT_TYPE, mimeType); // set the Last-Modified header if (lastModified.IsValid()) AddHeader(response, MHD_HTTP_HEADER_LAST_MODIFIED, lastModified.GetAsRFC1123DateTime()); // set the Expires header CDateTime now = CDateTime::GetCurrentDateTime(); CDateTime expiryTime = now; if (StringUtils::EqualsNoCase(mimeType, "text/html") || StringUtils::EqualsNoCase(mimeType, "text/css") || StringUtils::EqualsNoCase(mimeType, "application/javascript")) expiryTime += CDateTimeSpan(1, 0, 0, 0); else expiryTime += CDateTimeSpan(365, 0, 0, 0); AddHeader(response, MHD_HTTP_HEADER_EXPIRES, expiryTime.GetAsRFC1123DateTime()); // set the Cache-Control header int maxAge = (expiryTime - now).GetSecondsTotal(); AddHeader(response, MHD_HTTP_HEADER_CACHE_CONTROL, StringUtils::Format("max-age=%d, public", maxAge)); return MHD_YES; }