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  !database_settings.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  QString fn = GetShareDir() + "backend-config/";
66 
67  if (request->m_sBaseUrl == "/Config/Database")
68  {
70  checkResult))
71  okToSave = true;
72  }
73  else if (request->m_sBaseUrl == "/Config/General")
74  {
76  checkResult))
77  okToSave = true;
78  }
79 
80  if (okToSave)
81  LOG(VB_UPNP, LOG_INFO, "HTTP method 'Save' called, but not handled");
82 #if 0
83  QTextStream os(&request->m_response);
84  os << "<html><body><h3>The Save function for this screen is "
85  << "not hooked up yet</h3><dl>";
86  QStringMap::const_iterator it = request->m_mapParams.begin();
87  for (; it!=request->m_mapParams.end(); ++it)
88  {
89  if (it.key() == "__group__")
90  continue;
91 
92  os << "<dt>"<<it.key()<<"</dt><dd>"
93  <<*it<<"</dd>\r\n";
94  }
95  os << "</dl></body></html>";
96  handled = true;
97 #else
98  QTextStream os(&request->m_response);
99  os << checkResult;
101  request->m_sResponseTypeText = "application/json";
102  request->m_mapRespHeaders[ "Cache-Control" ] =
103  "no-cache=\"Ext\", max-age = 0";
104 
105  return true;
106 #endif
107  }
108  }
109  else if (request->m_sMethod == "Settings")
110  {
111  QString result = "{ \"Error\": \"Unknown Settings List\" }";
112  QString fn = GetShareDir() + "backend-config/";
113 
114  if (request->m_sBaseUrl == "/Config/Database")
115  {
116  fn += "config_backend_database.xml";
118  result = StringMapToJSON(
120  }
121  else if (request->m_sBaseUrl == "/Config/General")
122  {
123  fn += "config_backend_general.xml";
125  result = StringMapToJSON(
127  }
128 
129  QTextStream os(&request->m_response);
130  os << result;
132  request->m_sResponseTypeText = "application/json";
133  request->m_mapRespHeaders[ "Cache-Control" ] =
134  "no-cache=\"Ext\", max-age = 0";
135 
136  return true;
137  }
138  else if (request->m_sMethod == "XML")
139  {
140  QString fn = GetShareDir() + "backend-config/";
141 
142  if (request->m_sBaseUrl == "/Config/Database")
143  fn += "config_backend_database.xml";
144  else if (request->m_sBaseUrl == "/Config/General")
145  fn += "config_backend_general.xml";
146 
147  request->FormatFileResponse(fn);
148  return true;
149  }
150  else if ((request->m_sMethod == "InstallPackage") &&
151  (request->m_mapParams.contains("package")))
152  {
153  QString package = QUrl::fromPercentEncoding(request->m_mapParams["package"].toUtf8());
154  QString url = QString("http://www.mythtv.org/ftp/3rdParty/%1").arg(package);
155  StorageGroup tmpGroup("Temp", gCoreContext->GetHostName());
156  QString tmpFile = tmpGroup.GetFirstDir(true) + "package.zip";
157  StorageGroup destGroup("3rdParty", gCoreContext->GetHostName());
158  QString outDir = destGroup.GetFirstDir();
159 
160  QString result = "false";
161  if ((GetMythDownloadManager()->download(url, tmpFile)) &&
162  (extractZIP(tmpFile, outDir)))
163  {
164  result = "true";
165  }
166 
167  QTextStream os(&request->m_response);
168  os << StringListToJSON("Result", QStringList(result));
169 
171  request->m_sResponseTypeText = "application/json";
172  request->m_mapRespHeaders[ "Cache-Control" ] =
173  "no-cache=\"Ext\", max-age = 0";
174 
175  return true;
176  }
177  else if ((request->m_sMethod == "FileBrowser") &&
178  (request->m_mapParams.contains("dir")))
179  {
180  QString startingDir = QUrl::fromPercentEncoding(request->m_mapParams["dir"].toUtf8());
181  if (startingDir.startsWith("myth://"))
182  {
183  QUrl qurl(startingDir);
184  QString dir;
185 
186  QString host = qurl.host();
187  int port = qurl.port();
188 
189  dir = qurl.path();
190 
191  QString storageGroup = qurl.userName();
192 
193  StorageGroup sgroup(storageGroup);
194  QStringList entries = sgroup.GetFileInfoList(dir);
195 
196  if ((entries.size() == 1) &&
197  (entries[0].startsWith("sgdir::")))
198  {
199  QStringList parts = entries[0].split("::");
200  entries = sgroup.GetFileInfoList(parts[1]);
201  }
202 
203  if (entries.size())
204  {
205  QTextStream os(&request->m_response);
206  os << "<ul class=\"jqueryFileTree\" style=\"display: none;\">\r\n";
207 
208  for (QStringList::iterator it = entries.begin();
209  it != entries.end(); ++it)
210  {
211  QString entry = *it;
212  QStringList parts = entry.split("::");
213  QFileInfo fi(parts[1]);
214  if (dir == "/")
215  dir = "";
216  QString path =
217  gCoreContext->GenMythURL(host,
218  port,
219  dir + parts[1],
220  storageGroup);
221  if (entry.startsWith("sgdir::"))
222  {
223  os << " <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 << " <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() << "\"><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 (QFileInfoList::iterator it = infoList.begin();
250  it != infoList.end();
251  ++it )
252  {
253  QFileInfo &fi = *it;
254  if (!fi.isDir())
255  continue;
256  if (fi.fileName().startsWith("."))
257  continue;
258 
259  os << " <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();
266 
267  if (!dirsOnly)
268  {
269  for (QFileInfoList::iterator it = infoList.begin();
270  it != infoList.end();
271  ++it )
272  {
273  QFileInfo &fi = *it;
274  if (fi.isDir())
275  continue;
276  if (fi.fileName().startsWith("."))
277  continue;
278 
279  os << " <li class=\"file ext_" << fi.suffix() << "\"><a href=\"#\" rel=\""
280  << fi.absoluteFilePath() << "\">" << fi.fileName() << "</a></li>\r\n";
281  }
282  }
283  os << "</ul>\r\n";
284 
285  handled = true;
286  }
287  }
288  }
289  else if ((request->m_sMethod == "GetValueList") &&
290  (request->m_mapParams.contains("List")))
291  {
292  QString key = request->m_mapParams["List"];
293  QStringList sList = GetSettingValueList(key);
294  QTextStream os(&request->m_response);
295  os << StringListToJSON(key, sList);
296 
298  request->m_sResponseTypeText = "application/json";
299  request->m_mapRespHeaders[ "Cache-Control" ] =
300  "no-cache=\"Ext\", max-age = 0";
301 
302  return true;
303  }
304  else if ((request->m_sMethod == "Database") || (nullptr == gContext))
305  {
306  QString fn = GetShareDir() + "backend-config/"
307  "config_backend_database.xml";
308  QString group;
309  QString form("/Config/Database/Save");
310 
311  if (request->m_mapParams.contains("__group__"))
312  group = request->m_mapParams["__group__"];
313 
314  if (group.isEmpty())
315  PrintHeader(request->m_response, form);
316  else
317  OpenForm(request->m_response, form, group);
318 
319  parse_settings(general_settings, fn, group);
322 
323  if (group.isEmpty())
324  PrintFooter(request->m_response);
325  else
326  CloseForm(request->m_response, group);
327 
328  handled = true;
329  }
330  else if (request->m_sMethod == "General")
331  {
332  QString fn = GetShareDir() + "backend-config/"
333  "config_backend_general.xml";
334  QString group;
335  QString form("/Config/General/Save");
336 
337  if (request->m_mapParams.contains("__group__"))
338  group = request->m_mapParams["__group__"];
339 
340  if (group.isEmpty())
341  PrintHeader(request->m_response, form);
342  else
343  OpenForm(request->m_response, form, group);
344 
345  parse_settings(general_settings, fn, group);
348 
349  if (group.isEmpty())
350  PrintFooter(request->m_response);
351  else
352  CloseForm(request->m_response, group);
353 
354  handled = true;
355  }
356 
357  if (handled)
358  {
360  request->m_mapRespHeaders[ "Cache-Control" ] =
361  "no-cache=\"Ext\", max-age = 0";
362  }
363 
364  return handled;
365 }
366 
367 void HttpConfig::PrintHeader(QBuffer &buffer, const QString &form,
368  const QString &group)
369 {
370  QTextStream os(&buffer);
371 
372  os.setCodec("UTF-8");
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  os.setCodec("UTF-8");
397 
398  os << " <form id=\"config_form_" << group << "\">\r\n"
399  << " <input type=\"hidden\" id=\"__config_form_action__\" value=\"" << form << "\" />\r\n"
400  << " <input type=\"hidden\" id=\"__group__\" value=\"" << group << "\" />\r\n";
401 }
402 
403 void HttpConfig::CloseForm(QBuffer &buffer, const QString &group)
404 {
405  QTextStream os(&buffer);
406 
407 // os << " <div class=\"config_form_submit\"\r\n"
408 // << " id=\"config_form_submit\">\r\n";
409  os << " <input type=\"button\" value=\"Save Changes\" onClick=\"javascript:submitConfigForm('" << group << "')\" />\r\n"
410 // << " </div>\r\n"
411  << " </form>\r\n";
412 }
413 
414 void HttpConfig::PrintFooter(QBuffer &buffer, const QString &group)
415 {
416  CloseForm(buffer, group);
417 
418  QTextStream os(&buffer);
419 
420  os << "</div>\r\n"
421  << "</body>\r\n"
422  << "</html>\r\n";
423 }
424 
425 void HttpConfig::PrintSettings(QBuffer &buffer, const MythSettingList &settings)
426 {
427  QTextStream os(&buffer);
428 
429  MythSettingList::const_iterator it = settings.begin();
430  for (; it != settings.end(); ++it)
431  os << (*it)->ToHTML(1);
432 }
static void CloseForm(QBuffer &, const QString &group="")
Definition: httpconfig.cpp:403
bool check_settings(MythSettingList &, const QMap< QString, QString > &params, QString &result)
QString m_sBaseUrl
Definition: httprequest.h:123
QString GenMythURL(QString host=QString(), QString port=QString(), QString path=QString(), QString storageGroup=QString())
MythSettingList database_settings
Definition: httpconfig.h:33
static void PrintHeader(QBuffer &, const QString &form, const QString &group="")
Definition: httpconfig.cpp:367
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:125
QStringList GetFileInfoList(const QString &Path)
QStringMap m_mapRespHeaders
Definition: httprequest.h:149
QString m_sResponseTypeText
Definition: httprequest.h:146
QMap< QString, QString > GetSettingsMap(MythSettingList &settings, const QString &hostname)
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:63
QString StringListToJSON(const QString &key, const QStringList &sList)
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
QString GetShareDir(void)
Definition: mythdirs.cpp:222
MythDownloadManager * GetMythDownloadManager(void)
Gets the pointer to the MythDownloadManager singleton.
ResponseType m_eResponseType
Definition: httprequest.h:145
QList< MythSettingBase * > MythSettingList
Definition: mythsettings.h:16
QStringMap m_mapParams
Definition: httprequest.h:127
MythSettingList general_settings
Definition: httpconfig.h:34
static void PrintFooter(QBuffer &, const QString &group="")
Definition: httpconfig.cpp:414
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
QBuffer m_response
Definition: httprequest.h:153
static void PrintSettings(QBuffer &, const MythSettingList &)
Definition: httpconfig.cpp:425
void FormatFileResponse(const QString &sFileName)
static void OpenForm(QBuffer &, const QString &form, const QString &group="")
Definition: httpconfig.cpp:391
QString GetHostName(void)
QStringList GetBasePaths() override
Definition: httpconfig.cpp:21
QStringList GetSettingValueList(const QString &type)