Пример #1
0
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;
}
Пример #2
0
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;
}
Пример #3
0
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;
}
Пример #4
0
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;
}
Пример #5
0
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--;
    }
  }
}
Пример #6
0
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();
}
Пример #7
0
void CEpgInfoTag::SetFirstAiredFromLocalTime(const CDateTime &firstAired)
{
  CDateTime tmp = firstAired.GetAsUTCDateTime();
  SetStartFromUTC(tmp);
}
Пример #8
0
void CEpgInfoTag::SetEndFromLocalTime(const CDateTime &end)
{
  CDateTime tmp = end.GetAsUTCDateTime();
  SetEndFromUTC(tmp);
}
Пример #9
0
void CEpgInfoTag::SetStartFromLocalTime(const CDateTime &start)
{
  CDateTime tmp = start.GetAsUTCDateTime();
  SetStartFromUTC(tmp);
}
Пример #10
0
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);
}
Пример #11
0
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;
}
Пример #12
0
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;
}
Пример #13
0
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;
}
Пример #14
0
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);
}
Пример #15
0
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;
}