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