MythTV  0.26-pre
mythcontext.cpp
Go to the documentation of this file.
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 &params, 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 &params, 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 &params)
01230 {
01231     return d->SaveDatabaseParams(params, false);
01232 }
01233 
01234 /* vim: set expandtab tabstop=4 shiftwidth=4: */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends