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