Ticket #4075: backend-autoselect-2.patch

File backend-autoselect-2.patch, 33.0 KB (added by Nigel, 13 years ago)

New version, addresses issues 1 thru 4

  • libs/libmyth/dbsettings.cpp

     
    66
    77class MythDbSettings1: public VerticalConfigurationGroup {
    88public:
    9     MythDbSettings1();
     9    MythDbSettings1(const QString &DBhostOverride = QString::null);
    1010
    1111    void load();
    1212    void save();
     
    2020    TransLineEditSetting *dbUserName;
    2121    TransLineEditSetting *dbPassword;
    2222    TransComboBoxSetting *dbType;
     23
     24    QString              m_DBhostOverride;
    2325};
    2426
    2527class MythDbSettings2: public VerticalConfigurationGroup {
     
    7173    }
    7274};
    7375
    74 MythDbSettings1::MythDbSettings1() :
     76MythDbSettings1::MythDbSettings1(const QString &DbHostOverride) :
    7577    VerticalConfigurationGroup(false, true, false, false)
    7678{
     79    m_DBhostOverride = DbHostOverride;
     80
    7781    setLabel(QObject::tr("Database Configuration") + " 1/2");
    7882
    7983    info = new TransLabelSetting();
     
    236240        info->setValue(info->getValue() + "\nRequired fields are marked "
    237241                                          "with an asterisk (*).");
    238242
    239     dbHostName->setValue(params.dbHostName);
    240243    if (params.dbHostName.isEmpty())
     244    {
    241245        dbHostName->setLabel("* " + dbHostName->getLabel());
     246        dbHostName->setValue(m_DBhostOverride);
     247    }
     248    else
     249        dbHostName->setValue(params.dbHostName);
    242250
    243251    dbHostPing->setValue(params.dbHostPing);
    244252
     
    304312    gContext->SaveDatabaseParams(params);
    305313}
    306314
    307 DatabaseSettings::DatabaseSettings()
     315DatabaseSettings::DatabaseSettings(const QString &DBhostOverride)
    308316{
    309     addChild(new MythDbSettings1());
     317    addChild(new MythDbSettings1(DBhostOverride));
    310318    addChild(new MythDbSettings2());
    311319}
    312320
  • libs/libmyth/dbsettings.h

     
    55
    66class MPUBLIC DatabaseSettings: public ConfigurationWizard {
    77public:
    8     DatabaseSettings();
     8    DatabaseSettings(const QString &DBhostOverride = QString::null);
    99   
    1010    // This routine calls wizard->addChild() for each of
    1111    // the database configuration screens.  This allows
  • libs/libmyth/mythcontext.cpp

     
    2727#include "mythplugin.h"
    2828#include "screensaver.h"
    2929#include "DisplayRes.h"
     30#include "backendselect.h"
    3031#include "dbsettings.h"
    3132#include "langsettings.h"
    3233#include "mythdbcon.h"
     
    3536#include "themeinfo.h"
    3637
    3738#include "libmythui/mythmainwindow.h"
     39#include "libmythupnp/mythxmlclient.h"
     40#include "libmythupnp/upnp.h"
    3841
    3942// These defines provide portability for different
    4043// plugin file names.
     
    5558
    5659QMutex avcodeclock(true);
    5760
     61// Some common UPnP search and XML value strings
     62const QString kDefaultPIN = "UPnP/MythFrontend/DefaultBackend/SecurityPin";
     63const QString kDefaultUSN = "UPnP/MythFrontend/DefaultBackend/USN";
     64const QString gBackendURI = "urn:schemas-mythtv-org:device:MasterMediaServer:1";
     65
     66
    5867int parse_verbose_arg(QString arg)
    5968{
    6069    QString option;
     
    190199    }
    191200}
    192201
     202
    193203class MythContextPrivate
    194204{
    195205  public:
    196206    MythContextPrivate(MythContext *lparent);
    197207   ~MythContextPrivate();
    198208
    199     bool Init(bool gui, DatabaseParams *pParams = NULL );
    200     bool FindDatabase(const DatabaseParams *pParams);
     209    bool Init        (const bool gui, UPnp *UPnPclient,
     210                      const bool prompt, const bool noPrompt);
     211    bool FindDatabase(const bool prompt, const bool noPrompt);
    201212
    202213    bool IsWideMode() const {return (m_baseWidth == 1280);}
    203214    void SetWideMode() {m_baseWidth = 1280; m_baseHeight = 720;}
     
    211222    void StoreGUIsettings(void);
    212223
    213224    void LoadLogSettings(void);
    214     bool LoadDatabaseSettings(const DatabaseParams *pParams = NULL);
     225    void LoadDatabaseSettings(void);
    215226   
    216227    bool LoadSettingsFile(void);
    217228    bool WriteSettingsFile(const DatabaseParams &params,
     
    220231
    221232    QString getResponse(const QString &query, const QString &def);
    222233    int     intResponse(const QString &query, int def);
    223     bool    PromptForDatabaseParams(QString error);
     234    bool    PromptForDatabaseParams(const QString &error);
    224235    QString TestDBconnection(void);
    225236    void    ResetDatabase(void);
    226237
     238    bool    InitUPnP(void);
     239    void    CleanUPnP(void);
     240    int     ChooseBackend(const QString &error);
     241    int     UPnPautoconf(const int milliSeconds = 2000);
     242    bool    DefaultUPnP(QString &error);
     243    bool    UPnPconnect(const DeviceLocation *device, const QString &PIN);
    227244
     245
    228246    MythContext *parent;
    229247
    230248    Settings *m_settings;          ///< connection stuff, theme, button style
     
    257275    QString m_localhostname;     ///< hostname from mysql.txt or gethostname()
    258276
    259277    DatabaseParams  m_DBparams;  ///< Current database host & WOL details
     278    QString         m_DBhostCp;  ///< dbHostName backup
    260279
     280    UPnp             *m_UPnP;    ///< For automatic backend discover
     281    XmlConfiguration *m_XML;
     282    HttpServer       *m_HTTP;
     283
    261284    QMutex serverSockLock;
    262285    bool attemptingToConnect;
    263286
     
    322345      m_xbase(0), m_ybase(0), m_height(0), m_width(0),
    323346      m_baseWidth(800), m_baseHeight(600),
    324347      m_localhostname(QString::null),
     348      m_UPnP(NULL), m_XML(NULL), m_HTTP(NULL),
    325349      serverSockLock(false),
    326350      attemptingToConnect(false),
    327351      language(""),
     
    350374{
    351375    imageCache.clear();
    352376
     377    CleanUPnP();
    353378    if (m_settings)
    354379        delete m_settings;
    355380    if (m_qtThemeSettings)
     
    378403 */
    379404void MythContextPrivate::TempMainWindow(bool languagePrompt)
    380405{
     406    if (mainWindow)
     407        return;
     408
     409    // We clear the hostname so MSqlQuery will fail, instead of long
     410    // timeouts per DB value, or hundreds of lines of DB connect errors.
     411    // We save the value for later possible editing in the DbSettings pages
     412    if (m_DBparams.dbHostName.length())
     413    {
     414        m_DBhostCp = m_DBparams.dbHostName;
     415        m_DBparams.dbHostName = "";
     416    }
     417
    381418    m_settings->SetSetting("Theme", "blue");
    382419#ifdef Q_WS_MACX
    383420    // Myth looks horrible in default Mac style for Qt
     
    482519    }
    483520}
    484521
    485 bool MythContextPrivate::Init(bool gui, DatabaseParams *pParams)
     522bool MythContextPrivate::Init(const bool gui, UPnp *UPnPclient,
     523                              const bool promptForBackend, const bool noPrompt)
    486524{
    487525    m_gui = gui;
     526    if (UPnPclient)
     527    {
     528        m_UPnP = UPnPclient;
     529        m_XML  = (XmlConfiguration *)UPnp::g_pConfig;
     530    }
    488531
    489532    // Creates screen saver control if we will have a GUI
    490533    if (gui)
     
    492535
    493536    // ---- database connection stuff ----
    494537
    495     if (!FindDatabase(pParams))
     538    if (!FindDatabase(promptForBackend, noPrompt))
    496539        return false;
    497540
    498541    // ---- keep all DB-using stuff below this line ----
     
    508551
    509552/**
    510553 * Get database connection settings and test connectivity.
     554 *
     555 * Can use UPnP AutoDiscovery to locate backends, and get their DB settings.
     556 * The user can force the AutoDiscovery chooser with the --prompt argument,
     557 * and disable it by using the --disable-autodiscovery argument.
     558 * There is also an autoconfigure function, which counts the backends,
     559 * and if there is exactly one, uses it as above.
     560 *
     561 * Despite its name, the disable argument currently only disables the chooser.
     562 * If set, autoconfigure will still be attempted in some situations.
    511563 */
    512 bool MythContextPrivate::FindDatabase(const DatabaseParams *pParams)
     564bool MythContextPrivate::FindDatabase(const bool prompt, const bool noPrompt)
    513565{
    514     // Attempts to read DB info from "mysql.txt" from the
    515     // filesystem, or create it if it does not exist.
    516     if (!LoadDatabaseSettings(pParams))
    517         return false;
     566    // The two bool. args actually form a Yes/Maybe/No (A tristate bool :-)
     567    bool manualSelect = prompt && !noPrompt;
    518568
    519     // Attempt to connect to the database, get message for user if it failed.
    520     QString failure = TestDBconnection();
     569    // In addition to the UI chooser, we can also try to autoconfigure
     570    bool autoSelect = !manualSelect;
    521571
     572    QString failure;
     573
     574
     575    // 1. Load either mysql.txt, or use sensible "localhost" defaults:
     576    LoadDatabaseSettings();
     577
     578
     579    // 2. If the user isn't forcing up the chooser UI, look for a default
     580    //    backend in config.xml, then test DB settings we've got so far:
     581    if (!manualSelect)
     582    {
     583        // config.xml may contain a backend host UUID and PIN.
     584        // If so, try to AutoDiscover UPnP server, and use its DB settings:
     585
     586        if (DefaultUPnP(failure))                // Probably a valid backend,
     587            autoSelect = manualSelect = false;   // so disable any further UPnP
     588
     589
     590        failure = TestDBconnection();
     591        if (failure.isEmpty())
     592            goto DBfound;
     593    }
     594
     595
     596    // 3. Try to automatically find the single backend:
     597    if (autoSelect)
     598    {
     599        int count = UPnPautoconf();
     600
     601        if (count == 0)
     602            failure = "No UPnP backends found";
     603
     604        if (count == 1)
     605        {
     606            failure = TestDBconnection();
     607            if (failure.isEmpty())
     608                goto DBfound;
     609        }
     610
     611        if (count > 1 || count == -1)     // Multiple BEs, or needs PIN.
     612            manualSelect = !noPrompt;     // If allowed, prompt user
     613    }
     614
     615    if (!m_gui)
     616        manualSelect = false;  // no interactive command-line chooser yet
     617
     618
     619
     620    // Last, get the user to select a backend from a possible list:
     621    if (manualSelect)
     622    {
     623        switch (ChooseBackend(QString::null))
     624        {
     625            case -1:    // User asked to configure database manually
     626                if (PromptForDatabaseParams(""))
     627                    break;
     628                else
     629                    goto NoDBfound;   // User cancelled - changed their mind?
     630   
     631            case 0:   // User cancelled. Exit application
     632                goto NoDBfound;
     633
     634            case 1:    // User selected a backend, so m_DBparams
     635                break; // should now contain the database details
     636
     637            default:
     638                goto NoDBfound;
     639        }
     640        failure = TestDBconnection();
     641    }
     642
     643
    522644    // Queries the user for the DB info, using the command
    523645    // line or the GUI depending on the application.
    524646    while (failure.length())
    525647    {
    526648        VERBOSE(VB_IMPORTANT, failure);
    527         if (PromptForDatabaseParams(failure))
     649        if (( manualSelect && ChooseBackend(failure)) ||
     650            (!manualSelect && PromptForDatabaseParams(failure)))
    528651        {
    529652            failure = TestDBconnection();
    530653            if (failure.length())
    531654                VERBOSE(VB_IMPORTANT, failure);
    532655        }
    533656        else
    534             return false;
     657            goto NoDBfound;
    535658    }
    536659
     660DBfound:
     661    //VERBOSE(VB_GENERAL, "FindDatabase() - Success!");
     662    ResetDatabase();
     663    CleanUPnP();
    537664    return true;
     665
     666NoDBfound:
     667    //VERBOSE(VB_GENERAL, "FindDatabase() - failed");
     668    CleanUPnP();
     669    return false;
    538670}
    539671
    540672/**
     
    610742}
    611743
    612744/**     
    613  * Load database and host settings from mysql.txt and UPnP BE discovery.
    614  *     
    615  * \note Creating a default mysql.txt is actually not necessary.
    616  *       The defaults are enough for a simple "localhost" FE & MBE,
    617  *       and UPnP covers the other situations.
     745 * Load database and host settings from mysql.txt, or set some defaults
     746 *
     747 * \returns true if mysql.txt was parsed
    618748 */
    619 bool MythContextPrivate::LoadDatabaseSettings(const DatabaseParams *pParams)
     749void MythContextPrivate::LoadDatabaseSettings(void)
    620750{
    621     // Always load settings first from mysql.txt so LocalHostName can be used.
    622 
    623751    if (!LoadSettingsFile())
    624752    {
    625753        VERBOSE(VB_IMPORTANT, "Unable to read configuration file mysql.txt");
     
    638766        m_DBparams.wolReconnect  = 0;
    639767        m_DBparams.wolRetry      = 5;
    640768        m_DBparams.wolCommand    = "echo 'WOLsqlServerCommand not set'";
    641 
    642         VERBOSE(VB_IMPORTANT, "Trying to create a basic mysql.txt file");
    643         if (!WriteSettingsFile(m_DBparams))
    644             return false;
    645769    }
    646770
    647     // Overlay mysql.txt settings if we were passed a DatabaseParams
    648 
    649     if (pParams != NULL)
    650     {
    651         m_DBparams.dbHostName   = pParams->dbHostName;
    652         m_DBparams.dbPort       = pParams->dbPort;
    653         m_DBparams.dbUserName   = pParams->dbUserName;
    654         m_DBparams.dbPassword   = pParams->dbPassword;
    655         m_DBparams.dbName       = pParams->dbName;
    656         m_DBparams.dbType       = pParams->dbType;
    657       //m_DBparams.wolEnabled   = pParams->wolEnabled;
    658         m_DBparams.wolReconnect = pParams->wolReconnect;
    659         m_DBparams.wolRetry     = pParams->wolRetry;
    660         m_DBparams.wolCommand   = pParams->wolCommand;
    661     }
    662 
    663771    // Even if we have loaded the settings file, it may be incomplete,
    664772    // so we check for missing values and warn user
    665773    FindSettingsProbs();
     
    673781        {
    674782            VERBOSE(VB_IMPORTANT,
    675783                    "MCP: Error, could not determine host name." + ENO);
    676             return false;
     784            localhostname[0] = '\0';
    677785        }
    678786        m_localhostname = localhostname;
    679787        VERBOSE(VB_IMPORTANT, "Empty LocalHostName.");
    680788    }
    681789    VERBOSE(VB_GENERAL, "Using localhost value of " + m_localhostname);
    682 
    683     return true;
    684790}
    685791
    686792/**
     
    889995    return (ok ? resp : def);
    890996}
    891997
    892 bool MythContextPrivate::PromptForDatabaseParams(QString error)
     998bool MythContextPrivate::PromptForDatabaseParams(const QString &error)
    893999{
    8941000    bool accepted = false;
    8951001    if (m_gui)
     
    9011007            MythPopupBox::showOkPopup(mainWindow, "DB connect failure", error);
    9021008       
    9031009        // ask user for database parameters
    904         DatabaseSettings settings;
     1010        DatabaseSettings settings(m_DBhostCp);
    9051011        accepted = (settings.exec() == QDialog::Accepted);
    9061012        if (!accepted)
    907             VERBOSE(VB_IMPORTANT, "User canceled database configuration");
     1013            VERBOSE(VB_IMPORTANT, "User cancelled database configuration");
    9081014
    9091015        EndTempWindow();
    9101016    }
     
    9861092        VERBOSE(VB_GENERAL, "Testing network connectivity to " + host);
    9871093    if (doPing && !ping(host, 3))  // Fail after trying for 3 seconds
    9881094    {
     1095        // Save, to display in DatabaseSettings screens
     1096        m_DBhostCp = m_DBparams.dbHostName;
     1097
    9891098        // Cause MSqlQuery to fail, instead of minutes timeout per DB value
    9901099        m_DBparams.dbHostName = "";
    9911100
     
    9981107
    9991108    if (port && !telnet(host, port))
    10001109    {
    1001         // Cause MSqlQuery to fail, instead of several error lines per DB value
    1002         m_DBparams.dbHostName = "";
    1003 
    10041110        err = parent->tr("Cannot connect to port %1 on database host %2");
    10051111        return err.arg(port).arg(host);
    10061112    }
     
    10091115    // 3. Finally, try to login, et c:
    10101116
    10111117    if (!MSqlQuery::testDBConnection())
    1012     {
    1013         // Cause MSqlQuery to fail, instead of several error lines per DB value
    1014         m_DBparams.dbHostName = "";
    1015 
    10161118        return parent->tr(QString("Cannot login to database?"));
    1017     }
    10181119
    10191120
    10201121    return QString::null;
     
    10381139}
    10391140
    10401141
     1142bool MythContextPrivate::InitUPnP(void)
     1143{
     1144    if (m_UPnP)
     1145        return true;
     1146
     1147    VERBOSE(VB_UPNP, "Setting UPnP client for backend autodiscovery...");
     1148
     1149    if (!m_XML)
     1150        m_XML = new XmlConfiguration("");   // No file - use defaults only
     1151
     1152    m_UPnP = new UPnp();
     1153    m_UPnP->SetConfiguration(m_XML);
     1154
     1155    int port=6549;
     1156    m_HTTP = new HttpServer(port);
     1157
     1158    if (!m_HTTP->ok())
     1159    {
     1160        VERBOSE(VB_IMPORTANT, "MCP::InitUPnP() - HttpServer Create Error");
     1161        CleanUPnP();
     1162        return false;
     1163    }
     1164
     1165    if (!m_UPnP->Initialize(port, m_HTTP))
     1166    {
     1167        VERBOSE(VB_IMPORTANT, "MCP::InitUPnP() - UPnp::Initialize() Error");
     1168        CleanUPnP();
     1169        return false;
     1170    }
     1171
     1172    m_UPnP->Start();
     1173
     1174    return true;
     1175}
     1176
     1177void MythContextPrivate::CleanUPnP(void)
     1178{
     1179    if (m_UPnP && !m_HTTP)  // Init was passed an existing UPnP
     1180        return;             // so let the caller delete it cleanly
     1181
     1182    if (m_UPnP)
     1183    {
     1184        // This takes a few seconds, so inform the user:
     1185        VERBOSE(VB_GENERAL, "Deleting UPnP client...");
     1186
     1187        delete m_UPnP;  // This also deletes m_XML
     1188        m_UPnP = NULL;
     1189        m_XML  = NULL;
     1190    }
     1191
     1192    if (m_HTTP)
     1193    {
     1194        delete m_HTTP;
     1195        m_HTTP = NULL;
     1196    }
     1197}
     1198
     1199/**
     1200 * Search for backends via UPnP, put up a UI for the user to choose one
     1201 */
     1202int MythContextPrivate::ChooseBackend(const QString &error)
     1203{
     1204    if (!InitUPnP())
     1205        return -1;
     1206
     1207    TempMainWindow();
     1208 
     1209    // Tell the user what went wrong:
     1210    if (error.length())
     1211        MythPopupBox::showOkPopup(mainWindow, "DB connect failure", error);
     1212
     1213    VERBOSE(VB_GENERAL, "Putting up the UPnP backend chooser");
     1214
     1215    BackendSelect *BEsel = new BackendSelect(mainWindow, &m_DBparams);
     1216    switch (BEsel->exec())
     1217    {
     1218        case kDialogCodeRejected:
     1219            VERBOSE(VB_IMPORTANT, "User canceled database configuration");
     1220            return 0;
     1221
     1222        case kDialogCodeButton0:
     1223            VERBOSE(VB_IMPORTANT, "User requested Manual Config");
     1224            return -1;
     1225    }
     1226    BEsel->hide();
     1227    //BEsel->deleteLater();
     1228
     1229    QStringList buttons;
     1230    QString     message;
     1231
     1232    buttons += QObject::tr("Save database details");
     1233    buttons += QObject::tr("Save backend details");
     1234    buttons += QObject::tr("Don't Save");
     1235
     1236    message = parent->tr("Save that backend or database as the default?");
     1237
     1238    DialogCode selected = MythPopupBox::ShowButtonPopup(
     1239        mainWindow, "Save default", message, buttons, kDialogCodeButton2);
     1240    switch (selected)
     1241    {
     1242        case kDialogCodeButton0:
     1243            WriteSettingsFile(m_DBparams, true);
     1244            break;
     1245        case kDialogCodeButton1:
     1246            if (BEsel->m_PIN.length())
     1247                m_XML->SetValue(kDefaultPIN, BEsel->m_PIN);
     1248            m_XML->SetValue(kDefaultUSN, BEsel->m_USN);
     1249            m_XML->Save();
     1250            break;
     1251    }
     1252
     1253    delete BEsel;
     1254    EndTempWindow();
     1255
     1256    return 1;
     1257}
     1258
     1259/**
     1260 * If there is only a single UPnP backend, use it.
     1261 *
     1262 * This does <i>not</i> prompt for PIN entry. If the backend requires one,
     1263 * it will fail, and the caller needs to put up a UI to ask for one.
     1264 */
     1265int MythContextPrivate::UPnPautoconf(const int milliSeconds)
     1266{
     1267    if (!InitUPnP())
     1268        return 0;
     1269
     1270    SSDPCacheEntries *backends = NULL;
     1271    int               count;
     1272    QString           LOC = "UPnPautoconf() - ";
     1273    QTime             timer;
     1274
     1275    m_UPnP->PerformSearch(gBackendURI);
     1276    for (timer.start(); timer.elapsed() < milliSeconds; usleep(25000))
     1277    {
     1278        backends = m_UPnP->g_SSDPCache.Find(gBackendURI);
     1279        if (backends)
     1280        {
     1281            backends->AddRef();
     1282            break;
     1283        }
     1284        putchar('.');
     1285    }
     1286    putchar('\n');
     1287
     1288    if (!backends)
     1289    {
     1290        VERBOSE(VB_GENERAL, LOC + "No UPnP backends found");
     1291        return 0;
     1292    }
     1293
     1294
     1295    // This could be tied to VB_UPNP?
     1296    //m_UPnP->g_SSDPCache.Dump();
     1297
     1298
     1299    count = backends->Count();
     1300    switch (count)
     1301    {
     1302        case 0:
     1303            VERBOSE(VB_IMPORTANT,
     1304                    LOC + "No UPnP backends found, but SSDP::Find() not NULL!");
     1305            break;
     1306        case 1:
     1307            VERBOSE(VB_GENERAL, LOC + "Found one UPnP backend");
     1308            break;
     1309        default:
     1310            VERBOSE(VB_GENERAL,
     1311                    (LOC + "More than one UPnP backend found (%1)").arg(count));
     1312    }
     1313
     1314    if (count != 1)
     1315    {
     1316        backends->Release();
     1317        return count;
     1318    }
     1319
     1320
     1321    // Get this backend's location:
     1322    backends->Lock();
     1323    DeviceLocation *BE = backends->GetEntryMap()->begin().data();
     1324    backends->Unlock();
     1325    backends->Release();
     1326
     1327    // We don't actually know the backend's access PIN, so this will
     1328    // only work for ones that have PIN access disabled (i.e. 0000)
     1329    if (UPnPconnect(BE, QString::null))
     1330        return 1;
     1331   
     1332    return -1;   // Try to force chooser & PIN
     1333}
     1334
     1335/**
     1336 * Get the default backend from config.xml, use UPnP to find it.
     1337 *
     1338 * Sets a string if there any connection problems
     1339 */
     1340bool MythContextPrivate::DefaultUPnP(QString &error)
     1341{
     1342    XmlConfiguration *XML = new XmlConfiguration("config.xml");
     1343    QString           loc = "MCP::DefaultUPnP() - ";
     1344    QString           PIN = XML->GetValue(kDefaultPIN, "");
     1345    QString           USN = XML->GetValue(kDefaultUSN, "");
     1346
     1347    delete XML;
     1348
     1349    if (USN.isEmpty())
     1350    {
     1351        VERBOSE(VB_UPNP, loc + "No default UPnP backend");
     1352        return false;
     1353    }
     1354
     1355    VERBOSE(VB_UPNP, loc + "config.xml has default PIN '"
     1356                         + PIN + "' and host USN: " + USN);
     1357
     1358    if (!InitUPnP())
     1359    {
     1360        error = "UPnP is broken?";
     1361        return false;
     1362    }
     1363
     1364    m_UPnP->PerformSearch(gBackendURI);
     1365    DeviceLocation *pDevLoc = m_UPnP->g_SSDPCache.Find(gBackendURI, USN);
     1366    if (!pDevLoc)
     1367    {
     1368        error = "Cannot find default UPnP backend";
     1369        return false;
     1370
     1371    }
     1372
     1373    if (UPnPconnect(pDevLoc, PIN))
     1374        return true;
     1375   
     1376    error = "Cannot connect to defalt backend via UPnP. Wrong saved PIN?";
     1377    return false;
     1378}
     1379
     1380/**
     1381 * Query a backend via UPnP for its database connection parameters
     1382 */
     1383bool MythContextPrivate::UPnPconnect(const DeviceLocation *backend,
     1384                                     const QString        &PIN)
     1385{
     1386    QString        error;
     1387    QString        LOC = "UPnPconnect() - ";
     1388    QString        URL = backend->m_sLocation;
     1389    MythXMLClient  XML(URL);
     1390 
     1391    VERBOSE(VB_UPNP, LOC + "Trying host at " + URL);
     1392    switch (XML.GetConnectionInfo(PIN, &m_DBparams, error))
     1393    {
     1394        case UPnPResult_Success:
     1395            break;
     1396
     1397        case UPnPResult_ActionNotAuthorized:
     1398            // The stored PIN is probably not correct.
     1399            // We could prompt for the PIN and try again, but that needs a UI.
     1400            // Easier to fail for now, and put up the full UI selector later
     1401            VERBOSE(VB_UPNP, LOC + error + ". Wrong PIN?");
     1402            return false;
     1403
     1404        default:
     1405            VERBOSE(VB_UPNP, LOC + error);
     1406            return false;
     1407    }
     1408
     1409    QString DBhost = m_DBparams.dbHostName;
     1410    VERBOSE(VB_UPNP, LOC + "Got database hostname: " + DBhost);
     1411
     1412    return true;
     1413}
     1414
     1415
    10411416MythContext::MythContext(const QString &binversion)
    10421417    : QObject(), d(NULL), app_binary_version(binversion)
    10431418{
     
    10461421    d = new MythContextPrivate(this);
    10471422}
    10481423
    1049 bool MythContext::Init(bool gui, DatabaseParams *pParams )
     1424bool MythContext::Init(const bool gui, UPnp *UPnPclient,
     1425                       const bool promptForBackend,
     1426                       const bool disableAutoDiscovery)
    10501427{
    10511428    if (app_binary_version != MYTH_BINARY_VERSION)
    10521429    {
     
    10621439            d->TempMainWindow(false);
    10631440            MythPopupBox::showOkPopup(d->mainWindow,
    10641441                                      "Library version error", warning);
    1065             SetMainWindow(NULL);
    1066             DestroyMythMainWindow();
     1442            d->EndTempWindow();
    10671443        }
    10681444        VERBOSE(VB_IMPORTANT, warning);
    10691445
    10701446        return false;
    10711447    }
    10721448
    1073     if (!d->Init(gui, pParams))
     1449    if (!d->Init(gui, UPnPclient, promptForBackend, disableAutoDiscovery))
    10741450        return false;
    10751451
    10761452    ActivateSettingsCache(true);
     
    21012477        .arg(err.databaseText());
    21022478}
    21032479
    2104 /**
    2105  * \todo  Remove MythContext::settings() - it is not used anywhere?
    2106  */
    2107 Settings *MythContext::settings(void)
    2108 {
    2109     return d->m_settings;
    2110 }
    2111 
    21122480Settings *MythContext::qtconfig(void)
    21132481{
    21142482    return d->m_qtThemeSettings;
  • libs/libmyth/mythcontext.h

     
    3939class DisplayRes;
    4040class MDBManager;
    4141class MythContextPrivate;
     42class UPnp;
    4243
    4344/// This MAP is for the various VERBOSITY flags, used to select which
    4445/// messages we want printed to the console.
     
    248249    MythContext(const QString &binversion);
    249250    virtual ~MythContext();
    250251
    251     bool Init(bool gui = true, DatabaseParams *pParams = NULL );
     252    bool Init(const bool gui = true,
     253              UPnp *UPnPclient = NULL,
     254              const bool promptForBackend = false,
     255              const bool bypassAutoDiscovery = false);
    252256
    253257    QString GetMasterHostPrefix(void);
    254258
     
    334338    void LogEntry(const QString &module, int priority,
    335339                  const QString &message, const QString &details);
    336340
    337     Settings *settings(void);
    338341    Settings *qtconfig(void);
    339342
    340343    void SaveSetting(const QString &key, int newValue);
     
    463466    MYTH_SCHEMA_USE_EXISTING = 4
    464467};
    465468
     469/// Service type for the backend's UPnP server
     470extern MPUBLIC const QString gBackendURI;
     471
    466472#endif
    467473
    468474/* vim: set expandtab tabstop=4 shiftwidth=4: */
  • libs/libmyth/libmyth.pro

     
    1616HEADERS += mythdialogs.h audiooutput.h httpcomms.h mythmedia.h mythmediamonitor.h
    1717HEADERS += uilistbtntype.h generictree.h screensaver.h
    1818HEADERS += managedlist.h DisplayRes.h volumebase.h audiooutputbase.h
    19 HEADERS += dbsettings.h screensaver-null.h output.h visual.h
     19HEADERS += backendselect.h dbsettings.h screensaver-null.h output.h visual.h
    2020HEADERS += langsettings.h audiooutputnull.h mythsocket.h
    2121HEADERS += DisplayResScreen.h util-x11.h mythdeque.h qmdcodec.h
    2222HEADERS += exitcodes.h virtualkeyboard.h mythobservable.h mythevent.h
     
    3030SOURCES += httpcomms.cpp mythmedia.cpp mythmediamonitor.cpp uilistbtntype.cpp
    3131SOURCES += generictree.cpp managedlist.cpp DisplayRes.cpp
    3232SOURCES += volumecontrol.cpp volumebase.cpp audiooutputbase.cpp
    33 SOURCES += dbsettings.cpp screensaver.cpp screensaver-null.cpp output.cpp
     33SOURCES += backendselect.cpp dbsettings.cpp screensaver.cpp screensaver-null.cpp output.cpp
    3434SOURCES += langsettings.cpp mythdbcon.cpp audiooutputnull.cpp
    3535SOURCES += DisplayResScreen.cpp util-x11.cpp qmdcodec.cpp
    3636SOURCES += virtualkeyboard.cpp mythobservable.cpp mythsocket.cpp themeinfo.cpp
     
    3838
    3939INCLUDEPATH += ../libmythsamplerate ../libmythsoundtouch ../.. ../
    4040DEPENDPATH += ../libmythsamplerate ../libmythsoundtouch ../ ../libmythui
     41DEPENDPATH += ../libmythupnp
    4142
    4243LIBS += -L../libmythsamplerate -lmythsamplerate-$${LIBVERSION}
    4344LIBS += -L../libmythsoundtouch -lmythsoundtouch-$${LIBVERSION}
     
    111112    QMAKE_CXXFLAGS += -F/System/Library/Frameworks/$${FC}.framework/Frameworks
    112113    LIBS           += -framework $$join(FWKS," -framework ")
    113114
    114     # There is a dependence on some stuff in libmythui.
     115    # There is a dependence on some stuff in libmythui and libmythupnp.
    115116    # It isn't built yet, so we have to ignore these for now:
    116117    QMAKE_LFLAGS_SHLIB += -flat_namespace -undefined warning
    117118
  • libs/libmythupnp/upnpdevice.h

     
    264264            UPnpDeviceDesc *pDevice = GetDeviceDesc( bInQtThread );
    265265
    266266            if ( pDevice == NULL)
    267                return "<Unknown>";
     267               return "<Unknown> (" + m_sLocation + ")";
    268268
    269269            return pDevice->m_rootDevice.m_sFriendlyName
    270270                   + " (" + pDevice->m_sHostName + "), "
  • libs/libmythupnp/upnputil.cpp

     
    4747
    4848        UPnp::g_pConfig->SetValue( sName, sUDN );
    4949
    50         UPnp::g_pConfig->Save();
     50        //UPnp::g_pConfig->Save();
    5151    }
    5252
    5353    return( sUDN );
  • libs/libmythupnp/upnp.cpp

     
    144144    {
    145145        VERBOSE(VB_UPNP, QString(  "UPnp::UPnp:Starting SSDP Thread (Multicast)" ));
    146146        g_pSSDP->start();
     147        VERBOSE(VB_UPNP, QString(  "Enabling Notifications" ));
    147148        g_pSSDP->EnableNotifications();
    148149    }
    149150}
  • programs/mythfrontend/mediarenderer.cpp

     
    117117    if (m_pHttpServer)
    118118        delete m_pHttpServer;
    119119}
    120 
    121 /////////////////////////////////////////////////////////////////////////////
    122 // Caller MUST call Release on returned pointer
    123 /////////////////////////////////////////////////////////////////////////////
    124 
    125 DeviceLocation *MediaRenderer::GetDefaultMaster()
    126 {
    127     UPnp::PerformSearch( "urn:schemas-mythtv-org:device:MasterMediaServer:1" );
    128 
    129     QString sUSN = g_pConfig->GetValue( "UPnP/MythFrontend/DefaultBackend/USN"        , "" );
    130     QString sPin = g_pConfig->GetValue( "UPnP/MythFrontend/DefaultBackend/SecurityPin", "" );
    131 
    132     if (sUSN.isEmpty())
    133         return NULL;
    134 
    135     DeviceLocation *pDeviceLoc = NULL;
    136 
    137     // Lets wait up to 2 seconds for the backend to answer our Search request;
    138 
    139     QTime timer;
    140     timer.start();
    141 
    142     while (timer.elapsed() < 2000 )
    143     {
    144        pDeviceLoc = UPnp::g_SSDPCache.Find( "urn:schemas-mythtv-org:device:MasterMediaServer:1",
    145                                             sUSN );
    146 
    147         if ( pDeviceLoc != NULL)
    148         {
    149             pDeviceLoc->AddRef();
    150 
    151             pDeviceLoc->m_sSecurityPin = sPin;
    152 
    153             return pDeviceLoc;
    154         }
    155 
    156        usleep(10000);
    157     }
    158 
    159     return NULL;
    160 }
    161 
    162 /////////////////////////////////////////////////////////////////////////////
    163 //
    164 /////////////////////////////////////////////////////////////////////////////
    165 
    166 void MediaRenderer::SetDefaultMaster( DeviceLocation *pDeviceLoc, const QString &sPin )
    167 {
    168     if ( pDeviceLoc != NULL)
    169     {
    170         pDeviceLoc->m_sSecurityPin = sPin;
    171 
    172         g_pConfig->SetValue( "UPnP/MythFrontend/DefaultBackend/USN"        , pDeviceLoc->m_sUSN );
    173         g_pConfig->SetValue( "UPnP/MythFrontend/DefaultBackend/SecurityPin", sPin );
    174         g_pConfig->Save();
    175     }
    176 }
  • programs/mythfrontend/main.cpp

     
    5353#include "libmythui/myththemedmenu.h"
    5454#include "libmythui/myththemebase.h"
    5555#include "mediarenderer.h"
    56 #include "masterselection.h"
    5756
    5857#define NO_EXIT  0
    5958#define QUIT     1
     
    10591058    gContext = new MythContext(MYTH_BINARY_VERSION);
    10601059    g_pUPnp  = new MediaRenderer();
    10611060
    1062     DatabaseParams *pParams = NULL;
    1063 
    1064     if (!bBypassAutoDiscovery)
     1061    if (!gContext->Init(true, g_pUPnp, bPromptForBackend, bBypassAutoDiscovery))
    10651062    {
    1066         pParams = new DatabaseParams;
    1067 
    1068         int nRetCode = MasterSelection::GetConnectionInfo( g_pUPnp,
    1069                                                            pParams,
    1070                                                            bPromptForBackend );
    1071         switch( nRetCode )
    1072         {
    1073             case -1:    // Exit Application
    1074                 return FRONTEND_EXIT_OK;
    1075 
    1076             case  0:    // Continue with no Connection Infomation
    1077             {
    1078                 delete pParams;
    1079                 pParams = NULL;
    1080 
    1081                 break;
    1082             }
    1083 
    1084             case 1:     // Connection Information found
    1085             default:
    1086                 break;
    1087         }
    1088     }
    1089 
    1090     if (!gContext->Init( true, pParams ))
    1091     {
    10921063        VERBOSE(VB_IMPORTANT, "Failed to init MythContext, exiting.");
    10931064        return FRONTEND_EXIT_NO_MYTHCONTEXT;
    10941065    }
    10951066
    1096     if (pParams != NULL)
    1097     {
    1098         delete pParams;
    1099         pParams = NULL;
    1100     }
    1101 
    11021067    for(int argpos = 1; argpos < a.argc(); ++argpos)
    11031068    {
    11041069        if (!strcmp(a.argv()[argpos],"-l") ||
     
    14801445    DestroyMythMainWindow();
    14811446    delete themeBase;
    14821447    delete gContext;
     1448    // This takes a few seconds, so inform the user:
     1449    VERBOSE(VB_GENERAL, "Deleting UPnP client...");
    14831450    delete g_pUPnp;
    14841451
    14851452    return FRONTEND_EXIT_OK;
  • programs/mythfrontend/mythfrontend.pro

     
    2626HEADERS += manualbox.h playbackbox.h viewscheduled.h globalsettings.h
    2727HEADERS += manualschedule.h programrecpriority.h channelrecpriority.h
    2828HEADERS += statusbox.h networkcontrol.h custompriority.h
    29 HEADERS += mediarenderer.h masterselection.h
     29HEADERS += mediarenderer.h
    3030
    3131SOURCES += main.cpp manualbox.cpp playbackbox.cpp viewscheduled.cpp
    3232SOURCES += globalsettings.cpp manualschedule.cpp programrecpriority.cpp
    3333SOURCES += channelrecpriority.cpp statusbox.cpp networkcontrol.cpp
    34 SOURCES += mediarenderer.cpp masterselection.cpp
     34SOURCES += mediarenderer.cpp
    3535SOURCES += custompriority.cpp
    3636
    3737macx {