Cutelyst  1.11.0
headers.cpp
1 /*
2  * Copyright (C) 2014-2017 Daniel Nicoletti <dantti12@gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 #include "headers.h"
19 
20 #include "common.h"
21 
22 #include <QStringList>
23 
24 using namespace Cutelyst;
25 
26 inline QString normalizeHeaderKey(const QString &field);
27 inline QByteArray decodeBasicAuth(const QString &auth);
28 inline QPair<QString, QString> decodeBasicAuthPair(const QString &auth);
29 
31 {
32 
33 }
34 
36 {
37  return m_data.value(QStringLiteral("CONTENT_DISPOSITION"));
38 }
39 
41 {
42  m_data.insert(QStringLiteral("CONTENT_DISPOSITION"), contentDisposition);
43 }
44 
45 void Headers::setContentDispositionAttachment(const QString &filename)
46 {
47  if (filename.isEmpty()) {
48  setContentDisposition(QStringLiteral("attachment"));
49  } else {
50  setContentDisposition(QLatin1String("attachment; filename=\"") + filename + QLatin1Char('"'));
51  }
52 }
53 
54 QString Headers::contentEncoding() const
55 {
56  return m_data.value(QStringLiteral("CONTENT_ENCODING"));
57 }
58 
59 void Headers::setContentEncoding(const QString &encoding)
60 {
61  m_data.insert(QStringLiteral("CONTENT_ENCODING"), encoding);
62 }
63 
64 QString Headers::contentType() const
65 {
66  QString ret;
67  const auto it = m_data.constFind(QStringLiteral("CONTENT_TYPE"));
68  if (it != m_data.constEnd()) {
69  const QString ct = it.value();
70  ret = ct.mid(0, ct.indexOf(QLatin1Char(';'))).toLower();
71  }
72  return ret;
73 }
74 
76 {
77  m_data.insert(QStringLiteral("CONTENT_TYPE"), contentType);
78 }
79 
81 {
82  QString ret;
83  const auto it = m_data.constFind(QStringLiteral("CONTENT_TYPE"));
84  if (it != m_data.constEnd()) {
85  const QString contentType = it.value();
86  int pos = contentType.indexOf(QLatin1String("charset="), 0, Qt::CaseInsensitive);
87  if (pos != -1) {
88  int endPos = contentType.indexOf(QLatin1Char(';'), pos);
89  ret = contentType.mid(pos + 8, endPos).trimmed().toUpper();
90  }
91  }
92 
93  return ret;
94 }
95 
96 void Headers::setContentTypeCharset(const QString &charset)
97 {
98  const auto it = m_data.constFind(QStringLiteral("CONTENT_TYPE"));
99  if (it == m_data.constEnd() || (it.value().isEmpty() && !charset.isEmpty())) {
100  m_data.insert(QStringLiteral("CONTENT_TYPE"), QLatin1String("charset=") + charset);
101  return;
102  }
103 
104  QString contentType = it.value();
105  int pos = contentType.indexOf(QLatin1String("charset="), 0, Qt::CaseInsensitive);
106  if (pos != -1) {
107  int endPos = contentType.indexOf(QLatin1Char(';'), pos);
108  if (endPos == -1) {
109  if (charset.isEmpty()) {
110  int lastPos = contentType.lastIndexOf(QLatin1Char(';'), pos);
111  if (lastPos == -1) {
112  m_data.remove(QStringLiteral("CONTENT_TYPE"));
113  return;
114  } else {
115  contentType.remove(lastPos, contentType.length() - lastPos);
116  }
117  } else {
118  contentType.replace(pos + 8, contentType.length() - pos + 8, charset);
119  }
120  } else {
121  contentType.replace(pos + 8, endPos, charset);
122  }
123  } else if (!charset.isEmpty()) {
124  contentType.append(QLatin1String("; charset=") + charset);
125  }
126  m_data.insert(QStringLiteral("CONTENT_TYPE"), contentType);
127 }
128 
130 {
131  return m_data.value(QStringLiteral("CONTENT_TYPE")).startsWith(QLatin1String("text/"));
132 }
133 
135 {
136  const QString ct = contentType();
137  return ct == QLatin1String("text/html") ||
138  ct == QLatin1String("application/xhtml+xml") ||
139  ct == QLatin1String("application/vnd.wap.xhtml+xml");
140 }
141 
143 {
144  const QString ct = contentType();
145  return ct == QLatin1String("application/xhtml+xml") ||
146  ct == QLatin1String("application/vnd.wap.xhtml+xml");
147 }
148 
150 {
151  const QString ct = contentType();
152  return ct == QLatin1String("text/xml") ||
153  ct == QLatin1String("application/xml") ||
154  ct.endsWith(QLatin1String("xml"));
155 }
156 
158 {
159  auto it = m_data.constFind(QStringLiteral("CONTENT_LENGTH"));
160  if (it != m_data.constEnd()) {
161  return it.value().toLongLong();
162  }
163  return -1;
164 }
165 
166 void Headers::setContentLength(qint64 value)
167 {
168  m_data.insert(QStringLiteral("CONTENT_LENGTH"), QString::number(value));
169 }
170 
171 QString Headers::setDateWithDateTime(const QDateTime &date)
172 {
173  // ALL dates must be in GMT timezone http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html
174  // and follow RFC 822
175  const QString dt = QLocale::c().toString(date.toUTC(),
176  QStringLiteral("ddd, dd MMM yyyy hh:mm:ss 'GMT"));
177  m_data.insert(QStringLiteral("DATE"), dt);
178  return dt;
179 }
180 
181 QDateTime Headers::date() const
182 {
183  QDateTime ret;
184  auto it = m_data.constFind(QStringLiteral("DATE"));
185  if (it != m_data.constEnd()) {
186  const QString date = it.value();
187 
188  if (date.endsWith(QLatin1String(" GMT"))) {
189  ret = QLocale::c().toDateTime(date.left(date.size() - 4),
190  QStringLiteral("ddd, dd MMM yyyy hh:mm:ss"));
191  } else {
192  ret = QLocale::c().toDateTime(date,
193  QStringLiteral("ddd, dd MMM yyyy hh:mm:ss"));
194  }
195  ret.setTimeSpec(Qt::UTC);
196  }
197 
198  return ret;
199 }
200 
202 {
203  return header(QStringLiteral("IF_MODIFIED_SINCE"));
204 }
205 
207 {
208  QDateTime ret;
209  auto it = m_data.constFind(QStringLiteral("IF_MODIFIED_SINCE"));
210  if (it != m_data.constEnd()) {
211  const QString ifModifiedStr = it.value();
212 
213  if (ifModifiedStr.endsWith(QLatin1String(" GMT"))) {
214  ret = QLocale::c().toDateTime(ifModifiedStr.left(ifModifiedStr.size() - 4),
215  QStringLiteral("ddd, dd MMM yyyy hh:mm:ss"));
216  } else {
217  ret = QLocale::c().toDateTime(ifModifiedStr,
218  QStringLiteral("ddd, dd MMM yyyy hh:mm:ss"));
219  }
220  ret.setTimeSpec(Qt::UTC);
221  }
222 
223  return ret;
224 }
225 
226 QString Headers::lastModified() const
227 {
228  return m_data.value(QStringLiteral("LAST_MODIFIED"));
229 }
230 
231 void Headers::setLastModified(const QString &value)
232 {
233  m_data.insert(QStringLiteral("LAST_MODIFIED"), value);
234 }
235 
236 QString Headers::setLastModified(const QDateTime &lastModified)
237 {
238  // ALL dates must be in GMT timezone http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html
239  // and follow RFC 822
240  const auto dt = QLocale::c().toString(lastModified.toUTC(),
241  QStringLiteral("ddd, dd MMM yyyy hh:mm:ss 'GMT"));
242  setLastModified(dt);
243  return dt;
244 }
245 
246 QString Headers::server() const
247 {
248  return m_data.value(QStringLiteral("SERVER"));
249 }
250 
251 void Headers::setServer(const QString &value)
252 {
253  m_data.insert(QStringLiteral("SERVER"), value);
254 }
255 
256 QString Headers::connection() const
257 {
258  return m_data.value(QStringLiteral("CONNECTION"));
259 }
260 
261 QString Headers::host() const
262 {
263  return m_data.value(QStringLiteral("HOST"));
264 }
265 
266 QString Headers::userAgent() const
267 {
268  return m_data.value(QStringLiteral("USER_AGENT"));
269 }
270 
271 QString Headers::referer() const
272 {
273  return m_data.value(QStringLiteral("REFERER"));
274 }
275 
276 void Headers::setReferer(const QString &uri)
277 {
278  int fragmentPos = uri.indexOf(QLatin1Char('#'));
279  if (fragmentPos != -1) {
280  // Strip fragment per RFC 2616, section 14.36.
281  m_data.insert(QStringLiteral("REFERER"), uri.mid(0, fragmentPos));
282  } else {
283  m_data.insert(QStringLiteral("REFERER"), uri);
284  }
285 }
286 
287 void Headers::setWwwAuthenticate(const QString &value)
288 {
289  m_data.insert(QStringLiteral("WWW_AUTHENTICATE"), value);
290 }
291 
292 void Headers::setProxyAuthenticate(const QString &value)
293 {
294  m_data.insert(QStringLiteral("PROXY_AUTHENTICATE"), value);
295 }
296 
297 QString Headers::authorization() const
298 {
299  return m_data.value(QStringLiteral("AUTHORIZATION"));
300 }
301 
303 {
304  return QString::fromLatin1(decodeBasicAuth(authorization()));
305 }
306 
307 QPair<QString, QString> Headers::authorizationBasicPair() const
308 {
309  return decodeBasicAuthPair(authorization());
310 }
311 
312 QString Headers::setAuthorizationBasic(const QString &username, const QString &password)
313 {
314  QString ret;
315  if (username.contains(QLatin1Char(':'))) {
316  qCWarning(CUTELYST_CORE) << "Headers::Basic authorization user name can't contain ':'";
317  return ret;
318  }
319 
320  const QString result = username + QLatin1Char(':') + password;
321  ret = QStringLiteral("Basic ") + QString::fromLatin1(result.toLatin1().toBase64());
322  m_data.insert(QStringLiteral("AUTHORIZATION"), ret);
323  return ret;
324 }
325 
326 QHash<QString, QString> Headers::authorizationDigest() const
327 {
328  QHash<QString, QString> ret;
329  qCWarning(CUTELYST_CORE) << "Headers::authorizationDigest not implemented";
330  return ret;
331 }
332 
334 {
335  return m_data.value(QStringLiteral("PROXY_AUTHORIZATION"));
336 }
337 
339 {
340  return QString::fromLatin1(decodeBasicAuth(proxyAuthorization()));
341 }
342 
343 QPair<QString, QString> Headers::proxyAuthorizationBasicPair() const
344 {
345  return decodeBasicAuthPair(proxyAuthorization());
346 }
347 
348 QString Headers::header(const QString &field) const
349 {
350  return m_data.value(normalizeHeaderKey(field));
351 }
352 
353 QString Headers::header(const QString &field, const QString &defaultValue) const
354 {
355  return m_data.value(normalizeHeaderKey(field), defaultValue);
356 }
357 
358 void Headers::setHeader(const QString &field, const QString &value)
359 {
360  m_data.insert(normalizeHeaderKey(field), value);
361 }
362 
363 void Headers::setHeader(const QString &field, const QStringList &values)
364 {
365  setHeader(field, values.join(QStringLiteral(", ")));
366 }
367 
368 void Headers::pushHeader(const QString &field, const QString &value)
369 {
370  m_data.insertMulti(normalizeHeaderKey(field), value);
371 }
372 
373 void Headers::pushHeader(const QString &field, const QStringList &values)
374 {
375  m_data.insertMulti(normalizeHeaderKey(field), values.join(QStringLiteral(", ")));
376 }
377 
378 void Headers::removeHeader(const QString &field)
379 {
380  m_data.remove(normalizeHeaderKey(field));
381 }
382 
383 bool Headers::contains(const QString &field)
384 {
385  return m_data.contains(normalizeHeaderKey(field));
386 }
387 
388 QString &Headers::operator[](const QString &key)
389 {
390  return m_data[key];
391 }
392 
393 const QString Headers::operator[](const QString &key) const
394 {
395  return m_data[key];
396 }
397 
398 QString normalizeHeaderKey(const QString &field)
399 {
400  QString key = field;
401  int i = 0;
402  while (i < key.size()) {
403  QCharRef c = key[i];
404  if (c.isLetter()) {
405  if (c.isLower()) {
406  c = c.toUpper();
407  }
408  } else if (c == QLatin1Char('-')) {
409  c = QLatin1Char('_');
410  }
411  ++i;
412  }
413  return key;
414 }
415 
416 QByteArray decodeBasicAuth(const QString &auth)
417 {
418  QByteArray ret;
419  if (!auth.isEmpty() && auth.startsWith(QLatin1String("Basic "))) {
420  int pos = auth.lastIndexOf(QLatin1Char(' '));
421  if (pos != -1) {
422  ret = QByteArray::fromBase64(auth.mid(pos).toLatin1());
423  }
424  }
425  return ret;
426 }
427 
428 QPair<QString, QString> decodeBasicAuthPair(const QString &auth)
429 {
430  QPair<QString, QString> ret;
431  const QByteArray authorization = decodeBasicAuth(auth);
432  if (!authorization.isEmpty()) {
433  int pos = authorization.indexOf(':');
434  if (pos == -1) {
435  ret.first = QString::fromLatin1(authorization);
436  } else {
437  ret = { QString::fromLatin1(authorization.left(pos)),
438  QString::fromLatin1(authorization.mid(pos + 1)) };
439  }
440  }
441  return ret;
442 }
QString contentType() const
Definition: headers.cpp:64
void pushHeader(const QString &field, const QString &value)
Definition: headers.cpp:368
QString proxyAuthorization() const
Definition: headers.cpp:333
bool contentIsXml() const
Definition: headers.cpp:149
QPair< QString, QString > authorizationBasicPair() const
Definition: headers.cpp:307
QString authorizationBasic() const
Definition: headers.cpp:302
bool contentIsHtml() const
Definition: headers.cpp:134
qint64 contentLength() const
Definition: headers.cpp:157
QString ifModifiedSince() const
Definition: headers.cpp:201
QString userAgent() const
Definition: headers.cpp:266
void setContentDispositionAttachment(const QString &filename=QString())
Definition: headers.cpp:45
QString contentEncoding() const
Definition: headers.cpp:54
QString server() const
Definition: headers.cpp:246
void setProxyAuthenticate(const QString &value)
Definition: headers.cpp:292
QPair< QString, QString > proxyAuthorizationBasicPair() const
Definition: headers.cpp:343
QString contentTypeCharset() const
Definition: headers.cpp:80
QString & operator[](const QString &key)
Definition: headers.cpp:388
void removeHeader(const QString &field)
Definition: headers.cpp:378
bool contains(const QString &field)
Definition: headers.cpp:383
QString setDateWithDateTime(const QDateTime &date)
Definition: headers.cpp:171
QString contentDisposition() const
Definition: headers.cpp:35
void setHeader(const QString &field, const QString &value)
Definition: headers.cpp:358
void setLastModified(const QString &value)
Definition: headers.cpp:231
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:7
QString header(const QString &field) const
Definition: headers.cpp:348
QDateTime ifModifiedSinceDateTime() const
Definition: headers.cpp:206
void setContentDisposition(const QString &contentDisposition)
Definition: headers.cpp:40
QString connection() const
Definition: headers.cpp:256
void setContentType(const QString &contentType)
Definition: headers.cpp:75
QString authorization() const
Definition: headers.cpp:297
QDateTime date() const
Definition: headers.cpp:181
bool contentIsXHtml() const
Definition: headers.cpp:142
void setWwwAuthenticate(const QString &value)
Definition: headers.cpp:287
void setServer(const QString &value)
Definition: headers.cpp:251
QString setAuthorizationBasic(const QString &username, const QString &password)
Definition: headers.cpp:312
QString host() const
Definition: headers.cpp:261
void setContentTypeCharset(const QString &charset)
Definition: headers.cpp:96
void setContentEncoding(const QString &encoding)
Definition: headers.cpp:59
void setReferer(const QString &value)
Definition: headers.cpp:276
QString referer() const
Definition: headers.cpp:271
void setContentLength(qint64 value)
Definition: headers.cpp:166
bool contentIsText() const
Definition: headers.cpp:129
QHash< QString, QString > authorizationDigest() const
Definition: headers.cpp:326
QString lastModified() const
Definition: headers.cpp:226
QString proxyAuthorizationBasic() const
Definition: headers.cpp:338