diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
commit | c04dcc2e7d834218ef2d4194331e383402495ae1 (patch) | |
tree | 7333e38d10d75386e60f336b80c2443c1166031d /xbmc/utils/HttpHeader.cpp | |
parent | Initial commit. (diff) | |
download | kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.tar.xz kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.zip |
Adding upstream version 2:20.4+dfsg.upstream/2%20.4+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xbmc/utils/HttpHeader.cpp')
-rw-r--r-- | xbmc/utils/HttpHeader.cpp | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/xbmc/utils/HttpHeader.cpp b/xbmc/utils/HttpHeader.cpp new file mode 100644 index 0000000..ad73bb2 --- /dev/null +++ b/xbmc/utils/HttpHeader.cpp @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "HttpHeader.h" + +#include "utils/StringUtils.h" + +// header white space characters according to RFC 2616 +const char* const CHttpHeader::m_whitespaceChars = " \t"; + + +CHttpHeader::CHttpHeader() +{ + m_headerdone = false; +} + +CHttpHeader::~CHttpHeader() = default; + +void CHttpHeader::Parse(const std::string& strData) +{ + size_t pos = 0; + const size_t len = strData.length(); + const char* const strDataC = strData.c_str(); + + // According to RFC 2616 any header line can have continuation on next line, if next line is started from whitespace char + // This code at first checks for whitespace char at the begging of the line, and if found, then current line is appended to m_lastHeaderLine + // If current line is NOT started from whitespace char, then previously stored (and completed) m_lastHeaderLine is parsed and current line is assigned to m_lastHeaderLine (to be parsed later) + while (pos < len) + { + size_t lineEnd = strData.find('\x0a', pos); // use '\x0a' instead of '\n' to be platform independent + + if (lineEnd == std::string::npos) + return; // error: expected only complete lines + + const size_t nextLine = lineEnd + 1; + if (lineEnd > pos && strDataC[lineEnd - 1] == '\x0d') // use '\x0d' instead of '\r' to be platform independent + lineEnd--; + + if (m_headerdone) + Clear(); // clear previous header and process new one + + if (strDataC[pos] == ' ' || strDataC[pos] == '\t') // same chars as in CHttpHeader::m_whitespaceChars + { // line is started from whitespace char: this is continuation of previous line + pos = strData.find_first_not_of(m_whitespaceChars, pos); + + m_lastHeaderLine.push_back(' '); // replace all whitespace chars at start of the line with single space + m_lastHeaderLine.append(strData, pos, lineEnd - pos); // append current line + } + else + { // this line is NOT continuation, this line is new header line + if (!m_lastHeaderLine.empty()) + ParseLine(m_lastHeaderLine); // process previously stored completed line (if any) + + m_lastHeaderLine.assign(strData, pos, lineEnd - pos); // store current line to (possibly) complete later. Will be parsed on next turns. + + if (pos == lineEnd) + m_headerdone = true; // current line is bare "\r\n" (or "\n"), means end of header; no need to process current m_lastHeaderLine + } + + pos = nextLine; // go to next line (if any) + } +} + +bool CHttpHeader::ParseLine(const std::string& headerLine) +{ + const size_t valueStart = headerLine.find(':'); + + if (valueStart != std::string::npos) + { + std::string strParam(headerLine, 0, valueStart); + std::string strValue(headerLine, valueStart + 1); + + StringUtils::Trim(strParam, m_whitespaceChars); + StringUtils::ToLower(strParam); + + StringUtils::Trim(strValue, m_whitespaceChars); + + if (!strParam.empty() && !strValue.empty()) + m_params.push_back(HeaderParams::value_type(strParam, strValue)); + else + return false; + } + else if (m_protoLine.empty()) + m_protoLine = headerLine; + + return true; +} + +void CHttpHeader::AddParam(const std::string& param, const std::string& value, const bool overwrite /*= false*/) +{ + std::string paramLower(param); + StringUtils::ToLower(paramLower); + StringUtils::Trim(paramLower, m_whitespaceChars); + if (paramLower.empty()) + return; + + if (overwrite) + { // delete ALL parameters with the same name + // note: 'GetValue' always returns last added parameter, + // so you probably don't need to overwrite + for (size_t i = 0; i < m_params.size();) + { + if (m_params[i].first == paramLower) + m_params.erase(m_params.begin() + i); + else + ++i; + } + } + + std::string valueTrim(value); + StringUtils::Trim(valueTrim, m_whitespaceChars); + if (valueTrim.empty()) + return; + + m_params.push_back(HeaderParams::value_type(paramLower, valueTrim)); +} + +std::string CHttpHeader::GetValue(const std::string& strParam) const +{ + std::string paramLower(strParam); + StringUtils::ToLower(paramLower); + + return GetValueRaw(paramLower); +} + +std::string CHttpHeader::GetValueRaw(const std::string& strParam) const +{ + // look in reverse to find last parameter (probably most important) + for (HeaderParams::const_reverse_iterator iter = m_params.rbegin(); iter != m_params.rend(); ++iter) + { + if (iter->first == strParam) + return iter->second; + } + + return ""; +} + +std::vector<std::string> CHttpHeader::GetValues(std::string strParam) const +{ + StringUtils::ToLower(strParam); + std::vector<std::string> values; + + for (HeaderParams::const_iterator iter = m_params.begin(); iter != m_params.end(); ++iter) + { + if (iter->first == strParam) + values.push_back(iter->second); + } + + return values; +} + +std::string CHttpHeader::GetHeader(void) const +{ + if (m_protoLine.empty() && m_params.empty()) + return ""; + + std::string strHeader(m_protoLine + "\r\n"); + + for (HeaderParams::const_iterator iter = m_params.begin(); iter != m_params.end(); ++iter) + strHeader += ((*iter).first + ": " + (*iter).second + "\r\n"); + + strHeader += "\r\n"; + return strHeader; +} + +std::string CHttpHeader::GetMimeType(void) const +{ + std::string strValue(GetValueRaw("content-type")); + + std::string mimeType(strValue, 0, strValue.find(';')); + StringUtils::TrimRight(mimeType, m_whitespaceChars); + + return mimeType; +} + +std::string CHttpHeader::GetCharset(void) const +{ + std::string strValue(GetValueRaw("content-type")); + if (strValue.empty()) + return strValue; + + StringUtils::ToUpper(strValue); + const size_t len = strValue.length(); + + // extract charset value from 'contenttype/contentsubtype;pram1=param1Val ; charset=XXXX\t;param2=param2Val' + // most common form: 'text/html; charset=XXXX' + // charset value can be in double quotes: 'text/xml; charset="XXX XX"' + + size_t pos = strValue.find(';'); + while (pos < len) + { + // move to the next non-whitespace character + pos = strValue.find_first_not_of(m_whitespaceChars, pos + 1); + + if (pos != std::string::npos) + { + if (strValue.compare(pos, 8, "CHARSET=", 8) == 0) + { + pos += 8; // move position to char after 'CHARSET=' + size_t len = strValue.find(';', pos); + if (len != std::string::npos) + len -= pos; + std::string charset(strValue, pos, len); // intentionally ignoring possible ';' inside quoted string + // as we don't support any charset with ';' in name + StringUtils::Trim(charset, m_whitespaceChars); + if (!charset.empty()) + { + if (charset[0] != '"') + return charset; + else + { // charset contains quoted string (allowed according to RFC 2616) + StringUtils::Replace(charset, "\\", ""); // unescape chars, ignoring possible '\"' and '\\' + const size_t closingQ = charset.find('"', 1); + if (closingQ == std::string::npos) + return ""; // no closing quote + + return charset.substr(1, closingQ - 1); + } + } + } + pos = strValue.find(';', pos); // find next parameter + } + } + + return ""; // no charset is detected +} + +void CHttpHeader::Clear() +{ + m_params.clear(); + m_protoLine.clear(); + m_headerdone = false; + m_lastHeaderLine.clear(); +} |