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.arg(qPrintable(strlist[1]))
172  .arg(qPrintable(strlist[2]))
173  .arg(qPrintable(strlist[3]));
174 
175  myth_system(s);
176 
177  strlist = QStringList(QString("FREE_TUNER %1").arg(cardid));
179  QString ret = strlist[0];
180  }
181  else
182  {
183  QString label;
184 
185  if (cardidok)
186  {
187  if (cardid == -1)
188  {
189  label = QObject::tr("Could not find specified tuner (%1).")
190  .arg(wantcardid);
191  }
192  else
193  {
194  label = QObject::tr("Specified tuner (%1) is already in use.")
195  .arg(wantcardid);
196  }
197  }
198  else
199  {
200  label = QObject::tr("All tuners are currently in use. If you want "
201  "to watch TV, you can cancel one of the "
202  "in-progress recordings from the delete menu");
203  }
204 
205  LOG(VB_GENERAL, LOG_ALERT, QString("exec_program_tv: ") + label);
206 
207  ShowOkPopup(label);
208  }
209 }
210 
211 static void configplugin_cb(const QString &cmd)
212 {
214  if (!pmanager)
215  return;
216 
217  if (GetNotificationCenter() && pmanager->config_plugin(cmd.trimmed()))
218  {
220  QObject::tr("Failed to configure plugin"));
221  }
222 }
223 
224 static void plugin_cb(const QString &cmd)
225 {
227  if (!pmanager)
228  return;
229 
230  if (GetNotificationCenter() && pmanager->run_plugin(cmd.trimmed()))
231  {
232  ShowNotificationError(QObject::tr("Plugin failure"),
233  _Location,
234  QObject::tr("%1 failed to run for some reason").arg(cmd));
235  }
236 }
237 
238 static void eject_cb(void)
239 {
241 }
242 
244  : m_parent(lparent),
245  m_sh(new MythContextSlotHandler(this))
246 {
247  m_loop = new QEventLoop(this);
249 }
250 
252 {
253  delete m_pConfig;
255  {
257  }
258 
259  delete m_loop;
260 
261  if (m_ui)
262  DestroyMythUI();
263  if (m_sh)
264  m_sh->deleteLater();
265 }
266 
277 void MythContextPrivate::TempMainWindow(bool languagePrompt)
278 {
279  if (HasMythMainWindow())
280  return;
281 
282  SilenceDBerrors();
283 
284 #ifdef Q_OS_MAC
285  // Qt 4.4 has window-focus problems
286  gCoreContext->OverrideSettingForSession("RunFrontendInWindow", "1");
287 #endif
288  GetMythUI()->Init();
289  GetMythUI()->LoadQtConfig();
290 
291  MythMainWindow *mainWindow = MythMainWindow::getMainWindow(false);
292  mainWindow->Init();
293 
294  if (languagePrompt)
295  {
296  // ask user for language settings
298  MythTranslation::load("mythfrontend");
299  }
300 }
301 
303 {
304  if (HasMythMainWindow())
305  {
307  {
309  if (mainStack) {
310  mainStack->PopScreen(m_guiStartup, false);
311  m_guiStartup = nullptr;
312  }
313  }
314  }
315  EnableDBerrors();
316 }
317 
327 bool MythContextPrivate::checkPort(QString &host, int port, int timeLimit)
328 {
329  PortChecker checker;
330  if (m_guiStartup)
331  QObject::connect(m_guiStartup,SIGNAL(cancelPortCheck()),&checker,SLOT(cancelPortCheck()));
332  return checker.checkPort(host, port, timeLimit*1000);
333 }
334 
335 
336 bool MythContextPrivate::Init(const bool gui,
337  const bool promptForBackend,
338  const bool noPrompt,
339  const bool ignoreDB)
340 {
341  gCoreContext->GetDB()->IgnoreDatabase(ignoreDB);
342  m_gui = gui;
344 
345  if (gCoreContext->IsFrontend())
346  m_needsBackend = true;
347 
348  // We don't have a database yet, so lets use the config.xml file.
349  m_pConfig = new XmlConfiguration("config.xml");
350 
351  // Creates screen saver control if we will have a GUI
352  if (gui)
353  m_ui = GetMythUI();
354 
355  // ---- database connection stuff ----
356 
357  if (!ignoreDB && !FindDatabase(promptForBackend, noPrompt))
358  {
359  EndTempWindow();
360  return false;
361  }
362 
363  // ---- keep all DB-using stuff below this line ----
364 
365  // Prompt for language if this is a first time install and
366  // we didn't already do so.
367  if (m_gui && !gCoreContext->GetDB()->HaveSchema())
368  {
369  TempMainWindow(false);
371  MythTranslation::load("mythfrontend");
372  }
375 
376  // Close GUI Startup Window.
377  if (m_guiStartup) {
379  if (mainStack)
380  mainStack->PopScreen(m_guiStartup, false);
381  m_guiStartup=nullptr;
382  }
383  EndTempWindow();
384 
385  if (gui)
386  {
387  MythUIMenuCallbacks cbs {};
389  cbs.exec_program_tv = exec_program_tv_cb;
390  cbs.configplugin = configplugin_cb;
391  cbs.plugin = plugin_cb;
392  cbs.eject = eject_cb;
393 
394  m_ui->Init(cbs);
395  }
396 
397  return true;
398 }
399 
412 bool MythContextPrivate::FindDatabase(bool prompt, bool noAutodetect)
413 {
414  // We can only prompt if autodiscovery is enabled..
415  bool manualSelect = prompt && !noAutodetect;
416 
417  QString failure;
418 
419  // 1. Either load config.xml or use sensible "localhost" defaults:
420  bool loaded = LoadDatabaseSettings();
421  DatabaseParams dbParamsFromFile = m_dbParams;
422 
423  // In addition to the UI chooser, we can also try to autoSelect later,
424  // but only if we're not doing manualSelect and there was no
425  // valid config.xml
426  bool autoSelect = !manualSelect && !loaded && !noAutodetect;
427 
428  // 2. If the user isn't forcing up the chooser UI, look for a default
429  // backend in config.xml, then test DB settings we've got so far:
430  if (!manualSelect)
431  {
432  // config.xml may contain a backend host UUID and PIN.
433  // If so, try to AutoDiscover UPnP server, and use its DB settings:
434 
435  if (DefaultUPnP(failure)) // Probably a valid backend,
436  autoSelect = manualSelect = false; // so disable any further UPnP
437  else
438  if (failure.length())
439  LOG(VB_GENERAL, LOG_ALERT, failure);
440 
441  failure = TestDBconnection(loaded);
442  if (failure.isEmpty())
443  goto DBfound;
445  return false;
447  autoSelect=true;
448  }
449 
450  // 3. Try to automatically find the single backend:
451  if (autoSelect)
452  {
453  int count = UPnPautoconf();
454 
455  if (count == 0)
456  failure = QObject::tr("No UPnP backends found", "Backend Setup");
457 
458  if (count == 1)
459  {
460  failure = TestDBconnection();
461  if (failure.isEmpty())
462  goto DBfound;
464  return false;
465  }
466 
467  // Multiple BEs, or needs PIN.
468  manualSelect |= (count > 1 || count == -1);
469  // Search requested
471  manualSelect=true;
472  }
473 
474  manualSelect &= m_gui; // no interactive command-line chooser yet
475 
476  // Queries the user for the DB info
477  do
478  {
479  if (manualSelect)
480  {
481  // Get the user to select a backend from a possible list:
482  auto d = (BackendSelection::Decision)ChooseBackend(failure);
483  switch (d)
484  {
486  break;
488  manualSelect = false;
489  break;
491  goto NoDBfound;
492  }
493  }
494 
495  if (!manualSelect)
496  {
497  if (!PromptForDatabaseParams(failure))
498  goto NoDBfound;
499  }
500  failure = TestDBconnection();
501  if (!failure.isEmpty())
502  LOG(VB_GENERAL, LOG_ALERT, failure);
504  return false;
506  manualSelect=true;
508  manualSelect=false;
509  }
510  while (!failure.isEmpty());
511 
512 DBfound:
513  LOG(VB_GENERAL, LOG_DEBUG, "FindDatabase() - Success!");
514  // If we got the database from UPNP then the wakeup settings are lost.
515  // Restore them.
516  m_dbParams.m_wolEnabled = dbParamsFromFile.m_wolEnabled;
517  m_dbParams.m_wolReconnect = dbParamsFromFile.m_wolReconnect;
518  m_dbParams.m_wolRetry = dbParamsFromFile.m_wolRetry;
519  m_dbParams.m_wolCommand = dbParamsFromFile.m_wolCommand;
520 
522  !loaded || m_dbParams.m_forceSave ||
523  dbParamsFromFile != m_dbParams);
524  EnableDBerrors();
525  ResetDatabase();
526  return true;
527 
528 NoDBfound:
529  LOG(VB_GENERAL, LOG_DEBUG, "FindDatabase() - failed");
530  return false;
531 }
532 
537 {
538  // try new format first
540 
541  m_dbParams.m_localHostName = m_pConfig->GetValue("LocalHostName", "");
544  m_dbParams.m_dbUserName = m_pConfig->GetValue(kDefaultDB + "UserName", "");
545  m_dbParams.m_dbPassword = m_pConfig->GetValue(kDefaultDB + "Password", "");
546  m_dbParams.m_dbName = m_pConfig->GetValue(kDefaultDB + "DatabaseName", "");
548 
550  m_pConfig->GetBoolValue(kDefaultWOL + "Enabled", false);
552  m_pConfig->GetValue(kDefaultWOL + "SQLReconnectWaitTime", 0);
554  m_pConfig->GetValue(kDefaultWOL + "SQLConnectRetry", 5);
556  m_pConfig->GetValue(kDefaultWOL + "Command", "");
557 
558  bool ok = m_dbParams.IsValid("config.xml");
559  if (!ok) // if new format fails, try legacy format
560  {
563  kDefaultMFE + "DBHostName", "");
565  kDefaultMFE + "DBUserName", "");
567  kDefaultMFE + "DBPassword", "");
569  kDefaultMFE + "DBName", "");
571  kDefaultMFE + "DBPort", 0);
572  m_dbParams.m_forceSave = true;
573  ok = m_dbParams.IsValid("config.xml");
574  }
575  if (!ok)
577 
578  gCoreContext->GetDB()->SetDatabaseParams(m_dbParams);
579 
581  if (hostname.isEmpty() ||
582  hostname == "my-unique-identifier-goes-here")
583  {
584  char localhostname[1024];
585  if (gethostname(localhostname, 1024))
586  {
587  LOG(VB_GENERAL, LOG_ALERT,
588  "MCP: Error, could not determine host name." + ENO);
589  localhostname[0] = '\0';
590  }
591 #ifdef Q_OS_ANDROID
592 #define ANDROID_EXCEPTION_CHECK \
593  if (env->ExceptionCheck()) { \
594  env->ExceptionClear(); \
595  exception=true; \
596  }
597  if (strcmp(localhostname, "localhost") == 0
598  || localhostname[0] == '\0')
599  {
600  hostname = "android";
601  bool exception=false;
602  QAndroidJniEnvironment env;
603  QAndroidJniObject myID = QAndroidJniObject::fromString("android_id");
604  QAndroidJniObject activity = QtAndroid::androidActivity();
606  QAndroidJniObject appctx = activity.callObjectMethod
607  ("getApplicationContext","()Landroid/content/Context;");
609  QAndroidJniObject contentR = appctx.callObjectMethod
610  ("getContentResolver", "()Landroid/content/ContentResolver;");
612  QAndroidJniObject androidId = QAndroidJniObject::callStaticObjectMethod
613  ("android/provider/Settings$Secure","getString",
614  "(Landroid/content/ContentResolver;Ljava/lang/String;)Ljava/lang/String;",
615  contentR.object<jobject>(),
616  myID.object<jstring>());
618  if (exception)
619  LOG(VB_GENERAL, LOG_ALERT,
620  "Java exception looking for android id");
621  else
622  hostname = QString("android-%1").arg(androidId.toString());
623  }
624  else
625  hostname = localhostname;
626 #else
627  hostname = localhostname;
628 #endif
629  LOG(VB_GENERAL, LOG_INFO, "Empty LocalHostName. This is typical.");
630  }
631  else
632  {
633  m_dbParams.m_localEnabled = true;
634  }
635 
636  LOG(VB_GENERAL, LOG_INFO, QString("Using a profile name of: '%1' (Usually the "
637  "same as this host's name.)")
638  .arg(hostname));
640 
641  return ok;
642 }
643 
645  const DatabaseParams &params, bool force)
646 {
647  bool ret = true;
648 
649  // only rewrite file if it has changed
650  if (params != m_dbParams || force)
651  {
653  "LocalHostName", params.m_localHostName);
654 
656  kDefaultDB + "PingHost", params.m_dbHostPing);
657 
658  // If dbHostName is an IPV6 address with scope,
659  // remove the scope. Unescaped % signs are an
660  // xml violation
661  QString dbHostName(params.m_dbHostName);
662  QHostAddress addr;
663  if (addr.setAddress(dbHostName))
664  {
665  addr.setScopeId(QString());
666  dbHostName = addr.toString();
667  }
669  kDefaultDB + "Host", dbHostName);
671  kDefaultDB + "UserName", params.m_dbUserName);
673  kDefaultDB + "Password", params.m_dbPassword);
675  kDefaultDB + "DatabaseName", params.m_dbName);
677  kDefaultDB + "Port", params.m_dbPort);
678 
680  kDefaultWOL + "Enabled", params.m_wolEnabled);
682  kDefaultWOL + "SQLReconnectWaitTime", params.m_wolReconnect);
684  kDefaultWOL + "SQLConnectRetry", params.m_wolRetry);
686  kDefaultWOL + "Command", params.m_wolCommand);
687 
688  // clear out any legacy nodes..
689  m_pConfig->ClearValue(kDefaultMFE + "DBHostName");
690  m_pConfig->ClearValue(kDefaultMFE + "DBUserName");
691  m_pConfig->ClearValue(kDefaultMFE + "DBPassword");
692  m_pConfig->ClearValue(kDefaultMFE + "DBName");
693  m_pConfig->ClearValue(kDefaultMFE + "DBPort");
694  m_pConfig->ClearValue(kDefaultMFE + "DBHostPing");
695 
696  // actually save the file
697  m_pConfig->Save();
698 
699  // Save the new settings:
700  m_dbParams = params;
701  gCoreContext->GetDB()->SetDatabaseParams(m_dbParams);
702 
703  // If database has changed, force its use:
704  ResetDatabase();
705  }
706  return ret;
707 }
708 
710 {
711  if (d && d->m_loop
712  && d->m_loop->isRunning())
713  d->m_loop->exit();
714 }
715 
716 
718 {
719  bool accepted = false;
720  if (m_gui)
721  {
722  TempMainWindow();
723 
724  // Tell the user what went wrong:
725  if (error.length())
727 
728  // ask user for database parameters
729 
730  EnableDBerrors();
732  auto *dbsetting = new DatabaseSettings();
733  auto *ssd = new StandardSettingDialog(mainStack, "databasesettings",
734  dbsetting);
735  if (ssd->Create())
736  {
737  mainStack->AddScreen(ssd);
738  connect(dbsetting, &DatabaseSettings::isClosing,
740  if (!m_loop->isRunning())
741  m_loop->exec();
742  }
743  else
744  delete ssd;
745  SilenceDBerrors();
746  EndTempWindow();
747  accepted = true;
748  }
749  else
750  {
752  QString response;
753  std::this_thread::sleep_for(std::chrono::seconds(1));
754  // give user chance to skip config
755  cout << endl << error.toLocal8Bit().constData() << endl << endl;
756  response = getResponse("Would you like to configure the database "
757  "connection now?",
758  "no");
759  if (!response.startsWith('y', Qt::CaseInsensitive))
760  return false;
761 
762  params.m_dbHostName = getResponse("Database host name:",
763  params.m_dbHostName);
764  response = getResponse("Should I test connectivity to this host "
765  "using the ping command?", "yes");
766  params.m_dbHostPing = response.startsWith('y', Qt::CaseInsensitive);
767 
768  params.m_dbPort = intResponse("Database non-default port:",
769  params.m_dbPort);
770  params.m_dbName = getResponse("Database name:",
771  params.m_dbName);
772  params.m_dbUserName = getResponse("Database user name:",
773  params.m_dbUserName);
774  params.m_dbPassword = getResponse("Database password:",
775  params.m_dbPassword);
776 
777  params.m_localHostName = getResponse("Unique identifier for this machine "
778  "(if empty, the local host name "
779  "will be used):",
780  params.m_localHostName);
781  params.m_localEnabled = !params.m_localHostName.isEmpty();
782 
783  response = getResponse("Would you like to use Wake-On-LAN to retry "
784  "database connections?",
785  (params.m_wolEnabled ? "yes" : "no"));
786  params.m_wolEnabled = response.startsWith('y', Qt::CaseInsensitive);
787 
788  if (params.m_wolEnabled)
789  {
790  params.m_wolReconnect = intResponse("Seconds to wait for "
791  "reconnection:",
792  params.m_wolReconnect);
793  params.m_wolRetry = intResponse("Number of times to retry:",
794  params.m_wolRetry);
795  params.m_wolCommand = getResponse("Command to use to wake server or server MAC address:",
796  params.m_wolCommand);
797  }
798 
799  accepted = m_parent->SaveDatabaseParams(params);
800  }
801  return accepted;
802 }
803 
810 {
811  QString err;
812  QString host;
813 
814  // Jan 20, 2017
815  // Changed to use port check instead of ping
816 
817  int port = 0;
818 
819  // 1 = db awake, 2 = db listening, 3 = db connects,
820  // 4 = backend awake, 5 = backend listening
821  // 9 = all ok, 10 = quit
822 
823  enum startupStates {
824  st_start = 0,
825  st_dbAwake = 1,
826  st_dbStarted = 2,
827  st_dbConnects = 3,
828  st_beWOL = 4,
829  st_beAwake = 5,
830  st_success = 6
831  } startupState = st_start;
832 
833  static const QString kGuiStatuses[7] =
834  {"start","dbAwake","dbStarted","dbConnects","beWOL","beAwake",
835  "success" };
836 
837  int msStartupScreenDelay = gCoreContext->GetNumSetting("StartupScreenDelay",2);
838  if (msStartupScreenDelay > 0)
839  msStartupScreenDelay *= 1000;
840  do
841  {
842  QElapsedTimer timer;
843  timer.start();
844  if (m_dbParams.m_dbHostName.isNull() && m_dbHostCp.length())
845  host = m_dbHostCp;
846  else
847  host = m_dbParams.m_dbHostName;
848  port = m_dbParams.m_dbPort;
849  if (port == 0)
850  port = 3306;
851  int wakeupTime = 3;
852  int attempts = 11;
853  if (m_dbParams.m_wolEnabled) {
854  wakeupTime = m_dbParams.m_wolReconnect;
855  attempts = m_dbParams.m_wolRetry + 1;
856  startupState = st_start;
857  }
858  else
859  startupState = st_dbAwake;
860  if (attempts < 6)
861  attempts = 6;
862  if (!prompt)
863  attempts=1;
864  if (wakeupTime < 5)
865  wakeupTime = 5;
866 
867  int progressTotal = wakeupTime * attempts;
868 
870  m_guiStartup->setTotal(progressTotal);
871 
872  QString beWOLCmd = QString();
873  QString backendIP = QString();
874  int backendPort = 0;
875  QString masterserver;
876 
877  for (int attempt = 0;
878  attempt < attempts && startupState != st_success;
879  ++attempt)
880  {
881  // The first time do everything with minimum timeout and
882  // no GUI, for the normal case where all is OK
883  // After that show the GUI (if this is a GUI program)
884 
885  LOG(VB_GENERAL, LOG_INFO,
886  QString("Start up testing connections. DB %1, BE %2, attempt %3, status %4, Delay: %5")
887  .arg(host).arg(backendIP).arg(attempt).arg(kGuiStatuses[startupState]).arg(msStartupScreenDelay) );
888 
889  int useTimeout = wakeupTime;
890  if (attempt == 0)
891  useTimeout=1;
892 
893  if (m_gui && !m_guiStartup)
894  {
895  if (msStartupScreenDelay==0 || timer.hasExpired(msStartupScreenDelay))
896  {
897  ShowGuiStartup();
898  if (m_guiStartup)
899  m_guiStartup->setTotal(progressTotal);
900  }
901  }
903  {
904  if (attempt > 0)
905  m_guiStartup->setStatusState(kGuiStatuses[startupState]);
906  m_guiStartup->setMessageState("empty");
907  processEvents();
908  }
909  switch (startupState) {
910  case st_start:
912  {
913  if (attempt > 0)
915  if (!checkPort(host, port, useTimeout))
916  break;
917  }
918  startupState = st_dbAwake;
919  [[clang::fallthrough]];
920  case st_dbAwake:
921  if (!checkPort(host, port, useTimeout))
922  break;
923  startupState = st_dbStarted;
924  [[clang::fallthrough]];
925  case st_dbStarted:
926  // If the database is connecting with link-local
927  // address, it may have changed
928  if (m_dbParams.m_dbHostName != host)
929  {
930  m_dbParams.m_dbHostName = host;
931  gCoreContext->GetDB()->SetDatabaseParams(m_dbParams);
932  }
933  EnableDBerrors();
934  ResetDatabase();
936  {
937  for (int temp = 0; temp < useTimeout * 2 ; temp++)
938  {
939  processEvents();
940  std::this_thread::sleep_for(std::chrono::milliseconds(500));
941  }
942  break;
943  }
944  startupState = st_dbConnects;
945  [[clang::fallthrough]];
946  case st_dbConnects:
947  if (m_needsBackend)
948  {
949  beWOLCmd = gCoreContext->GetSetting("WOLbackendCommand", "");
950  if (!beWOLCmd.isEmpty())
951  {
952  wakeupTime += gCoreContext->GetNumSetting
953  ("WOLbackendReconnectWaitTime", 0);
954  attempts += gCoreContext->GetNumSetting
955  ("WOLbackendConnectRetry", 0);
956  useTimeout = wakeupTime;
957  if (m_gui && !m_guiStartup && attempt == 0)
958  useTimeout=1;
959  progressTotal = wakeupTime * attempts;
961  m_guiStartup->setTotal(progressTotal);
962  startupState = st_beWOL;
963  }
964  }
965  else {
966  startupState = st_success;
967  break;
968  }
969  masterserver = gCoreContext->GetSetting
970  ("MasterServerName");
971  backendIP = gCoreContext->GetSettingOnHost
972  ("BackendServerAddr", masterserver);
973  backendPort = MythCoreContext::GetMasterServerPort();
974  [[clang::fallthrough]];
975  case st_beWOL:
976  if (!beWOLCmd.isEmpty()) {
977  if (attempt > 0)
978  MythWakeup(beWOLCmd);
979  if (!checkPort(backendIP, backendPort, useTimeout))
980  break;
981  }
982  startupState = st_beAwake;
983  [[clang::fallthrough]];
984  case st_beAwake:
985  if (!checkPort(backendIP, backendPort, useTimeout))
986  break;
987  startupState = st_success;
988  [[clang::fallthrough]];
989  case st_success:
990  // Quiet compiler warning.
991  break;
992  }
993  if (m_guiStartup)
994  {
995  if (m_guiStartup->m_Exit
998  || m_guiStartup->m_Retry)
999  break;
1000  }
1001  processEvents();
1002  }
1003  if (startupState == st_success)
1004  break;
1005 
1006  QString stateMsg = kGuiStatuses[startupState];
1007  stateMsg.append("Fail");
1008  LOG(VB_GENERAL, LOG_INFO,
1009  QString("Start up failure. host %1, status %2")
1010  .arg(host).arg(stateMsg));
1011 
1012  if (m_gui && !m_guiStartup)
1013  {
1014  ShowGuiStartup();
1015  if (m_guiStartup)
1016  m_guiStartup->setTotal(progressTotal);
1017  }
1018 
1019  if (m_guiStartup
1020  && !m_guiStartup->m_Exit
1021  && !m_guiStartup->m_Setup
1022  && !m_guiStartup->m_Search
1023  && !m_guiStartup->m_Retry)
1024  {
1026  m_guiStartup->setStatusState(stateMsg);
1027  m_guiStartup->setMessageState("makeselection");
1028  m_loop->exec();
1029  }
1030  }
1031  while (m_guiStartup && m_guiStartup->m_Retry);
1032 
1033  if (startupState < st_dbAwake)
1034  {
1035  LOG(VB_GENERAL, LOG_WARNING, QString("Pinging to %1 failed, database will be unavailable").arg(host));
1036  SilenceDBerrors();
1037  err = QObject::tr(
1038  "Cannot find (ping) database host %1 on the network",
1039  "Backend Setup");
1040  return err.arg(host);
1041  }
1042 
1043  if (startupState < st_dbConnects)
1044  {
1045  SilenceDBerrors();
1046  return QObject::tr("Cannot login to database", "Backend Setup");
1047  }
1048 
1049  if (startupState < st_success)
1050  {
1051  return QObject::tr("Cannot connect to backend", "Backend Setup");
1052  }
1053 
1054  // Current DB connection may have been silenced (invalid):
1055  EnableDBerrors();
1056  ResetDatabase();
1057 
1058  return QString();
1059 }
1060 
1061 // Show the Gui Startup window.
1062 // This is called if there is a delay in startup for any reason
1063 // such as the database being unavailable
1065 {
1066  if (!m_gui)
1067  return;
1068  TempMainWindow(false);
1069  MythMainWindow *mainWindow = GetMythMainWindow();
1070  MythScreenStack *mainStack = mainWindow->GetMainStack();
1071  if (mainStack) {
1072  if (!m_guiStartup) {
1073  m_guiStartup = new GUIStartup(mainStack,m_loop);
1074  if (!m_guiStartup->Create()) {
1075  delete m_guiStartup;
1076  m_guiStartup = nullptr;
1077  }
1078  if (m_guiStartup) {
1079  mainStack->AddScreen(m_guiStartup, false);
1080  processEvents();
1081  }
1082  }
1083  }
1084 }
1085 
1095 {
1096  // This silences any DB errors from Get*Setting(),
1097  // (which is the vast majority of them)
1098  gCoreContext->GetDB()->SetSuppressDBMessages(true);
1099 
1100  // Save the configured hostname, so that we can
1101  // still display it in the DatabaseSettings screens
1102  if (m_dbParams.m_dbHostName.length())
1104 
1105  m_dbParams.m_dbHostName.clear();
1106  gCoreContext->GetDB()->SetDatabaseParams(m_dbParams);
1107 }
1108 
1110 {
1111  // Restore (possibly) blanked hostname
1112  if (m_dbParams.m_dbHostName.isNull() && m_dbHostCp.length())
1113  {
1115  gCoreContext->GetDB()->SetDatabaseParams(m_dbParams);
1116  }
1117 
1118  gCoreContext->GetDB()->SetSuppressDBMessages(false);
1119 }
1120 
1121 
1134 {
1136  gCoreContext->GetDB()->SetDatabaseParams(m_dbParams);
1138 }
1139 
1144 {
1145  TempMainWindow();
1146 
1147  // Tell the user what went wrong:
1148  if (!error.isEmpty())
1149  {
1150  LOG(VB_GENERAL, LOG_ERR, QString("Error: %1").arg(error));
1151  ShowOkPopup(error);
1152  }
1153 
1154  LOG(VB_GENERAL, LOG_INFO, "Putting up the UPnP backend chooser");
1155 
1158 
1159  EndTempWindow();
1160 
1161  return (int)ret;
1162 }
1163 
1170 int MythContextPrivate::UPnPautoconf(const int milliSeconds)
1171 {
1172  LOG(VB_GENERAL, LOG_INFO, QString("UPNP Search %1 secs")
1173  .arg(milliSeconds / 1000));
1174 
1175  SSDP::Instance()->PerformSearch(kBackendURI, milliSeconds / 1000);
1176 
1177  // Search for a total of 'milliSeconds' ms, sending new search packet
1178  // about every 250 ms until less than one second remains.
1179  MythTimer totalTime; totalTime.start();
1180  MythTimer searchTime; searchTime.start();
1181  while (totalTime.elapsed() < milliSeconds)
1182  {
1183  usleep(25000);
1184  int ttl = milliSeconds - totalTime.elapsed();
1185  if ((searchTime.elapsed() > 249) && (ttl > 1000))
1186  {
1187  LOG(VB_GENERAL, LOG_INFO, QString("UPNP Search %1 secs")
1188  .arg(ttl / 1000));
1189  SSDP::Instance()->PerformSearch(kBackendURI, ttl / 1000);
1190  searchTime.start();
1191  }
1192  }
1193 
1195 
1196  if (!backends)
1197  {
1198  LOG(VB_GENERAL, LOG_INFO, "No UPnP backends found");
1199  return 0;
1200  }
1201 
1202  int count = backends->Count();
1203  if (count)
1204  {
1205  LOG(VB_GENERAL, LOG_INFO,
1206  QString("Found %1 UPnP backends").arg(count));
1207  }
1208  else
1209  {
1210  LOG(VB_GENERAL, LOG_ERR,
1211  "No UPnP backends found, but SSDP::Find() not NULL");
1212  }
1213 
1214  if (count != 1)
1215  {
1216  backends->DecrRef();
1217  return count;
1218  }
1219 
1220  // Get this backend's location:
1221  DeviceLocation *BE = backends->GetFirst();
1222  backends->DecrRef();
1223  backends = nullptr;
1224 
1225  // We don't actually know the backend's access PIN, so this will
1226  // only work for ones that have PIN access disabled (i.e. 0000)
1227  int ret = (UPnPconnect(BE, QString())) ? 1 : -1;
1228 
1229  BE->DecrRef();
1230 
1231  return ret;
1232 }
1233 
1240 {
1241  QString loc = "DefaultUPnP() - ";
1242  QString PIN = m_pConfig->GetValue(kDefaultPIN, "");
1243  QString USN = m_pConfig->GetValue(kDefaultUSN, "");
1244 
1245  if (USN.isEmpty())
1246  {
1247  LOG(VB_UPNP, LOG_INFO, loc + "No default UPnP backend");
1248  return false;
1249  }
1250 
1251  LOG(VB_UPNP, LOG_INFO, loc + "config.xml has default " +
1252  QString("PIN '%1' and host USN: %2") .arg(PIN).arg(USN));
1253 
1254  // ----------------------------------------------------------------------
1255 
1256  int timeout_ms = 2000;
1257  LOG(VB_GENERAL, LOG_INFO, QString("UPNP Search up to %1 secs")
1258  .arg(timeout_ms / 1000));
1259  SSDP::Instance()->PerformSearch(kBackendURI, timeout_ms / 1000);
1260 
1261  // ----------------------------------------------------------------------
1262  // We need to give the server time to respond...
1263  // ----------------------------------------------------------------------
1264 
1265  DeviceLocation *pDevLoc = nullptr;
1266  MythTimer totalTime; totalTime.start();
1267  MythTimer searchTime; searchTime.start();
1268  while (totalTime.elapsed() < timeout_ms)
1269  {
1270  pDevLoc = SSDP::Find( kBackendURI, USN );
1271 
1272  if (pDevLoc)
1273  break;
1274 
1275  usleep(25000);
1276 
1277  int ttl = timeout_ms - totalTime.elapsed();
1278  if ((searchTime.elapsed() > 249) && (ttl > 1000))
1279  {
1280  LOG(VB_GENERAL, LOG_INFO, QString("UPNP Search up to %1 secs")
1281  .arg(ttl / 1000));
1282  SSDP::Instance()->PerformSearch(kBackendURI, ttl / 1000);
1283  searchTime.start();
1284  }
1285  }
1286 
1287  // ----------------------------------------------------------------------
1288 
1289  if (!pDevLoc)
1290  {
1291  error = "Cannot find default UPnP backend";
1292  return false;
1293  }
1294 
1295  if (UPnPconnect(pDevLoc, PIN))
1296  {
1297  pDevLoc->DecrRef();
1298  return true;
1299  }
1300 
1301  pDevLoc->DecrRef();
1302 
1303  error = "Cannot connect to default backend via UPnP. Wrong saved PIN?";
1304  return false;
1305 }
1306 
1311  const QString &PIN)
1312 {
1313  QString error;
1314  QString loc = "UPnPconnect() - ";
1315  QString URL = backend->m_sLocation;
1316  MythXMLClient client(URL);
1317 
1318  LOG(VB_UPNP, LOG_INFO, loc + QString("Trying host at %1").arg(URL));
1319  switch (client.GetConnectionInfo(PIN, &m_dbParams, error))
1320  {
1321  case UPnPResult_Success:
1322  gCoreContext->GetDB()->SetDatabaseParams(m_dbParams);
1323  LOG(VB_UPNP, LOG_INFO, loc +
1324  "Got database hostname: " + m_dbParams.m_dbHostName);
1325  return true;
1326 
1328  // The stored PIN is probably not correct.
1329  // We could prompt for the PIN and try again, but that needs a UI.
1330  // Easier to fail for now, and put up the full UI selector later
1331  LOG(VB_UPNP, LOG_ERR, loc + "Wrong PIN?");
1332  return false;
1333 
1334  default:
1335  LOG(VB_UPNP, LOG_ERR, loc + error);
1336  break;
1337  }
1338 
1339  // This backend may have a local DB with the default user/pass/DBname.
1340  // For whatever reason, we have failed to get anything back via UPnP,
1341  // so we might as well try the database directly as a last resort.
1342  QUrl theURL(URL);
1343  URL = theURL.host();
1344  if (URL.isEmpty())
1345  return false;
1346 
1347  LOG(VB_UPNP, LOG_INFO, "Trying default DB credentials at " + URL);
1349 
1350  return true;
1351 }
1352 
1354 {
1355  if (e->type() == MythEvent::MythEventMessage)
1356  {
1357  if (m_disableeventpopup)
1358  return true;
1359 
1361  {
1363  }
1364 
1365  auto *me = dynamic_cast<MythEvent*>(e);
1366  if (me == nullptr)
1367  return true;
1368 
1369  if (me->Message() == "VERSION_MISMATCH" && (1 == me->ExtraDataCount()))
1370  ShowVersionMismatchPopup(me->ExtraData(0).toUInt());
1371  else if (me->Message() == "CONNECTION_FAILURE")
1373  else if (me->Message() == "PERSISTENT_CONNECTION_FAILURE")
1375  else if (me->Message() == "CONNECTION_RESTABLISHED")
1377  return true;
1378  }
1379 
1380  return QObject::event(e);
1381 }
1382 
1384 {
1385  QDateTime now = MythDate::current();
1386 
1387  if (!GetNotificationCenter() || !m_ui || !m_ui->IsScreenSetup())
1388  return;
1389 
1390  if (m_lastCheck.isValid() && now < m_lastCheck)
1391  return;
1392 
1393  // When WOL is disallowed, standy mode,
1394  // we should not show connection failures.
1395  if (!gCoreContext->IsWOLAllowed())
1396  return;
1397 
1398  m_lastCheck = now.addMSecs(5000); // don't refresh notification more than every 5s
1399 
1400  QString description = (persistent) ?
1401  QObject::tr(
1402  "The connection to the master backend "
1403  "server has gone away for some reason. "
1404  "Is it running?") :
1405  QObject::tr(
1406  "Could not connect to the master backend server. Is "
1407  "it running? Is the IP address set for it in "
1408  "mythtv-setup correct?");
1409 
1410  QString message = QObject::tr("Could not connect to master backend");
1411  MythErrorNotification n(message, _Location, description);
1412  n.SetId(m_registration);
1413  n.SetParent(this);
1415 }
1416 
1418 {
1419  if (!GetNotificationCenter())
1420  return;
1421 
1422  if (!m_lastCheck.isValid())
1423  return;
1424 
1425  MythCheckNotification n(QObject::tr("Backend is online"), _Location);
1426  n.SetId(m_registration);
1427  n.SetParent(this);
1428  n.SetDuration(5);
1430  m_lastCheck = QDateTime();
1431 }
1432 
1434 {
1435  if (m_mbeVersionPopup)
1436  return;
1437 
1438  QString message =
1439  QObject::tr(
1440  "The server uses network protocol version %1, "
1441  "but this client only understands version %2. "
1442  "Make sure you are running compatible versions of "
1443  "the backend and frontend.")
1444  .arg(remote_version).arg(MYTH_PROTO_VERSION);
1445 
1446  if (HasMythMainWindow() && m_ui && m_ui->IsScreenSetup())
1447  {
1449  message, m_sh, SLOT(VersionMismatchPopupClosed()));
1450  }
1451  else
1452  {
1453  LOG(VB_GENERAL, LOG_ERR, LOC + message);
1454  qApp->exit(GENERIC_EXIT_SOCKET_ERROR);
1455  }
1456 }
1457 
1458 // Process Events while waiting for connection
1459 // return true if progress is 100%
1461 {
1462 // bool ret = false;
1463 // if (m_guiStartup)
1464 // ret = m_guiStartup->updateProgress();
1465  qApp->processEvents(QEventLoop::AllEvents, 250);
1466  qApp->processEvents(QEventLoop::AllEvents, 250);
1467 // return ret;
1468 }
1469 
1470 // cache some settings in cache/contextcache.xml
1471 // only call this if the database is available.
1472 
1473 const QString MythContextPrivate::kSettingsToSave[] =
1474 { "Theme", "Language", "Country", "GuiHeight",
1475  "GuiOffsetX", "GuiOffsetY", "GuiWidth", "RunFrontendInWindow",
1476  "AlwaysOnTop", "HideMouseCursor", "ThemePainter", "libCECEnabled",
1477  "StartupScreenDelay" };
1478 
1479 
1481 {
1482  if (!m_gui)
1483  return true;
1484  QString cacheDirName = GetConfDir() + "/cache/";
1485  QDir dir(cacheDirName);
1486  dir.mkpath(cacheDirName);
1487  XmlConfiguration config = XmlConfiguration("cache/contextcache.xml");
1488  for (const auto & setting : kSettingsToSave)
1489  {
1490  QString cacheValue = config.GetValue("Settings/"+setting,QString());
1492  QString value = gCoreContext->GetSetting(setting,QString());
1493  if (value != cacheValue)
1494  {
1495  config.SetValue("Settings/"+setting,value);
1496  m_settingsCacheDirty = true;
1497  }
1498  }
1500  return config.Save();
1501 }
1502 
1504 {
1505  if (!m_gui)
1506  return;
1507  XmlConfiguration config = XmlConfiguration("cache/contextcache.xml");
1508  for (const auto & setting : kSettingsToSave)
1509  {
1510  if (!gCoreContext->GetSetting(setting,QString()).isEmpty())
1511  continue;
1512  QString value = config.GetValue("Settings/"+setting,QString());
1513  if (!value.isEmpty())
1514  gCoreContext->OverrideSettingForSession(setting, value);
1515  }
1516  // Prevent power off TV after temporary window
1517  gCoreContext->OverrideSettingForSession("PowerOffTVAllowed", nullptr);
1518 
1519  QString language = gCoreContext->GetSetting("Language",QString());
1520  MythTranslation::load("mythfrontend");
1521 }
1522 
1524 {
1525  QString language = gCoreContext->GetSetting("Language",QString());
1526  for (const auto & setting : kSettingsToSave)
1528  // Restore power off TV setting
1529  gCoreContext->ClearOverrideSettingForSession("PowerOffTVAllowed");
1530 
1531  if (language != gCoreContext->GetSetting("Language",QString()))
1532  MythTranslation::load("mythfrontend");
1533 }
1534 
1535 
1537 {
1538  d->m_mbeVersionPopup = nullptr;
1539  qApp->exit(GENERIC_EXIT_SOCKET_ERROR);
1540 }
1541 
1542 MythContext::MythContext(QString binversion, bool needsBackend)
1543  : m_appBinaryVersion(std::move(binversion))
1544 {
1545 #ifdef _WIN32
1546  static bool WSAStarted = false;
1547  if (!WSAStarted) {
1548  WSADATA wsadata;
1549  int res = WSAStartup(MAKEWORD(2, 0), &wsadata);
1550  LOG(VB_SOCKET, LOG_INFO,
1551  QString("WSAStartup returned %1").arg(res));
1552  }
1553 #endif
1554 
1555  d = new MythContextPrivate(this);
1556  d->m_needsBackend = needsBackend;
1557 
1559 
1560  if (!gCoreContext || !gCoreContext->Init())
1561  {
1562  LOG(VB_GENERAL, LOG_EMERG, LOC + "Unable to allocate MythCoreContext");
1563  qApp->exit(GENERIC_EXIT_NO_MYTHCONTEXT);
1564  }
1565 }
1566 
1567 bool MythContext::Init(const bool gui,
1568  const bool promptForBackend,
1569  const bool disableAutoDiscovery,
1570  const bool ignoreDB)
1571 {
1572  if (!d)
1573  {
1574  LOG(VB_GENERAL, LOG_EMERG, LOC + "Init() Out-of-memory");
1575  return false;
1576  }
1577 
1578  SetDisableEventPopup(true);
1579 
1581  {
1582  LOG(VB_GENERAL, LOG_EMERG,
1583  QString("Application binary version (%1) does not "
1584  "match libraries (%2)")
1586 
1587  QString warning = QObject::tr(
1588  "This application is not compatible "
1589  "with the installed MythTV libraries.");
1590  if (gui)
1591  {
1592  d->TempMainWindow(false);
1593  ShowOkPopup(warning);
1594  }
1595  LOG(VB_GENERAL, LOG_WARNING, warning);
1596 
1597  return false;
1598  }
1599 
1600 #ifdef _WIN32
1601  // HOME environment variable might not be defined
1602  // some libraries will fail without it
1603  QString home = getenv("HOME");
1604  if (home.isEmpty())
1605  {
1606  home = getenv("LOCALAPPDATA"); // Vista
1607  if (home.isEmpty())
1608  home = getenv("APPDATA"); // XP
1609  if (home.isEmpty())
1610  home = QString("."); // getenv("TEMP")?
1611 
1612  _putenv(QString("HOME=%1").arg(home).toLocal8Bit().constData());
1613  }
1614 #endif
1615 
1616  // If HOME isn't defined, we won't be able to use default confdir of
1617  // $HOME/.mythtv nor can we rely on a MYTHCONFDIR that references $HOME
1618  QString homedir = QDir::homePath();
1619  QString confdir = getenv("MYTHCONFDIR");
1620  if ((homedir.isEmpty() || homedir == "/") &&
1621  (confdir.isEmpty() || confdir.contains("$HOME")))
1622  {
1623  QString warning = "Cannot locate your home directory."
1624  " Please set the environment variable HOME";
1625  if (gui)
1626  {
1627  d->TempMainWindow(false);
1628  ShowOkPopup(warning);
1629  }
1630  LOG(VB_GENERAL, LOG_WARNING, warning);
1631 
1632  return false;
1633  }
1634 
1635  if (!d->Init(gui, promptForBackend, disableAutoDiscovery, ignoreDB))
1636  {
1637  return false;
1638  }
1639 
1640  SetDisableEventPopup(false);
1642  if (d->m_settingsCacheDirty)
1643  {
1644 #ifndef Q_OS_ANDROID
1646 #endif
1647  d->m_settingsCacheDirty = false;
1648  }
1650 
1651  return true;
1652 }
1653 
1655 {
1656  if (MThreadPool::globalInstance()->activeThreadCount())
1657  LOG(VB_GENERAL, LOG_INFO, "Waiting for threads to exit.");
1658 
1660  logStop();
1661 
1662  SSDP::Shutdown();
1664 
1665  delete gCoreContext;
1666  gCoreContext = nullptr;
1667 
1668  delete d;
1669 }
1670 
1672 {
1673  d->m_disableeventpopup = check;
1674 }
1675 
1677 {
1678  return d->m_dbParams;
1679 }
1680 
1682 {
1683  return d->SaveDatabaseParams(params, false);
1684 }
1685 
1687 {
1688  return d->saveSettingsCache();
1689 }
1690 
1691 /* vim: set expandtab tabstop=4 shiftwidth=4: */
void SaveLocaleDefaults(void)
static MythMainWindow * getMainWindow(bool useDB=true)
Return the existing main window, or create one.
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:73
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:30
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
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:179
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:72
static void plugin_cb(const QString &cmd)
void PerformSearch(const QString &sST, uint timeout_secs=2)
Definition: ssdp.cpp:201
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
const QString kBackendURI
Service type for the backend's UPnP server.
Definition: backendselect.h:21
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:852
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:233
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 Init(bool gui=true, bool promptForBackend=false, bool disableAutoDiscovery=false, bool ignoreDB=false)
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)
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)
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:161
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?