MythTV  0.28pre
mythcontext.cpp
Go to the documentation of this file.
1 #include <QCoreApplication>
2 #include <QDir>
3 #include <QFileInfo>
4 #include <QDebug>
5 #include <QMutex>
6 #include <QDateTime>
7 
8 #include <cmath>
9 #include <iostream>
10 
11 #include <queue>
12 #include <algorithm>
13 using namespace std;
14 
15 #include "config.h"
16 #include "mythcontext.h"
17 #include "exitcodes.h"
18 #include "mythdate.h"
19 #include "remotefile.h"
20 #include "backendselect.h"
21 #include "dbsettings.h"
22 #include "langsettings.h"
23 #include "mythtranslation.h"
24 #include "mythxdisplay.h"
25 #include "mythevent.h"
26 #include "dbutil.h"
27 #include "DisplayRes.h"
28 #include "mythmediamonitor.h"
29 
30 #include "mythdb.h"
31 #include "mythdirs.h"
32 #include "mythversion.h"
33 #include "mythdialogbox.h"
34 #include "mythmainwindow.h"
35 #include "mythuihelper.h"
36 #include "mythimage.h"
37 #include "mythxmlclient.h"
38 #include "upnp.h"
39 #include "mythlogging.h"
40 #include "mythsystemlegacy.h"
41 #include "mythmiscutil.h"
42 
43 #include "mythplugin.h"
44 
45 #include <unistd.h> // for usleep(), gethostname
46 
47 #ifdef _WIN32
48 #include "compat.h"
49 #endif
50 
51 #define LOC QString("MythContext: ")
52 
54 
55 static const QString _Location = "MythContext";
56 
57 class MythContextPrivate : public QObject
58 {
59  friend class MythContextSlotHandler;
60 
61  public:
64 
65  bool Init (const bool gui,
66  const bool prompt, const bool noPrompt,
67  const bool ignoreDB);
68  bool FindDatabase(const bool prompt, const bool noPrompt);
69 
70  void TempMainWindow(bool languagePrompt = true);
71  void EndTempWindow(void);
72 
73  bool LoadDatabaseSettings(void);
74  bool SaveDatabaseParams(const DatabaseParams &params, bool force);
75 
76  bool PromptForDatabaseParams(const QString &error);
77  QString TestDBconnection(void);
78  void SilenceDBerrors(void);
79  void EnableDBerrors(void);
80  void ResetDatabase(void);
81 
82  int ChooseBackend(const QString &error);
83  int UPnPautoconf(const int milliSeconds = 2000);
84  bool DefaultUPnP(QString &error);
85  bool UPnPconnect(const DeviceLocation *device, const QString &PIN);
86 
87  protected:
88  bool event(QEvent*);
89 
90  void ShowConnectionFailurePopup(bool persistent);
91  void HideConnectionFailurePopup(void);
92 
93  void ShowVersionMismatchPopup(uint remoteVersion);
94 
95  public:
97 
98  bool m_gui;
99 
101 
103  QString m_DBhostCp;
104 
106 
108 
111 
112  private:
115  QDateTime m_lastCheck;
116 };
117 
118 static void exec_program_cb(const QString &cmd)
119 {
120  myth_system(cmd);
121 }
122 
123 static void exec_program_tv_cb(const QString &cmd)
124 {
125  QString s = cmd;
126  QStringList tokens = cmd.simplified().split(" ");
127  QStringList strlist;
128 
129  bool cardidok;
130  int wantcardid = tokens[0].toInt(&cardidok, 10);
131 
132  if (cardidok && wantcardid > 0)
133  {
134  strlist << QString("LOCK_TUNER %1").arg(wantcardid);
135  s = s.replace(0, tokens[0].length() + 1, "");
136  }
137  else
138  strlist << "LOCK_TUNER";
139 
141  int cardid = strlist[0].toInt();
142 
143  if (cardid >= 0)
144  {
145  s = s.sprintf(qPrintable(s),
146  qPrintable(strlist[1]),
147  qPrintable(strlist[2]),
148  qPrintable(strlist[3]));
149 
150  myth_system(s);
151 
152  strlist = QStringList(QString("FREE_TUNER %1").arg(cardid));
154  QString ret = strlist[0];
155  }
156  else
157  {
158  QString label;
159 
160  if (cardidok)
161  {
162  if (cardid == -1)
163  label = QObject::tr("Could not find specified tuner (%1).")
164  .arg(wantcardid);
165  else
166  label = QObject::tr("Specified tuner (%1) is already in use.")
167  .arg(wantcardid);
168  }
169  else
170  {
171  label = QObject::tr("All tuners are currently in use. If you want "
172  "to watch TV, you can cancel one of the "
173  "in-progress recordings from the delete menu");
174  }
175 
176  LOG(VB_GENERAL, LOG_ALERT, QString("exec_program_tv: ") + label);
177 
178  ShowOkPopup(label);
179  }
180 }
181 
182 static void configplugin_cb(const QString &cmd)
183 {
185  if (!pmanager)
186  return;
187 
188  if (GetNotificationCenter() && pmanager->config_plugin(cmd.trimmed()))
189  {
191  QObject::tr("Failed to configure plugin"));
192  }
193 }
194 
195 static void plugin_cb(const QString &cmd)
196 {
198  if (!pmanager)
199  return;
200 
201  if (GetNotificationCenter() && pmanager->run_plugin(cmd.trimmed()))
202  {
203  ShowNotificationError(QObject::tr("Plugin failure"),
204  _Location,
205  QObject::tr("%1 failed to run for some reason").arg(cmd));
206  }
207 }
208 
209 static void eject_cb(void)
210 {
212 }
213 
215  : parent(lparent),
216  m_gui(false),
217  m_pConfig(NULL),
218  disableeventpopup(false),
219  m_ui(NULL),
220  m_sh(new MythContextSlotHandler(this)),
221  MBEversionPopup(NULL),
222  m_registration(-1)
223 {
225 }
226 
228 {
229  if (m_pConfig)
230  delete m_pConfig;
232  {
234  }
235  if (m_ui)
236  DestroyMythUI();
237  if (m_sh)
238  m_sh->deleteLater();
239 }
240 
251 void MythContextPrivate::TempMainWindow(bool languagePrompt)
252 {
253  if (HasMythMainWindow())
254  return;
255 
256  SilenceDBerrors();
257 
258  gCoreContext->OverrideSettingForSession("Theme", DEFAULT_UI_THEME);
259 #ifdef Q_OS_MAC
260  // Qt 4.4 has window-focus problems
261  gCoreContext->OverrideSettingForSession("RunFrontendInWindow", "1");
262 #endif
263  GetMythUI()->LoadQtConfig();
264 
265  MythMainWindow *mainWindow = MythMainWindow::getMainWindow(false);
266  mainWindow->Init();
267 
268  if (languagePrompt)
269  {
270  // ask user for language settings
272  MythTranslation::load("mythfrontend");
273  }
274 }
275 
277 {
280  EnableDBerrors();
281 }
282 
283 bool MythContextPrivate::Init(const bool gui,
284  const bool promptForBackend,
285  const bool noPrompt,
286  const bool ignoreDB)
287 {
288  gCoreContext->GetDB()->IgnoreDatabase(ignoreDB);
289  m_gui = gui;
290 
291  // We don't have a database yet, so lets use the config.xml file.
292  m_pConfig = new XmlConfiguration("config.xml");
293 
294  // Creates screen saver control if we will have a GUI
295  if (gui)
296  m_ui = GetMythUI();
297 
298  // ---- database connection stuff ----
299 
300  if (!ignoreDB && !FindDatabase(promptForBackend, noPrompt))
301  return false;
302 
303  // ---- keep all DB-using stuff below this line ----
304 
305  // Prompt for language if this is a first time install and
306  // we didn't already do so.
307  if (m_gui && !gCoreContext->GetDB()->HaveSchema())
308  {
309  TempMainWindow(false);
311  MythTranslation::load("mythfrontend");
312  EndTempWindow();
313  }
316 
317  if (gui)
318  {
323  cbs.plugin = plugin_cb;
324  cbs.eject = eject_cb;
325 
326  m_ui->Init(cbs);
327  }
328 
329  return true;
330 }
331 
344 bool MythContextPrivate::FindDatabase(bool prompt, bool noAutodetect)
345 {
346  // We can only prompt if autodiscovery is enabled..
347  bool manualSelect = prompt && !noAutodetect;
348 
349  QString failure;
350 
351  // 1. Either load config.xml or use sensible "localhost" defaults:
352  bool loaded = LoadDatabaseSettings();
353  DatabaseParams dbParamsFromFile = m_DBparams;
354 
355  // In addition to the UI chooser, we can also try to autoSelect later,
356  // but only if we're not doing manualSelect and there was no
357  // valid config.xml
358  bool autoSelect = !manualSelect && !loaded && !noAutodetect;
359 
360  // 2. If the user isn't forcing up the chooser UI, look for a default
361  // backend in config.xml, then test DB settings we've got so far:
362  if (!manualSelect)
363  {
364  // config.xml may contain a backend host UUID and PIN.
365  // If so, try to AutoDiscover UPnP server, and use its DB settings:
366 
367  if (DefaultUPnP(failure)) // Probably a valid backend,
368  autoSelect = manualSelect = false; // so disable any further UPnP
369  else
370  if (failure.length())
371  LOG(VB_GENERAL, LOG_ALERT, failure);
372 
373  failure = TestDBconnection();
374  if (failure.isEmpty())
375  goto DBfound;
376  }
377 
378 
379  // 3. Try to automatically find the single backend:
380  if (autoSelect)
381  {
382  int count = UPnPautoconf();
383 
384  if (count == 0)
385  failure = QObject::tr("No UPnP backends found", "Backend Setup");
386 
387  if (count == 1)
388  {
389  failure = TestDBconnection();
390  if (failure.isEmpty())
391  goto DBfound;
392  }
393 
394  // Multiple BEs, or needs PIN.
395  manualSelect |= (count > 1 || count == -1);
396  }
397 
398  manualSelect &= m_gui; // no interactive command-line chooser yet
399 
400  // Queries the user for the DB info
401  do
402  {
403  if (manualSelect)
404  {
405  // Get the user to select a backend from a possible list:
407  ChooseBackend(failure);
408  switch (d)
409  {
411  break;
413  manualSelect = false;
414  break;
416  goto NoDBfound;
417  }
418  }
419 
420  if (!manualSelect)
421  {
422  if (!PromptForDatabaseParams(failure))
423  goto NoDBfound;
424  }
425 
426  failure = TestDBconnection();
427  if (!failure.isEmpty())
428  LOG(VB_GENERAL, LOG_ALERT, failure);
429  }
430  while (!failure.isEmpty());
431 
432 DBfound:
433  LOG(VB_GENERAL, LOG_DEBUG, "FindDatabase() - Success!");
435  !loaded || m_DBparams.forceSave ||
436  dbParamsFromFile != m_DBparams);
437  EnableDBerrors();
438  ResetDatabase();
439  return true;
440 
441 NoDBfound:
442  LOG(VB_GENERAL, LOG_DEBUG, "FindDatabase() - failed");
443  return false;
444 }
445 
450 {
451  // try new format first
453 
454  m_DBparams.localHostName = m_pConfig->GetValue("LocalHostName", "");
455  m_DBparams.dbHostPing = m_pConfig->GetValue(kDefaultDB + "PingHost", true);
457  m_DBparams.dbUserName = m_pConfig->GetValue(kDefaultDB + "UserName", "");
458  m_DBparams.dbPassword = m_pConfig->GetValue(kDefaultDB + "Password", "");
459  m_DBparams.dbName = m_pConfig->GetValue(kDefaultDB + "DatabaseName", "");
461 
463  m_pConfig->GetValue(kDefaultWOL + "Enabled", false);
465  m_pConfig->GetValue(kDefaultWOL + "SQLReconnectWaitTime", 0);
467  m_pConfig->GetValue(kDefaultWOL + "SQLConnectRetry", 5);
469  m_pConfig->GetValue(kDefaultWOL + "Command", "");
470 
471  bool ok = m_DBparams.IsValid("config.xml");
472  if (!ok) // if new format fails, try legacy format
473  {
476  kDefaultMFE + "DBHostName", "");
478  kDefaultMFE + "DBUserName", "");
480  kDefaultMFE + "DBPassword", "");
482  kDefaultMFE + "DBName", "");
484  kDefaultMFE + "DBPort", 0);
485  m_DBparams.forceSave = true;
486  ok = m_DBparams.IsValid("config.xml");
487  }
488  if (!ok)
490 
491  gCoreContext->GetDB()->SetDatabaseParams(m_DBparams);
492 
493  QString hostname = m_DBparams.localHostName;
494  if (hostname.isEmpty() ||
495  hostname == "my-unique-identifier-goes-here")
496  {
497  char localhostname[1024];
498  if (gethostname(localhostname, 1024))
499  {
500  LOG(VB_GENERAL, LOG_ALERT,
501  "MCP: Error, could not determine host name." + ENO);
502  localhostname[0] = '\0';
503  }
504  hostname = localhostname;
505  LOG(VB_GENERAL, LOG_NOTICE, "Empty LocalHostName.");
506  }
507  else
508  {
509  m_DBparams.localEnabled = true;
510  }
511 
512  LOG(VB_GENERAL, LOG_INFO, QString("Using localhost value of %1")
513  .arg(hostname));
514  gCoreContext->SetLocalHostname(hostname);
515 
516  return ok;
517 }
518 
520  const DatabaseParams &params, bool force)
521 {
522  bool ret = true;
523 
524  // only rewrite file if it has changed
525  if (params != m_DBparams || force)
526  {
528  "LocalHostName", params.localHostName);
529 
531  kDefaultDB + "PingHost", params.dbHostPing);
533  kDefaultDB + "Host", params.dbHostName);
535  kDefaultDB + "UserName", params.dbUserName);
537  kDefaultDB + "Password", params.dbPassword);
539  kDefaultDB + "DatabaseName", params.dbName);
541  kDefaultDB + "Port", params.dbPort);
542 
544  kDefaultWOL + "Enabled", params.wolEnabled);
546  kDefaultWOL + "SQLReconnectWaitTime", params.wolReconnect);
548  kDefaultWOL + "SQLConnectRetry", params.wolRetry);
550  kDefaultWOL + "Command", params.wolCommand);
551 
552  // clear out any legacy nodes..
553  m_pConfig->ClearValue(kDefaultMFE + "DBHostName");
554  m_pConfig->ClearValue(kDefaultMFE + "DBUserName");
555  m_pConfig->ClearValue(kDefaultMFE + "DBPassword");
556  m_pConfig->ClearValue(kDefaultMFE + "DBName");
557  m_pConfig->ClearValue(kDefaultMFE + "DBPort");
558  m_pConfig->ClearValue(kDefaultMFE + "DBHostPing");
559 
560  // actually save the file
561  m_pConfig->Save();
562 
563  // Save the new settings:
564  m_DBparams = params;
565  gCoreContext->GetDB()->SetDatabaseParams(m_DBparams);
566 
567  // If database has changed, force its use:
568  ResetDatabase();
569  }
570  return ret;
571 }
572 
574 {
575  bool accepted = false;
576  if (m_gui)
577  {
578  TempMainWindow();
579 
580  // Tell the user what went wrong:
581  if (error.length())
582  ShowOkPopup(error);
583 
584  // ask user for database parameters
585  DatabaseSettings settings(m_DBhostCp);
586  accepted = (settings.exec() == QDialog::Accepted);
587  if (!accepted)
588  LOG(VB_GENERAL, LOG_ALERT,
589  "User cancelled database configuration");
590 
591  EndTempWindow();
592  }
593  else
594  {
596  QString response;
597 
598  // give user chance to skip config
599  cout << endl << error.toLocal8Bit().constData() << endl << endl;
600  response = getResponse("Would you like to configure the database "
601  "connection now?",
602  "no");
603  if (!response.startsWith('y', Qt::CaseInsensitive))
604  return false;
605 
606  params.dbHostName = getResponse("Database host name:",
607  params.dbHostName);
608  response = getResponse("Should I test connectivity to this host "
609  "using the ping command?", "yes");
610  params.dbHostPing = response.startsWith('y', Qt::CaseInsensitive);
611 
612  params.dbPort = intResponse("Database non-default port:",
613  params.dbPort);
614  params.dbName = getResponse("Database name:",
615  params.dbName);
616  params.dbUserName = getResponse("Database user name:",
617  params.dbUserName);
618  params.dbPassword = getResponse("Database password:",
619  params.dbPassword);
620 
621  params.localHostName = getResponse("Unique identifier for this machine "
622  "(if empty, the local host name "
623  "will be used):",
624  params.localHostName);
625  params.localEnabled = !params.localHostName.isEmpty();
626 
627  response = getResponse("Would you like to use Wake-On-LAN to retry "
628  "database connections?",
629  (params.wolEnabled ? "yes" : "no"));
630  params.wolEnabled = response.startsWith('y', Qt::CaseInsensitive);
631 
632  if (params.wolEnabled)
633  {
634  params.wolReconnect = intResponse("Seconds to wait for "
635  "reconnection:",
636  params.wolReconnect);
637  params.wolRetry = intResponse("Number of times to retry:",
638  params.wolRetry);
639  params.wolCommand = getResponse("Command to use to wake server:",
640  params.wolCommand);
641  }
642 
643  accepted = parent->SaveDatabaseParams(params);
644  }
645  return accepted;
646 }
647 
654 {
655  bool doPing = m_DBparams.dbHostPing;
656  QString err = QString::null;
657  QString host = m_DBparams.dbHostName;
658 
659 
660  // 1. Check the supplied host or IP address, to prevent the app
661  // appearing to hang if we cannot route to the machine:
662 
663  // No need to ping myself
664  if ((host == "localhost") ||
665  (host == "127.0.0.1") ||
666  gCoreContext->IsThisHost(host))
667  doPing = false;
668 
669  // If WOL is setup, the backend might be sleeping:
670  if (doPing && m_DBparams.wolEnabled)
671  for (int attempt = 0; attempt < m_DBparams.wolRetry; ++attempt)
672  {
673  int wakeupTime = m_DBparams.wolReconnect;
674 
675  if (ping(host, wakeupTime))
676  {
677  doPing = false;
678  break;
679  }
680 
681  LOG(VB_GENERAL, LOG_INFO,
682  QString("Trying to wake up host %1, attempt %2")
683  .arg(host).arg(attempt));
685 
686  LOG(VB_GENERAL, LOG_INFO,
687  QString("Waiting for %1 seconds").arg(wakeupTime));
689  }
690 
691  if (doPing)
692  {
693  LOG(VB_GENERAL, LOG_INFO,
694  QString("Testing network connectivity to '%1'").arg(host));
695  }
696 
697  if (doPing && !ping(host, 3)) // Fail after trying for 3 seconds
698  {
699  SilenceDBerrors();
700  err = QObject::tr(
701  "Cannot find (ping) database host %1 on the network",
702  "Backend Setup");
703  return err.arg(host);
704  }
705 
706 
707  // 2. Try to login, et c:
708 
709  // Current DB connection may have been silenced (invalid):
710  ResetDatabase();
711 
713  {
714  SilenceDBerrors();
715  return QObject::tr("Cannot login to database", "Backend Setup");
716  }
717 
718 
719  return QString::null;
720 }
721 
731 {
732  // This silences any DB errors from Get*Setting(),
733  // (which is the vast majority of them)
734  gCoreContext->GetDB()->SetSuppressDBMessages(true);
735 
736  // Save the configured hostname, so that we can
737  // still display it in the DatabaseSettings screens
738  if (m_DBparams.dbHostName.length())
740 
741  m_DBparams.dbHostName.clear();
742  gCoreContext->GetDB()->SetDatabaseParams(m_DBparams);
743 }
744 
746 {
747  // Restore (possibly) blanked hostname
748  if (m_DBparams.dbHostName.isNull() && m_DBhostCp.length())
749  {
751  gCoreContext->GetDB()->SetDatabaseParams(m_DBparams);
752  }
753 
754  gCoreContext->GetDB()->SetSuppressDBMessages(false);
755 }
756 
757 
770 {
772  gCoreContext->GetDB()->SetDatabaseParams(m_DBparams);
774 }
775 
780 {
781  TempMainWindow();
782 
783  // Tell the user what went wrong:
784  if (!error.isEmpty())
785  {
786  LOG(VB_GENERAL, LOG_ERR, QString("Error: %1").arg(error));
787  ShowOkPopup(error);
788  }
789 
790  LOG(VB_GENERAL, LOG_INFO, "Putting up the UPnP backend chooser");
791 
794 
795  EndTempWindow();
796 
797  return (int)ret;
798 }
799 
806 int MythContextPrivate::UPnPautoconf(const int milliSeconds)
807 {
808  LOG(VB_GENERAL, LOG_INFO, QString("UPNP Search %1 secs")
809  .arg(milliSeconds / 1000));
810 
811  SSDP::Instance()->PerformSearch(gBackendURI, milliSeconds / 1000);
812 
813  // Search for a total of 'milliSeconds' ms, sending new search packet
814  // about every 250 ms until less than one second remains.
815  MythTimer totalTime; totalTime.start();
816  MythTimer searchTime; searchTime.start();
817  while (totalTime.elapsed() < milliSeconds)
818  {
819  usleep(25000);
820  int ttl = milliSeconds - totalTime.elapsed();
821  if ((searchTime.elapsed() > 249) && (ttl > 1000))
822  {
823  LOG(VB_GENERAL, LOG_INFO, QString("UPNP Search %1 secs")
824  .arg(ttl / 1000));
825  SSDP::Instance()->PerformSearch(gBackendURI, ttl / 1000);
826  searchTime.start();
827  }
828  }
829 
831 
832  if (!backends)
833  {
834  LOG(VB_GENERAL, LOG_INFO, "No UPnP backends found");
835  return 0;
836  }
837 
838  int count = backends->Count();
839  if (count)
840  {
841  LOG(VB_GENERAL, LOG_INFO,
842  QString("Found %1 UPnP backends").arg(count));
843  }
844  else
845  {
846  LOG(VB_GENERAL, LOG_ERR,
847  "No UPnP backends found, but SSDP::Find() not NULL");
848  }
849 
850  if (count != 1)
851  {
852  backends->DecrRef();
853  return count;
854  }
855 
856  // Get this backend's location:
857  DeviceLocation *BE = backends->GetFirst();
858  backends->DecrRef();
859  backends = NULL;
860 
861  // We don't actually know the backend's access PIN, so this will
862  // only work for ones that have PIN access disabled (i.e. 0000)
863  int ret = (UPnPconnect(BE, QString::null)) ? 1 : -1;
864 
865  BE->DecrRef();
866 
867  return ret;
868 }
869 
876 {
877  QString loc = "DefaultUPnP() - ";
878  QString PIN = m_pConfig->GetValue(kDefaultPIN, "");
879  QString USN = m_pConfig->GetValue(kDefaultUSN, "");
880 
881  if (USN.isEmpty())
882  {
883  LOG(VB_UPNP, LOG_INFO, loc + "No default UPnP backend");
884  return false;
885  }
886 
887  LOG(VB_UPNP, LOG_INFO, loc + "config.xml has default " +
888  QString("PIN '%1' and host USN: %2") .arg(PIN).arg(USN));
889 
890  // ----------------------------------------------------------------------
891 
892  int timeout_ms = 2000;
893  LOG(VB_GENERAL, LOG_INFO, QString("UPNP Search up to %1 secs")
894  .arg(timeout_ms / 1000));
895  SSDP::Instance()->PerformSearch(gBackendURI, timeout_ms / 1000);
896 
897  // ----------------------------------------------------------------------
898  // We need to give the server time to respond...
899  // ----------------------------------------------------------------------
900 
901  DeviceLocation *pDevLoc = NULL;
902  MythTimer totalTime; totalTime.start();
903  MythTimer searchTime; searchTime.start();
904  while (totalTime.elapsed() < timeout_ms)
905  {
906  pDevLoc = SSDP::Instance()->Find( gBackendURI, USN );
907 
908  if (pDevLoc)
909  break;
910 
911  usleep(25000);
912 
913  int ttl = timeout_ms - totalTime.elapsed();
914  if ((searchTime.elapsed() > 249) && (ttl > 1000))
915  {
916  LOG(VB_GENERAL, LOG_INFO, QString("UPNP Search up to %1 secs")
917  .arg(ttl / 1000));
918  SSDP::Instance()->PerformSearch(gBackendURI, ttl / 1000);
919  searchTime.start();
920  }
921  }
922 
923  // ----------------------------------------------------------------------
924 
925  if (!pDevLoc)
926  {
927  error = "Cannot find default UPnP backend";
928  return false;
929  }
930 
931  if (UPnPconnect(pDevLoc, PIN))
932  {
933  pDevLoc->DecrRef();
934  return true;
935  }
936 
937  pDevLoc->DecrRef();
938 
939  error = "Cannot connect to default backend via UPnP. Wrong saved PIN?";
940  return false;
941 }
942 
947  const QString &PIN)
948 {
949  QString error;
950  QString loc = "UPnPconnect() - ";
951  QString URL = backend->m_sLocation;
952  MythXMLClient client(URL);
953 
954  LOG(VB_UPNP, LOG_INFO, loc + QString("Trying host at %1").arg(URL));
955  switch (client.GetConnectionInfo(PIN, &m_DBparams, error))
956  {
957  case UPnPResult_Success:
958  gCoreContext->GetDB()->SetDatabaseParams(m_DBparams);
959  LOG(VB_UPNP, LOG_INFO, loc +
960  "Got database hostname: " + m_DBparams.dbHostName);
961  return true;
962 
964  // The stored PIN is probably not correct.
965  // We could prompt for the PIN and try again, but that needs a UI.
966  // Easier to fail for now, and put up the full UI selector later
967  LOG(VB_UPNP, LOG_ERR, loc + "Wrong PIN?");
968  return false;
969 
970  default:
971  LOG(VB_UPNP, LOG_ERR, loc + error);
972  break;
973  }
974 
975  // This backend may have a local DB with the default user/pass/DBname.
976  // For whatever reason, we have failed to get anything back via UPnP,
977  // so we might as well try the database directly as a last resort.
978  QUrl theURL(URL);
979  URL = theURL.host();
980  if (URL.isEmpty())
981  return false;
982 
983  LOG(VB_UPNP, LOG_INFO, "Trying default DB credentials at " + URL);
985 
986  return true;
987 }
988 
990 {
991  if (e->type() == (QEvent::Type) MythEvent::MythEventMessage)
992  {
993  if (disableeventpopup)
994  return true;
995 
997  {
999  }
1000 
1001  MythEvent *me = (MythEvent*)e;
1002  if (me->Message() == "VERSION_MISMATCH" && (1 == me->ExtraDataCount()))
1003  ShowVersionMismatchPopup(me->ExtraData(0).toUInt());
1004  else if (me->Message() == "CONNECTION_FAILURE")
1006  else if (me->Message() == "PERSISTENT_CONNECTION_FAILURE")
1008  else if (me->Message() == "CONNECTION_RESTABLISHED")
1010  return true;
1011  }
1012 
1013  return QObject::event(e);
1014 }
1015 
1017 {
1018  QDateTime now = MythDate::current();
1019 
1020  if (!GetNotificationCenter() || !m_ui || !m_ui->IsScreenSetup())
1021  return;
1022 
1023  if (m_lastCheck.isValid() && now < m_lastCheck)
1024  return;
1025 
1026  m_lastCheck = now.addMSecs(5000); // don't refresh notification more than every 5s
1027 
1028  QString description = (persistent) ?
1029  QObject::tr(
1030  "The connection to the master backend "
1031  "server has gone away for some reason. "
1032  "Is it running?") :
1033  QObject::tr(
1034  "Could not connect to the master backend server. Is "
1035  "it running? Is the IP address set for it in "
1036  "mythtv-setup correct?");
1037 
1038  QString message = QObject::tr("Could not connect to master backend");
1039  MythErrorNotification n(message, _Location, description);
1040  n.SetId(m_registration);
1041  n.SetParent(this);
1043 }
1044 
1046 {
1047  if (!GetNotificationCenter())
1048  return;
1049 
1050  if (!m_lastCheck.isValid())
1051  return;
1052 
1053  MythCheckNotification n(QObject::tr("Backend is online"), _Location);
1054  n.SetId(m_registration);
1055  n.SetParent(this);
1056  n.SetDuration(5);
1058  m_lastCheck = QDateTime();
1059 }
1060 
1062 {
1063  if (MBEversionPopup)
1064  return;
1065 
1066  QString message =
1067  QObject::tr(
1068  "The server uses network protocol version %1, "
1069  "but this client only understands version %2. "
1070  "Make sure you are running compatible versions of "
1071  "the backend and frontend.")
1072  .arg(remote_version).arg(MYTH_PROTO_VERSION);
1073 
1074  if (HasMythMainWindow() && m_ui && m_ui->IsScreenSetup())
1075  {
1077  message, m_sh, SLOT(VersionMismatchPopupClosed()));
1078  }
1079  else
1080  {
1081  LOG(VB_GENERAL, LOG_ERR, LOC + message);
1082  qApp->exit(GENERIC_EXIT_SOCKET_ERROR);
1083  }
1084 }
1085 
1087 {
1088  d->MBEversionPopup = NULL;
1089  qApp->exit(GENERIC_EXIT_SOCKET_ERROR);
1090 }
1091 
1092 MythContext::MythContext(const QString &binversion)
1093  : d(NULL), app_binary_version(binversion)
1094 {
1095 #ifdef _WIN32
1096  static bool WSAStarted = false;
1097  if (!WSAStarted) {
1098  WSADATA wsadata;
1099  int res = WSAStartup(MAKEWORD(2, 0), &wsadata);
1100  LOG(VB_SOCKET, LOG_INFO,
1101  QString("WSAStartup returned %1").arg(res));
1102  }
1103 #endif
1104 
1105  d = new MythContextPrivate(this);
1106 
1108 
1109  if (!gCoreContext || !gCoreContext->Init())
1110  {
1111  LOG(VB_GENERAL, LOG_EMERG, LOC + "Unable to allocate MythCoreContext");
1112  qApp->exit(GENERIC_EXIT_NO_MYTHCONTEXT);
1113  }
1114 }
1115 
1116 bool MythContext::Init(const bool gui,
1117  const bool promptForBackend,
1118  const bool disableAutoDiscovery,
1119  const bool ignoreDB)
1120 {
1121  if (!d)
1122  {
1123  LOG(VB_GENERAL, LOG_EMERG, LOC + "Init() Out-of-memory");
1124  return false;
1125  }
1126 
1127  if (app_binary_version != MYTH_BINARY_VERSION)
1128  {
1129  LOG(VB_GENERAL, LOG_EMERG,
1130  QString("Application binary version (%1) does not "
1131  "match libraries (%2)")
1132  .arg(app_binary_version) .arg(MYTH_BINARY_VERSION));
1133 
1134  QString warning = QObject::tr(
1135  "This application is not compatible "
1136  "with the installed MythTV libraries.");
1137  if (gui)
1138  {
1139  d->TempMainWindow(false);
1140  ShowOkPopup(warning);
1141  }
1142  LOG(VB_GENERAL, LOG_WARNING, warning);
1143 
1144  return false;
1145  }
1146 
1147 #ifdef _WIN32
1148  // HOME environment variable might not be defined
1149  // some libraries will fail without it
1150  QString home = getenv("HOME");
1151  if (home.isEmpty())
1152  {
1153  home = getenv("LOCALAPPDATA"); // Vista
1154  if (home.isEmpty())
1155  home = getenv("APPDATA"); // XP
1156  if (home.isEmpty())
1157  home = QString("."); // getenv("TEMP")?
1158 
1159  _putenv(QString("HOME=%1").arg(home).toLocal8Bit().constData());
1160  }
1161 #endif
1162 
1163  // If HOME isn't defined, we won't be able to use default confdir of
1164  // $HOME/.mythtv nor can we rely on a MYTHCONFDIR that references $HOME
1165  QString homedir = QDir::homePath();
1166  QString confdir = getenv("MYTHCONFDIR");
1167  if ((homedir.isEmpty() || homedir == "/") &&
1168  (confdir.isEmpty() || confdir.contains("$HOME")))
1169  {
1170  QString warning = "Cannot locate your home directory."
1171  " Please set the environment variable HOME";
1172  if (gui)
1173  {
1174  d->TempMainWindow(false);
1175  ShowOkPopup(warning);
1176  }
1177  LOG(VB_GENERAL, LOG_WARNING, warning);
1178 
1179  return false;
1180  }
1181 
1182  if (!d->Init(gui, promptForBackend, disableAutoDiscovery, ignoreDB))
1183  {
1184  return false;
1185  }
1186 
1188 
1189  return true;
1190 }
1191 
1193 {
1194  if (MThreadPool::globalInstance()->activeThreadCount())
1195  LOG(VB_GENERAL, LOG_INFO, "Waiting for threads to exit.");
1196 
1198  logStop();
1199 
1200  SSDP::Shutdown();
1202 
1203  delete gCoreContext;
1204  gCoreContext = NULL;
1205 
1206  delete d;
1207 }
1208 
1210 {
1211  d->disableeventpopup = check;
1212 }
1213 
1215 {
1216  return d->m_DBparams;
1217 }
1218 
1220 {
1221  return d->SaveDatabaseParams(params, false);
1222 }
1223 
1224 /* vim: set expandtab tabstop=4 shiftwidth=4: */
void SaveLocaleDefaults(void)
This class contains the runtime context for MythTV.
Definition: mythcontext.h:40
void SetParent(void *parent)
contains the parent address.
QString TestDBconnection(void)
Some quick sanity checks before opening a database connection.
bool wolEnabled
true if wake-on-lan params are used
Definition: mythdbparams.h:32
virtual int GetValue(const QString &sSetting, int Default)=0
int intResponse(const QString &query, int def)
In an interactive shell, prompt the user to input a number.
A QElapsedTimer based timer to replace use of QTime as a timer.
Definition: mythtimer.h:13
QString wolCommand
command to use for wake-on-lan
Definition: mythdbparams.h:35
static void configplugin_cb(const QString &cmd)
QString dbName
database name
Definition: mythdbparams.h:26
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:83
QString app_binary_version
Definition: mythcontext.h:58
void LoadQtConfig(void)
Dialog asking for user confirmation.
int UPnPautoconf(const int milliSeconds=2000)
If there is only a single UPnP backend, use it.
void EndTempWindow(void)
MythConfirmationDialog * ShowOkPopup(const QString &message, QObject *parent, const char *slot, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
virtual void ClearValue(const QString &sSetting)=0
void SetId(int id)
Optional MythNotification elements.
static void exec_program_tv_cb(const QString &cmd)
const QString kDefaultWOL
Definition: backendselect.h:23
static Type MythEventMessage
Definition: mythevent.h:65
static void error(const char *str,...)
Definition: vbi.c:41
static void ejectOpticalDisc(void)
Eject a disk, unmount a drive, open a tray.
static MythMainWindow * getMainWindow(const bool useDB=true)
Return the existing main window, or create one.
const QString gBackendURI
Service type for the backend's UPnP server.
Definition: backendselect.h:21
static const char URL[]
Definition: cddb.cpp:29
void UnRegister(void *from, int id, bool closeimemdiately=false)
Unregister the client.
AllMusic * parent
bool SaveDatabaseParams(const DatabaseParams &params, bool force)
void DestroyMythMainWindow(void)
void SetDisableEventPopup(bool check)
bool IsScreenSetup(void)
unsigned int uint
Definition: compat.h:136
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static Decision Prompt(DatabaseParams *dbParams, Configuration *pConfig)
DatabaseParams GetDatabaseParams(void)
void waitForDone(void)
bool localEnabled
true if localHostName is not default
Definition: mythdbparams.h:29
MythContext * gContext
This global variable contains the MythContext instance for the application.
Definition: mythcontext.cpp:53
bool Init(const bool gui, const bool prompt, const bool noPrompt, const bool ignoreDB)
void DestroyMythUI()
void SetLocalHostname(const QString &hostname)
const QString kDefaultDB
Definition: backendselect.h:22
unsigned sleep(unsigned int x)
Definition: compat.h:148
void(* exec_program)(const QString &cmd)
Definition: mythuihelper.h:35
bool IsThisHost(const QString &addr)
is this address mapped to this host
QString dbPassword
DB password.
Definition: mythdbparams.h:25
static void Shutdown()
Definition: taskqueue.cpp:76
void Init(MythUIMenuCallbacks &cbs)
MythContextPrivate(MythContext *lparent)
static void eject_cb(void)
Settings page 2.
Definition: dbsettings.h:10
bool FindDatabase(const bool prompt, const bool noPrompt)
Get database connection settings and test connectivity.
bool config_plugin(const QString &plugname)
Definition: mythplugin.cpp:202
bool SendReceiveStringList(QStringList &strlist, bool quickTimeout=false, bool block=true)
MythContext * parent
Definition: mythcontext.cpp:96
MythContextSlotHandler * m_sh
QString dbUserName
DB user name.
Definition: mythdbparams.h:24
DeviceLocation * GetFirst(void)
Returns random entry in cache, returns NULL when list is empty.
Definition: ssdpcache.cpp:72
bool Init(const bool gui=true, const bool promptForBackend=false, const bool bypassAutoDiscovery=false, const bool ignoreDB=false)
static void plugin_cb(const QString &cmd)
void PerformSearch(const QString &sST, uint timeout_secs=2)
Definition: ssdp.cpp:209
MythPluginManager * GetPluginManager(void)
This class is used as a container for messages.
Definition: mythevent.h:15
void ClearOverrideSettingForSession(const QString &key)
void EnableDBerrors(void)
void VersionMismatchPopupClosed(void)
int ChooseBackend(const QString &error)
Search for backends via UPnP, put up a UI for the user to choose one.
int elapsed(void) const
Returns milliseconds elapsed since last start() or restart()
Definition: mythtimer.cpp:90
uint Count(void) const
Definition: ssdpcache.h:44
bool PromptForDatabaseParams(const QString &error)
static const uint16_t * d
void OverrideSettingForSession(const QString &key, const QString &value)
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
DatabaseParams m_DBparams
Current database host & WOL details.
void SetDuration(int duration)
contains a duration during which the notification will be displayed for.
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
virtual bool Save(void)=0
bool ping(const QString &host, int timeout)
Can we ping host within timeout seconds?
MythContextPrivate * d
Definition: mythcontext.h:57
static void load(const QString &module_name)
Load a QTranslator for the user's preferred language.
MythConfirmationDialog * MBEversionPopup
void ShowNotificationError(const QString &msg, const QString &from, const QString &detail, const VNMask visibility, const MythNotification::Priority priority)
convenience utility to display error message as notification
const QString & ExtraData(int idx=0) const
Definition: mythevent.h:58
static bool testDBConnection()
Checks DB connection + login (login info via Mythcontext)
Definition: mythdbcon.cpp:872
void(* configplugin)(const QString &cmd)
Definition: mythuihelper.h:37
bool Queue(const MythNotification &notification)
Queue a notification Queue() is thread-safe and can be called from anywhere.
bool dbHostPing
Can we test connectivity using ping?
Definition: mythdbparams.h:22
void TempMainWindow(bool languagePrompt=true)
Setup a minimal themed main window, and prompt for user's language.
void Init(QString forcedpainter=QString())
QString m_masterhostname
master backend hostname
QString m_DBhostCp
dbHostName backup
QString getResponse(const QString &query, const QString &def)
In an interactive shell, prompt the user to input a string.
static bool prompt(bool force=false)
Ask the user for the language to use.
uint myth_system(const QString &command, uint flags, uint timeout)
static const QString _Location
Definition: mythcontext.cpp:55
bool LoadDatabaseSettings(void)
Load database and host settings from config.xml, or set some defaults.
MythUIHelper * GetMythUI()
void SilenceDBerrors(void)
Cause MSqlDatabase::OpenDatabase() and MSqlQuery to fail silently.
int wolReconnect
seconds to wait for reconnect
Definition: mythdbparams.h:33
bool HasMythMainWindow(void)
static MThreadPool * globalInstance(void)
static void exec_program_cb(const QString &cmd)
bool SaveDatabaseParams(const DatabaseParams &params)
int Register(void *from)
An application can register in which case it will be assigned a reusable screen, which can be modifie...
bool event(QEvent *)
bool UPnPconnect(const DeviceLocation *device, const QString &PIN)
Query a backend via UPnP for its database connection parameters.
Structure containing the basic Database parameters.
Definition: mythdbparams.h:9
QString m_sLocation
Definition: upnpdevice.h:232
QString dbHostName
database server
Definition: mythdbparams.h:21
void LoadDefaults(void)
Load sensible connection defaults.
Definition: mythdbparams.cpp:5
static SSDP * Instance()
Definition: ssdp.cpp:49
bool forceSave
set to true to force a save of the settings file
Definition: mythdbparams.h:37
void(* eject)(void)
Definition: mythuihelper.h:39
void logStop(void)
Entry point for stopping logging for an application.
Definition: logging.cpp:936
MythContextPrivate * d
Definition: mythcontext.h:27
Configuration * m_pConfig
UPnPResultCode GetConnectionInfo(const QString &sPin, DatabaseParams *pParams, QString &sMsg)
bool IsValid(const QString &source=QString("Unknown")) const
virtual DialogCode exec(bool saveOnExec=true, bool doLoad=true)
void(* plugin)(const QString &cmd)
Definition: mythuihelper.h:38
const QString kDefaultPIN
Definition: backendselect.h:25
void ClearSettingsCache(const QString &myKey=QString(""))
static MythPluginManager * pmanager
MythDB * GetDB(void)
MDBManager * GetDBManager(void)
static const QString LOC
void HideConnectionFailurePopup(void)
const QString kDefaultUSN
Definition: backendselect.h:26
MythUIHelper * m_ui
static SSDPCacheEntries * Find(const QString &sURI)
Definition: ssdp.h:129
const QString kDefaultMFE
Definition: backendselect.h:24
GLenum GLint * params
void(* exec_program_tv)(const QString &cmd)
Definition: mythuihelper.h:36
void ResetDatabase(void)
Called when the user changes the DB connection settings.
QString localHostName
name used for loading/saving settings
Definition: mythdbparams.h:30
void ActivateSettingsCache(bool activate=true)
int dbPort
database port
Definition: mythdbparams.h:23
void ShowConnectionFailurePopup(bool persistent)
const QString & Message() const
Definition: mythevent.h:57
MythContext(const QString &binversion)
void ShowVersionMismatchPopup(uint remoteVersion)
void start(void)
starts measuring elapsed time.
Definition: mythtimer.cpp:47
void CloseDatabases(void)
Definition: mythdbcon.cpp:470
virtual ~MythContext()
int wolRetry
times to retry to reconnect
Definition: mythdbparams.h:34
bool DefaultUPnP(QString &error)
Get the default backend from config.xml, use UPnP to find it.
static QString confdir
Definition: mythdirs.cpp:15
This class contains the runtime context for MythTV.
int ExtraDataCount() const
Definition: mythevent.h:60
static void Shutdown()
Definition: ssdp.cpp:59
MythNotificationCenter * GetNotificationCenter(void)
bool run_plugin(const QString &plugname)
Definition: mythplugin.cpp:184
void InitializeMythDirs(void)
Definition: mythdirs.cpp:21
virtual void SetValue(const QString &sSetting, int value)=0
bool m_gui
Should this context use GUI elements?
Definition: mythcontext.cpp:98