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