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_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 
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  for (QStringList::iterator it = entries.begin();
208  it != entries.end(); ++it)
209  {
210  QString entry = *it;
211  QStringList parts = entry.split("::");
212  QFileInfo fi(parts[1]);
213  if (dir == "/")
214  dir = "";
215  QString path =
216  gCoreContext->GenMythURL(host,
217  port,
218  dir + parts[1],
219  storageGroup);
220  if (entry.startsWith("sgdir::"))
221  {
222  os << " <li class=\"directory collapsed\"><a href=\"#\" rel=\""
223  << path << "/\">" << parts[1] << "</a></li>\r\n";
224  }
225  else if (entry.startsWith("dir::"))
226  {
227  os << " <li class=\"directory collapsed\"><a href=\"#\" rel=\""
228  << path << "/\">" << fi.fileName() << "</a></li>\r\n";
229  }
230  else if (entry.startsWith("file::"))
231  {
232  os << " <li class=\"file ext_" << fi.suffix() << "\"><a href=\"#\" rel=\""
233  << parts[3] << "\">" << fi.fileName() << "</a></li>\r\n";
234  }
235  }
236  os << "</ul>\r\n";
237 
238  handled = true;
239  }
240  } else {
241  QDir dir(startingDir);
242  if (dir.exists())
243  {
244  QTextStream os(&request->m_response);
245  os << "<ul class=\"jqueryFileTree\" style=\"display: none;\">\r\n";
246 
247  QFileInfoList infoList = dir.entryInfoList();
248  for (QFileInfoList::iterator it = infoList.begin();
249  it != infoList.end();
250  ++it )
251  {
252  QFileInfo &fi = *it;
253  if (!fi.isDir())
254  continue;
255  if (fi.fileName().startsWith("."))
256  continue;
257 
258  os << " <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 (QFileInfoList::iterator it = infoList.begin();
269  it != infoList.end();
270  ++it )
271  {
272  QFileInfo &fi = *it;
273  if (fi.isDir())
274  continue;
275  if (fi.fileName().startsWith("."))
276  continue;
277 
278  os << " <li class=\"file ext_" << fi.suffix() << "\"><a href=\"#\" rel=\""
279  << fi.absoluteFilePath() << "\">" << fi.fileName() << "</a></li>\r\n";
280  }
281  }
282  os << "</ul>\r\n";
283 
284  handled = true;
285  }
286  }
287  }
288  else if ((request->m_sMethod == "GetValueList") &&
289  (request->m_mapParams.contains("List")))
290  {
291  QString key = request->m_mapParams["List"];
292  QStringList sList = GetSettingValueList(key);
293  QTextStream os(&request->m_response);
294  os << StringListToJSON(key, sList);
295 
297  request->m_sResponseTypeText = "application/json";
298  request->m_mapRespHeaders[ "Cache-Control" ] =
299  "no-cache=\"Ext\", max-age = 0";
300 
301  return true;
302  }
303  else if ((request->m_sMethod == "Database") || (nullptr == gContext))
304  {
305  QString fn = GetShareDir() + "backend-config/"
306  "config_backend_database.xml";
307  QString group;
308  QString form("/Config/Database/Save");
309 
310  if (request->m_mapParams.contains("__group__"))
311  group = request->m_mapParams["__group__"];
312 
313  if (group.isEmpty())
314  PrintHeader(request->m_response, form);
315  else
316  OpenForm(request->m_response, form, group);
317 
321 
322  if (group.isEmpty())
323  PrintFooter(request->m_response);
324  else
325  CloseForm(request->m_response, group);
326 
327  handled = true;
328  }
329  else if (request->m_sMethod == "General")
330  {
331  QString fn = GetShareDir() + "backend-config/"
332  "config_backend_general.xml";
333  QString group;
334  QString form("/Config/General/Save");
335 
336  if (request->m_mapParams.contains("__group__"))
337  group = request->m_mapParams["__group__"];
338 
339  if (group.isEmpty())
340  PrintHeader(request->m_response, form);
341  else
342  OpenForm(request->m_response, form, group);
343 
347 
348  if (group.isEmpty())
349  PrintFooter(request->m_response);
350  else
351  CloseForm(request->m_response, group);
352 
353  handled = true;
354  }
355 
356  if (handled)
357  {
359  request->m_mapRespHeaders[ "Cache-Control" ] =
360  "no-cache=\"Ext\", max-age = 0";
361  }
362 
363  return handled;
364 }
365 
366 void HttpConfig::PrintHeader(QBuffer &buffer, const QString &form,
367  const QString &group)
368 {
369  QTextStream os(&buffer);
370 
371  os.setCodec("UTF-8");
372 
373  os << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" "
374  << "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\r\n"
375  << "<html xmlns=\"http://www.w3.org/1999/xhtml\""
376  << " xml:lang=\"en\" lang=\"en\">\r\n"
377  << "<head>\r\n"
378  << " <meta http-equiv=\"Content-Type\"\r\n"
379  << " content=\"text/html; charset=UTF-8\" />\r\n"
380  << " <link rel=\"stylesheet\" href=\"/setup/css/Config.css\" type=\"text/css\">\r\n"
381  << " <title>MythTV Config</title>"
382  << "</head>\r\n"
383  << "<body>\r\n\r\n"
384  << "<div class=\"config\">\r\n"
385  << " <h1 class=\"config\">MythTV Configuration</h1>\r\n";
386 
387  OpenForm(buffer, form, group);
388 }
389 
390 void HttpConfig::OpenForm(QBuffer &buffer, const QString &form,
391  const QString &group)
392 {
393  QTextStream os(&buffer);
394 
395  os.setCodec("UTF-8");
396 
397  os << " <form id=\"config_form_" << group << "\">\r\n"
398  << " <input type=\"hidden\" id=\"__config_form_action__\" value=\"" << form << "\" />\r\n"
399  << " <input type=\"hidden\" id=\"__group__\" value=\"" << group << "\" />\r\n";
400 }
401 
402 void HttpConfig::CloseForm(QBuffer &buffer, const QString &group)
403 {
404  QTextStream os(&buffer);
405 
406 // os << " <div class=\"config_form_submit\"\r\n"
407 // << " id=\"config_form_submit\">\r\n";
408  os << " <input type=\"button\" value=\"Save Changes\" onClick=\"javascript:submitConfigForm('" << group << "')\" />\r\n"
409 // << " </div>\r\n"
410  << " </form>\r\n";
411 }
412 
413 void HttpConfig::PrintFooter(QBuffer &buffer, const QString &group)
414 {
415  CloseForm(buffer, group);
416 
417  QTextStream os(&buffer);
418 
419  os << "</div>\r\n"
420  << "</body>\r\n"
421  << "</html>\r\n";
422 }
423 
424 void HttpConfig::PrintSettings(QBuffer &buffer, const MythSettingList &settings)
425 {
426  QTextStream os(&buffer);
427 
428  MythSettingList::const_iterator it = settings.begin();
429  for (; it != settings.end(); ++it)
430  os << (*it)->ToHTML(1);
431 }
static void CloseForm(QBuffer &, const QString &group="")
Definition: httpconfig.cpp:402
bool check_settings(MythSettingList &, const QMap< QString, QString > &params, QString &result)
QString m_sBaseUrl
Definition: httprequest.h:123
static void PrintHeader(QBuffer &, const QString &form, const QString &group="")
Definition: httpconfig.cpp:366
QString GetFirstDir(bool appendSlash=false) const
MythSettingList m_general_settings
Definition: httpconfig.h:34
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 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.
QList< MythSettingBase * > MythSettingList
Definition: mythsettings.h:16
QStringMap m_mapParams
Definition: httprequest.h:127
MythSettingList m_database_settings
Definition: httpconfig.h:33
static void PrintFooter(QBuffer &, const QString &group="")
Definition: httpconfig.cpp:413
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
QBuffer m_response
Definition: httprequest.h:153
HttpResponseType m_eResponseType
Definition: httprequest.h:145
static void PrintSettings(QBuffer &, const MythSettingList &)
Definition: httpconfig.cpp:424
void FormatFileResponse(const QString &sFileName)
static void OpenForm(QBuffer &, const QString &form, const QString &group="")
Definition: httpconfig.cpp:390
QString GetHostName(void)
QStringList GetBasePaths() override
Definition: httpconfig.cpp:21
QStringList GetSettingValueList(const QString &type)