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 = "{ \"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  foreach (auto entry, 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 << " <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 << " <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() << "\"><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  foreach (auto & fi, infoList)
247  {
248  if (!fi.isDir())
249  continue;
250  if (fi.fileName().startsWith("."))
251  continue;
252 
253  os << " <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  foreach (auto & fi, infoList)
264  {
265  if (fi.isDir())
266  continue;
267  if (fi.fileName().startsWith("."))
268  continue;
269 
270  os << " <li class=\"file ext_" << fi.suffix() << "\"><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  << " <input type=\"hidden\" id=\"__config_form_action__\" value=\"" << form << "\" />\r\n"
391  << " <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 << " <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  foreach (auto setting, settings)
421  os << setting->ToHTML(1);
422 }
static void PrintSettings(QBuffer &buffer, const MythSettingList &settings)
Definition: httpconfig.cpp:416
static void PrintHeader(QBuffer &buffer, const QString &form, const QString &group="")
Definition: httpconfig.cpp:358
bool check_settings(MythSettingList &, const QMap< QString, QString > &params, QString &result)
QString m_sBaseUrl
Definition: httprequest.h:125
QString GetFirstDir(bool appendSlash=false) const
QString StringMapToJSON(const QMap< QString, QString > &map)
bool load_settings(MythSettingList &settings, const QString &hostname)
QString m_sMethod
Definition: httprequest.h:127
QStringList GetFileInfoList(const QString &Path)
QStringMap m_mapRespHeaders
Definition: httprequest.h:151
QString m_sResponseTypeText
Definition: httprequest.h:148
QMap< QString, QString > GetSettingsMap(MythSettingList &settings, const QString &hostname)
MythSettingList m_generalSettings
Definition: httpconfig.h:34
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
MythContext * gContext
This global variable contains the MythContext instance for the application.
Definition: mythcontext.cpp:62
QString StringListToJSON(const QString &key, const QStringList &sList)
MythSettingList m_databaseSettings
Definition: httpconfig.h:33
bool extractZIP(const QString &zipFile, const QString &outDir)
bool parse_settings(MythSettingList &settings, const QString &filename, const QString &group)
bool ProcessRequest(HTTPRequest *pRequest) override
Definition: httpconfig.cpp:30
static QString GenMythURL(const QString &host=QString(), int port=0, QString path=QString(), const QString &storageGroup=QString())
QString GetShareDir(void)
Definition: mythdirs.cpp:222
MythDownloadManager * GetMythDownloadManager(void)
Gets the pointer to the MythDownloadManager singleton.
static void CloseForm(QBuffer &buffer, const QString &group="")
Definition: httpconfig.cpp:394
QStringMap m_mapParams
Definition: httprequest.h:129
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
static void PrintFooter(QBuffer &buffer, const QString &group="")
Definition: httpconfig.cpp:405
QList< MythSettingBase * > MythSettingList
Definition: mythsettings.h:19
QBuffer m_response
Definition: httprequest.h:155
HttpResponseType m_eResponseType
Definition: httprequest.h:147
static void OpenForm(QBuffer &buffer, const QString &form, const QString &group="")
Definition: httpconfig.cpp:382
void FormatFileResponse(const QString &sFileName)
QString GetHostName(void)
QStringList GetBasePaths() override
Definition: httpconfig.cpp:21
QStringList GetSettingValueList(const QString &type)