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 <queue>
18 #include <algorithm>
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 "mythxdisplay.h"
33 #include "mythevent.h"
34 #include "dbutil.h"
35 #include "DisplayRes.h"
36 #include "mythmediamonitor.h"
37 
38 #include "mythdb.h"
39 #include "mythdirs.h"
40 #include "mythversion.h"
41 #include "mythdialogbox.h"
42 #include "mythmainwindow.h"
43 #include "mythuihelper.h"
44 #include "mythimage.h"
45 #include "mythxmlclient.h"
46 #include "upnp.h"
47 #include "mythlogging.h"
48 #include "mythsystemlegacy.h"
49 #include "mythmiscutil.h"
50 
51 #include "mythplugin.h"
52 #include "portchecker.h"
53 #include "guistartup.h"
54 
55 #include <unistd.h> // for usleep(), gethostname
56 
57 #ifdef _WIN32
58 #include "compat.h"
59 #endif
60 
61 #define LOC QString("MythContext: ")
62 
63 MythContext *gContext = nullptr;
64 
65 static const QString _Location = "MythContext";
66 
67 class MythContextPrivate : public QObject
68 {
69  friend class MythContextSlotHandler;
70 
71  public:
74 
75  bool Init (const bool gui,
76  const bool prompt, const bool noPrompt,
77  const bool ignoreDB);
78  bool FindDatabase(const bool prompt, const bool noPrompt);
79 
80  void TempMainWindow(bool languagePrompt = true);
81  void EndTempWindow(void);
82 
83  bool LoadDatabaseSettings(void);
84  bool SaveDatabaseParams(const DatabaseParams &params, bool force);
85 
86  bool PromptForDatabaseParams(const QString &error);
87  QString TestDBconnection(bool prompt=true);
88  void SilenceDBerrors(void);
89  void EnableDBerrors(void);
90  void ResetDatabase(void);
91 
92  int ChooseBackend(const QString &error);
93  int UPnPautoconf(const int milliSeconds = 2000);
94  bool DefaultUPnP(QString &error);
95  bool UPnPconnect(const DeviceLocation *device, const QString &PIN);
96  void ShowGuiStartup(void);
97  bool checkPort(QString &host, int port, int wakeupTime);
98  void processEvents(void);
99  bool saveSettingsCache(void);
100  void loadSettingsCacheOverride(void);
101  void clearSettingsCacheOverride(void);
102 
103 
104  protected:
105  bool event(QEvent*) override; // QObject
106 
107  void ShowConnectionFailurePopup(bool persistent);
108  void HideConnectionFailurePopup(void);
109 
110  void ShowVersionMismatchPopup(uint remoteVersion);
111 
112  public slots:
113  void OnCloseDialog();
114 
115  public:
117 
118  bool m_gui;
119 
121 
123  QString m_DBhostCp;
124 
126 
128 
132  QEventLoop *m_loop;
135 
136  private:
139  QDateTime m_lastCheck;
140  QTcpSocket *m_socket;
141  static const QString settingsToSave[];
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;
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  : parent(lparent),
242  m_gui(false),
243  m_pConfig(nullptr),
244  disableeventpopup(false),
245  m_ui(nullptr),
246  m_sh(new MythContextSlotHandler(this)),
247  m_guiStartup(nullptr),
248  needsBackend(false),
249  m_settingsCacheDirty(false),
250  MBEversionPopup(nullptr),
251  m_registration(-1),
252  m_socket(nullptr)
253 {
254  m_loop = new QEventLoop(this);
256 }
257 
259 {
260  if (m_pConfig)
261  delete m_pConfig;
263  {
265  }
266 
267  if (m_loop)
268  {
269  delete m_loop;
270  }
271 
272  if (m_ui)
273  DestroyMythUI();
274  if (m_sh)
275  m_sh->deleteLater();
276 }
277 
288 void MythContextPrivate::TempMainWindow(bool languagePrompt)
289 {
290  if (HasMythMainWindow())
291  return;
292 
293  SilenceDBerrors();
294 
295 #ifdef Q_OS_MAC
296  // Qt 4.4 has window-focus problems
297  gCoreContext->OverrideSettingForSession("RunFrontendInWindow", "1");
298 #endif
299  GetMythUI()->Init();
300  GetMythUI()->LoadQtConfig();
301 
302  MythMainWindow *mainWindow = MythMainWindow::getMainWindow(false);
303  mainWindow->Init();
304 
305  if (languagePrompt)
306  {
307  // ask user for language settings
309  MythTranslation::load("mythfrontend");
310  }
311 }
312 
314 {
315  if (HasMythMainWindow())
316  {
318  {
320  if (mainStack) {
321  mainStack->PopScreen(m_guiStartup, false);
322  m_guiStartup = nullptr;
323  }
324  }
325  }
326  EnableDBerrors();
327 }
328 
338 bool MythContextPrivate::checkPort(QString &host, int port, int timeLimit)
339 {
340  PortChecker checker;
341  if (m_guiStartup)
342  QObject::connect(m_guiStartup,SIGNAL(cancelPortCheck()),&checker,SLOT(cancelPortCheck()));
343  return checker.checkPort(host, port, timeLimit*1000);
344 }
345 
346 
347 bool MythContextPrivate::Init(const bool gui,
348  const bool promptForBackend,
349  const bool noPrompt,
350  const bool ignoreDB)
351 {
352  gCoreContext->GetDB()->IgnoreDatabase(ignoreDB);
353  m_gui = gui;
355 
356  if (gCoreContext->IsFrontend())
357  needsBackend = true;
358 
359  // We don't have a database yet, so lets use the config.xml file.
360  m_pConfig = new XmlConfiguration("config.xml");
361 
362  // Creates screen saver control if we will have a GUI
363  if (gui)
364  m_ui = GetMythUI();
365 
366  // ---- database connection stuff ----
367 
368  if (!ignoreDB && !FindDatabase(promptForBackend, noPrompt))
369  {
370  EndTempWindow();
371  return false;
372  }
373 
374  // ---- keep all DB-using stuff below this line ----
375 
376  // Prompt for language if this is a first time install and
377  // we didn't already do so.
378  if (m_gui && !gCoreContext->GetDB()->HaveSchema())
379  {
380  TempMainWindow(false);
382  MythTranslation::load("mythfrontend");
383  }
386 
387  // Close GUI Startup Window.
388  if (m_guiStartup) {
390  if (mainStack)
391  mainStack->PopScreen(m_guiStartup, false);
392  m_guiStartup=nullptr;
393  }
394  EndTempWindow();
395 
396  if (gui)
397  {
402  cbs.plugin = plugin_cb;
403  cbs.eject = eject_cb;
404 
405  m_ui->Init(cbs);
406  }
407 
408  return true;
409 }
410 
423 bool MythContextPrivate::FindDatabase(bool prompt, bool noAutodetect)
424 {
425  // We can only prompt if autodiscovery is enabled..
426  bool manualSelect = prompt && !noAutodetect;
427 
428  QString failure;
429 
430  // 1. Either load config.xml or use sensible "localhost" defaults:
431  bool loaded = LoadDatabaseSettings();
432  DatabaseParams dbParamsFromFile = m_DBparams;
433 
434  // In addition to the UI chooser, we can also try to autoSelect later,
435  // but only if we're not doing manualSelect and there was no
436  // valid config.xml
437  bool autoSelect = !manualSelect && !loaded && !noAutodetect;
438 
439  // 2. If the user isn't forcing up the chooser UI, look for a default
440  // backend in config.xml, then test DB settings we've got so far:
441  if (!manualSelect)
442  {
443  // config.xml may contain a backend host UUID and PIN.
444  // If so, try to AutoDiscover UPnP server, and use its DB settings:
445 
446  if (DefaultUPnP(failure)) // Probably a valid backend,
447  autoSelect = manualSelect = false; // so disable any further UPnP
448  else
449  if (failure.length())
450  LOG(VB_GENERAL, LOG_ALERT, failure);
451 
452  failure = TestDBconnection(loaded);
453  if (failure.isEmpty())
454  goto DBfound;
456  return false;
458  autoSelect=true;
459  }
460 
461  // 3. Try to automatically find the single backend:
462  if (autoSelect)
463  {
464  int count = UPnPautoconf();
465 
466  if (count == 0)
467  failure = QObject::tr("No UPnP backends found", "Backend Setup");
468 
469  if (count == 1)
470  {
471  failure = TestDBconnection();
472  if (failure.isEmpty())
473  goto DBfound;
475  return false;
476  }
477 
478  // Multiple BEs, or needs PIN.
479  manualSelect |= (count > 1 || count == -1);
480  // Search requested
482  manualSelect=true;
483  }
484 
485  manualSelect &= m_gui; // no interactive command-line chooser yet
486 
487  // Queries the user for the DB info
488  do
489  {
490  if (manualSelect)
491  {
492  // Get the user to select a backend from a possible list:
494  ChooseBackend(failure);
495  switch (d)
496  {
498  break;
500  manualSelect = false;
501  break;
503  goto NoDBfound;
504  }
505  }
506 
507  if (!manualSelect)
508  {
509  if (!PromptForDatabaseParams(failure))
510  goto NoDBfound;
511  }
512  failure = TestDBconnection();
513  if (!failure.isEmpty())
514  LOG(VB_GENERAL, LOG_ALERT, failure);
516  return false;
518  manualSelect=true;
520  manualSelect=false;
521  }
522  while (!failure.isEmpty());
523 
524 DBfound:
525  LOG(VB_GENERAL, LOG_DEBUG, "FindDatabase() - Success!");
526  // If we got the database from UPNP then the wakeup settings are lost.
527  // Restore them.
528  m_DBparams.wolEnabled = dbParamsFromFile.wolEnabled;
529  m_DBparams.wolReconnect = dbParamsFromFile.wolReconnect;
530  m_DBparams.wolRetry = dbParamsFromFile.wolRetry;
531  m_DBparams.wolCommand = dbParamsFromFile.wolCommand;
532 
534  !loaded || m_DBparams.forceSave ||
535  dbParamsFromFile != m_DBparams);
536  EnableDBerrors();
537  ResetDatabase();
538  return true;
539 
540 NoDBfound:
541  LOG(VB_GENERAL, LOG_DEBUG, "FindDatabase() - failed");
542  return false;
543 }
544 
549 {
550  // try new format first
552 
553  m_DBparams.localHostName = m_pConfig->GetValue("LocalHostName", "");
554  m_DBparams.dbHostPing = m_pConfig->GetValue(kDefaultDB + "PingHost", true);
556  m_DBparams.dbUserName = m_pConfig->GetValue(kDefaultDB + "UserName", "");
557  m_DBparams.dbPassword = m_pConfig->GetValue(kDefaultDB + "Password", "");
558  m_DBparams.dbName = m_pConfig->GetValue(kDefaultDB + "DatabaseName", "");
560 
562  m_pConfig->GetValue(kDefaultWOL + "Enabled", false);
564  m_pConfig->GetValue(kDefaultWOL + "SQLReconnectWaitTime", 0);
566  m_pConfig->GetValue(kDefaultWOL + "SQLConnectRetry", 5);
568  m_pConfig->GetValue(kDefaultWOL + "Command", "");
569 
570  bool ok = m_DBparams.IsValid("config.xml");
571  if (!ok) // if new format fails, try legacy format
572  {
575  kDefaultMFE + "DBHostName", "");
577  kDefaultMFE + "DBUserName", "");
579  kDefaultMFE + "DBPassword", "");
581  kDefaultMFE + "DBName", "");
583  kDefaultMFE + "DBPort", 0);
584  m_DBparams.forceSave = true;
585  ok = m_DBparams.IsValid("config.xml");
586  }
587  if (!ok)
589 
590  gCoreContext->GetDB()->SetDatabaseParams(m_DBparams);
591 
593  if (hostname.isEmpty() ||
594  hostname == "my-unique-identifier-goes-here")
595  {
596  char localhostname[1024];
597  if (gethostname(localhostname, 1024))
598  {
599  LOG(VB_GENERAL, LOG_ALERT,
600  "MCP: Error, could not determine host name." + ENO);
601  localhostname[0] = '\0';
602  }
603 #ifdef Q_OS_ANDROID
604 #define ANDROID_EXCEPTION_CHECK \
605  if (env->ExceptionCheck()) { \
606  env->ExceptionClear(); \
607  exception=true; \
608  }
609  if (strcmp(localhostname, "localhost") == 0
610  || localhostname[0] == '\0')
611  {
612  hostname = "android";
613  bool exception=false;
614  QAndroidJniEnvironment env;
615  QAndroidJniObject myID = QAndroidJniObject::fromString("android_id");
616  QAndroidJniObject activity = QtAndroid::androidActivity();
617  ANDROID_EXCEPTION_CHECK;
618  QAndroidJniObject appctx = activity.callObjectMethod
619  ("getApplicationContext","()Landroid/content/Context;");
620  ANDROID_EXCEPTION_CHECK;
621  QAndroidJniObject contentR = appctx.callObjectMethod
622  ("getContentResolver", "()Landroid/content/ContentResolver;");
623  ANDROID_EXCEPTION_CHECK;
624  QAndroidJniObject androidId = QAndroidJniObject::callStaticObjectMethod
625  ("android/provider/Settings$Secure","getString",
626  "(Landroid/content/ContentResolver;Ljava/lang/String;)Ljava/lang/String;",
627  contentR.object<jobject>(),
628  myID.object<jstring>());
629  ANDROID_EXCEPTION_CHECK;
630  if (exception)
631  LOG(VB_GENERAL, LOG_ALERT,
632  "Java exception looking for android id");
633  else
634  hostname = QString("android-%1").arg(androidId.toString());
635  }
636  else
637  hostname = localhostname;
638 #else
639  hostname = localhostname;
640 #endif
641  LOG(VB_GENERAL, LOG_INFO, "Empty LocalHostName. This is typical.");
642  }
643  else
644  {
645  m_DBparams.localEnabled = true;
646  }
647 
648  LOG(VB_GENERAL, LOG_INFO, QString("Using a profile name of: '%1' (Usually the "
649  "same as this host's name.)")
650  .arg(hostname));
652 
653  return ok;
654 }
655 
657  const DatabaseParams &params, bool force)
658 {
659  bool ret = true;
660 
661  // only rewrite file if it has changed
662  if (params != m_DBparams || force)
663  {
665  "LocalHostName", params.localHostName);
666 
668  kDefaultDB + "PingHost", params.dbHostPing);
669 
670  // If dbHostName is an IPV6 address with scope,
671  // remove the scope. Unescaped % signs are an
672  // xml violation
673  QString dbHostName(params.dbHostName);
674  QHostAddress addr;
675  if (addr.setAddress(dbHostName))
676  {
677  addr.setScopeId(QString());
678  dbHostName = addr.toString();
679  }
681  kDefaultDB + "Host", dbHostName);
683  kDefaultDB + "UserName", params.dbUserName);
685  kDefaultDB + "Password", params.dbPassword);
687  kDefaultDB + "DatabaseName", params.dbName);
689  kDefaultDB + "Port", params.dbPort);
690 
692  kDefaultWOL + "Enabled", params.wolEnabled);
694  kDefaultWOL + "SQLReconnectWaitTime", params.wolReconnect);
696  kDefaultWOL + "SQLConnectRetry", params.wolRetry);
698  kDefaultWOL + "Command", params.wolCommand);
699 
700  // clear out any legacy nodes..
701  m_pConfig->ClearValue(kDefaultMFE + "DBHostName");
702  m_pConfig->ClearValue(kDefaultMFE + "DBUserName");
703  m_pConfig->ClearValue(kDefaultMFE + "DBPassword");
704  m_pConfig->ClearValue(kDefaultMFE + "DBName");
705  m_pConfig->ClearValue(kDefaultMFE + "DBPort");
706  m_pConfig->ClearValue(kDefaultMFE + "DBHostPing");
707 
708  // actually save the file
709  m_pConfig->Save();
710 
711  // Save the new settings:
712  m_DBparams = params;
713  gCoreContext->GetDB()->SetDatabaseParams(m_DBparams);
714 
715  // If database has changed, force its use:
716  ResetDatabase();
717  }
718  return ret;
719 }
720 
722 {
723  if (d && d->m_loop
724  && d->m_loop->isRunning())
725  d->m_loop->exit();
726 }
727 
728 
730 {
731  bool accepted = false;
732  if (m_gui)
733  {
734  TempMainWindow();
735 
736  // Tell the user what went wrong:
737  if (error.length())
739 
740  // ask user for database parameters
741 
742  EnableDBerrors();
744  DatabaseSettings *dbsetting = new DatabaseSettings();
745  StandardSettingDialog *ssd =
746  new StandardSettingDialog(mainStack, "databasesettings",
747  dbsetting);
748  if (ssd->Create())
749  {
750  mainStack->AddScreen(ssd);
751  connect(dbsetting, &DatabaseSettings::isClosing,
753  if (!m_loop->isRunning())
754  m_loop->exec();
755  }
756  else
757  delete ssd;
758  SilenceDBerrors();
759  EndTempWindow();
760  accepted = true;
761  }
762  else
763  {
765  QString response;
766  std::this_thread::sleep_for(std::chrono::seconds(1));
767  // give user chance to skip config
768  cout << endl << error.toLocal8Bit().constData() << endl << endl;
769  response = getResponse("Would you like to configure the database "
770  "connection now?",
771  "no");
772  if (!response.startsWith('y', Qt::CaseInsensitive))
773  return false;
774 
775  params.dbHostName = getResponse("Database host name:",
776  params.dbHostName);
777  response = getResponse("Should I test connectivity to this host "
778  "using the ping command?", "yes");
779  params.dbHostPing = response.startsWith('y', Qt::CaseInsensitive);
780 
781  params.dbPort = intResponse("Database non-default port:",
782  params.dbPort);
783  params.dbName = getResponse("Database name:",
784  params.dbName);
785  params.dbUserName = getResponse("Database user name:",
786  params.dbUserName);
787  params.dbPassword = getResponse("Database password:",
788  params.dbPassword);
789 
790  params.localHostName = getResponse("Unique identifier for this machine "
791  "(if empty, the local host name "
792  "will be used):",
793  params.localHostName);
794  params.localEnabled = !params.localHostName.isEmpty();
795 
796  response = getResponse("Would you like to use Wake-On-LAN to retry "
797  "database connections?",
798  (params.wolEnabled ? "yes" : "no"));
799  params.wolEnabled = response.startsWith('y', Qt::CaseInsensitive);
800 
801  if (params.wolEnabled)
802  {
803  params.wolReconnect = intResponse("Seconds to wait for "
804  "reconnection:",
805  params.wolReconnect);
806  params.wolRetry = intResponse("Number of times to retry:",
807  params.wolRetry);
808  params.wolCommand = getResponse("Command to use to wake server or server MAC address:",
809  params.wolCommand);
810  }
811 
812  accepted = parent->SaveDatabaseParams(params);
813  }
814  return accepted;
815 }
816 
823 {
824  QString err;
825  QString host;
826 
827  // Jan 20, 2017
828  // Changed to use port check instead of ping
829 
830  int port = 0;
831 
832  // 1 = db awake, 2 = db listening, 3 = db connects,
833  // 4 = backend awake, 5 = backend listening
834  // 9 = all ok, 10 = quit
835 
836  enum startupStates {
837  st_start = 0,
838  st_dbAwake = 1,
839  st_dbStarted = 2,
840  st_dbConnects = 3,
841  st_beWOL = 4,
842  st_beAwake = 5,
843  st_success = 6
844  } startupState = st_start;
845 
846  static const QString guiStatuses[7] =
847  {"start","dbAwake","dbStarted","dbConnects","beWOL","beAwake",
848  "success" };
849 
850  int msStartupScreenDelay = gCoreContext->GetNumSetting("StartupScreenDelay",2);
851  if (msStartupScreenDelay > 0)
852  msStartupScreenDelay *= 1000;
853  do
854  {
855  QElapsedTimer timer;
856  timer.start();
857  if (m_DBparams.dbHostName.isNull() && m_DBhostCp.length())
858  host = m_DBhostCp;
859  else
860  host = m_DBparams.dbHostName;
861  port = m_DBparams.dbPort;
862  if (port == 0)
863  port = 3306;
864  int wakeupTime = 3;
865  int attempts = 11;
866  if (m_DBparams.wolEnabled) {
867  wakeupTime = m_DBparams.wolReconnect;
868  attempts = m_DBparams.wolRetry + 1;
869  startupState = st_start;
870  }
871  else
872  startupState = st_dbAwake;
873  if (attempts < 6)
874  attempts = 6;
875  if (!prompt)
876  attempts=1;
877  if (wakeupTime < 5)
878  wakeupTime = 5;
879 
880  int progressTotal = wakeupTime * attempts;
881 
883  m_guiStartup->setTotal(progressTotal);
884 
885  QString beWOLCmd = QString();
886  QString backendIP = QString();
887  int backendPort = 0;
888  QString masterserver;
889 
890  for (int attempt = 0;
891  attempt < attempts && startupState != st_success;
892  ++attempt)
893  {
894  // The first time do everything with minimum timeout and
895  // no GUI, for the normal case where all is OK
896  // After that show the GUI (if this is a GUI program)
897 
898  LOG(VB_GENERAL, LOG_INFO,
899  QString("Start up testing connections. DB %1, BE %2, attempt %3, status %4, Delay: %5")
900  .arg(host).arg(backendIP).arg(attempt).arg(guiStatuses[startupState]).arg(msStartupScreenDelay) );
901 
902  int useTimeout = wakeupTime;
903  if (attempt == 0)
904  useTimeout=1;
905 
906  if (m_gui && !m_guiStartup)
907  {
908  if (msStartupScreenDelay==0 || timer.hasExpired(msStartupScreenDelay))
909  {
910  ShowGuiStartup();
911  if (m_guiStartup)
912  m_guiStartup->setTotal(progressTotal);
913  }
914  }
916  {
917  if (attempt > 0)
918  m_guiStartup->setStatusState(guiStatuses[startupState]);
919  m_guiStartup->setMessageState("empty");
920  processEvents();
921  }
922  switch (startupState) {
923  case st_start:
925  {
926  if (attempt > 0)
928  if (!checkPort(host, port, useTimeout))
929  break;
930  }
931  startupState = st_dbAwake;
932  [[clang::fallthrough]];
933  case st_dbAwake:
934  if (!checkPort(host, port, useTimeout))
935  break;
936  startupState = st_dbStarted;
937  [[clang::fallthrough]];
938  case st_dbStarted:
939  // If the database is connecting with link-local
940  // address, it may have changed
941  if (m_DBparams.dbHostName != host)
942  {
943  m_DBparams.dbHostName = host;
944  gCoreContext->GetDB()->SetDatabaseParams(m_DBparams);
945  }
946  EnableDBerrors();
947  ResetDatabase();
949  {
950  for (int temp = 0; temp < useTimeout * 2 ; temp++)
951  {
952  processEvents();
953  std::this_thread::sleep_for(std::chrono::milliseconds(500));
954  }
955  break;
956  }
957  startupState = st_dbConnects;
958  [[clang::fallthrough]];
959  case st_dbConnects:
960  if (needsBackend)
961  {
962  beWOLCmd = gCoreContext->GetSetting("WOLbackendCommand", "");
963  if (!beWOLCmd.isEmpty())
964  {
965  wakeupTime += gCoreContext->GetNumSetting
966  ("WOLbackendReconnectWaitTime", 0);
967  attempts += gCoreContext->GetNumSetting
968  ("WOLbackendConnectRetry", 0);
969  useTimeout = wakeupTime;
970  if (m_gui && !m_guiStartup && attempt == 0)
971  useTimeout=1;
972  progressTotal = wakeupTime * attempts;
974  m_guiStartup->setTotal(progressTotal);
975  startupState = st_beWOL;
976  }
977  }
978  else {
979  startupState = st_success;
980  break;
981  }
982  masterserver = gCoreContext->GetSetting
983  ("MasterServerName");
984  backendIP = gCoreContext->GetSettingOnHost
985  ("BackendServerAddr", masterserver);
986  backendPort = gCoreContext->GetMasterServerPort();
987  [[clang::fallthrough]];
988  case st_beWOL:
989  if (!beWOLCmd.isEmpty()) {
990  if (attempt > 0)
991  MythWakeup(beWOLCmd);
992  if (!checkPort(backendIP, backendPort, useTimeout))
993  break;
994  }
995  startupState = st_beAwake;
996  [[clang::fallthrough]];
997  case st_beAwake:
998  if (!checkPort(backendIP, backendPort, useTimeout))
999  break;
1000  startupState = st_success;
1001  [[clang::fallthrough]];
1002  case st_success:
1003  // Quiet compiler warning.
1004  break;
1005  }
1006  if (m_guiStartup)
1007  {
1008  if (m_guiStartup->m_Exit
1009  || m_guiStartup->m_Setup
1011  || m_guiStartup->m_Retry)
1012  break;
1013  }
1014  processEvents();
1015  }
1016  if (startupState == st_success)
1017  break;
1018 
1019  QString stateMsg = guiStatuses[startupState];
1020  stateMsg.append("Fail");
1021  LOG(VB_GENERAL, LOG_INFO,
1022  QString("Start up failure. host %1, status %2")
1023  .arg(host).arg(stateMsg));
1024 
1025  if (m_gui && !m_guiStartup)
1026  {
1027  ShowGuiStartup();
1028  if (m_guiStartup)
1029  m_guiStartup->setTotal(progressTotal);
1030  }
1031 
1032  if (m_guiStartup
1033  && !m_guiStartup->m_Exit
1034  && !m_guiStartup->m_Setup
1035  && !m_guiStartup->m_Search
1036  && !m_guiStartup->m_Retry)
1037  {
1039  m_guiStartup->setStatusState(stateMsg);
1040  m_guiStartup->setMessageState("makeselection");
1041  m_loop->exec();
1042  }
1043  }
1044  while (m_guiStartup && m_guiStartup->m_Retry);
1045 
1046  if (startupState < st_dbAwake)
1047  {
1048  LOG(VB_GENERAL, LOG_WARNING, QString("Pinging to %1 failed, database will be unavailable").arg(host));
1049  SilenceDBerrors();
1050  err = QObject::tr(
1051  "Cannot find (ping) database host %1 on the network",
1052  "Backend Setup");
1053  return err.arg(host);
1054  }
1055 
1056  if (startupState < st_dbConnects)
1057  {
1058  SilenceDBerrors();
1059  return QObject::tr("Cannot login to database", "Backend Setup");
1060  }
1061 
1062  if (startupState < st_success)
1063  {
1064  return QObject::tr("Cannot connect to backend", "Backend Setup");
1065  }
1066 
1067  // Current DB connection may have been silenced (invalid):
1068  EnableDBerrors();
1069  ResetDatabase();
1070 
1071  return QString();
1072 }
1073 
1074 // Show the Gui Startup window.
1075 // This is called if there is a delay in startup for any reason
1076 // such as the database being unavailable
1078 {
1079  if (!m_gui)
1080  return;
1081  TempMainWindow(false);
1082  MythMainWindow *mainWindow = GetMythMainWindow();
1083  MythScreenStack *mainStack = mainWindow->GetMainStack();
1084  if (mainStack) {
1085  if (!m_guiStartup) {
1086  m_guiStartup = new GUIStartup(mainStack,m_loop);
1087  if (!m_guiStartup->Create()) {
1088  delete m_guiStartup;
1089  m_guiStartup = nullptr;
1090  }
1091  if (m_guiStartup) {
1092  mainStack->AddScreen(m_guiStartup, false);
1093  processEvents();
1094  }
1095  }
1096  }
1097 }
1098 
1108 {
1109  // This silences any DB errors from Get*Setting(),
1110  // (which is the vast majority of them)
1111  gCoreContext->GetDB()->SetSuppressDBMessages(true);
1112 
1113  // Save the configured hostname, so that we can
1114  // still display it in the DatabaseSettings screens
1115  if (m_DBparams.dbHostName.length())
1117 
1118  m_DBparams.dbHostName.clear();
1119  gCoreContext->GetDB()->SetDatabaseParams(m_DBparams);
1120 }
1121 
1123 {
1124  // Restore (possibly) blanked hostname
1125  if (m_DBparams.dbHostName.isNull() && m_DBhostCp.length())
1126  {
1128  gCoreContext->GetDB()->SetDatabaseParams(m_DBparams);
1129  }
1130 
1131  gCoreContext->GetDB()->SetSuppressDBMessages(false);
1132 }
1133 
1134 
1147 {
1149  gCoreContext->GetDB()->SetDatabaseParams(m_DBparams);
1151 }
1152 
1157 {
1158  TempMainWindow();
1159 
1160  // Tell the user what went wrong:
1161  if (!error.isEmpty())
1162  {
1163  LOG(VB_GENERAL, LOG_ERR, QString("Error: %1").arg(error));
1164  ShowOkPopup(error);
1165  }
1166 
1167  LOG(VB_GENERAL, LOG_INFO, "Putting up the UPnP backend chooser");
1168 
1171 
1172  EndTempWindow();
1173 
1174  return (int)ret;
1175 }
1176 
1183 int MythContextPrivate::UPnPautoconf(const int milliSeconds)
1184 {
1185  LOG(VB_GENERAL, LOG_INFO, QString("UPNP Search %1 secs")
1186  .arg(milliSeconds / 1000));
1187 
1188  SSDP::Instance()->PerformSearch(gBackendURI, milliSeconds / 1000);
1189 
1190  // Search for a total of 'milliSeconds' ms, sending new search packet
1191  // about every 250 ms until less than one second remains.
1192  MythTimer totalTime; totalTime.start();
1193  MythTimer searchTime; searchTime.start();
1194  while (totalTime.elapsed() < milliSeconds)
1195  {
1196  usleep(25000);
1197  int ttl = milliSeconds - totalTime.elapsed();
1198  if ((searchTime.elapsed() > 249) && (ttl > 1000))
1199  {
1200  LOG(VB_GENERAL, LOG_INFO, QString("UPNP Search %1 secs")
1201  .arg(ttl / 1000));
1202  SSDP::Instance()->PerformSearch(gBackendURI, ttl / 1000);
1203  searchTime.start();
1204  }
1205  }
1206 
1208 
1209  if (!backends)
1210  {
1211  LOG(VB_GENERAL, LOG_INFO, "No UPnP backends found");
1212  return 0;
1213  }
1214 
1215  int count = backends->Count();
1216  if (count)
1217  {
1218  LOG(VB_GENERAL, LOG_INFO,
1219  QString("Found %1 UPnP backends").arg(count));
1220  }
1221  else
1222  {
1223  LOG(VB_GENERAL, LOG_ERR,
1224  "No UPnP backends found, but SSDP::Find() not NULL");
1225  }
1226 
1227  if (count != 1)
1228  {
1229  backends->DecrRef();
1230  return count;
1231  }
1232 
1233  // Get this backend's location:
1234  DeviceLocation *BE = backends->GetFirst();
1235  backends->DecrRef();
1236  backends = nullptr;
1237 
1238  // We don't actually know the backend's access PIN, so this will
1239  // only work for ones that have PIN access disabled (i.e. 0000)
1240  int ret = (UPnPconnect(BE, QString())) ? 1 : -1;
1241 
1242  BE->DecrRef();
1243 
1244  return ret;
1245 }
1246 
1253 {
1254  QString loc = "DefaultUPnP() - ";
1255  QString PIN = m_pConfig->GetValue(kDefaultPIN, "");
1256  QString USN = m_pConfig->GetValue(kDefaultUSN, "");
1257 
1258  if (USN.isEmpty())
1259  {
1260  LOG(VB_UPNP, LOG_INFO, loc + "No default UPnP backend");
1261  return false;
1262  }
1263 
1264  LOG(VB_UPNP, LOG_INFO, loc + "config.xml has default " +
1265  QString("PIN '%1' and host USN: %2") .arg(PIN).arg(USN));
1266 
1267  // ----------------------------------------------------------------------
1268 
1269  int timeout_ms = 2000;
1270  LOG(VB_GENERAL, LOG_INFO, QString("UPNP Search up to %1 secs")
1271  .arg(timeout_ms / 1000));
1272  SSDP::Instance()->PerformSearch(gBackendURI, timeout_ms / 1000);
1273 
1274  // ----------------------------------------------------------------------
1275  // We need to give the server time to respond...
1276  // ----------------------------------------------------------------------
1277 
1278  DeviceLocation *pDevLoc = nullptr;
1279  MythTimer totalTime; totalTime.start();
1280  MythTimer searchTime; searchTime.start();
1281  while (totalTime.elapsed() < timeout_ms)
1282  {
1283  pDevLoc = SSDP::Instance()->Find( gBackendURI, USN );
1284 
1285  if (pDevLoc)
1286  break;
1287 
1288  usleep(25000);
1289 
1290  int ttl = timeout_ms - totalTime.elapsed();
1291  if ((searchTime.elapsed() > 249) && (ttl > 1000))
1292  {
1293  LOG(VB_GENERAL, LOG_INFO, QString("UPNP Search up to %1 secs")
1294  .arg(ttl / 1000));
1295  SSDP::Instance()->PerformSearch(gBackendURI, ttl / 1000);
1296  searchTime.start();
1297  }
1298  }
1299 
1300  // ----------------------------------------------------------------------
1301 
1302  if (!pDevLoc)
1303  {
1304  error = "Cannot find default UPnP backend";
1305  return false;
1306  }
1307 
1308  if (UPnPconnect(pDevLoc, PIN))
1309  {
1310  pDevLoc->DecrRef();
1311  return true;
1312  }
1313 
1314  pDevLoc->DecrRef();
1315 
1316  error = "Cannot connect to default backend via UPnP. Wrong saved PIN?";
1317  return false;
1318 }
1319 
1324  const QString &PIN)
1325 {
1326  QString error;
1327  QString loc = "UPnPconnect() - ";
1328  QString URL = backend->m_sLocation;
1329  MythXMLClient client(URL);
1330 
1331  LOG(VB_UPNP, LOG_INFO, loc + QString("Trying host at %1").arg(URL));
1332  switch (client.GetConnectionInfo(PIN, &m_DBparams, error))
1333  {
1334  case UPnPResult_Success:
1335  gCoreContext->GetDB()->SetDatabaseParams(m_DBparams);
1336  LOG(VB_UPNP, LOG_INFO, loc +
1337  "Got database hostname: " + m_DBparams.dbHostName);
1338  return true;
1339 
1341  // The stored PIN is probably not correct.
1342  // We could prompt for the PIN and try again, but that needs a UI.
1343  // Easier to fail for now, and put up the full UI selector later
1344  LOG(VB_UPNP, LOG_ERR, loc + "Wrong PIN?");
1345  return false;
1346 
1347  default:
1348  LOG(VB_UPNP, LOG_ERR, loc + error);
1349  break;
1350  }
1351 
1352  // This backend may have a local DB with the default user/pass/DBname.
1353  // For whatever reason, we have failed to get anything back via UPnP,
1354  // so we might as well try the database directly as a last resort.
1355  QUrl theURL(URL);
1356  URL = theURL.host();
1357  if (URL.isEmpty())
1358  return false;
1359 
1360  LOG(VB_UPNP, LOG_INFO, "Trying default DB credentials at " + URL);
1362 
1363  return true;
1364 }
1365 
1367 {
1368  if (e->type() == MythEvent::MythEventMessage)
1369  {
1370  if (disableeventpopup)
1371  return true;
1372 
1374  {
1376  }
1377 
1378  MythEvent *me = static_cast<MythEvent*>(e);
1379  if (me->Message() == "VERSION_MISMATCH" && (1 == me->ExtraDataCount()))
1380  ShowVersionMismatchPopup(me->ExtraData(0).toUInt());
1381  else if (me->Message() == "CONNECTION_FAILURE")
1383  else if (me->Message() == "PERSISTENT_CONNECTION_FAILURE")
1385  else if (me->Message() == "CONNECTION_RESTABLISHED")
1387  return true;
1388  }
1389 
1390  return QObject::event(e);
1391 }
1392 
1394 {
1395  QDateTime now = MythDate::current();
1396 
1397  if (!GetNotificationCenter() || !m_ui || !m_ui->IsScreenSetup())
1398  return;
1399 
1400  if (m_lastCheck.isValid() && now < m_lastCheck)
1401  return;
1402 
1403  // When WOL is disallowed, standy mode,
1404  // we should not show connection failures.
1405  if (!gCoreContext->IsWOLAllowed())
1406  return;
1407 
1408  m_lastCheck = now.addMSecs(5000); // don't refresh notification more than every 5s
1409 
1410  QString description = (persistent) ?
1411  QObject::tr(
1412  "The connection to the master backend "
1413  "server has gone away for some reason. "
1414  "Is it running?") :
1415  QObject::tr(
1416  "Could not connect to the master backend server. Is "
1417  "it running? Is the IP address set for it in "
1418  "mythtv-setup correct?");
1419 
1420  QString message = QObject::tr("Could not connect to master backend");
1421  MythErrorNotification n(message, _Location, description);
1422  n.SetId(m_registration);
1423  n.SetParent(this);
1425 }
1426 
1428 {
1429  if (!GetNotificationCenter())
1430  return;
1431 
1432  if (!m_lastCheck.isValid())
1433  return;
1434 
1435  MythCheckNotification n(QObject::tr("Backend is online"), _Location);
1436  n.SetId(m_registration);
1437  n.SetParent(this);
1438  n.SetDuration(5);
1440  m_lastCheck = QDateTime();
1441 }
1442 
1444 {
1445  if (MBEversionPopup)
1446  return;
1447 
1448  QString message =
1449  QObject::tr(
1450  "The server uses network protocol version %1, "
1451  "but this client only understands version %2. "
1452  "Make sure you are running compatible versions of "
1453  "the backend and frontend.")
1454  .arg(remote_version).arg(MYTH_PROTO_VERSION);
1455 
1456  if (HasMythMainWindow() && m_ui && m_ui->IsScreenSetup())
1457  {
1459  message, m_sh, SLOT(VersionMismatchPopupClosed()));
1460  }
1461  else
1462  {
1463  LOG(VB_GENERAL, LOG_ERR, LOC + message);
1464  qApp->exit(GENERIC_EXIT_SOCKET_ERROR);
1465  }
1466 }
1467 
1468 // Process Events while waiting for connection
1469 // return true if progress is 100%
1471 {
1472 // bool ret = false;
1473 // if (m_guiStartup)
1474 // ret = m_guiStartup->updateProgress();
1475  qApp->processEvents(QEventLoop::AllEvents, 250);
1476  qApp->processEvents(QEventLoop::AllEvents, 250);
1477 // return ret;
1478 }
1479 
1480 // cache some settings in cache/contextcache.xml
1481 // only call this if the database is available.
1482 
1483 const QString MythContextPrivate::settingsToSave[] =
1484 { "Theme", "Language", "Country", "GuiHeight",
1485  "GuiOffsetX", "GuiOffsetY", "GuiWidth", "RunFrontendInWindow",
1486  "AlwaysOnTop", "HideMouseCursor", "ThemePainter", "libCECEnabled",
1487  "StartupScreenDelay" };
1488 
1489 
1491 {
1492  if (!m_gui)
1493  return true;
1494  QString cacheDirName = GetConfDir() + "/cache/";
1495  QDir dir(cacheDirName);
1496  dir.mkpath(cacheDirName);
1497  XmlConfiguration config = XmlConfiguration("cache/contextcache.xml");
1498  static const int arraySize = sizeof(settingsToSave)/sizeof(settingsToSave[0]);
1499  for (int ix = 0; ix < arraySize; ix++)
1500  {
1501  QString cacheValue = config.GetValue("Settings/"+settingsToSave[ix],QString());
1503  QString value = gCoreContext->GetSetting(settingsToSave[ix],QString());
1504  if (value != cacheValue)
1505  {
1506  config.SetValue("Settings/"+settingsToSave[ix],value);
1507  m_settingsCacheDirty = true;
1508  }
1509  }
1511  return config.Save();
1512 }
1513 
1515 {
1516  if (!m_gui)
1517  return;
1518  XmlConfiguration config = XmlConfiguration("cache/contextcache.xml");
1519  static const int arraySize = sizeof(settingsToSave)/sizeof(settingsToSave[0]);
1520  for (int ix = 0; ix < arraySize; ix++)
1521  {
1522  if (!gCoreContext->GetSetting(settingsToSave[ix],QString()).isEmpty())
1523  continue;
1524  QString value = config.GetValue("Settings/"+settingsToSave[ix],QString());
1525  if (!value.isEmpty())
1527  }
1528  // Prevent power off TV after temporary window
1529  gCoreContext->OverrideSettingForSession("PowerOffTVAllowed", nullptr);
1530 
1531  QString language = gCoreContext->GetSetting("Language",QString());
1532  MythTranslation::load("mythfrontend");
1533 }
1534 
1536 {
1537  QString language = gCoreContext->GetSetting("Language",QString());
1538  static const int arraySize = sizeof(settingsToSave)/sizeof(settingsToSave[0]);
1539  for (int ix = 0; ix < arraySize; ix++)
1540  {
1542  }
1543  // Restore power off TV setting
1544  gCoreContext->ClearOverrideSettingForSession("PowerOffTVAllowed");
1545 
1546  if (language != gCoreContext->GetSetting("Language",QString()))
1547  MythTranslation::load("mythfrontend");
1548 }
1549 
1550 
1552 {
1553  d->MBEversionPopup = nullptr;
1554  qApp->exit(GENERIC_EXIT_SOCKET_ERROR);
1555 }
1556 
1557 MythContext::MythContext(const QString &binversion, bool needsBackend)
1558  : d(nullptr), app_binary_version(binversion)
1559 {
1560 #ifdef _WIN32
1561  static bool WSAStarted = false;
1562  if (!WSAStarted) {
1563  WSADATA wsadata;
1564  int res = WSAStartup(MAKEWORD(2, 0), &wsadata);
1565  LOG(VB_SOCKET, LOG_INFO,
1566  QString("WSAStartup returned %1").arg(res));
1567  }
1568 #endif
1569 
1570  d = new MythContextPrivate(this);
1571  d->needsBackend = needsBackend;
1572 
1574 
1575  if (!gCoreContext || !gCoreContext->Init())
1576  {
1577  LOG(VB_GENERAL, LOG_EMERG, LOC + "Unable to allocate MythCoreContext");
1578  qApp->exit(GENERIC_EXIT_NO_MYTHCONTEXT);
1579  }
1580 }
1581 
1582 bool MythContext::Init(const bool gui,
1583  const bool promptForBackend,
1584  const bool disableAutoDiscovery,
1585  const bool ignoreDB)
1586 {
1587  if (!d)
1588  {
1589  LOG(VB_GENERAL, LOG_EMERG, LOC + "Init() Out-of-memory");
1590  return false;
1591  }
1592 
1593  SetDisableEventPopup(true);
1594 
1596  {
1597  LOG(VB_GENERAL, LOG_EMERG,
1598  QString("Application binary version (%1) does not "
1599  "match libraries (%2)")
1601 
1602  QString warning = QObject::tr(
1603  "This application is not compatible "
1604  "with the installed MythTV libraries.");
1605  if (gui)
1606  {
1607  d->TempMainWindow(false);
1608  ShowOkPopup(warning);
1609  }
1610  LOG(VB_GENERAL, LOG_WARNING, warning);
1611 
1612  return false;
1613  }
1614 
1615 #ifdef _WIN32
1616  // HOME environment variable might not be defined
1617  // some libraries will fail without it
1618  QString home = getenv("HOME");
1619  if (home.isEmpty())
1620  {
1621  home = getenv("LOCALAPPDATA"); // Vista
1622  if (home.isEmpty())
1623  home = getenv("APPDATA"); // XP
1624  if (home.isEmpty())
1625  home = QString("."); // getenv("TEMP")?
1626 
1627  _putenv(QString("HOME=%1").arg(home).toLocal8Bit().constData());
1628  }
1629 #endif
1630 
1631  // If HOME isn't defined, we won't be able to use default confdir of
1632  // $HOME/.mythtv nor can we rely on a MYTHCONFDIR that references $HOME
1633  QString homedir = QDir::homePath();
1634  QString confdir = getenv("MYTHCONFDIR");
1635  if ((homedir.isEmpty() || homedir == "/") &&
1636  (confdir.isEmpty() || confdir.contains("$HOME")))
1637  {
1638  QString warning = "Cannot locate your home directory."
1639  " Please set the environment variable HOME";
1640  if (gui)
1641  {
1642  d->TempMainWindow(false);
1643  ShowOkPopup(warning);
1644  }
1645  LOG(VB_GENERAL, LOG_WARNING, warning);
1646 
1647  return false;
1648  }
1649 
1650  if (!d->Init(gui, promptForBackend, disableAutoDiscovery, ignoreDB))
1651  {
1652  return false;
1653  }
1654 
1655  SetDisableEventPopup(false);
1657  if (d->m_settingsCacheDirty)
1658  {
1659 #ifndef Q_OS_ANDROID
1661 #endif
1662  d->m_settingsCacheDirty = false;
1663  }
1665 
1666  return true;
1667 }
1668 
1670 {
1671  if (MThreadPool::globalInstance()->activeThreadCount())
1672  LOG(VB_GENERAL, LOG_INFO, "Waiting for threads to exit.");
1673 
1675  logStop();
1676 
1677  SSDP::Shutdown();
1679 
1680  delete gCoreContext;
1681  gCoreContext = nullptr;
1682 
1683  delete d;
1684 }
1685 
1687 {
1688  d->disableeventpopup = check;
1689 }
1690 
1692 {
1693  return d->m_DBparams;
1694 }
1695 
1697 {
1698  return d->SaveDatabaseParams(params, false);
1699 }
1700 
1702 {
1703  return d->saveSettingsCache();
1704 }
1705 
1706 /* 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.
bool wolEnabled
true if wake-on-lan params are used
Definition: mythdbparams.h:32
virtual int GetValue(const QString &sSetting, int Default)=0
bool setStatusState(const QString &name)
Definition: guistartup.cpp:123
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
QString wolCommand
command to use for wake-on-lan
Definition: mythdbparams.h:35
static void configplugin_cb(const QString &cmd)
QString dbName
database name
Definition: mythdbparams.h:26
QString app_binary_version
Definition: mythcontext.h:61
void LoadQtConfig(void)
Dialog asking for user confirmation.
int UPnPautoconf(const int milliSeconds=2000)
If there is only a single UPnP backend, use it.
void EndTempWindow(void)
MythConfirmationDialog * ShowOkPopup(const QString &message, QObject *parent, const char *slot, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
virtual void ClearValue(const QString &sSetting)=0
void SetId(int id)
Optional MythNotification elements.
bool setMessageState(const QString &name)
Definition: guistartup.cpp:133
static void exec_program_tv_cb(const QString &cmd)
const QString kDefaultWOL
Definition: backendselect.h:23
static Type MythEventMessage
Definition: mythevent.h:65
static void error(const char *str,...)
Definition: vbi.c:41
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.
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)
unsigned int uint
Definition: compat.h:140
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)
bool localEnabled
true if localHostName is not default
Definition: mythdbparams.h:29
MythContext * gContext
This global variable contains the MythContext instance for the application.
Definition: mythcontext.cpp:63
bool Init(const bool gui, const bool prompt, const bool noPrompt, const bool ignoreDB)
void DestroyMythUI()
void SetLocalHostname(const QString &hostname)
const QString kDefaultDB
Definition: backendselect.h:22
void(* exec_program)(const QString &cmd)
Definition: mythuihelper.h:34
bool checkPort(QString &host, int port, int wakeupTime)
Check if a port is open and sort out the link-local scope.
QString dbPassword
DB password.
Definition: mythdbparams.h:25
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:78
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 FindDatabase(const bool prompt, const bool noPrompt)
Get database connection settings and test connectivity.
bool config_plugin(const QString &plugname)
Definition: mythplugin.cpp:198
bool SendReceiveStringList(QStringList &strlist, bool quickTimeout=false, bool block=true)
Send a message to the backend and wait for a response.
MythContext * parent
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
MythContextSlotHandler * m_sh
QString dbUserName
DB user name.
Definition: mythdbparams.h:24
DeviceLocation * GetFirst(void)
Returns random entry in cache, returns nullptr when list is empty.
Definition: ssdpcache.cpp:73
bool Init(const bool gui=true, const bool promptForBackend=false, const bool bypassAutoDiscovery=false, const bool ignoreDB=false)
static void plugin_cb(const QString &cmd)
void PerformSearch(const QString &sST, uint timeout_secs=2)
Definition: ssdp.cpp:214
MythPluginManager * GetPluginManager(void)
This class is used as a container for messages.
Definition: mythevent.h:15
void ClearOverrideSettingForSession(const QString &key)
void EnableDBerrors(void)
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:85
void VersionMismatchPopupClosed(void)
void Init(QString forcedpainter=QString(), bool mayReInit=true)
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:144
bool PromptForDatabaseParams(const QString &error)
static const uint16_t * d
void OverrideSettingForSession(const QString &key, const QString &value)
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
QString GetSetting(const QString &key, const QString &defaultval="")
DatabaseParams m_DBparams
Current database host & WOL details.
MythContext(const QString &binversion, bool needsBackend=false)
void SetDuration(int duration)
contains a duration during which the notification will be displayed for.
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:161
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.
MythConfirmationDialog * MBEversionPopup
void ShowNotificationError(const QString &msg, const QString &from, const QString &detail, const VNMask visibility, const MythNotification::Priority priority)
convenience utility to display error message as notification
#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
static bool testDBConnection()
Checks DB connection + login (login info via Mythcontext)
Definition: mythdbcon.cpp:863
void(* configplugin)(const QString &cmd)
Definition: mythuihelper.h:36
bool Queue(const MythNotification &notification)
Queue a notification Queue() is thread-safe and can be called from anywhere.
bool dbHostPing
Can we test connectivity using ping?
Definition: mythdbparams.h:22
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
QString m_DBhostCp
dbHostName backup
QString getResponse(const QString &query, const QString &def)
In an interactive shell, prompt the user to input a string.
static bool prompt(bool force=false)
Ask the user for the language to use.
uint myth_system(const QString &command, uint flags, uint timeout)
#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:65
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.
int wolReconnect
seconds to wait for reconnect
Definition: mythdbparams.h:33
bool HasMythMainWindow(void)
MythMainWindow * GetMythMainWindow(void)
void clearSettingsCacheOverride(void)
int elapsed(void) const
Returns milliseconds elapsed since last start() or restart()
Definition: mythtimer.cpp:90
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)
static const QString settingsToSave[]
bool UPnPconnect(const DeviceLocation *device, const QString &PIN)
Query a backend via UPnP for its database connection parameters.
Structure containing the basic Database parameters.
Definition: mythdbparams.h:9
QString m_sLocation
Definition: upnpdevice.h:230
bool Create(void) override
Definition: guistartup.cpp:83
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
QString dbHostName
database server
Definition: mythdbparams.h:21
bool Create(void) override
void LoadDefaults(void)
Load sensible connection defaults.
Definition: mythdbparams.cpp:5
static SSDP * Instance()
Definition: ssdp.cpp:54
bool forceSave
set to true to force a save of the settings file
Definition: mythdbparams.h:37
int GetValue(const QString &sSetting, int Default) override
const QString & ExtraData(int idx=0) const
Definition: mythevent.h:58
virtual void PopScreen(MythScreenType *screen=nullptr, bool allowFade=true, bool deleteScreen=true)
void(* eject)(void)
Definition: mythuihelper.h:38
void logStop(void)
Entry point for stopping logging for an application.
Definition: logging.cpp:759
MythContextPrivate * d
Definition: mythcontext.h:30
Configuration * m_pConfig
UPnPResultCode GetConnectionInfo(const QString &sPin, DatabaseParams *pParams, QString &sMsg)
QTcpSocket * m_socket
void(* plugin)(const QString &cmd)
Definition: mythuihelper.h:37
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)
MDBManager * GetDBManager(void)
void loadSettingsCacheOverride(void)
bool event(QEvent *) override
void HideConnectionFailurePopup(void)
const QString kDefaultUSN
Definition: backendselect.h:26
int ExtraDataCount() const
Definition: mythevent.h:60
bool m_Search
Definition: guistartup.h:57
MythUIHelper * m_ui
static SSDPCacheEntries * Find(const QString &sURI)
Definition: ssdp.h:129
const QString kDefaultMFE
Definition: backendselect.h:24
#define GENERIC_EXIT_NO_MYTHCONTEXT
No MythContext available.
Definition: exitcodes.h:13
void(* exec_program_tv)(const QString &cmd)
Definition: mythuihelper.h:35
void ResetDatabase(void)
Called when the user changes the DB connection settings.
const QString & Message() const
Definition: mythevent.h:57
QString localHostName
name used for loading/saving settings
Definition: mythdbparams.h:30
void ActivateSettingsCache(bool activate=true)
int dbPort
database port
Definition: mythdbparams.h:23
void 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 ShowVersionMismatchPopup(uint remoteVersion)
void start(void)
starts measuring elapsed time.
Definition: mythtimer.cpp:47
void CloseDatabases(void)
Definition: mythdbcon.cpp:479
virtual ~MythContext()
int wolRetry
times to retry to reconnect
Definition: mythdbparams.h:34
bool DefaultUPnP(QString &error)
Get the default backend from config.xml, use UPnP to find it.
static QString confdir
Definition: mythdirs.cpp: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:61
MythNotificationCenter * GetNotificationCenter(void)
bool run_plugin(const QString &plugname)
Definition: mythplugin.cpp:180
void InitializeMythDirs(void)
Definition: mythdirs.cpp:30
virtual void SetValue(const QString &sSetting, int value)=0
bool m_gui
Should this context use GUI elements?