MythTV master
htmlserver.cpp
Go to the documentation of this file.
1
2// Program Name: htmlserver.cpp
3// Created : Mar. 9, 2011
4//
5// Purpose : Http server extension to serve up static html content
6//
7// Copyright (c) 2011 David Blain <dblain@mythtv.org>
8//
9// Licensed under the GPL v2 or later, see LICENSE for details
10//
12#include "htmlserver.h"
13
14#include <QFileInfo>
15#include <QDir>
16#include <QTextStream>
17#include <QUuid>
18
21
22#include "httprequest.h"
23
25//
27
29 const QString &sApplicationPrefix)
30 : HttpServerExtension( "Html" , sSharePath),
31 m_indexFilename(sApplicationPrefix + "index")
32{
33#if CONFIG_QTSCRIPT
34 LOG(VB_HTTP, LOG_INFO, QString("HtmlServerExtension() - SharePath = %1")
35 .arg(m_sSharePath));
36 m_scripting.SetResourceRootPath( m_sSharePath );
37#endif
38}
39
41//
43
45{
46 if (pRequest)
47 {
48 if ( !pRequest->m_sBaseUrl.startsWith("/"))
49 return( false );
50
51 if ((pRequest->m_eType != RequestTypeGet) &&
52 (pRequest->m_eType != RequestTypeHead) &&
53 (pRequest->m_eType != RequestTypePost))
54 {
56 pRequest->m_nResponseStatus = 405; // Method not allowed
57 // Conservative list, we can't really know what methods we
58 // actually allow for an arbitrary resource without some sort of
59 // high maintenance database
60 pRequest->m_response.write( pRequest->GetResponsePage() );
61 pRequest->SetResponseHeader("Allow", "GET, HEAD");
62 return true;
63 }
64
65 bool bStorageGroupFile = false;
66 QFileInfo oInfo( m_sSharePath + pRequest->m_sResourceUrl );
67
68 if (oInfo.isDir())
69 {
70 QString sIndexFileName = oInfo.filePath() + m_indexFilename + ".qsp";
71
72 if (QFile::exists( sIndexFileName ))
73 oInfo.setFile( sIndexFileName );
74 else
75 oInfo.setFile( oInfo.filePath() + m_indexFilename + ".html" );
76 }
77
78 if (pRequest->m_sResourceUrl.startsWith("/StorageGroup/"))
79 {
80 StorageGroup oGroup(pRequest->m_sResourceUrl.section('/', 2, 2));
81 QString sFile =
82 oGroup.FindFile(pRequest->m_sResourceUrl.section('/', 3));
83 if (!sFile.isEmpty())
84 {
85 oInfo.setFile(sFile);
86 bStorageGroupFile = true;
87 }
88 }
89
90 if (bStorageGroupFile || oInfo.exists() )
91 {
92 QString sResName = oInfo.canonicalFilePath();
93
94 // --------------------------------------------------------------
95 // Checking for url's that contain ../ or similar.
96 // --------------------------------------------------------------
97
98 if (( bStorageGroupFile ) ||
99 (sResName.startsWith( m_sSharePath, Qt::CaseInsensitive )))
100 {
101 if (oInfo.exists())
102 {
103 if (oInfo.isSymLink())
104 sResName = oInfo.symLinkTarget();
105
106 // ------------------------------------------------------
107 // CSP Nonce
108 // ------------------------------------------------------
109 QByteArray cspNonce = QUuid::createUuid().toByteArray().toBase64();
110 cspNonce = cspNonce.mid(1, cspNonce.length() - 2); // UUID, with braces removed
111
112 // ------------------------------------------------------
113 // Is this a Qt Server Page (File contains script)...
114 // ------------------------------------------------------
115
116 QString sSuffix = oInfo.suffix().toLower();
117
118 QString sMimeType = HTTPRequest::GetMimeType(sSuffix);
119
120 if (sMimeType == "text/html")
122 else if (sMimeType == "text/xml")
124 else if (sMimeType == "application/javascript")
126 else if (sMimeType == "text/css")
128 else if (sMimeType == "text/plain")
130 else if (sMimeType == "image/svg+xml" &&
131 sSuffix != "svgz") // svgz are pre-compressed
133
134 // ---------------------------------------------------------
135 // Force IE into 'standards' mode
136 // ---------------------------------------------------------
137 pRequest->SetResponseHeader("X-UA-Compatible", "IE=Edge");
138
139 // ---------------------------------------------------------
140 // SECURITY: Set X-Content-Type-Options to 'nosniff'
141 //
142 // IE only for now. Prevents browsers ignoring the
143 // Content-Type header we supply and potentially executing
144 // malicious script embedded in an image or css file.
145 //
146 // Yes, really, you need to explicitly disable this sort of
147 // dangerous behaviour in 2015!
148 // ---------------------------------------------------------
149 pRequest->SetResponseHeader("X-Content-Type-Options",
150 "nosniff");
151
152 // ---------------------------------------------------------
153 // SECURITY: Set Content Security Policy
154 //
155 // *No external content allowed*
156 //
157 // This is an important safeguard. Third party content
158 // should never be permitted. It compromises security,
159 // privacy and violates the key principal that the
160 // WebFrontend should work on an isolated network with no
161 // internet access. Keep all content hosted locally!
162 // ---------------------------------------------------------
163
164 // For now the following are disabled as we use xhr to
165 // trigger playback on frontends if we switch to triggering
166 // that through an internal request then these would be
167 // better enabled
168 //"default-src 'self'; "
169 //"connect-src 'self' https://services.mythtv.org; "
170
171 // FIXME: unsafe-inline should be phased out, replaced by nonce-{csp_nonce} but it requires
172 // all inline event handlers and style attributes to be removed ...
173 QString cspPolicy = "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://services.mythtv.org; " // QString('nonce-%1').arg(QString(cspNonce))
174 "style-src 'self' 'unsafe-inline'; "
175 "frame-src 'self'; "
176 "object-src 'self'; " // TODO: When we no longer require flash for some browsers, change this to 'none'
177 "media-src 'self'; "
178 "font-src 'self'; "
179 "img-src 'self'; "
180 "form-action 'self'; "
181 "frame-ancestors 'self'; ";
182
183 pRequest->SetResponseHeader("X-XSS-Protection", "1; mode=block");
184
185 // For standards compliant browsers
186 pRequest->SetResponseHeader("Content-Security-Policy",
187 cspPolicy);
188 // For Internet Explorer
189 pRequest->SetResponseHeader("X-Content-Security-Policy",
190 cspPolicy);
191
192 if ((sSuffix == "qsp") ||
193 (sSuffix == "qxml") ||
194 (sSuffix == "qjs" ))
195 {
196 QTextStream stream( &pRequest->m_response );
197
198#if CONFIG_QTSCRIPT
199 m_scripting.EvaluatePage( &stream, sResName, pRequest, cspNonce);
200#endif
201
202 return true;
203 }
204
205 // ------------------------------------------------------
206 // Return the file.
207 // ------------------------------------------------------
208
209 pRequest->FormatFileResponse( sResName );
210
211 return true;
212 }
213 }
214 }
215
216 // force return as a 404...
217 pRequest->FormatFileResponse( "" );
218 }
219
220 return( true );
221}
222
QByteArray GetResponsePage(void)
HttpResponseType m_eResponseType
Definition: httprequest.h:150
long m_nResponseStatus
Definition: httprequest.h:153
void FormatFileResponse(const QString &sFileName)
static QString GetMimeType(const QString &sFileExtension)
HttpRequestType m_eType
Definition: httprequest.h:121
QString m_sResourceUrl
Definition: httprequest.h:129
QString m_sBaseUrl
Definition: httprequest.h:128
void SetResponseHeader(const QString &sKey, const QString &sValue, bool replace=false)
QBuffer m_response
Definition: httprequest.h:158
QString m_indexFilename
Definition: htmlserver.h:36
bool ProcessRequest(HTTPRequest *pRequest) override
Definition: htmlserver.cpp:44
HtmlServerExtension(const QString &sSharePath, const QString &sApplicationPrefix)
Definition: htmlserver.cpp:28
QString m_sSharePath
Definition: httpserver.h:74
QString FindFile(const QString &filename)
@ ResponseTypeSVG
Definition: httprequest.h:84
@ ResponseTypeCSS
Definition: httprequest.h:82
@ ResponseTypeHTML
Definition: httprequest.h:80
@ ResponseTypeXML
Definition: httprequest.h:79
@ ResponseTypeJS
Definition: httprequest.h:81
@ ResponseTypeText
Definition: httprequest.h:83
@ RequestTypePost
Definition: httprequest.h:51
@ RequestTypeGet
Definition: httprequest.h:49
@ RequestTypeHead
Definition: httprequest.h:50
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
bool exists(str path)
Definition: xbmcvfs.py:51