00001 #include <QCoreApplication>
00002 #include <QDir>
00003 #include <QFileInfo>
00004 #include <QDebug>
00005 #include <QMutex>
00006
00007 #include <cmath>
00008 #include <iostream>
00009
00010 #include <queue>
00011 #include <algorithm>
00012 using namespace std;
00013
00014 #include "config.h"
00015 #include "mythcontext.h"
00016 #include "exitcodes.h"
00017 #include "mythmiscutil.h"
00018 #include "remotefile.h"
00019 #include "mythplugin.h"
00020 #include "backendselect.h"
00021 #include "dbsettings.h"
00022 #include "langsettings.h"
00023 #include "mythtranslation.h"
00024 #include "mythxdisplay.h"
00025 #include "mythevent.h"
00026 #include "dbutil.h"
00027 #include "DisplayRes.h"
00028 #include "mythmediamonitor.h"
00029 #include "mythsocketthread.h"
00030
00031 #include "mythdb.h"
00032 #include "mythdirs.h"
00033 #include "mythversion.h"
00034 #include "mythdialogbox.h"
00035 #include "mythmainwindow.h"
00036 #include "mythuihelper.h"
00037 #include "mythimage.h"
00038 #include "mythxmlclient.h"
00039 #include "upnp.h"
00040 #include "mythlogging.h"
00041
00042 #ifdef USING_MINGW
00043 #include <unistd.h>
00044 #include "compat.h"
00045 #endif
00046
00047 #define LOC QString("MythContext: ")
00048
00049 MythContext *gContext = NULL;
00050
00051 class MythContextPrivate : public QObject
00052 {
00053 friend class MythContextSlotHandler;
00054
00055 public:
00056 MythContextPrivate(MythContext *lparent);
00057 ~MythContextPrivate();
00058
00059 bool Init (const bool gui,
00060 const bool prompt, const bool noPrompt,
00061 const bool ignoreDB);
00062 bool FindDatabase(const bool prompt, const bool noPrompt);
00063
00064 void TempMainWindow(bool languagePrompt = true);
00065 void EndTempWindow(void);
00066
00067 bool LoadDatabaseSettings(void);
00068 bool SaveDatabaseParams(const DatabaseParams ¶ms, bool force);
00069
00070 bool PromptForDatabaseParams(const QString &error);
00071 QString TestDBconnection(void);
00072 void SilenceDBerrors(void);
00073 void EnableDBerrors(void);
00074 void ResetDatabase(void);
00075
00076 int ChooseBackend(const QString &error);
00077 int UPnPautoconf(const int milliSeconds = 2000);
00078 bool DefaultUPnP(QString &error);
00079 bool UPnPconnect(const DeviceLocation *device, const QString &PIN);
00080
00081 protected:
00082 bool event(QEvent*);
00083
00084 void ShowConnectionFailurePopup(bool persistent);
00085 void HideConnectionFailurePopup(void);
00086 void ConnectFailurePopupClosed(void);
00087
00088 void ShowVersionMismatchPopup(uint remoteVersion);
00089 void VersionMismatchPopupClosed(void);
00090
00091 public:
00092 MythContext *parent;
00093
00094 bool m_gui;
00095
00096 QString m_masterhostname;
00097
00098 DatabaseParams m_DBparams;
00099 QString m_DBhostCp;
00100
00101 Configuration *m_pConfig;
00102
00103 bool disableeventpopup;
00104 bool disablelibrarypopup;
00105
00106 MythPluginManager *pluginmanager;
00107
00108 MythUIHelper *m_ui;
00109 MythContextSlotHandler *m_sh;
00110
00111 private:
00112 MythConfirmationDialog *MBEconnectPopup;
00113 MythConfirmationDialog *MBEversionPopup;
00114 };
00115
00116 static void exec_program_cb(const QString &cmd)
00117 {
00118 myth_system(cmd);
00119 }
00120
00121 static void exec_program_tv_cb(const QString &cmd)
00122 {
00123 QString s = cmd;
00124 QStringList tokens = cmd.simplified().split(" ");
00125 QStringList strlist;
00126
00127 bool cardidok;
00128 int wantcardid = tokens[0].toInt(&cardidok, 10);
00129
00130 if (cardidok && wantcardid > 0)
00131 {
00132 strlist << QString("LOCK_TUNER %1").arg(wantcardid);
00133 s = s.replace(0, tokens[0].length() + 1, "");
00134 }
00135 else
00136 strlist << "LOCK_TUNER";
00137
00138 gCoreContext->SendReceiveStringList(strlist);
00139 int cardid = strlist[0].toInt();
00140
00141 if (cardid >= 0)
00142 {
00143 s = s.sprintf(qPrintable(s),
00144 qPrintable(strlist[1]),
00145 qPrintable(strlist[2]),
00146 qPrintable(strlist[3]));
00147
00148 myth_system(s);
00149
00150 strlist = QStringList(QString("FREE_TUNER %1").arg(cardid));
00151 gCoreContext->SendReceiveStringList(strlist);
00152 QString ret = strlist[0];
00153 }
00154 else
00155 {
00156 QString label;
00157
00158 if (cardidok)
00159 {
00160 if (cardid == -1)
00161 label = QObject::tr("Could not find specified tuner (%1).")
00162 .arg(wantcardid);
00163 else
00164 label = QObject::tr("Specified tuner (%1) is already in use.")
00165 .arg(wantcardid);
00166 }
00167 else
00168 {
00169 label = QObject::tr("All tuners are currently in use. If you want "
00170 "to watch TV, you can cancel one of the "
00171 "in-progress recordings from the delete menu");
00172 }
00173
00174 LOG(VB_GENERAL, LOG_ALERT, QString("exec_program_tv: ") + label);
00175
00176 ShowOkPopup(label);
00177 }
00178 }
00179
00180 static void configplugin_cb(const QString &cmd)
00181 {
00182 MythPluginManager *pmanager = gContext->getPluginManager();
00183 if (pmanager)
00184 if (pmanager->config_plugin(cmd.trimmed()))
00185 ShowOkPopup(QObject::tr("Failed to configure plugin %1").arg(cmd));
00186 }
00187
00188 static void plugin_cb(const QString &cmd)
00189 {
00190 MythPluginManager *pmanager = gContext->getPluginManager();
00191 if (pmanager)
00192 if (pmanager->run_plugin(cmd.trimmed()))
00193 ShowOkPopup(QObject::tr("The plugin %1 has failed "
00194 "to run for some reason...").arg(cmd));
00195 }
00196
00197 static void eject_cb(void)
00198 {
00199 MediaMonitor::ejectOpticalDisc();
00200 }
00201
00202 MythContextPrivate::MythContextPrivate(MythContext *lparent)
00203 : parent(lparent),
00204 m_gui(false),
00205 m_pConfig(NULL),
00206 disableeventpopup(false),
00207 disablelibrarypopup(false),
00208 pluginmanager(NULL),
00209 m_ui(NULL),
00210 m_sh(new MythContextSlotHandler(this)),
00211 MBEconnectPopup(NULL),
00212 MBEversionPopup(NULL)
00213 {
00214 InitializeMythDirs();
00215 }
00216
00217 MythContextPrivate::~MythContextPrivate()
00218 {
00219 if (m_pConfig)
00220 delete m_pConfig;
00221 if (m_ui)
00222 DestroyMythUI();
00223 if (m_sh)
00224 m_sh->deleteLater();
00225 }
00226
00237 void MythContextPrivate::TempMainWindow(bool languagePrompt)
00238 {
00239 if (HasMythMainWindow())
00240 return;
00241
00242 SilenceDBerrors();
00243
00244 gCoreContext->OverrideSettingForSession("Theme", DEFAULT_UI_THEME);
00245 #ifdef Q_WS_MACX
00246
00247 gCoreContext->OverrideSettingForSession("RunFrontendInWindow", "1");
00248 #endif
00249 GetMythUI()->LoadQtConfig();
00250
00251 MythMainWindow *mainWindow = MythMainWindow::getMainWindow(false);
00252 mainWindow->Init();
00253
00254 if (languagePrompt)
00255 {
00256
00257 LanguageSelection::prompt();
00258 MythTranslation::load("mythfrontend");
00259 }
00260 }
00261
00262 void MythContextPrivate::EndTempWindow(void)
00263 {
00264 DestroyMythMainWindow();
00265 gCoreContext->ClearOverrideSettingForSession("Theme");
00266 EnableDBerrors();
00267 }
00268
00269 bool MythContextPrivate::Init(const bool gui,
00270 const bool promptForBackend,
00271 const bool noPrompt,
00272 const bool ignoreDB)
00273 {
00274 gCoreContext->GetDB()->IgnoreDatabase(ignoreDB);
00275 m_gui = gui;
00276
00277
00278 m_pConfig = new XmlConfiguration("config.xml");
00279
00280
00281 if (gui)
00282 m_ui = GetMythUI();
00283
00284
00285
00286 if (!ignoreDB && !FindDatabase(promptForBackend, noPrompt))
00287 return false;
00288
00289
00290
00291
00292
00293 if (m_gui && !gCoreContext->GetDB()->HaveSchema())
00294 {
00295 TempMainWindow(false);
00296 LanguageSelection::prompt();
00297 MythTranslation::load("mythfrontend");
00298 EndTempWindow();
00299 }
00300 gCoreContext->InitLocale();
00301 gCoreContext->SaveLocaleDefaults();
00302
00303 if (gui)
00304 {
00305 MythUIMenuCallbacks cbs;
00306 cbs.exec_program = exec_program_cb;
00307 cbs.exec_program_tv = exec_program_tv_cb;
00308 cbs.configplugin = configplugin_cb;
00309 cbs.plugin = plugin_cb;
00310 cbs.eject = eject_cb;
00311
00312 m_ui->Init(cbs);
00313 }
00314
00315 return true;
00316 }
00317
00330 bool MythContextPrivate::FindDatabase(bool prompt, bool noAutodetect)
00331 {
00332
00333 bool manualSelect = prompt && !noAutodetect;
00334
00335 QString failure;
00336
00337
00338 bool loaded = LoadDatabaseSettings();
00339 DatabaseParams dbParamsFromFile = m_DBparams;
00340
00341
00342
00343
00344 bool autoSelect = !manualSelect && !loaded && !noAutodetect;
00345
00346
00347
00348 if (!manualSelect)
00349 {
00350
00351
00352
00353 if (DefaultUPnP(failure))
00354 autoSelect = manualSelect = false;
00355 else
00356 if (failure.length())
00357 LOG(VB_GENERAL, LOG_ALERT, failure);
00358
00359 failure = TestDBconnection();
00360 if (failure.isEmpty())
00361 goto DBfound;
00362 }
00363
00364
00365
00366 if (autoSelect)
00367 {
00368 int count = UPnPautoconf();
00369
00370 if (count == 0)
00371 failure = QObject::tr("No UPnP backends found", "Backend Setup");
00372
00373 if (count == 1)
00374 {
00375 failure = TestDBconnection();
00376 if (failure.isEmpty())
00377 goto DBfound;
00378 }
00379
00380
00381 manualSelect |= (count > 1 || count == -1);
00382 }
00383
00384 manualSelect &= m_gui;
00385
00386
00387 do
00388 {
00389 if (manualSelect)
00390 {
00391
00392 BackendSelection::Decision d = (BackendSelection::Decision)
00393 ChooseBackend(failure);
00394 switch (d)
00395 {
00396 case BackendSelection::kAcceptConfigure:
00397 break;
00398 case BackendSelection::kManualConfigure:
00399 manualSelect = false;
00400 break;
00401 case BackendSelection::kCancelConfigure:
00402 goto NoDBfound;
00403 }
00404 }
00405
00406 if (!manualSelect)
00407 {
00408 if (!PromptForDatabaseParams(failure))
00409 goto NoDBfound;
00410 }
00411
00412 failure = TestDBconnection();
00413 if (!failure.isEmpty())
00414 LOG(VB_GENERAL, LOG_ALERT, failure);
00415 }
00416 while (!failure.isEmpty());
00417
00418 DBfound:
00419 LOG(VB_GENERAL, LOG_DEBUG, "FindDatabase() - Success!");
00420 SaveDatabaseParams(m_DBparams,
00421 !loaded || m_DBparams.forceSave ||
00422 dbParamsFromFile != m_DBparams);
00423 EnableDBerrors();
00424 ResetDatabase();
00425 return true;
00426
00427 NoDBfound:
00428 LOG(VB_GENERAL, LOG_DEBUG, "FindDatabase() - failed");
00429 return false;
00430 }
00431
00435 bool MythContextPrivate::LoadDatabaseSettings(void)
00436 {
00437
00438 m_DBparams.LoadDefaults();
00439
00440 m_DBparams.localHostName = m_pConfig->GetValue("LocalHostName", "");
00441
00442 m_DBparams.dbHostName = m_pConfig->GetValue(kDefaultDB + "Host", "");
00443 m_DBparams.dbUserName = m_pConfig->GetValue(kDefaultDB + "UserName", "");
00444 m_DBparams.dbPassword = m_pConfig->GetValue(kDefaultDB + "Password", "");
00445 m_DBparams.dbName = m_pConfig->GetValue(kDefaultDB + "DatabaseName", "");
00446 m_DBparams.dbPort = m_pConfig->GetValue(kDefaultDB + "Port", 0);
00447
00448 m_DBparams.wolEnabled =
00449 m_pConfig->GetValue(kDefaultWOL + "Enabled", false);
00450 m_DBparams.wolReconnect =
00451 m_pConfig->GetValue(kDefaultWOL + "SQLReconnectWaitTime", 0);
00452 m_DBparams.wolRetry =
00453 m_pConfig->GetValue(kDefaultWOL + "SQLConnectRetry", 5);
00454 m_DBparams.wolCommand =
00455 m_pConfig->GetValue(kDefaultWOL + "Command", "");
00456
00457 bool ok = m_DBparams.IsValid("config.xml");
00458 if (!ok)
00459 {
00460 m_DBparams.LoadDefaults();
00461 m_DBparams.dbHostName = m_pConfig->GetValue(
00462 kDefaultMFE + "DBHostName", "");
00463 m_DBparams.dbUserName = m_pConfig->GetValue(
00464 kDefaultMFE + "DBUserName", "");
00465 m_DBparams.dbPassword = m_pConfig->GetValue(
00466 kDefaultMFE + "DBPassword", "");
00467 m_DBparams.dbName = m_pConfig->GetValue(
00468 kDefaultMFE + "DBName", "");
00469 m_DBparams.dbPort = m_pConfig->GetValue(
00470 kDefaultMFE + "DBPort", 0);
00471 m_DBparams.forceSave = true;
00472 ok = m_DBparams.IsValid("config.xml");
00473 }
00474 if (!ok)
00475 m_DBparams.LoadDefaults();
00476
00477 gCoreContext->GetDB()->SetDatabaseParams(m_DBparams);
00478
00479 QString hostname = m_DBparams.localHostName;
00480 if (hostname.isEmpty() ||
00481 hostname == "my-unique-identifier-goes-here")
00482 {
00483 char localhostname[1024];
00484 if (gethostname(localhostname, 1024))
00485 {
00486 LOG(VB_GENERAL, LOG_ALERT,
00487 "MCP: Error, could not determine host name." + ENO);
00488 localhostname[0] = '\0';
00489 }
00490 hostname = localhostname;
00491 LOG(VB_GENERAL, LOG_NOTICE, "Empty LocalHostName.");
00492 }
00493 else
00494 {
00495 m_DBparams.localEnabled = true;
00496 }
00497
00498 LOG(VB_GENERAL, LOG_INFO, QString("Using localhost value of %1")
00499 .arg(hostname));
00500 gCoreContext->SetLocalHostname(hostname);
00501
00502 return ok;
00503 }
00504
00505 bool MythContextPrivate::SaveDatabaseParams(
00506 const DatabaseParams ¶ms, bool force)
00507 {
00508 bool ret = true;
00509
00510
00511 if (params != m_DBparams || force)
00512 {
00513 m_pConfig->SetValue(
00514 "LocalHostName", params.localHostName);
00515
00516 m_pConfig->SetValue(
00517 kDefaultDB + "PingHost", params.dbHostPing);
00518 m_pConfig->SetValue(
00519 kDefaultDB + "Host", params.dbHostName);
00520 m_pConfig->SetValue(
00521 kDefaultDB + "UserName", params.dbUserName);
00522 m_pConfig->SetValue(
00523 kDefaultDB + "Password", params.dbPassword);
00524 m_pConfig->SetValue(
00525 kDefaultDB + "DatabaseName", params.dbName);
00526 m_pConfig->SetValue(
00527 kDefaultDB + "Port", params.dbPort);
00528
00529 m_pConfig->SetValue(
00530 kDefaultWOL + "Enabled", params.wolEnabled);
00531 m_pConfig->SetValue(
00532 kDefaultWOL + "SQLReconnectWaitTime", params.wolReconnect);
00533 m_pConfig->SetValue(
00534 kDefaultWOL + "SQLConnectRetry", params.wolRetry);
00535 m_pConfig->SetValue(
00536 kDefaultWOL + "Command", params.wolCommand);
00537
00538
00539 m_pConfig->ClearValue(kDefaultMFE + "DBHostName");
00540 m_pConfig->ClearValue(kDefaultMFE + "DBUserName");
00541 m_pConfig->ClearValue(kDefaultMFE + "DBPassword");
00542 m_pConfig->ClearValue(kDefaultMFE + "DBName");
00543 m_pConfig->ClearValue(kDefaultMFE + "DBPort");
00544 m_pConfig->ClearValue(kDefaultMFE + "DBHostPing");
00545
00546
00547 m_pConfig->Save();
00548
00549
00550 m_DBparams = params;
00551 gCoreContext->GetDB()->SetDatabaseParams(m_DBparams);
00552
00553
00554 ResetDatabase();
00555 }
00556 return ret;
00557 }
00558
00559 bool MythContextPrivate::PromptForDatabaseParams(const QString &error)
00560 {
00561 bool accepted = false;
00562 if (m_gui)
00563 {
00564 TempMainWindow();
00565
00566
00567 if (error.length())
00568 ShowOkPopup(error);
00569
00570
00571 DatabaseSettings settings(m_DBhostCp);
00572 accepted = (settings.exec() == QDialog::Accepted);
00573 if (!accepted)
00574 LOG(VB_GENERAL, LOG_ALERT,
00575 "User cancelled database configuration");
00576
00577 EndTempWindow();
00578 }
00579 else
00580 {
00581 DatabaseParams params = parent->GetDatabaseParams();
00582 QString response;
00583
00584
00585 cout << endl << error.toLocal8Bit().constData() << endl << endl;
00586 response = getResponse("Would you like to configure the database "
00587 "connection now?",
00588 "no");
00589 if (!response.startsWith('y', Qt::CaseInsensitive))
00590 return false;
00591
00592 params.dbHostName = getResponse("Database host name:",
00593 params.dbHostName);
00594 response = getResponse("Should I test connectivity to this host "
00595 "using the ping command?", "yes");
00596 params.dbHostPing = response.startsWith('y', Qt::CaseInsensitive);
00597
00598 params.dbPort = intResponse("Database non-default port:",
00599 params.dbPort);
00600 params.dbName = getResponse("Database name:",
00601 params.dbName);
00602 params.dbUserName = getResponse("Database user name:",
00603 params.dbUserName);
00604 params.dbPassword = getResponse("Database password:",
00605 params.dbPassword);
00606
00607 params.localHostName = getResponse("Unique identifier for this machine "
00608 "(if empty, the local host name "
00609 "will be used):",
00610 params.localHostName);
00611 params.localEnabled = !params.localHostName.isEmpty();
00612
00613 response = getResponse("Would you like to use Wake-On-LAN to retry "
00614 "database connections?",
00615 (params.wolEnabled ? "yes" : "no"));
00616 params.wolEnabled = response.startsWith('y', Qt::CaseInsensitive);
00617
00618 if (params.wolEnabled)
00619 {
00620 params.wolReconnect = intResponse("Seconds to wait for "
00621 "reconnection:",
00622 params.wolReconnect);
00623 params.wolRetry = intResponse("Number of times to retry:",
00624 params.wolRetry);
00625 params.wolCommand = getResponse("Command to use to wake server:",
00626 params.wolCommand);
00627 }
00628
00629 accepted = parent->SaveDatabaseParams(params);
00630 }
00631 return accepted;
00632 }
00633
00639 QString MythContextPrivate::TestDBconnection(void)
00640 {
00641 bool doPing = m_DBparams.dbHostPing;
00642 QString err = QString::null;
00643 QString host = m_DBparams.dbHostName;
00644
00645
00646
00647
00648
00649
00650 if ((host == "localhost") ||
00651 (host == "127.0.0.1") ||
00652 (host == gCoreContext->GetHostName()))
00653 doPing = false;
00654
00655
00656 if (doPing && m_DBparams.wolEnabled)
00657 for (int attempt = 0; attempt < m_DBparams.wolRetry; ++attempt)
00658 {
00659 int wakeupTime = m_DBparams.wolReconnect;
00660
00661 if (ping(host, wakeupTime))
00662 {
00663 doPing = false;
00664 break;
00665 }
00666
00667 LOG(VB_GENERAL, LOG_INFO,
00668 QString("Trying to wake up host %1, attempt %2")
00669 .arg(host).arg(attempt));
00670 myth_system(m_DBparams.wolCommand);
00671
00672 LOG(VB_GENERAL, LOG_INFO,
00673 QString("Waiting for %1 seconds").arg(wakeupTime));
00674 sleep(m_DBparams.wolReconnect);
00675 }
00676
00677 if (doPing)
00678 {
00679 LOG(VB_GENERAL, LOG_INFO,
00680 QString("Testing network connectivity to '%1'").arg(host));
00681 }
00682
00683 if (doPing && !ping(host, 3))
00684 {
00685 SilenceDBerrors();
00686 err = QObject::tr(
00687 "Cannot find (ping) database host %1 on the network",
00688 "Backend Setup");
00689 return err.arg(host);
00690 }
00691
00692
00693
00694
00695
00696 ResetDatabase();
00697
00698 if (!MSqlQuery::testDBConnection())
00699 {
00700 SilenceDBerrors();
00701 return QObject::tr("Cannot login to database", "Backend Setup");
00702 }
00703
00704
00705 return QString::null;
00706 }
00707
00716 void MythContextPrivate::SilenceDBerrors(void)
00717 {
00718
00719
00720 gCoreContext->GetDB()->SetSuppressDBMessages(true);
00721
00722
00723
00724 if (m_DBparams.dbHostName.length())
00725 m_DBhostCp = m_DBparams.dbHostName;
00726
00727 m_DBparams.dbHostName.clear();
00728 gCoreContext->GetDB()->SetDatabaseParams(m_DBparams);
00729 }
00730
00731 void MythContextPrivate::EnableDBerrors(void)
00732 {
00733
00734 if (m_DBparams.dbHostName.isNull() && m_DBhostCp.length())
00735 {
00736 m_DBparams.dbHostName = m_DBhostCp;
00737 gCoreContext->GetDB()->SetDatabaseParams(m_DBparams);
00738 }
00739
00740 gCoreContext->GetDB()->SetSuppressDBMessages(false);
00741 }
00742
00743
00755 void MythContextPrivate::ResetDatabase(void)
00756 {
00757 gCoreContext->GetDBManager()->CloseDatabases();
00758 gCoreContext->GetDB()->SetDatabaseParams(m_DBparams);
00759 gCoreContext->ClearSettingsCache();
00760 }
00761
00765 int MythContextPrivate::ChooseBackend(const QString &error)
00766 {
00767 TempMainWindow();
00768
00769
00770 if (!error.isEmpty())
00771 {
00772 LOG(VB_GENERAL, LOG_ERR, QString("Error: %1").arg(error));
00773 ShowOkPopup(error);
00774 }
00775
00776 LOG(VB_GENERAL, LOG_INFO, "Putting up the UPnP backend chooser");
00777
00778 BackendSelection::Decision ret =
00779 BackendSelection::Prompt(&m_DBparams, m_pConfig);
00780
00781 EndTempWindow();
00782
00783 return (int)ret;
00784 }
00785
00792 int MythContextPrivate::UPnPautoconf(const int milliSeconds)
00793 {
00794 LOG(VB_GENERAL, LOG_INFO, QString("UPNP Search %1 secs")
00795 .arg(milliSeconds / 1000));
00796
00797 SSDP::Instance()->PerformSearch(gBackendURI, milliSeconds / 1000);
00798
00799
00800
00801 MythTimer totalTime; totalTime.start();
00802 MythTimer searchTime; searchTime.start();
00803 while (totalTime.elapsed() < milliSeconds)
00804 {
00805 usleep(25000);
00806 int ttl = milliSeconds - totalTime.elapsed();
00807 if ((searchTime.elapsed() > 249) && (ttl > 1000))
00808 {
00809 LOG(VB_GENERAL, LOG_INFO, QString("UPNP Search %1 secs")
00810 .arg(ttl / 1000));
00811 SSDP::Instance()->PerformSearch(gBackendURI, ttl / 1000);
00812 searchTime.start();
00813 }
00814 }
00815
00816 SSDPCacheEntries *backends = SSDP::Instance()->Find(gBackendURI);
00817
00818 if (!backends)
00819 {
00820 LOG(VB_GENERAL, LOG_INFO, "No UPnP backends found");
00821 return 0;
00822 }
00823
00824 int count = backends->Count();
00825 if (count)
00826 {
00827 LOG(VB_GENERAL, LOG_INFO,
00828 QString("Found %1 UPnP backends").arg(count));
00829 }
00830 else
00831 {
00832 LOG(VB_GENERAL, LOG_ERR,
00833 "No UPnP backends found, but SSDP::Find() not NULL");
00834 }
00835
00836 if (count != 1)
00837 {
00838 backends->Release();
00839 return count;
00840 }
00841
00842
00843 DeviceLocation *BE = backends->GetFirst();
00844 backends->Release();
00845
00846
00847
00848 int ret = (UPnPconnect(BE, QString::null)) ? 1 : -1;
00849
00850 BE->Release();
00851
00852 return ret;
00853 }
00854
00860 bool MythContextPrivate::DefaultUPnP(QString &error)
00861 {
00862 QString loc = "DefaultUPnP() - ";
00863 QString PIN = m_pConfig->GetValue(kDefaultPIN, "");
00864 QString USN = m_pConfig->GetValue(kDefaultUSN, "");
00865
00866 if (USN.isEmpty())
00867 {
00868 LOG(VB_UPNP, LOG_INFO, loc + "No default UPnP backend");
00869 return false;
00870 }
00871
00872 LOG(VB_UPNP, LOG_INFO, loc + "config.xml has default " +
00873 QString("PIN '%1' and host USN: %2") .arg(PIN).arg(USN));
00874
00875
00876
00877 int timeout_ms = 2000;
00878 LOG(VB_GENERAL, LOG_INFO, QString("UPNP Search up to %1 secs")
00879 .arg(timeout_ms / 1000));
00880 SSDP::Instance()->PerformSearch(gBackendURI, timeout_ms / 1000);
00881
00882
00883
00884
00885
00886 DeviceLocation *pDevLoc = NULL;
00887 MythTimer totalTime; totalTime.start();
00888 MythTimer searchTime; searchTime.start();
00889 while (totalTime.elapsed() < timeout_ms)
00890 {
00891 pDevLoc = SSDP::Instance()->Find( gBackendURI, USN );
00892
00893 if (pDevLoc)
00894 break;
00895
00896 usleep(25000);
00897
00898 int ttl = timeout_ms - totalTime.elapsed();
00899 if ((searchTime.elapsed() > 249) && (ttl > 1000))
00900 {
00901 LOG(VB_GENERAL, LOG_INFO, QString("UPNP Search up to %1 secs")
00902 .arg(ttl / 1000));
00903 SSDP::Instance()->PerformSearch(gBackendURI, ttl / 1000);
00904 searchTime.start();
00905 }
00906 }
00907
00908
00909
00910 if (!pDevLoc)
00911 {
00912 error = "Cannot find default UPnP backend";
00913 return false;
00914 }
00915
00916 if (UPnPconnect(pDevLoc, PIN))
00917 {
00918 pDevLoc->Release();
00919
00920 return true;
00921 }
00922
00923 pDevLoc->Release();
00924
00925 error = "Cannot connect to default backend via UPnP. Wrong saved PIN?";
00926 return false;
00927 }
00928
00932 bool MythContextPrivate::UPnPconnect(const DeviceLocation *backend,
00933 const QString &PIN)
00934 {
00935 QString error;
00936 QString loc = "UPnPconnect() - ";
00937 QString URL = backend->m_sLocation;
00938 MythXMLClient client(URL);
00939
00940 LOG(VB_UPNP, LOG_INFO, loc + QString("Trying host at %1").arg(URL));
00941 switch (client.GetConnectionInfo(PIN, &m_DBparams, error))
00942 {
00943 case UPnPResult_Success:
00944 gCoreContext->GetDB()->SetDatabaseParams(m_DBparams);
00945 LOG(VB_UPNP, LOG_INFO, loc +
00946 "Got database hostname: " + m_DBparams.dbHostName);
00947 return true;
00948
00949 case UPnPResult_ActionNotAuthorized:
00950
00951
00952
00953 LOG(VB_UPNP, LOG_ERR, loc + "Wrong PIN?");
00954 return false;
00955
00956 default:
00957 LOG(VB_UPNP, LOG_ERR, loc + error);
00958 break;
00959 }
00960
00961
00962
00963
00964 URL.remove("http://");
00965 URL.remove(QRegExp("[:/].*"));
00966 if (URL.isEmpty())
00967 return false;
00968
00969 LOG(VB_UPNP, LOG_INFO, "Trying default DB credentials at " + URL);
00970 m_DBparams.dbHostName = URL;
00971
00972 return true;
00973 }
00974
00975 bool MythContextPrivate::event(QEvent *e)
00976 {
00977 if (e->type() == (QEvent::Type) MythEvent::MythEventMessage)
00978 {
00979 if (disableeventpopup)
00980 return true;
00981
00982 MythEvent *me = (MythEvent*)e;
00983 if (me->Message() == "VERSION_MISMATCH" && (1 == me->ExtraDataCount()))
00984 ShowVersionMismatchPopup(me->ExtraData(0).toUInt());
00985 else if (me->Message() == "CONNECTION_FAILURE")
00986 ShowConnectionFailurePopup(false);
00987 else if (me->Message() == "PERSISTENT_CONNECTION_FAILURE")
00988 ShowConnectionFailurePopup(true);
00989 else if (me->Message() == "CONNECTION_RESTABLISHED")
00990 HideConnectionFailurePopup();
00991 return true;
00992 }
00993
00994 return QObject::event(e);
00995 }
00996
00997 void MythContextPrivate::ShowConnectionFailurePopup(bool persistent)
00998 {
00999 if (MBEconnectPopup)
01000 return;
01001
01002 QString message = (persistent) ?
01003 QObject::tr(
01004 "The connection to the master backend "
01005 "server has gone away for some reason. "
01006 "Is it running?") :
01007 QObject::tr(
01008 "Could not connect to the master backend server. Is "
01009 "it running? Is the IP address set for it in "
01010 "mythtv-setup correct?");
01011
01012 if (HasMythMainWindow() && m_ui && m_ui->IsScreenSetup())
01013 {
01014 MBEconnectPopup = ShowOkPopup(
01015 message, m_sh, SLOT(ConnectFailurePopupClosed()));
01016 }
01017 }
01018
01019 void MythContextPrivate::HideConnectionFailurePopup(void)
01020 {
01021 if (MBEconnectPopup)
01022 {
01023 MBEconnectPopup->Close();
01024 MBEconnectPopup = NULL;
01025 }
01026 }
01027
01028 void MythContextPrivate::ShowVersionMismatchPopup(uint remote_version)
01029 {
01030 if (MBEversionPopup)
01031 return;
01032
01033 QString message =
01034 QObject::tr(
01035 "The server uses network protocol version %1, "
01036 "but this client only understands version %2. "
01037 "Make sure you are running compatible versions of "
01038 "the backend and frontend.")
01039 .arg(remote_version).arg(MYTH_PROTO_VERSION);
01040
01041 if (HasMythMainWindow() && m_ui && m_ui->IsScreenSetup())
01042 {
01043 MBEversionPopup = ShowOkPopup(
01044 message, m_sh, SLOT(VersionMismatchPopupClosed()));
01045 }
01046 else
01047 {
01048 LOG(VB_GENERAL, LOG_ERR, LOC + message);
01049 qApp->exit(GENERIC_EXIT_SOCKET_ERROR);
01050 }
01051 }
01052
01053 void MythContextSlotHandler::ConnectFailurePopupClosed(void)
01054 {
01055 d->MBEconnectPopup = NULL;
01056 }
01057
01058 void MythContextSlotHandler::VersionMismatchPopupClosed(void)
01059 {
01060 d->MBEversionPopup = NULL;
01061 qApp->exit(GENERIC_EXIT_SOCKET_ERROR);
01062 }
01063
01064 MythContext::MythContext(const QString &binversion)
01065 : d(NULL), app_binary_version(binversion)
01066 {
01067 #ifdef USING_MINGW
01068 static bool WSAStarted = false;
01069 if (!WSAStarted) {
01070 WSADATA wsadata;
01071 int res = WSAStartup(MAKEWORD(2, 0), &wsadata);
01072 LOG(VB_SOCKET, LOG_INFO,
01073 QString("WSAStartup returned %1").arg(res));
01074 }
01075 #endif
01076
01077 d = new MythContextPrivate(this);
01078
01079 gCoreContext = new MythCoreContext(app_binary_version, d);
01080
01081 if (!gCoreContext || !gCoreContext->Init())
01082 {
01083 LOG(VB_GENERAL, LOG_EMERG, LOC + "Unable to allocate MythCoreContext");
01084 qApp->exit(GENERIC_EXIT_NO_MYTHCONTEXT);
01085 }
01086 }
01087
01088 bool MythContext::Init(const bool gui,
01089 const bool promptForBackend,
01090 const bool disableAutoDiscovery,
01091 const bool ignoreDB)
01092 {
01093 if (!d)
01094 {
01095 LOG(VB_GENERAL, LOG_EMERG, LOC + "Init() Out-of-memory");
01096 return false;
01097 }
01098
01099 if (app_binary_version != MYTH_BINARY_VERSION)
01100 {
01101 LOG(VB_GENERAL, LOG_EMERG,
01102 QString("Application binary version (%1) does not "
01103 "match libraries (%2)")
01104 .arg(app_binary_version) .arg(MYTH_BINARY_VERSION));
01105
01106 QString warning = QObject::tr(
01107 "This application is not compatible "
01108 "with the installed MythTV libraries.");
01109 if (gui)
01110 {
01111 d->TempMainWindow(false);
01112 ShowOkPopup(warning);
01113 }
01114 LOG(VB_GENERAL, LOG_WARNING, warning);
01115
01116 return false;
01117 }
01118
01119 #ifdef _WIN32
01120
01121
01122 QString home = getenv("HOME");
01123 if (home.isEmpty())
01124 {
01125 home = getenv("LOCALAPPDATA");
01126 if (home.isEmpty())
01127 home = getenv("APPDATA");
01128 if (home.isEmpty())
01129 home = QString(".");
01130
01131 _putenv(QString("HOME=%1").arg(home).toLocal8Bit().constData());
01132 }
01133 #endif
01134
01135
01136
01137 QString homedir = QDir::homePath();
01138 QString confdir = getenv("MYTHCONFDIR");
01139 if ((homedir.isEmpty() || homedir == "/") &&
01140 (confdir.isEmpty() || confdir.contains("$HOME")))
01141 {
01142 QString warning = "Cannot locate your home directory."
01143 " Please set the environment variable HOME";
01144 if (gui)
01145 {
01146 d->TempMainWindow(false);
01147 ShowOkPopup(warning);
01148 }
01149 LOG(VB_GENERAL, LOG_WARNING, warning);
01150
01151 return false;
01152 }
01153
01154 if (!d->Init(gui, promptForBackend, disableAutoDiscovery, ignoreDB))
01155 {
01156 return false;
01157 }
01158
01159 gCoreContext->ActivateSettingsCache(true);
01160
01161 return true;
01162 }
01163
01164 MythContext::~MythContext()
01165 {
01166 if (MThreadPool::globalInstance()->activeThreadCount())
01167 LOG(VB_GENERAL, LOG_INFO, "Waiting for threads to exit.");
01168
01169 ShutdownRRT();
01170 MThreadPool::globalInstance()->waitForDone();
01171 logStop();
01172
01173 SSDP::Shutdown();
01174 TaskQueue::Shutdown();
01175
01176 delete gCoreContext;
01177 gCoreContext = NULL;
01178
01179 delete d;
01180 }
01181
01182 bool MythContext::TestPopupVersion(const QString &name,
01183 const QString &libversion,
01184 const QString &pluginversion)
01185 {
01186 if (libversion == pluginversion)
01187 return true;
01188
01189 QString err = QObject::tr(
01190 "Plugin %1 is not compatible with the installed MythTV "
01191 "libraries.");
01192
01193 LOG(VB_GENERAL, LOG_EMERG,
01194 QString("Plugin %1 (%2) binary version does not "
01195 "match libraries (%3)")
01196 .arg(name).arg(pluginversion).arg(libversion));
01197
01198 if (GetMythMainWindow() && !d->disablelibrarypopup)
01199 ShowOkPopup(err.arg(name));
01200
01201 return false;
01202 }
01203
01204 void MythContext::SetDisableEventPopup(bool check)
01205 {
01206 d->disableeventpopup = check;
01207 }
01208
01209 void MythContext::SetDisableLibraryPopup(bool check)
01210 {
01211 d->disablelibrarypopup = check;
01212 }
01213
01214 void MythContext::SetPluginManager(MythPluginManager *pmanager)
01215 {
01216 d->pluginmanager = pmanager;
01217 }
01218
01219 MythPluginManager *MythContext::getPluginManager(void)
01220 {
01221 return d->pluginmanager;
01222 }
01223
01224 DatabaseParams MythContext::GetDatabaseParams(void)
01225 {
01226 return d->m_DBparams;
01227 }
01228
01229 bool MythContext::SaveDatabaseParams(const DatabaseParams ¶ms)
01230 {
01231 return d->SaveDatabaseParams(params, false);
01232 }
01233
01234