MythTV  master
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 #include <QTcpSocket>
8 #include <QEventLoop>
9 
10 #ifdef Q_OS_ANDROID
11 #include <QtAndroidExtras>
12 #endif
13 
14 #include <cmath>
15 #include <iostream>
16 
17 #include <algorithm>
18 #include <queue>
19 #include <thread>
20 
21 using namespace std;
22 
23 #include "config.h"
24 #include "mythcontext.h"
25 #include "exitcodes.h"
26 #include "mythdate.h"
27 #include "remotefile.h"
28 #include "backendselect.h"
29 #include "dbsettings.h"
30 #include "langsettings.h"
31 #include "mythtranslation.h"
32 #include "platforms/mythxdisplay.h"
33 #include "mythevent.h"
34 #include "dbutil.h"
35 #include "mythmediamonitor.h"
36 
37 #include "mythdb.h"
38 #include "mythdirs.h"
39 #include "mythversion.h"
40 #include "mythdialogbox.h"
41 #include "mythmainwindow.h"
42 #include "mythuihelper.h"
43 #include "mythimage.h"
44 #include "mythxmlclient.h"
45 #include "upnp.h"
46 #include "mythlogging.h"
47 #include "mythsystemlegacy.h"
48 #include "mythmiscutil.h"
49 
50 #include "mythplugin.h"
51 #include "portchecker.h"
52 #include "guistartup.h"
53 
54 #include <unistd.h> // for usleep(), gethostname
55 
56 #ifdef _WIN32
57 #include "compat.h"
58 #endif
59 
60 #define LOC QString("MythContext: ")
61 
62 MythContext *gContext = nullptr;
63 
64 static const QString _Location = "MythContext";
65 
66 class MythContextPrivate : public QObject
67 {
68  friend class MythContextSlotHandler;
69 
70  public:
71  explicit MythContextPrivate(MythContext *lparent);
72  ~MythContextPrivate() override;
73 
74  bool Init (bool gui,
75  bool prompt, bool noPrompt,
76  bool ignoreDB);
77  bool FindDatabase(bool prompt, bool noAutodetect);
78 
79  void TempMainWindow(bool languagePrompt = true);
80  void EndTempWindow(void);
81 
82  bool LoadDatabaseSettings(void);
83  bool SaveDatabaseParams(const DatabaseParams &params, bool force);
84 
85  bool PromptForDatabaseParams(const QString &error);
86  QString TestDBconnection(bool prompt=true);
87  void SilenceDBerrors(void);
88  void EnableDBerrors(void);
89  void ResetDatabase(void);
90 
91  int ChooseBackend(const QString &error);
92  int UPnPautoconf(int milliSeconds = 2000);
93  bool DefaultUPnP(QString &error);
94  bool UPnPconnect(const DeviceLocation *backend, const QString &PIN);
95  void ShowGuiStartup(void);
96  bool checkPort(QString &host, int port, int timeLimit);
97  static void processEvents(void);
98  bool saveSettingsCache(void);
99  void loadSettingsCacheOverride(void);
100  static void clearSettingsCacheOverride(void);
101 
102 
103  protected:
104  bool event(QEvent* /*e*/) override; // QObject
105 
106  void ShowConnectionFailurePopup(bool persistent);
107  void HideConnectionFailurePopup(void);
108 
109  void ShowVersionMismatchPopup(uint remote_version);
110 
111  public slots:
112  void OnCloseDialog();
113 
114  public:
115  MythContext *m_parent {nullptr};
116 
118  bool m_gui {false};
119 
121 
123  QString m_dbHostCp;
124 
125  Configuration *m_pConfig {nullptr};
126 
127  bool m_disableeventpopup {false};
128 
129  MythUIHelper *m_ui {nullptr};
130  MythContextSlotHandler *m_sh {nullptr};
131  GUIStartup *m_guiStartup {nullptr};
132  QEventLoop *m_loop {nullptr};
133  bool m_needsBackend {false};
134  bool m_settingsCacheDirty {false};
135 
136  private:
137  MythConfirmationDialog *m_mbeVersionPopup {nullptr};
138  int m_registration {-1};
139  QDateTime m_lastCheck;
140  QTcpSocket *m_socket {nullptr};
141  static const QString kSettingsToSave[];
142 };
143 
144 static void exec_program_cb(const QString &cmd)
145 {
146  myth_system(cmd);
147 }
148 
149 static void exec_program_tv_cb(const QString &cmd)
150 {
151  QString s = cmd;
152  QStringList tokens = cmd.simplified().split(" ");
153  QStringList strlist;
154 
155  bool cardidok = false;
156  int wantcardid = tokens[0].toInt(&cardidok, 10);
157 
158  if (cardidok && wantcardid > 0)
159  {
160  strlist << QString("LOCK_TUNER %1").arg(wantcardid);
161  s = s.replace(0, tokens[0].length() + 1, "");
162  }
163  else
164  strlist << "LOCK_TUNER";
165 
167  int cardid = strlist[0].toInt();
168 
169  if (cardid >= 0)
170  {
171  s = s.sprintf(qPrintable(s),
172  qPrintable(strlist[1]),
173  qPrintable(strlist[2]),
174  qPrintable(strlist[3]));
175 
176  myth_system(s);
177 
178  strlist = QStringList(QString("FREE_TUNER %1").arg(cardid));
180  QString ret = strlist[0];
181  }
182  else
183  {
184  QString label;
185 
186  if (cardidok)
187  {
188  if (cardid == -1)
189  label = QObject::tr("Could not find specified tuner (%1).")
190  .arg(wantcardid);
191  else
192  label = QObject::tr("Specified tuner (%1) is already in use.")
193  .arg(wantcardid);
194  }
195  else
196  {
197  label = QObject::tr("All tuners are currently in use. If you want "
198  "to watch TV, you can cancel one of the "
199  "in-progress recordings from the delete menu");
200  }
201 
202  LOG(VB_GENERAL, LOG_ALERT, QString("exec_program_tv: ") + label);
203 
204  ShowOkPopup(label);
205  }
206 }
207 
208 static void configplugin_cb(const QString &cmd)
209 {
211  if (!pmanager)
212  return;
213 
214  if (GetNotificationCenter() && pmanager->config_plugin(cmd.trimmed()))
215  {
217  QObject::tr("Failed to configure plugin"));
218  }
219 }
220 
221 static void plugin_cb(const QString &cmd)
222 {
224  if (!pmanager)
225  return;
226 
227  if (GetNotificationCenter() && pmanager->run_plugin(cmd.trimmed()))
228  {
229  ShowNotificationError(QObject::tr("Plugin failure"),
230  _Location,
231  QObject::tr("%1 failed to run for some reason").arg(cmd));
232  }
233 }
234 
235 static void eject_cb(void)
236 {
238 }
239 
241  : m_parent(lparent),
242  m_sh(new MythContextSlotHandler(this))
243 {
244  m_loop = new QEventLoop(this);
246 }
247 
249 {
250  delete m_pConfig;
252  {
254  }
255 
256  delete m_loop;
257 
258  if (m_ui)
259  DestroyMythUI();
260  if (m_sh)
261  m_sh->deleteLater();
262 }
263 
274 void MythContextPrivate::TempMainWindow(bool languagePrompt)
275 {
276  if (HasMythMainWindow())
277  return;
278 
279  SilenceDBerrors();
280 
281 #ifdef Q_OS_MAC
282  // Qt 4.4 has window-focus problems
283  gCoreContext->OverrideSettingForSession("RunFrontendInWindow", "1");
284 #endif
285  GetMythUI()->Init();
286  GetMythUI()->LoadQtConfig();
287 
288  MythMainWindow *mainWindow = MythMainWindow::getMainWindow(false);
289  mainWindow->Init();
290 
291  if (languagePrompt)
292  {
293  // ask user for language settings
295  MythTranslation::load("mythfrontend");
296  }
297 }
298 
300 {
301  if (HasMythMainWindow())
302  {
304  {
306  if (mainStack) {
307  mainStack->PopScreen(m_guiStartup, false);
308  m_guiStartup = nullptr;
309  }
310  }
311  }
312  EnableDBerrors();
313 }
314 
324 bool MythContextPrivate::checkPort(QString &host, int port, int timeLimit)
325 {
326  PortChecker checker;
327  if (m_guiStartup)
328  QObject::connect(m_guiStartup,SIGNAL(cancelPortCheck()),&checker,SLOT(cancelPortCheck()));
329  return checker.checkPort(host, port, timeLimit*1000);
330 }
331 
332 
333 bool MythContextPrivate::Init(const bool gui,
334  const bool promptForBackend,
335  const bool noPrompt,
336  const bool ignoreDB)
337 {
338  gCoreContext->GetDB()->IgnoreDatabase(ignoreDB);
339  m_gui = gui;
341 
342  if (gCoreContext->IsFrontend())
343  m_needsBackend = true;
344 
345  // We don't have a database yet, so lets use the config.xml file.
346  m_pConfig = new XmlConfiguration("config.xml");
347 
348  // Creates screen saver control if we will have a GUI
349  if (gui)
350  m_ui = GetMythUI();
351 
352  // ---- database connection stuff ----
353 
354  if (!ignoreDB && !FindDatabase(promptForBackend, noPrompt))
355  {
356  EndTempWindow();
357  return false;
358  }
359 
360  // ---- keep all DB-using stuff below this line ----
361 
362  // Prompt for language if this is a first time install and
363  // we didn't already do so.
364  if (m_gui && !gCoreContext->GetDB()->HaveSchema())
365  {
366  TempMainWindow(false);
368  MythTranslation::load("mythfrontend");
369  }
372 
373  // Close GUI Startup Window.
374  if (m_guiStartup) {
376  if (mainStack)
377  mainStack->PopScreen(m_guiStartup, false);
378  m_guiStartup=nullptr;
379  }
380  EndTempWindow();
381 
382  if (gui)
383  {
384  MythUIMenuCallbacks cbs {};
386  cbs.exec_program_tv = exec_program_tv_cb;
387  cbs.configplugin = configplugin_cb;
388  cbs.plugin = plugin_cb;
389  cbs.eject = eject_cb;
390 
391  m_ui->Init(cbs);
392  }
393 
394  return true;
395 }
396 
409 bool MythContextPrivate::FindDatabase(bool prompt, bool noAutodetect)
410 {
411  // We can only prompt if autodiscovery is enabled..
412  bool manualSelect = prompt && !noAutodetect;
413 
414  QString failure;
415 
416  // 1. Either load config.xml or use sensible "localhost" defaults:
417  bool loaded = LoadDatabaseSettings();
418  DatabaseParams dbParamsFromFile = m_dbParams;
419 
420  // In addition to the UI chooser, we can also try to autoSelect later,
421  // but only if we're not doing manualSelect and there was no
422  // valid config.xml
423  bool autoSelect = !manualSelect && !loaded && !noAutodetect;
424 
425  // 2. If the user isn't forcing up the chooser UI, look for a default
426  // backend in config.xml, then test DB settings we've got so far:
427  if (!manualSelect)
428  {
429  // config.xml may contain a backend host UUID and PIN.
430  // If so, try to AutoDiscover UPnP server, and use its DB settings:
431 
432  if (DefaultUPnP(failure)) // Probably a valid backend,
433  autoSelect = manualSelect = false; // so disable any further UPnP
434  else
435  if (failure.length())
436  LOG(VB_GENERAL, LOG_ALERT, failure);
437 
438  failure = TestDBconnection(loaded);
439  if (failure.isEmpty())
440  goto DBfound;
442  return false;
444  autoSelect=true;
445  }
446 
447  // 3. Try to automatically find the single backend:
448  if (autoSelect)
449  {
450  int count = UPnPautoconf();
451 
452  if (count == 0)
453  failure = QObject::tr("No UPnP backends found", "Backend Setup");
454 
455  if (count == 1)
456  {
457  failure = TestDBconnection();
458  if (failure.isEmpty())
459  goto DBfound;
461  return false;
462  }
463 
464  // Multiple BEs, or needs PIN.
465  manualSelect |= (count > 1 || count == -1);
466  // Search requested
468  manualSelect=true;
469  }
470 
471  manualSelect &= m_gui; // no interactive command-line chooser yet
472 
473  // Queries the user for the DB info
474  do
475  {
476  if (manualSelect)
477  {
478  // Get the user to select a backend from a possible list:
479  auto d = (BackendSelection::Decision)ChooseBackend(failure);
480  switch (d)
481  {
483  break;
485  manualSelect = false;
486  break;
488  goto NoDBfound;
489  }
490  }
491 
492  if (!manualSelect)
493  {
494  if (!PromptForDatabaseParams(failure))
495  goto NoDBfound;
496  }
497  failure = TestDBconnection();
498  if (!failure.isEmpty())
499  LOG(VB_GENERAL, LOG_ALERT, failure);
501  return false;
503  manualSelect=true;
505  manualSelect=false;
506  }
507  while (!failure.isEmpty());
508 
509 DBfound:
510  LOG(VB_GENERAL, LOG_DEBUG, "FindDatabase() - Success!");
511  // If we got the database from UPNP then the wakeup settings are lost.
512  // Restore them.
513  m_dbParams.m_wolEnabled = dbParamsFromFile.m_wolEnabled;
514  m_dbParams.m_wolReconnect = dbParamsFromFile.m_wolReconnect;
515  m_dbParams.m_wolRetry = dbParamsFromFile.m_wolRetry;
516  m_dbParams.m_wolCommand = dbParamsFromFile.m_wolCommand;
517 
519  !loaded || m_dbParams.m_forceSave ||
520  dbParamsFromFile != m_dbParams);
521  EnableDBerrors();
522  ResetDatabase();
523  return true;
524 
525 NoDBfound:
526  LOG(VB_GENERAL, LOG_DEBUG, "FindDatabase() - failed");
527  return false;
528 }
529 
534 {
535  // try new format first
537 
538  m_dbParams.m_localHostName = m_pConfig->GetValue("LocalHostName", "");
541  m_dbParams.m_dbUserName = m_pConfig->GetValue(kDefaultDB + "UserName", "");
542  m_dbParams.m_dbPassword = m_pConfig->GetValue(kDefaultDB + "Password", "");
543  m_dbParams.m_dbName = m_pConfig->GetValue(kDefaultDB + "DatabaseName", "");
545 
547  m_pConfig->GetBoolValue(kDefaultWOL + "Enabled", false);
549  m_pConfig->GetValue(kDefaultWOL + "SQLReconnectWaitTime", 0);
551  m_pConfig->GetValue(kDefaultWOL + "SQLConnectRetry", 5);
553  m_pConfig->GetValue(kDefaultWOL + "Command", "");
554 
555  bool ok = m_dbParams.IsValid("config.xml");
556  if (!ok) // if new format fails, try legacy format
557  {
560  kDefaultMFE + "DBHostName", "");
562  kDefaultMFE + "DBUserName", "");
564  kDefaultMFE + "DBPassword", "");
566  kDefaultMFE + "DBName", "");
568  kDefaultMFE + "DBPort", 0);
569  m_dbParams.m_forceSave = true;
570  ok = m_dbParams.IsValid("config.xml");
571  }
572  if (!ok)
574 
575  gCoreContext->GetDB()->SetDatabaseParams(m_dbParams);
576 
578  if (hostname.isEmpty() ||
579  hostname == "my-unique-identifier-goes-here")
580  {
581  char localhostname[1024];
582  if (gethostname(localhostname, 1024))
583  {
584  LOG(VB_GENERAL, LOG_ALERT,
585  "MCP: Error, could not determine host name." + ENO);
586  localhostname[0] = '\0';
587  }
588 #ifdef Q_OS_ANDROID
589 #define ANDROID_EXCEPTION_CHECK \
590  if (env->ExceptionCheck()) { \
591  env->ExceptionClear(); \
592  exception=true; \
593  }
594  if (strcmp(localhostname, "localhost") == 0
595  || localhostname[0] == '\0')
596  {
597  hostname = "android";
598  bool exception=false;
599  QAndroidJniEnvironment env;
600  QAndroidJniObject myID = QAndroidJniObject::fromString("android_id");
601  QAndroidJniObject activity = QtAndroid::androidActivity();
603  QAndroidJniObject appctx = activity.callObjectMethod
604  ("getApplicationContext","()Landroid/content/Context;");
606  QAndroidJniObject contentR = appctx.callObjectMethod
607  ("getContentResolver", "()Landroid/content/ContentResolver;");
609  QAndroidJniObject androidId = QAndroidJniObject::callStaticObjectMethod
610  ("android/provider/Settings$Secure","getString",
611  "(Landroid/content/ContentResolver;Ljava/lang/String;)Ljava/lang/String;",
612  contentR.object<jobject>(),
613  myID.object<jstring>());
615  if (exception)
616  LOG(VB_GENERAL, LOG_ALERT,
617  "Java exception looking for android id");
618  else
619  hostname = QString("android-%1").arg(androidId.toString());
620  }
621  else
622  hostname = localhostname;
623 #else
624  hostname = localhostname;
625 #endif
626  LOG(VB_GENERAL, LOG_INFO, "Empty LocalHostName. This is typical.");
627  }
628  else
629  {
630  m_dbParams.m_localEnabled = true;
631  }
632 
633  LOG(VB_GENERAL, LOG_INFO, QString("Using a profile name of: '%1' (Usually the "
634  "same as this host's name.)")
635  .arg(hostname));
637 
638  return ok;
639 }
640 
642  const DatabaseParams &params, bool force)
643 {
644  bool ret = true;
645 
646  // only rewrite file if it has changed
647  if (params != m_dbParams || force)
648  {
650  "LocalHostName", params.m_localHostName);
651 
653  kDefaultDB + "PingHost", params.m_dbHostPing);
654 
655  // If dbHostName is an IPV6 address with scope,
656  // remove the scope. Unescaped % signs are an
657  // xml violation
658  QString dbHostName(params.m_dbHostName);
659  QHostAddress addr;
660  if (addr.setAddress(dbHostName))
661  {
662  addr.setScopeId(QString());
663  dbHostName = addr.toString();
664  }
666  kDefaultDB + "Host", dbHostName);
668  kDefaultDB + "UserName", params.m_dbUserName);
670  kDefaultDB + "Password", params.m_dbPassword);
672  kDefaultDB + "DatabaseName", params.m_dbName);
674  kDefaultDB + "Port", params.m_dbPort);
675 
677  kDefaultWOL + "Enabled", params.m_wolEnabled);
679  kDefaultWOL + "SQLReconnectWaitTime", params.m_wolReconnect);
681  kDefaultWOL + "SQLConnectRetry", params.m_wolRetry);
683  kDefaultWOL + "Command", params.m_wolCommand);
684 
685  // clear out any legacy nodes..
686  m_pConfig->ClearValue(kDefaultMFE + "DBHostName");
687  m_pConfig->ClearValue(kDefaultMFE + "DBUserName");
688  m_pConfig->ClearValue(kDefaultMFE + "DBPassword");
689  m_pConfig->ClearValue(kDefaultMFE + "DBName");
690  m_pConfig->ClearValue(kDefaultMFE + "DBPort");
691  m_pConfig->ClearValue(kDefaultMFE + "DBHostPing");
692 
693  // actually save the file
694  m_pConfig->Save();
695 
696  // Save the new settings:
697  m_dbParams = params;
698  gCoreContext->GetDB()->SetDatabaseParams(m_dbParams);
699 
700  // If database has changed, force its use:
701  ResetDatabase();
702  }
703  return ret;
704 }
705 
707 {
708  if (d && d->m_loop
709  && d->m_loop->isRunning())
710  d->m_loop->exit();
711 }
712 
713 
715 {
716  bool accepted = false;
717  if (m_gui)
718  {
719  TempMainWindow();
720 
721  // Tell the user what went wrong:
722  if (error.length())
724 
725  // ask user for database parameters
726 
727  EnableDBerrors();
729  auto *dbsetting = new DatabaseSettings();
730  auto *ssd = new StandardSettingDialog(mainStack, "databasesettings",
731  dbsetting);
732  if (ssd->Create())
733  {
734  mainStack->AddScreen(ssd);
735  connect(dbsetting, &DatabaseSettings::isClosing,
737  if (!m_loop->isRunning())
738  m_loop->exec();
739  }
740  else
741  delete ssd;
742  SilenceDBerrors();
743  EndTempWindow();
744  accepted = true;
745  }
746  else
747  {
749  QString response;
750  std::this_thread::sleep_for(std::chrono::seconds(1));
751  // give user chance to skip config
752  cout << endl << error.toLocal8Bit().constData() << endl << endl;
753  response = getResponse("Would you like to configure the database "
754  "connection now?",
755  "no");
756  if (!response.startsWith('y', Qt::CaseInsensitive))
757  return false;
758 
759  params.m_dbHostName = getResponse("Database host name:",
760  params.m_dbHostName);
761  response = getResponse("Should I test connectivity to this host "
762  "using the ping command?", "yes");
763  params.m_dbHostPing = response.startsWith('y', Qt::CaseInsensitive);
764 
765  params.m_dbPort = intResponse("Database non-default port:",
766  params.m_dbPort);
767  params.m_dbName = getResponse("Database name:",
768  params.m_dbName);
769  params.m_dbUserName = getResponse("Database user name:",
770  params.m_dbUserName);
771  params.m_dbPassword = getResponse("Database password:",
772  params.m_dbPassword);
773 
774  params.m_localHostName = getResponse("Unique identifier for this machine "
775  "(if empty, the local host name "
776  "will be used):",
777  params.m_localHostName);
778  params.m_localEnabled = !params.m_localHostName.isEmpty();
779 
780  response = getResponse("Would you like to use Wake-On-LAN to retry "
781  "database connections?",
782  (params.m_wolEnabled ? "yes" : "no"));
783  params.m_wolEnabled = response.startsWith('y', Qt::CaseInsensitive);
784 
785  if (params.m_wolEnabled)
786  {
787  params.m_wolReconnect = intResponse("Seconds to wait for "
788  "reconnection:",
789  params.m_wolReconnect);
790  params.m_wolRetry = intResponse("Number of times to retry:",
791  params.m_wolRetry);
792  params.m_wolCommand = getResponse("Command to use to wake server or server MAC address:",
793  params.m_wolCommand);
794  }
795 
796  accepted = m_parent->SaveDatabaseParams(params);
797  }
798  return accepted;
799 }
800 
807 {
808  QString err;
809  QString host;
810 
811  // Jan 20, 2017
812  // Changed to use port check instead of ping
813 
814  int port = 0;
815 
816  // 1 = db awake, 2 = db listening, 3 = db connects,
817  // 4 = backend awake, 5 = backend listening
818  // 9 = all ok, 10 = quit
819 
820  enum startupStates {
821  st_start = 0,
822  st_dbAwake = 1,
823  st_dbStarted = 2,
824  st_dbConnects = 3,
825  st_beWOL = 4,
826  st_beAwake = 5,
827  st_success = 6
828  } startupState = st_start;
829 
830  static const QString kGuiStatuses[7] =
831  {"start","dbAwake","dbStarted","dbConnects","beWOL","beAwake",
832  "success" };
833 
834  int msStartupScreenDelay = gCoreContext->GetNumSetting("StartupScreenDelay",2);
835  if (msStartupScreenDelay > 0)
836  msStartupScreenDelay *= 1000;
837  do
838  {
839  QElapsedTimer timer;
840  timer.start();
841  if (m_dbParams.m_dbHostName.isNull() && m_dbHostCp.length())
842  host = m_dbHostCp;
843  else
844  host = m_dbParams.m_dbHostName;
845  port = m_dbParams.m_dbPort;
846  if (port == 0)
847  port = 3306;
848  int wakeupTime = 3;
849  int attempts = 11;
850  if (m_dbParams.m_wolEnabled) {
851  wakeupTime = m_dbParams.m_wolReconnect;
852  attempts = m_dbParams.m_wolRetry + 1;
853  startupState = st_start;
854  }
855  else
856  startupState = st_dbAwake;
857  if (attempts < 6)
858  attempts = 6;
859  if (!prompt)
860  attempts=1;
861  if (wakeupTime < 5)
862  wakeupTime = 5;
863 
864  int progressTotal = wakeupTime * attempts;
865 
867  m_guiStartup->setTotal(progressTotal);
868 
869  QString beWOLCmd = QString();
870  QString backendIP = QString();
871  int backendPort = 0;
872  QString masterserver;
873 
874  for (int attempt = 0;
875  attempt < attempts && startupState != st_success;
876  ++attempt)
877  {
878  // The first time do everything with minimum timeout and
879  // no GUI, for the normal case where all is OK
880  // After that show the GUI (if this is a GUI program)
881 
882  LOG(VB_GENERAL, LOG_INFO,
883  QString("Start up testing connections. DB %1, BE %2, attempt %3, status %4, Delay: %5")
884  .arg(host).arg(backendIP).arg(attempt).arg(kGuiStatuses[startupState]).arg(msStartupScreenDelay) );
885 
886  int useTimeout = wakeupTime;
887  if (attempt == 0)
888  useTimeout=1;
889 
890  if (m_gui && !m_guiStartup)
891  {
892  if (msStartupScreenDelay==0 || timer.hasExpired(msStartupScreenDelay))
893  {
894  ShowGuiStartup();
895  if (m_guiStartup)
896  m_guiStartup->setTotal(progressTotal);
897  }
898  }
900  {
901  if (attempt > 0)
902  m_guiStartup->setStatusState(kGuiStatuses[startupState]);
903  m_guiStartup->setMessageState("empty");
904  processEvents();
905  }
906  switch (startupState) {
907  case st_start:
909  {
910  if (attempt > 0)
912  if (!checkPort(host, port, useTimeout))
913  break;
914  }
915  startupState = st_dbAwake;
916  [[clang::fallthrough]];
917  case st_dbAwake:
918  if (!checkPort(host, port, useTimeout))
919  break;
920  startupState = st_dbStarted;
921  [[clang::fallthrough]];
922  case st_dbStarted:
923  // If the database is connecting with link-local
924  // address, it may have changed
925  if (m_dbParams.m_dbHostName != host)
926  {
927  m_dbParams.m_dbHostName = host;
928  gCoreContext->GetDB()->SetDatabaseParams(m_dbParams);
929  }
930  EnableDBerrors();
931  ResetDatabase();
933  {
934  for (int temp = 0; temp < useTimeout * 2 ; temp++)
935  {
936  processEvents();
937  std::this_thread::sleep_for(std::chrono::milliseconds(500));
938  }
939  break;
940  }
941  startupState = st_dbConnects;
942  [[clang::fallthrough]];
943  case st_dbConnects:
944  if (m_needsBackend)
945  {
946  beWOLCmd = gCoreContext->GetSetting("WOLbackendCommand", "");
947  if (!beWOLCmd.isEmpty())
948  {
949  wakeupTime += gCoreContext->GetNumSetting
950  ("WOLbackendReconnectWaitTime", 0);
951  attempts += gCoreContext->GetNumSetting
952  ("WOLbackendConnectRetry", 0);
953  useTimeout = wakeupTime;
954  if (m_gui && !m_guiStartup && attempt == 0)
955  useTimeout=1;
956  progressTotal = wakeupTime * attempts;
958  m_guiStartup->setTotal(progressTotal);
959  startupState = st_beWOL;
960  }
961  }
962  else {
963  startupState = st_success;
964  break;
965  }
966  masterserver = gCoreContext->GetSetting
967  ("MasterServerName");
968  backendIP = gCoreContext->GetSettingOnHost
969  ("BackendServerAddr", masterserver);
970  backendPort = MythCoreContext::GetMasterServerPort();
971  [[clang::fallthrough]];
972  case st_beWOL:
973  if (!beWOLCmd.isEmpty()) {
974  if (attempt > 0)
975  MythWakeup(beWOLCmd);
976  if (!checkPort(backendIP, backendPort, useTimeout))
977  break;
978  }
979  startupState = st_beAwake;
980  [[clang::fallthrough]];
981  case st_beAwake:
982  if (!checkPort(backendIP, backendPort, useTimeout))
983  break;
984  startupState = st_success;
985  [[clang::fallthrough]];
986  case st_success:
987  // Quiet compiler warning.
988  break;
989  }
990  if (m_guiStartup)
991  {
992  if (m_guiStartup->m_Exit
995  || m_guiStartup->m_Retry)
996  break;
997  }
998  processEvents();
999  }
1000  if (startupState == st_success)
1001  break;
1002 
1003  QString stateMsg = kGuiStatuses[startupState];
1004  stateMsg.append("Fail");
1005  LOG(VB_GENERAL, LOG_INFO,
1006  QString("Start up failure. host %1, status %2")
1007  .arg(host).arg(stateMsg));
1008 
1009  if (m_gui && !m_guiStartup)
1010  {
1011  ShowGuiStartup();
1012  if (m_guiStartup)
1013  m_guiStartup->setTotal(progressTotal);
1014  }
1015 
1016  if (m_guiStartup
1017  && !m_guiStartup->m_Exit
1018  && !m_guiStartup->m_Setup
1019  && !m_guiStartup->m_Search
1020  && !m_guiStartup->m_Retry)
1021  {
1023  m_guiStartup->setStatusState(stateMsg);
1024  m_guiStartup->setMessageState("makeselection");
1025  m_loop->exec();
1026  }
1027  }
1028  while (m_guiStartup && m_guiStartup->m_Retry);
1029 
1030  if (startupState < st_dbAwake)
1031  {
1032  LOG(VB_GENERAL, LOG_WARNING, QString("Pinging to %1 failed, database will be unavailable").arg(host));
1033  SilenceDBerrors();
1034  err = QObject::tr(
1035  "Cannot find (ping) database host %1 on the network",
1036  "Backend Setup");
1037  return err.arg(host);
1038  }
1039 
1040  if (startupState < st_dbConnects)
1041  {
1042  SilenceDBerrors();
1043  return QObject::tr("Cannot login to database", "Backend Setup");
1044  }
1045 
1046  if (startupState < st_success)
1047  {
1048  return QObject::tr("Cannot connect to backend", "Backend Setup");
1049  }
1050 
1051  // Current DB connection may have been silenced (invalid):
1052  EnableDBerrors();
1053  ResetDatabase();
1054 
1055  return QString();
1056 }
1057 
1058 // Show the Gui Startup window.
1059 // This is called if there is a delay in startup for any reason
1060 // such as the database being unavailable
1062 {
1063  if (!m_gui)
1064  return;
1065  TempMainWindow(false);
1066  MythMainWindow *mainWindow = GetMythMainWindow();
1067  MythScreenStack *mainStack = mainWindow->GetMainStack();
1068  if (mainStack) {
1069  if (!m_guiStartup) {
1070  m_guiStartup = new GUIStartup(mainStack,m_loop);
1071  if (!m_guiStartup->Create()) {
1072  delete m_guiStartup;
1073  m_guiStartup = nullptr;
1074  }
1075  if (m_guiStartup) {
1076  mainStack->AddScreen(m_guiStartup, false);
1077  processEvents();
1078  }
1079  }
1080  }
1081 }
1082 
1092 {
1093  // This silences any DB errors from Get*Setting(),
1094  // (which is the vast majority of them)
1095  gCoreContext->GetDB()->SetSuppressDBMessages(true);
1096 
1097  // Save the configured hostname, so that we can
1098  // still display it in the DatabaseSettings screens
1099  if (m_dbParams.m_dbHostName.length())
1101 
1102  m_dbParams.m_dbHostName.clear();
1103  gCoreContext->GetDB()->SetDatabaseParams(m_dbParams);
1104 }
1105 
1107 {
1108  // Restore (possibly) blanked hostname
1109  if (m_dbParams.m_dbHostName.isNull() && m_dbHostCp.length())
1110  {
1112  gCoreContext->GetDB()->SetDatabaseParams(m_dbParams);
1113  }
1114 
1115  gCoreContext->GetDB()->SetSuppressDBMessages(false);
1116 }
1117 
1118 
1131 {
1133  gCoreContext->GetDB()->SetDatabaseParams(m_dbParams);
1135 }
1136 
1141 {
1142  TempMainWindow();
1143 
1144  // Tell the user what went wrong:
1145  if (!error.isEmpty())
1146  {
1147  LOG(VB_GENERAL, LOG_ERR, QString("Error: %1").arg(error));
1148  ShowOkPopup(error);
1149  }
1150 
1151  LOG(VB_GENERAL, LOG_INFO, "Putting up the UPnP backend chooser");
1152 
1155 
1156  EndTempWindow();
1157 
1158  return (int)ret;
1159 }
1160 
1167 int MythContextPrivate::UPnPautoconf(const int milliSeconds)
1168 {
1169  LOG(VB_GENERAL, LOG_INFO, QString("UPNP Search %1 secs")
1170  .arg(milliSeconds / 1000));
1171 
1172  SSDP::Instance()->PerformSearch(gBackendURI, milliSeconds / 1000);
1173 
1174  // Search for a total of 'milliSeconds' ms, sending new search packet
1175  // about every 250 ms until less than one second remains.
1176  MythTimer totalTime; totalTime.start();
1177  MythTimer searchTime; searchTime.start();
1178  while (totalTime.elapsed() < milliSeconds)
1179  {
1180  usleep(25000);
1181  int ttl = milliSeconds - totalTime.elapsed();
1182  if ((searchTime.elapsed() > 249) && (ttl > 1000))
1183  {
1184  LOG(VB_GENERAL, LOG_INFO, QString("UPNP Search %1 secs")
1185  .arg(ttl / 1000));
1186  SSDP::Instance()->PerformSearch(gBackendURI, ttl / 1000);
1187  searchTime.start();
1188  }
1189  }
1190 
1192 
1193  if (!backends)
1194  {
1195  LOG(VB_GENERAL, LOG_INFO, "No UPnP backends found");
1196  return 0;
1197  }
1198 
1199  int count = backends->Count();
1200  if (count)
1201  {
1202  LOG(VB_GENERAL, LOG_INFO,
1203  QString("Found %1 UPnP backends").arg(count));
1204  }
1205  else
1206  {
1207  LOG(VB_GENERAL, LOG_ERR,
1208  "No UPnP backends found, but SSDP::Find() not NULL");
1209  }
1210 
1211  if (count != 1)
1212  {
1213  backends->DecrRef();
1214  return count;
1215  }
1216 
1217  // Get this backend's location:
1218  DeviceLocation *BE = backends->GetFirst();
1219  backends->DecrRef();
1220  backends = nullptr;
1221 
1222  // We don't actually know the backend's access PIN, so this will
1223  // only work for ones that have PIN access disabled (i.e. 0000)
1224  int ret = (UPnPconnect(BE, QString())) ? 1 : -1;
1225 
1226  BE->DecrRef();
1227 
1228  return ret;
1229 }
1230 
1237 {
1238  QString loc = "DefaultUPnP() - ";
1239  QString PIN = m_pConfig->GetValue(kDefaultPIN, "");
1240  QString USN = m_pConfig->GetValue(kDefaultUSN, "");
1241 
1242  if (USN.isEmpty())
1243  {
1244  LOG(VB_UPNP, LOG_INFO, loc + "No default UPnP backend");
1245  return false;
1246  }
1247 
1248  LOG(VB_UPNP, LOG_INFO, loc + "config.xml has default " +
1249  QString("PIN '%1' and host USN: %2") .arg(PIN).arg(USN));
1250 
1251  // ----------------------------------------------------------------------
1252 
1253  int timeout_ms = 2000;
1254  LOG(VB_GENERAL, LOG_INFO, QString("UPNP Search up to %1 secs")
1255  .arg(timeout_ms / 1000));
1256  SSDP::Instance()->PerformSearch(gBackendURI, timeout_ms / 1000);
1257 
1258  // ----------------------------------------------------------------------
1259  // We need to give the server time to respond...
1260  // ----------------------------------------------------------------------
1261 
1262  DeviceLocation *pDevLoc = nullptr;
1263  MythTimer totalTime; totalTime.start();
1264  MythTimer searchTime; searchTime.start();
1265  while (totalTime.elapsed() < timeout_ms)
1266  {
1267  pDevLoc = SSDP::Find( gBackendURI, USN );
1268 
1269  if (pDevLoc)
1270  break;
1271 
1272  usleep(25000);
1273 
1274  int ttl = timeout_ms - totalTime.elapsed();
1275  if ((searchTime.elapsed() > 249) && (ttl > 1000))
1276  {
1277  LOG(VB_GENERAL, LOG_INFO, QString("UPNP Search up to %1 secs")
1278  .arg(ttl / 1000));
1279  SSDP::Instance()->PerformSearch(gBackendURI, ttl / 1000);
1280  searchTime.start();
1281  }
1282  }
1283 
1284  // ----------------------------------------------------------------------
1285 
1286  if (!pDevLoc)
1287  {
1288  error = "Cannot find default UPnP backend";
1289  return false;
1290  }
1291 
1292  if (UPnPconnect(pDevLoc, PIN))
1293  {
1294  pDevLoc->DecrRef();
1295  return true;
1296  }
1297 
1298  pDevLoc->DecrRef();
1299 
1300  error = "Cannot connect to default backend via UPnP. Wrong saved PIN?";
1301  return false;
1302 }
1303 
1308  const QString &PIN)
1309 {
1310  QString error;
1311  QString loc = "UPnPconnect() - ";
1312  QString URL = backend->m_sLocation;
1313  MythXMLClient client(URL);
1314 
1315  LOG(VB_UPNP, LOG_INFO, loc + QString("Trying host at %1").arg(URL));
1316  switch (client.GetConnectionInfo(PIN, &m_dbParams, error))
1317  {
1318  case UPnPResult_Success:
1319  gCoreContext->GetDB()->SetDatabaseParams(m_dbParams);
1320  LOG(VB_UPNP, LOG_INFO, loc +
1321  "Got database hostname: " + m_dbParams.m_dbHostName);
1322  return true;
1323 
1325  // The stored PIN is probably not correct.
1326  // We could prompt for the PIN and try again, but that needs a UI.
1327  // Easier to fail for now, and put up the full UI selector later
1328  LOG(VB_UPNP, LOG_ERR, loc + "Wrong PIN?");
1329  return false;
1330 
1331  default:
1332  LOG(VB_UPNP, LOG_ERR, loc + error);
1333  break;
1334  }
1335 
1336  // This backend may have a local DB with the default user/pass/DBname.
1337  // For whatever reason, we have failed to get anything back via UPnP,
1338  // so we might as well try the database directly as a last resort.
1339  QUrl theURL(URL);
1340  URL = theURL.host();
1341  if (URL.isEmpty())
1342  return false;
1343 
1344  LOG(VB_UPNP, LOG_INFO, "Trying default DB credentials at " + URL);
1346 
1347  return true;
1348 }
1349 
1351 {
1352  if (e->type() == MythEvent::MythEventMessage)
1353  {
1354  if (m_disableeventpopup)
1355  return true;
1356 
1358  {
1360  }
1361 
1362  auto *me = dynamic_cast<MythEvent*>(e);
1363  if (me == nullptr)
1364  return true;
1365 
1366  if (me->Message() == "VERSION_MISMATCH" && (1 == me->ExtraDataCount()))
1367  ShowVersionMismatchPopup(me->ExtraData(0).toUInt());
1368  else if (me->Message() == "CONNECTION_FAILURE")
1370  else if (me->Message() == "PERSISTENT_CONNECTION_FAILURE")
1372  else if (me->Message() == "CONNECTION_RESTABLISHED")
1374  return true;
1375  }
1376 
1377  return QObject::event(e);
1378 }
1379 
1381 {
1382  QDateTime now = MythDate::current();
1383 
1384  if (!GetNotificationCenter() || !m_ui || !m_ui->IsScreenSetup())
1385  return;
1386 
1387  if (m_lastCheck.isValid() && now < m_lastCheck)
1388  return;
1389 
1390  // When WOL is disallowed, standy mode,
1391  // we should not show connection failures.
1392  if (!gCoreContext->IsWOLAllowed())
1393  return;
1394 
1395  m_lastCheck = now.addMSecs(5000); // don't refresh notification more than every 5s
1396 
1397  QString description = (persistent) ?
1398  QObject::tr(
1399  "The connection to the master backend "
1400  "server has gone away for some reason. "
1401  "Is it running?") :
1402  QObject::tr(
1403  "Could not connect to the master backend server. Is "
1404  "it running? Is the IP address set for it in "
1405  "mythtv-setup correct?");
1406 
1407  QString message = QObject::tr("Could not connect to master backend");
1408  MythErrorNotification n(message, _Location, description);
1409  n.SetId(m_registration);
1410  n.SetParent(this);
1412 }
1413 
1415 {
1416  if (!GetNotificationCenter())
1417  return;
1418 
1419  if (!m_lastCheck.isValid())
1420  return;
1421 
1422  MythCheckNotification n(QObject::tr("Backend is online"), _Location);
1423  n.SetId(m_registration);
1424  n.SetParent(this);
1425  n.SetDuration(5);
1427  m_lastCheck = QDateTime();
1428 }
1429 
1431 {
1432  if (m_mbeVersionPopup)
1433  return;
1434 
1435  QString message =
1436  QObject::tr(
1437  "The server uses network protocol version %1, "
1438  "but this client only understands version %2. "
1439  "Make sure you are running compatible versions of "
1440  "the backend and frontend.")
1441  .arg(remote_version).arg(MYTH_PROTO_VERSION);
1442 
1443  if (HasMythMainWindow() && m_ui && m_ui->IsScreenSetup())
1444  {
1446  message, m_sh, SLOT(VersionMismatchPopupClosed()));
1447  }
1448  else
1449  {
1450  LOG(VB_GENERAL, LOG_ERR, LOC + message);
1451  qApp->exit(GENERIC_EXIT_SOCKET_ERROR);
1452  }
1453 }
1454 
1455 // Process Events while waiting for connection
1456 // return true if progress is 100%
1458 {
1459 // bool ret = false;
1460 // if (m_guiStartup)
1461 // ret = m_guiStartup->updateProgress();
1462  qApp->processEvents(QEventLoop::AllEvents, 250);
1463  qApp->processEvents(QEventLoop::AllEvents, 250);
1464 // return ret;
1465 }
1466 
1467 // cache some settings in cache/contextcache.xml
1468 // only call this if the database is available.
1469 
1470 const QString MythContextPrivate::kSettingsToSave[] =
1471 { "Theme", "Language", "Country", "GuiHeight",
1472  "GuiOffsetX", "GuiOffsetY", "GuiWidth", "RunFrontendInWindow",
1473  "AlwaysOnTop", "HideMouseCursor", "ThemePainter", "libCECEnabled",
1474  "StartupScreenDelay" };
1475 
1476 
1478 {
1479  if (!m_gui)
1480  return true;
1481  QString cacheDirName = GetConfDir() + "/cache/";
1482  QDir dir(cacheDirName);
1483  dir.mkpath(cacheDirName);
1484  XmlConfiguration config = XmlConfiguration("cache/contextcache.xml");
1485  static constexpr int kArraySize = sizeof(kSettingsToSave)/sizeof(kSettingsToSave[0]);
1486  for (int ix = 0; ix < kArraySize; ix++)
1487  {
1488  QString cacheValue = config.GetValue("Settings/"+kSettingsToSave[ix],QString());
1490  QString value = gCoreContext->GetSetting(kSettingsToSave[ix],QString());
1491  if (value != cacheValue)
1492  {
1493  config.SetValue("Settings/"+kSettingsToSave[ix],value);
1494  m_settingsCacheDirty = true;
1495  }
1496  }
1498  return config.Save();
1499 }
1500 
1502 {
1503  if (!m_gui)
1504  return;
1505  XmlConfiguration config = XmlConfiguration("cache/contextcache.xml");
1506  static constexpr int kArraySize = sizeof(kSettingsToSave)/sizeof(kSettingsToSave[0]);
1507  for (int ix = 0; ix < kArraySize; ix++)
1508  {
1509  if (!gCoreContext->GetSetting(kSettingsToSave[ix],QString()).isEmpty())
1510  continue;
1511  QString value = config.GetValue("Settings/"+kSettingsToSave[ix],QString());
1512  if (!value.isEmpty())
1514  }
1515  // Prevent power off TV after temporary window
1516  gCoreContext->OverrideSettingForSession("PowerOffTVAllowed", nullptr);
1517 
1518  QString language = gCoreContext->GetSetting("Language",QString());
1519  MythTranslation::load("mythfrontend");
1520 }
1521 
1523 {
1524  QString language = gCoreContext->GetSetting("Language",QString());
1525  static constexpr int kArraySize = sizeof(kSettingsToSave)/sizeof(kSettingsToSave[0]);
1526  for (int ix = 0; ix < kArraySize; ix++)
1527  {
1529  }
1530  // Restore power off TV setting
1531  gCoreContext->ClearOverrideSettingForSession("PowerOffTVAllowed");
1532 
1533  if (language != gCoreContext->GetSetting("Language",QString()))
1534  MythTranslation::load("mythfrontend");
1535 }
1536 
1537 
1539 {
1540  d->m_mbeVersionPopup = nullptr;
1541  qApp->exit(GENERIC_EXIT_SOCKET_ERROR);
1542 }
1543 
1544 MythContext::MythContext(QString binversion, bool needsBackend)
1545  : m_appBinaryVersion(std::move(binversion))
1546 {
1547 #ifdef _WIN32
1548  static bool WSAStarted = false;
1549  if (!WSAStarted) {
1550  WSADATA wsadata;
1551  int res = WSAStartup(MAKEWORD(2, 0), &wsadata);
1552  LOG(VB_SOCKET, LOG_INFO,
1553  QString("WSAStartup returned %1").arg(res));
1554  }
1555 #endif
1556 
1557  d = new MythContextPrivate(this);
1558  d->m_needsBackend = needsBackend;
1559 
1561 
1562  if (!gCoreContext || !gCoreContext->Init())
1563  {
1564  LOG(VB_GENERAL, LOG_EMERG, LOC + "Unable to allocate MythCoreContext");
1565  qApp->exit(GENERIC_EXIT_NO_MYTHCONTEXT);
1566  }
1567 }
1568 
1569 bool MythContext::Init(const bool gui,
1570  const bool promptForBackend,
1571  const bool disableAutoDiscovery,
1572  const bool ignoreDB)
1573 {
1574  if (!d)
1575  {
1576  LOG(VB_GENERAL, LOG_EMERG, LOC + "Init() Out-of-memory");
1577  return false;
1578  }
1579 
1580  SetDisableEventPopup(true);
1581 
1583  {
1584  LOG(VB_GENERAL, LOG_EMERG,
1585  QString("Application binary version (%1) does not "
1586  "match libraries (%2)")
1588 
1589  QString warning = QObject::tr(
1590  "This application is not compatible "
1591  "with the installed MythTV libraries.");
1592  if (gui)
1593  {
1594  d->TempMainWindow(false);
1595  ShowOkPopup(warning);
1596  }
1597  LOG(VB_GENERAL, LOG_WARNING, warning);
1598 
1599  return false;
1600  }
1601 
1602 #ifdef _WIN32
1603  // HOME environment variable might not be defined
1604  // some libraries will fail without it
1605  QString home = getenv("HOME");
1606  if (home.isEmpty())
1607  {
1608  home = getenv("LOCALAPPDATA"); // Vista
1609  if (home.isEmpty())
1610  home = getenv("APPDATA"); // XP
1611  if (home.isEmpty())
1612  home = QString("."); // getenv("TEMP")?
1613 
1614  _putenv(QString("HOME=%1").arg(home).toLocal8Bit().constData());
1615  }
1616 #endif
1617 
1618  // If HOME isn't defined, we won't be able to use default confdir of
1619  // $HOME/.mythtv nor can we rely on a MYTHCONFDIR that references $HOME
1620  QString homedir = QDir::homePath();
1621  QString confdir = getenv("MYTHCONFDIR");
1622  if ((homedir.isEmpty() || homedir == "/") &&
1623  (confdir.isEmpty() || confdir.contains("$HOME")))
1624  {
1625  QString warning = "Cannot locate your home directory."
1626  " Please set the environment variable HOME";
1627  if (gui)
1628  {
1629  d->TempMainWindow(false);
1630  ShowOkPopup(warning);
1631  }
1632  LOG(VB_GENERAL, LOG_WARNING, warning);
1633 
1634  return false;
1635  }
1636 
1637  if (!d->Init(gui, promptForBackend, disableAutoDiscovery, ignoreDB))
1638  {
1639  return false;
1640  }
1641 
1642  SetDisableEventPopup(false);
1644  if (d->m_settingsCacheDirty)
1645  {
1646 #ifndef Q_OS_ANDROID
1648 #endif
1649  d->m_settingsCacheDirty = false;
1650  }
1652 
1653  return true;
1654 }
1655 
1657 {
1658  if (MThreadPool::globalInstance()->activeThreadCount())
1659  LOG(VB_GENERAL, LOG_INFO, "Waiting for threads to exit.");
1660 
1662  logStop();
1663 
1664  SSDP::Shutdown();
1666 
1667  delete gCoreContext;
1668  gCoreContext = nullptr;
1669 
1670  delete d;
1671 }
1672 
1674 {
1675  d->m_disableeventpopup = check;
1676 }
1677 
1679 {
1680  return d->m_dbParams;
1681 }
1682 
1684 {
1685  return d->SaveDatabaseParams(params, false);
1686 }
1687 
1689 {
1690  return d->saveSettingsCache();
1691 }
1692 
1693 /* vim: set expandtab tabstop=4 shiftwidth=4: */
void SaveLocaleDefaults(void)
void ShowGuiStartup(void)
Startup context for MythTV.
Definition: mythcontext.h:42
void SetParent(void *parent)
contains the parent address.
QString m_dbHostName
database server
Definition: mythdbparams.h:21
DatabaseParams m_dbParams
Current database host & WOL details.
virtual int GetValue(const QString &sSetting, int Default)=0
bool setStatusState(const QString &name)
Definition: guistartup.cpp:107
QString m_localHostName
name used for loading/saving settings
Definition: mythdbparams.h:30
int intResponse(const QString &query, int def)
In an interactive shell, prompt the user to input a number.
unsigned int slots[4]
Definition: element.c:38
A QElapsedTimer based timer to replace use of QTime as a timer.
Definition: mythtimer.h:13
static void configplugin_cb(const QString &cmd)
void LoadQtConfig(void)
Dialog asking for user confirmation.
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
bool m_localEnabled
true if localHostName is not default
Definition: mythdbparams.h:29
void SetId(int id)
Optional MythNotification elements.
bool setMessageState(const QString &name)
Definition: guistartup.cpp:117
static void exec_program_tv_cb(const QString &cmd)
bool m_forceSave
set to true to force a save of the settings file
Definition: mythdbparams.h:39
const QString kDefaultWOL
Definition: backendselect.h:23
static const QString kSettingsToSave[]
static Type MythEventMessage
Definition: mythevent.h:66
static void error(const char *str,...)
Definition: vbi.c:42
int m_wolReconnect
seconds to wait for reconnect
Definition: mythdbparams.h:34
static int GetMasterServerPort(void)
Returns the Master Backend control port If no master server port has been defined in the database,...
static void ejectOpticalDisc(void)
Eject a disk, unmount a drive, open a tray.
QString m_appBinaryVersion
Definition: mythcontext.h:61
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
void isClosing(void)
static const char URL[]
Definition: cddb.cpp:29
bool m_Retry
Definition: guistartup.h:56
void UnRegister(void *from, int id, bool closeimemdiately=false)
Unregister the client.
bool IsFrontend(void) const
is this process a frontend process
QString GetSettingOnHost(const QString &key, const QString &host, const QString &defaultval="")
bool SaveDatabaseParams(const DatabaseParams &params, bool force)
void DestroyMythMainWindow(void)
bool MythWakeup(const QString &wakeUpCommand, uint flags, uint timeout)
void SetDisableEventPopup(bool check)
bool IsScreenSetup(void)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static Decision Prompt(DatabaseParams *dbParams, Configuration *pConfig)
MythScreenStack * GetMainStack()
DatabaseParams GetDatabaseParams(void)
bool saveSettingsCache(void)
void waitForDone(void)
MythContext * gContext
This global variable contains the MythContext instance for the application.
Definition: mythcontext.cpp:62
#define ANDROID_EXCEPTION_CHECK
void ShowVersionMismatchPopup(uint remote_version)
void DestroyMythUI()
void SetLocalHostname(const QString &hostname)
const QString kDefaultDB
Definition: backendselect.h:22
void(* exec_program)(const QString &cmd)
Definition: mythuihelper.h:34
~MythContextPrivate() override
bool checkPort(QString &host, int port, int timeLimit=30000, bool linkLocalOnly=false)
Check if a port is open and sort out the link-local scope.
Definition: portchecker.cpp:73
QString GetConfDir(void)
Definition: mythdirs.cpp:224
static void Shutdown()
Definition: taskqueue.cpp:68
void Init(MythUIMenuCallbacks &cbs)
MythContextPrivate(MythContext *lparent)
static void eject_cb(void)
bool config_plugin(const QString &plugname)
Definition: mythplugin.cpp:182
bool SendReceiveStringList(QStringList &strlist, bool quickTimeout=false, bool block=true)
Send a message to the backend and wait for a response.
int UPnPautoconf(int milliSeconds=2000)
If there is only a single UPnP backend, use it.
int m_wolRetry
times to retry to reconnect
Definition: mythdbparams.h:35
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
MythContextSlotHandler * m_sh
DeviceLocation * GetFirst(void)
Returns random entry in cache, returns nullptr when list is empty.
Definition: ssdpcache.cpp:73
static void plugin_cb(const QString &cmd)
void PerformSearch(const QString &sST, uint timeout_secs=2)
Definition: ssdp.cpp:203
MythPluginManager * GetPluginManager(void)
void ClearOverrideSettingForSession(const QString &key)
void EnableDBerrors(void)
void VersionMismatchPopupClosed(void)
MythConfirmationDialog * m_mbeVersionPopup
bool m_dbHostPing
Can we test connectivity using ping?
Definition: mythdbparams.h:22
int ChooseBackend(const QString &error)
Search for backends via UPnP, put up a UI for the user to choose one.
QString TestDBconnection(bool prompt=true)
Some quick sanity checks before opening a database connection.
uint Count(void) const
Definition: ssdpcache.h:44
void setTotal(int total)
Definition: guistartup.cpp:128
virtual bool GetBoolValue(const QString &sSetting, bool Default)=0
bool PromptForDatabaseParams(const QString &error)
MythContext * m_parent
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
QString GetSetting(const QString &key, const QString &defaultval="")
QString m_dbName
database name
Definition: mythdbparams.h:26
QString m_dbUserName
DB user name.
Definition: mythdbparams.h:24
void SetDuration(int duration)
contains a duration during which the notification will be displayed for.
bool UPnPconnect(const DeviceLocation *backend, const QString &PIN)
Query a backend via UPnP for its database connection parameters.
bool m_Setup
Definition: guistartup.h:55
#define GENERIC_EXIT_SOCKET_ERROR
Socket error.
Definition: exitcodes.h:18
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
virtual bool Save(void)=0
bool updateProgress(bool finished=false)
Definition: guistartup.cpp:144
QEventLoop * m_loop
MythContextPrivate * d
Definition: mythcontext.h:60
static void load(const QString &module_name)
Load a QTranslator for the user's preferred language.
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
#define MYTH_PROTO_VERSION
Increment this whenever the MythTV network protocol changes.
Definition: mythversion.h:48
Small class to handle TCP port checking and finding link-local context.
Definition: portchecker.h:43
QString m_dbPassword
DB password.
Definition: mythdbparams.h:25
static bool testDBConnection()
Checks DB connection + login (login info via Mythcontext)
Definition: mythdbcon.cpp:851
bool Queue(const MythNotification &notification)
Queue a notification Queue() is thread-safe and can be called from anywhere.
string hostname
Definition: caa.py:17
void TempMainWindow(bool languagePrompt=true)
Setup a minimal themed main window, and prompt for user's language.
QString m_masterhostname
master backend hostname
unsigned int uint
Definition: compat.h:140
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)
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:99
bool Save(void) override
static const QString _Location
Definition: mythcontext.cpp:64
bool LoadDatabaseSettings(void)
Load database and host settings from config.xml, or set some defaults.
bool IsValid(const QString &source=QString("Unknown")) const
MythUIHelper * GetMythUI()
void SilenceDBerrors(void)
Cause MSqlDatabase::OpenDatabase() and MSqlQuery to fail silently.
bool HasMythMainWindow(void)
int m_dbPort
database port
Definition: mythdbparams.h:23
MythMainWindow * GetMythMainWindow(void)
static void clearSettingsCacheOverride(void)
bool m_Exit
Definition: guistartup.h:54
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...
int GetNumSetting(const QString &key, int defaultval=0)
bool m_wolEnabled
true if wake-on-lan params are used
Definition: mythdbparams.h:33
Structure containing the basic Database parameters.
Definition: mythdbparams.h:9
QString m_sLocation
Definition: upnpdevice.h:229
bool Create(void) override
Definition: guistartup.cpp:67
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
void LoadDefaults(void)
Load sensible connection defaults.
Definition: mythdbparams.cpp:9
static SSDP * Instance()
Definition: ssdp.cpp:54
int GetValue(const QString &sSetting, int Default) override
virtual void PopScreen(MythScreenType *screen=nullptr, bool allowFade=true, bool deleteScreen=true)
virtual void SetBoolValue(const QString &sSetting, bool value)=0
int elapsed(void)
Returns milliseconds elapsed since last start() or restart()
Definition: mythtimer.cpp:90
void logStop(void)
Entry point for stopping logging for an application.
Definition: logging.cpp:750
bool checkPort(QString &host, int port, int timeLimit)
Check if a port is open and sort out the link-local scope.
MythContextPrivate * d
Definition: mythcontext.h:30
Configuration * m_pConfig
UPnPResultCode GetConnectionInfo(const QString &sPin, DatabaseParams *pParams, QString &sMsg)
const QString kDefaultPIN
Definition: backendselect.h:25
void ClearSettingsCache(const QString &myKey=QString(""))
#define MYTH_BINARY_VERSION
Update this whenever the plug-in ABI changes.
Definition: mythversion.h:16
bool IsWOLAllowed() const
bool saveSettingsCache(void)
MythDB * GetDB(void)
QString m_wolCommand
command to use for wake-on-lan
Definition: mythdbparams.h:36
MDBManager * GetDBManager(void)
void loadSettingsCacheOverride(void)
bool event(QEvent *) override
void HideConnectionFailurePopup(void)
bool Init(const bool gui=true, const bool promptForBackend=false, const bool disableAutoDiscovery=false, const bool ignoreDB=false)
const QString kDefaultUSN
Definition: backendselect.h:26
bool m_Search
Definition: guistartup.h:57
MythUIHelper * m_ui
bool Init(bool gui, bool prompt, bool noPrompt, bool ignoreDB)
static SSDPCacheEntries * Find(const QString &sURI)
Definition: ssdp.h:126
const QString kDefaultMFE
Definition: backendselect.h:24
#define GENERIC_EXIT_NO_MYTHCONTEXT
No MythContext available.
Definition: exitcodes.h:13
void ResetDatabase(void)
Called when the user changes the DB connection settings.
MythContext(QString binversion, bool needsBackend=false)
void ActivateSettingsCache(bool activate=true)
void Init(bool mayReInit=true)
static void processEvents(void)
GUIStartup * m_guiStartup
void ShowConnectionFailurePopup(bool persistent)
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:30
void start(void)
starts measuring elapsed time.
Definition: mythtimer.cpp:47
void CloseDatabases(void)
Definition: mythdbcon.cpp:468
virtual ~MythContext()
bool DefaultUPnP(QString &error)
Get the default backend from config.xml, use UPnP to find it.
static QString confdir
Definition: mythdirs.cpp:20
This class contains the runtime context for MythTV.
void SetValue(const QString &sSetting, int value) override
static void Shutdown()
Definition: ssdp.cpp:64
#define LOC
Definition: mythcontext.cpp:60
MythNotificationCenter * GetNotificationCenter(void)
QString m_dbHostCp
dbHostName backup
bool run_plugin(const QString &plugname)
Definition: mythplugin.cpp:164
void InitializeMythDirs(void)
Definition: mythdirs.cpp:30
virtual void SetValue(const QString &sSetting, int value)=0
bool FindDatabase(bool prompt, bool noAutodetect)
Get database connection settings and test connectivity.
bool m_gui
Should this context use GUI elements?