|
MythTV
0.26-pre
|
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 // Qt 4.4 has window-focus problems 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 // ask user for language settings 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 // We don't have a database yet, so lets use the config.xml file. 00278 m_pConfig = new XmlConfiguration("config.xml"); 00279 00280 // Creates screen saver control if we will have a GUI 00281 if (gui) 00282 m_ui = GetMythUI(); 00283 00284 // ---- database connection stuff ---- 00285 00286 if (!ignoreDB && !FindDatabase(promptForBackend, noPrompt)) 00287 return false; 00288 00289 // ---- keep all DB-using stuff below this line ---- 00290 00291 // Prompt for language if this is a first time install and 00292 // we didn't already do so. 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 // We can only prompt if autodiscovery is enabled.. 00333 bool manualSelect = prompt && !noAutodetect; 00334 00335 QString failure; 00336 00337 // 1. Either load config.xml or use sensible "localhost" defaults: 00338 bool loaded = LoadDatabaseSettings(); 00339 DatabaseParams dbParamsFromFile = m_DBparams; 00340 00341 // In addition to the UI chooser, we can also try to autoSelect later, 00342 // but only if we're not doing manualSelect and there was no 00343 // valid config.xml 00344 bool autoSelect = !manualSelect && !loaded && !noAutodetect; 00345 00346 // 2. If the user isn't forcing up the chooser UI, look for a default 00347 // backend in config.xml, then test DB settings we've got so far: 00348 if (!manualSelect) 00349 { 00350 // config.xml may contain a backend host UUID and PIN. 00351 // If so, try to AutoDiscover UPnP server, and use its DB settings: 00352 00353 if (DefaultUPnP(failure)) // Probably a valid backend, 00354 autoSelect = manualSelect = false; // so disable any further UPnP 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 // 3. Try to automatically find the single backend: 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 // Multiple BEs, or needs PIN. 00381 manualSelect |= (count > 1 || count == -1); 00382 } 00383 00384 manualSelect &= m_gui; // no interactive command-line chooser yet 00385 00386 // Queries the user for the DB info 00387 do 00388 { 00389 if (manualSelect) 00390 { 00391 // Get the user to select a backend from a possible list: 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 // try new format first 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) // if new format fails, try legacy format 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 // only rewrite file if it has changed 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 // clear out any legacy nodes.. 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 // actually save the file 00547 m_pConfig->Save(); 00548 00549 // Save the new settings: 00550 m_DBparams = params; 00551 gCoreContext->GetDB()->SetDatabaseParams(m_DBparams); 00552 00553 // If database has changed, force its use: 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 // Tell the user what went wrong: 00567 if (error.length()) 00568 ShowOkPopup(error); 00569 00570 // ask user for database parameters 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 // give user chance to skip config 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 // 1. Check the supplied host or IP address, to prevent the app 00647 // appearing to hang if we cannot route to the machine: 00648 00649 // No need to ping myself 00650 if ((host == "localhost") || 00651 (host == "127.0.0.1") || 00652 (host == gCoreContext->GetHostName())) 00653 doPing = false; 00654 00655 // If WOL is setup, the backend might be sleeping: 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)) // Fail after trying for 3 seconds 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 // 2. Try to login, et c: 00694 00695 // Current DB connection may have been silenced (invalid): 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 // This silences any DB errors from Get*Setting(), 00719 // (which is the vast majority of them) 00720 gCoreContext->GetDB()->SetSuppressDBMessages(true); 00721 00722 // Save the configured hostname, so that we can 00723 // still display it in the DatabaseSettings screens 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 // Restore (possibly) blanked hostname 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 // Tell the user what went wrong: 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 // Search for a total of 'milliSeconds' ms, sending new search packet 00800 // about every 250 ms until less than one second remains. 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 // Get this backend's location: 00843 DeviceLocation *BE = backends->GetFirst(); 00844 backends->Release(); 00845 00846 // We don't actually know the backend's access PIN, so this will 00847 // only work for ones that have PIN access disabled (i.e. 0000) 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 // We need to give the server time to respond... 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 // The stored PIN is probably not correct. 00951 // We could prompt for the PIN and try again, but that needs a UI. 00952 // Easier to fail for now, and put up the full UI selector later 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 // This backend may have a local DB with the default user/pass/DBname. 00962 // For whatever reason, we have failed to get anything back via UPnP, 00963 // so we might as well try the database directly as a last resort. 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 // HOME environment variable might not be defined 01121 // some libraries will fail without it 01122 QString home = getenv("HOME"); 01123 if (home.isEmpty()) 01124 { 01125 home = getenv("LOCALAPPDATA"); // Vista 01126 if (home.isEmpty()) 01127 home = getenv("APPDATA"); // XP 01128 if (home.isEmpty()) 01129 home = QString("."); // getenv("TEMP")? 01130 01131 _putenv(QString("HOME=%1").arg(home).toLocal8Bit().constData()); 01132 } 01133 #endif 01134 01135 // If HOME isn't defined, we won't be able to use default confdir of 01136 // $HOME/.mythtv nor can we rely on a MYTHCONFDIR that references $HOME 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 /* vim: set expandtab tabstop=4 shiftwidth=4: */
1.7.6.1