MythTV master
httpconfig.cpp
Go to the documentation of this file.
1// Qt headers
2#include <QByteArray>
3#include <QDir>
4#include <QFileInfo>
5#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
6#include <QStringConverter>
7#endif
8#include <QTextStream>
9#include <QUrl>
10
11// MythTV headers
13#include "libmythbase/mythdb.h"
19
20// MythBackend
21#include "httpconfig.h"
22
23HttpConfig::HttpConfig() : HttpServerExtension("HttpConfig", QString())
24{
25}
26
28{
29 QStringList paths;
30 paths << "/Config";
31 paths << "/Config/Database";
32 paths << "/Config/General";
33 return paths;
34}
35
37{
38 if (!request)
39 return false;
40
41 LOG(VB_UPNP, LOG_INFO,
42 QString("HttpConfig::ProcessRequest(): m_sBaseURL: '%1',"
43 "m_sMethod: '%2'")
44 .arg(request->m_sBaseUrl, request->m_sMethod));
45 if (!request->m_sBaseUrl.startsWith("/Config"))
46 {
47 return false;
48 }
49
50 bool handled = false;
51 if (request->m_sMethod == "Save")
52 {
53 // FIXME, this is always false, what's it for
54 // JMS "fixed" by using endsWith()
55 if (request->m_sBaseUrl.endsWith("config") &&
56 !m_databaseSettings.empty())
57 {
58 QString checkResult;
59 PrintHeader(request->m_response, "/Config/Database");
61 checkResult);
64 PrintFooter(request->m_response);
65 handled = true;
66 }
67 else
68 {
69 bool okToSave = false;
70 QString checkResult;
71
72 if (request->m_sBaseUrl == "/Config/Database")
73 {
75 checkResult))
76 okToSave = true;
77 }
78 else if (request->m_sBaseUrl == "/Config/General")
79 {
81 checkResult))
82 okToSave = true;
83 }
84
85 if (okToSave)
86 LOG(VB_UPNP, LOG_INFO, "HTTP method 'Save' called, but not handled");
87#if 0
88 QTextStream os(&request->m_response);
89 os << "<html><body><h3>The Save function for this screen is "
90 << "not hooked up yet</h3><dl>";
91 QStringMap::const_iterator it = request->m_mapParams.begin();
92 for (; it!=request->m_mapParams.end(); ++it)
93 {
94 if (it.key() == "__group__")
95 continue;
96
97 os << "<dt>"<<it.key()<<"</dt><dd>"
98 <<*it<<"</dd>\r\n";
99 }
100 os << "</dl></body></html>";
101 handled = true;
102#else
103 QTextStream os(&request->m_response);
104 os << checkResult;
106 request->m_sResponseTypeText = "application/json";
107 request->m_mapRespHeaders[ "Cache-Control" ] =
108 "no-cache=\"Ext\", max-age = 0";
109
110 return true;
111#endif
112 }
113 }
114 else if (request->m_sMethod == "Settings")
115 {
116 QString result = R"({ "Error": "Unknown Settings List" })";
117 QString fn = GetShareDir() + "backend-config/";
118
119 if (request->m_sBaseUrl == "/Config/Database")
120 {
121 fn += "config_backend_database.xml";
123 result = StringMapToJSON(
125 }
126 else if (request->m_sBaseUrl == "/Config/General")
127 {
128 fn += "config_backend_general.xml";
130 result = StringMapToJSON(
132 }
133
134 QTextStream os(&request->m_response);
135 os << result;
137 request->m_sResponseTypeText = "application/json";
138 request->m_mapRespHeaders[ "Cache-Control" ] =
139 "no-cache=\"Ext\", max-age = 0";
140
141 return true;
142 }
143 else if (request->m_sMethod == "XML")
144 {
145 QString fn = GetShareDir() + "backend-config/";
146
147 if (request->m_sBaseUrl == "/Config/Database")
148 fn += "config_backend_database.xml";
149 else if (request->m_sBaseUrl == "/Config/General")
150 fn += "config_backend_general.xml";
151
152 request->FormatFileResponse(fn);
153 return true;
154 }
155 else if ((request->m_sMethod == "InstallPackage") &&
156 (request->m_mapParams.contains("package")))
157 {
158 QString package = QUrl::fromPercentEncoding(request->m_mapParams["package"].toUtf8());
159 QString url = QString("http://www.mythtv.org/ftp/3rdParty/%1").arg(package);
160 StorageGroup tmpGroup("Temp", gCoreContext->GetHostName());
161 QString tmpFile = tmpGroup.GetFirstDir(true) + "package.zip";
162 StorageGroup destGroup("3rdParty", gCoreContext->GetHostName());
163 QString outDir = destGroup.GetFirstDir();
164
165 QString result = "false";
166 if ((GetMythDownloadManager()->download(url, tmpFile)) &&
167 (extractZIP(tmpFile, outDir)))
168 {
169 result = "true";
170 }
171
172 QTextStream os(&request->m_response);
173 os << StringListToJSON("Result", QStringList(result));
174
176 request->m_sResponseTypeText = "application/json";
177 request->m_mapRespHeaders[ "Cache-Control" ] =
178 "no-cache=\"Ext\", max-age = 0";
179
180 return true;
181 }
182 else if ((request->m_sMethod == "FileBrowser") &&
183 (request->m_mapParams.contains("dir")))
184 {
185 QString startingDir = QUrl::fromPercentEncoding(request->m_mapParams["dir"].toUtf8());
186 if (startingDir.startsWith("myth://"))
187 {
188 QUrl qurl(startingDir);
189 QString dir;
190
191 QString host = qurl.host();
192 int port = qurl.port();
193
194 dir = qurl.path();
195
196 QString storageGroup = qurl.userName();
197
198 StorageGroup sgroup(storageGroup);
199 QStringList entries = sgroup.GetFileInfoList(dir);
200
201 if ((entries.size() == 1) &&
202 (entries[0].startsWith("sgdir::")))
203 {
204 QStringList parts = entries[0].split("::");
205 entries = sgroup.GetFileInfoList(parts[1]);
206 }
207
208 if (!entries.empty())
209 {
210 QTextStream os(&request->m_response);
211 os << "<ul class=\"jqueryFileTree\" style=\"display: none;\">\r\n";
212
213 for (const auto & entry : std::as_const(entries))
214 {
215 QStringList parts = entry.split("::");
216 QFileInfo fi(parts[1]);
217 if (dir == "/")
218 dir = "";
219 QString path =
221 port,
222 dir + parts[1],
223 storageGroup);
224 if (entry.startsWith("sgdir::"))
225 {
226 os << R"( <li class="directory collapsed"><a href="#" rel=")"
227 << path << "/\">" << parts[1] << "</a></li>\r\n";
228 }
229 else if (entry.startsWith("dir::"))
230 {
231 os << R"( <li class="directory collapsed"><a href="#" rel=")"
232 << path << "/\">" << fi.fileName() << "</a></li>\r\n";
233 }
234 else if (entry.startsWith("file::"))
235 {
236 os << " <li class=\"file ext_" << fi.suffix() << R"("><a href="#" rel=")"
237 << parts[3] << "\">" << fi.fileName() << "</a></li>\r\n";
238 }
239 }
240 os << "</ul>\r\n";
241
242 handled = true;
243 }
244 } else {
245 QDir dir(startingDir);
246 if (dir.exists())
247 {
248 QTextStream os(&request->m_response);
249 os << "<ul class=\"jqueryFileTree\" style=\"display: none;\">\r\n";
250
251 QFileInfoList infoList = dir.entryInfoList();
252 for (const auto & fi : std::as_const(infoList))
253 {
254 if (!fi.isDir())
255 continue;
256 if (fi.fileName().startsWith("."))
257 continue;
258
259 os << R"( <li class="directory collapsed"><a href="#" rel=")"
260 << fi.absoluteFilePath() << "/\">" << fi.fileName() << "</a></li>\r\n";
261 }
262
263 bool dirsOnly = true;
264 if (request->m_mapParams.contains("dirsOnly"))
265 dirsOnly = (request->m_mapParams["dirsOnly"].toInt() != 0);
266
267 if (!dirsOnly)
268 {
269 for (const auto & fi : std::as_const(infoList))
270 {
271 if (fi.isDir())
272 continue;
273 if (fi.fileName().startsWith("."))
274 continue;
275
276 os << " <li class=\"file ext_" << fi.suffix() << R"("><a href="#" rel=")"
277 << fi.absoluteFilePath() << "\">" << fi.fileName() << "</a></li>\r\n";
278 }
279 }
280 os << "</ul>\r\n";
281
282 handled = true;
283 }
284 }
285 }
286 else if ((request->m_sMethod == "GetValueList") &&
287 (request->m_mapParams.contains("List")))
288 {
289 QString key = request->m_mapParams["List"];
290 QStringList sList = GetSettingValueList(key);
291 QTextStream os(&request->m_response);
292 os << StringListToJSON(key, sList);
293
295 request->m_sResponseTypeText = "application/json";
296 request->m_mapRespHeaders[ "Cache-Control" ] =
297 "no-cache=\"Ext\", max-age = 0";
298
299 return true;
300 }
301 else if (request->m_sMethod == "Database")
302 {
303 QString fn = GetShareDir() + "backend-config/"
304 "config_backend_database.xml";
305 QString group;
306 QString form("/Config/Database/Save");
307
308 if (request->m_mapParams.contains("__group__"))
309 group = request->m_mapParams["__group__"];
310
311 if (group.isEmpty())
312 PrintHeader(request->m_response, form);
313 else
314 OpenForm(request->m_response, form, group);
315
319
320 if (group.isEmpty())
321 PrintFooter(request->m_response);
322 else
323 CloseForm(request->m_response, group);
324
325 handled = true;
326 }
327 else if (request->m_sMethod == "General")
328 {
329 QString fn = GetShareDir() + "backend-config/"
330 "config_backend_general.xml";
331 QString group;
332 QString form("/Config/General/Save");
333
334 if (request->m_mapParams.contains("__group__"))
335 group = request->m_mapParams["__group__"];
336
337 if (group.isEmpty())
338 PrintHeader(request->m_response, form);
339 else
340 OpenForm(request->m_response, form, group);
341
345
346 if (group.isEmpty())
347 PrintFooter(request->m_response);
348 else
349 CloseForm(request->m_response, group);
350
351 handled = true;
352 }
353
354 if (handled)
355 {
357 request->m_mapRespHeaders[ "Cache-Control" ] =
358 "no-cache=\"Ext\", max-age = 0";
359 }
360
361 return handled;
362}
363
364void HttpConfig::PrintHeader(QBuffer &buffer, const QString &form,
365 const QString &group)
366{
367 QTextStream os(&buffer);
368
369#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
370 os.setCodec("UTF-8");
371#else
372 os.setEncoding(QStringConverter::Utf8);
373#endif
374
375 os << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" "
376 << "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\r\n"
377 << "<html xmlns=\"http://www.w3.org/1999/xhtml\""
378 << " xml:lang=\"en\" lang=\"en\">\r\n"
379 << "<head>\r\n"
380 << " <meta http-equiv=\"Content-Type\"\r\n"
381 << " content=\"text/html; charset=UTF-8\" />\r\n"
382 << " <link rel=\"stylesheet\" href=\"/setup/css/Config.css\" type=\"text/css\">\r\n"
383 << " <title>MythTV Config</title>"
384 << "</head>\r\n"
385 << "<body>\r\n\r\n"
386 << "<div class=\"config\">\r\n"
387 << " <h1 class=\"config\">MythTV Configuration</h1>\r\n";
388
389 OpenForm(buffer, form, group);
390}
391
392void HttpConfig::OpenForm(QBuffer &buffer, const QString &form,
393 const QString &group)
394{
395 QTextStream os(&buffer);
396
397#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
398 os.setCodec("UTF-8");
399#else
400 os.setEncoding(QStringConverter::Utf8);
401#endif
402
403 os << " <form id=\"config_form_" << group << "\">\r\n"
404 << R"( <input type="hidden" id="__config_form_action__" value=")" << form << "\" />\r\n"
405 << R"( <input type="hidden" id="__group__" value=")" << group << "\" />\r\n";
406}
407
408void HttpConfig::CloseForm(QBuffer &buffer, const QString &group)
409{
410 QTextStream os(&buffer);
411
412// os << " <div class=\"config_form_submit\"\r\n"
413// << " id=\"config_form_submit\">\r\n";
414 os << R"( <input type="button" value="Save Changes" onClick="javascript:submitConfigForm(')" << group << "')\" />\r\n"
415// << " </div>\r\n"
416 << " </form>\r\n";
417}
418
419void HttpConfig::PrintFooter(QBuffer &buffer, const QString &group)
420{
421 CloseForm(buffer, group);
422
423 QTextStream os(&buffer);
424
425 os << "</div>\r\n"
426 << "</body>\r\n"
427 << "</html>\r\n";
428}
429
430void HttpConfig::PrintSettings(QBuffer &buffer, const MythSettingList &settings)
431{
432 QTextStream os(&buffer);
433
434 for (const auto *setting : std::as_const(settings))
435 os << setting->ToHTML(1);
436}
HttpResponseType m_eResponseType
Definition: httprequest.h:150
QString m_sResponseTypeText
Definition: httprequest.h:151
QString m_sMethod
Definition: httprequest.h:130
void FormatFileResponse(const QString &sFileName)
QStringMap m_mapRespHeaders
Definition: httprequest.h:154
QString m_sBaseUrl
Definition: httprequest.h:128
QStringMap m_mapParams
Definition: httprequest.h:132
QBuffer m_response
Definition: httprequest.h:158
MythSettingList m_generalSettings
Definition: httpconfig.h:34
static void PrintHeader(QBuffer &buffer, const QString &form, const QString &group="")
Definition: httpconfig.cpp:364
bool ProcessRequest(HTTPRequest *pRequest) override
Definition: httpconfig.cpp:36
static void OpenForm(QBuffer &buffer, const QString &form, const QString &group="")
Definition: httpconfig.cpp:392
static void CloseForm(QBuffer &buffer, const QString &group="")
Definition: httpconfig.cpp:408
MythSettingList m_databaseSettings
Definition: httpconfig.h:33
static void PrintSettings(QBuffer &buffer, const MythSettingList &settings)
Definition: httpconfig.cpp:430
QStringList GetBasePaths() override
Definition: httpconfig.cpp:27
static void PrintFooter(QBuffer &buffer, const QString &group="")
Definition: httpconfig.cpp:419
QString GetHostName(void)
static QString GenMythURL(const QString &host=QString(), int port=0, QString path=QString(), const QString &storageGroup=QString())
QString GetFirstDir(bool appendSlash=false) const
QStringList GetFileInfoList(const QString &Path)
@ ResponseTypeHTML
Definition: httprequest.h:80
@ ResponseTypeOther
Definition: httprequest.h:86
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
QString GetShareDir(void)
Definition: mythdirs.cpp:261
MythDownloadManager * GetMythDownloadManager(void)
Gets the pointer to the MythDownloadManager singleton.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
QMap< QString, QString > GetSettingsMap(const MythSettingList &settings, const QString &hostname)
QString StringListToJSON(const QString &key, const QStringList &sList)
bool parse_settings(MythSettingList &settings, const QString &filename, const QString &group)
bool load_settings(MythSettingList &settings, const QString &hostname)
QStringList GetSettingValueList(const QString &type)
QString StringMapToJSON(const QMap< QString, QString > &map)
bool check_settings(MythSettingList &, const QMap< QString, QString > &params, QString &result)
QList< MythSettingBase * > MythSettingList
Definition: mythsettings.h:19
bool extractZIP(QString zipFile, const QString &outDir)
Definition: unziputil.cpp:17