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