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