summaryrefslogtreecommitdiffstats
path: root/xbmc/utils/HttpHeader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/utils/HttpHeader.cpp')
-rw-r--r--xbmc/utils/HttpHeader.cpp239
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 @@
1/*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
8
9#include "HttpHeader.h"
10
11#include "utils/StringUtils.h"
12
13// header white space characters according to RFC 2616
14const char* const CHttpHeader::m_whitespaceChars = " \t";
15
16
17CHttpHeader::CHttpHeader()
18{
19 m_headerdone = false;
20}
21
22CHttpHeader::~CHttpHeader() = default;
23
24void CHttpHeader::Parse(const std::string& strData)
25{
26 size_t pos = 0;
27 const size_t len = strData.length();
28 const char* const strDataC = strData.c_str();
29
30 // According to RFC 2616 any header line can have continuation on next line, if next line is started from whitespace char
31 // 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
32 // 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)
33 while (pos < len)
34 {
35 size_t lineEnd = strData.find('\x0a', pos); // use '\x0a' instead of '\n' to be platform independent
36
37 if (lineEnd == std::string::npos)
38 return; // error: expected only complete lines
39
40 const size_t nextLine = lineEnd + 1;
41 if (lineEnd > pos && strDataC[lineEnd - 1] == '\x0d') // use '\x0d' instead of '\r' to be platform independent
42 lineEnd--;
43
44 if (m_headerdone)
45 Clear(); // clear previous header and process new one
46
47 if (strDataC[pos] == ' ' || strDataC[pos] == '\t') // same chars as in CHttpHeader::m_whitespaceChars
48 { // line is started from whitespace char: this is continuation of previous line
49 pos = strData.find_first_not_of(m_whitespaceChars, pos);
50
51 m_lastHeaderLine.push_back(' '); // replace all whitespace chars at start of the line with single space
52 m_lastHeaderLine.append(strData, pos, lineEnd - pos); // append current line
53 }
54 else
55 { // this line is NOT continuation, this line is new header line
56 if (!m_lastHeaderLine.empty())
57 ParseLine(m_lastHeaderLine); // process previously stored completed line (if any)
58
59 m_lastHeaderLine.assign(strData, pos, lineEnd - pos); // store current line to (possibly) complete later. Will be parsed on next turns.
60
61 if (pos == lineEnd)
62 m_headerdone = true; // current line is bare "\r\n" (or "\n"), means end of header; no need to process current m_lastHeaderLine
63 }
64
65 pos = nextLine; // go to next line (if any)
66 }
67}
68
69bool CHttpHeader::ParseLine(const std::string& headerLine)
70{
71 const size_t valueStart = headerLine.find(':');
72
73 if (valueStart != std::string::npos)
74 {
75 std::string strParam(headerLine, 0, valueStart);
76 std::string strValue(headerLine, valueStart + 1);
77
78 StringUtils::Trim(strParam, m_whitespaceChars);
79 StringUtils::ToLower(strParam);
80
81 StringUtils::Trim(strValue, m_whitespaceChars);
82
83 if (!strParam.empty() && !strValue.empty())
84 m_params.push_back(HeaderParams::value_type(strParam, strValue));
85 else
86 return false;
87 }
88 else if (m_protoLine.empty())
89 m_protoLine = headerLine;
90
91 return true;
92}
93
94void CHttpHeader::AddParam(const std::string& param, const std::string& value, const bool overwrite /*= false*/)
95{
96 std::string paramLower(param);
97 StringUtils::ToLower(paramLower);
98 StringUtils::Trim(paramLower, m_whitespaceChars);
99 if (paramLower.empty())
100 return;
101
102 if (overwrite)
103 { // delete ALL parameters with the same name
104 // note: 'GetValue' always returns last added parameter,
105 // so you probably don't need to overwrite
106 for (size_t i = 0; i < m_params.size();)
107 {
108 if (m_params[i].first == paramLower)
109 m_params.erase(m_params.begin() + i);
110 else
111 ++i;
112 }
113 }
114
115 std::string valueTrim(value);
116 StringUtils::Trim(valueTrim, m_whitespaceChars);
117 if (valueTrim.empty())
118 return;
119
120 m_params.push_back(HeaderParams::value_type(paramLower, valueTrim));
121}
122
123std::string CHttpHeader::GetValue(const std::string& strParam) const
124{
125 std::string paramLower(strParam);
126 StringUtils::ToLower(paramLower);
127
128 return GetValueRaw(paramLower);
129}
130
131std::string CHttpHeader::GetValueRaw(const std::string& strParam) const
132{
133 // look in reverse to find last parameter (probably most important)
134 for (HeaderParams::const_reverse_iterator iter = m_params.rbegin(); iter != m_params.rend(); ++iter)
135 {
136 if (iter->first == strParam)
137 return iter->second;
138 }
139
140 return "";
141}
142
143std::vector<std::string> CHttpHeader::GetValues(std::string strParam) const
144{
145 StringUtils::ToLower(strParam);
146 std::vector<std::string> values;
147
148 for (HeaderParams::const_iterator iter = m_params.begin(); iter != m_params.end(); ++iter)
149 {
150 if (iter->first == strParam)
151 values.push_back(iter->second);
152 }
153
154 return values;
155}
156
157std::string CHttpHeader::GetHeader(void) const
158{
159 if (m_protoLine.empty() && m_params.empty())
160 return "";
161
162 std::string strHeader(m_protoLine + "\r\n");
163
164 for (HeaderParams::const_iterator iter = m_params.begin(); iter != m_params.end(); ++iter)
165 strHeader += ((*iter).first + ": " + (*iter).second + "\r\n");
166
167 strHeader += "\r\n";
168 return strHeader;
169}
170
171std::string CHttpHeader::GetMimeType(void) const
172{
173 std::string strValue(GetValueRaw("content-type"));
174
175 std::string mimeType(strValue, 0, strValue.find(';'));
176 StringUtils::TrimRight(mimeType, m_whitespaceChars);
177
178 return mimeType;
179}
180
181std::string CHttpHeader::GetCharset(void) const
182{
183 std::string strValue(GetValueRaw("content-type"));
184 if (strValue.empty())
185 return strValue;
186
187 StringUtils::ToUpper(strValue);
188 const size_t len = strValue.length();
189
190 // extract charset value from 'contenttype/contentsubtype;pram1=param1Val ; charset=XXXX\t;param2=param2Val'
191 // most common form: 'text/html; charset=XXXX'
192 // charset value can be in double quotes: 'text/xml; charset="XXX XX"'
193
194 size_t pos = strValue.find(';');
195 while (pos < len)
196 {
197 // move to the next non-whitespace character
198 pos = strValue.find_first_not_of(m_whitespaceChars, pos + 1);
199
200 if (pos != std::string::npos)
201 {
202 if (strValue.compare(pos, 8, "CHARSET=", 8) == 0)
203 {
204 pos += 8; // move position to char after 'CHARSET='
205 size_t len = strValue.find(';', pos);
206 if (len != std::string::npos)
207 len -= pos;
208 std::string charset(strValue, pos, len); // intentionally ignoring possible ';' inside quoted string
209 // as we don't support any charset with ';' in name
210 StringUtils::Trim(charset, m_whitespaceChars);
211 if (!charset.empty())
212 {
213 if (charset[0] != '"')
214 return charset;
215 else
216 { // charset contains quoted string (allowed according to RFC 2616)
217 StringUtils::Replace(charset, "\\", ""); // unescape chars, ignoring possible '\"' and '\\'
218 const size_t closingQ = charset.find('"', 1);
219 if (closingQ == std::string::npos)
220 return ""; // no closing quote
221
222 return charset.substr(1, closingQ - 1);
223 }
224 }
225 }
226 pos = strValue.find(';', pos); // find next parameter
227 }
228 }
229
230 return ""; // no charset is detected
231}
232
233void CHttpHeader::Clear()
234{
235 m_params.clear();
236 m_protoLine.clear();
237 m_headerdone = false;
238 m_lastHeaderLine.clear();
239}