00001
00002
00003 #include <QCoreApplication>
00004
00005 #include "mythuistatetype.h"
00006 #include "mythmainwindow.h"
00007 #include "mythdialogbox.h"
00008 #include "backendselect.h"
00009 #include "configuration.h"
00010 #include "mythxmlclient.h"
00011 #include "mythuibutton.h"
00012 #include "mythlogging.h"
00013 #include "mythversion.h"
00014
00015 BackendSelection::BackendSelection(MythScreenStack *parent, DatabaseParams *params,
00016 Configuration *conf, bool exitOnFinish)
00017 : MythScreenType(parent, "BackEnd Selection"),
00018 m_DBparams(params), m_pConfig(conf), m_exitOnFinish(exitOnFinish),
00019 m_backendList(NULL), m_manualButton(NULL), m_saveButton(NULL),
00020 m_cancelButton(NULL)
00021 {
00022 }
00023
00024 BackendSelection::~BackendSelection()
00025 {
00026 SSDP::RemoveListener(this);
00027
00028 ItemMap::iterator it;
00029 for (it = m_devices.begin(); it != m_devices.end(); ++it)
00030 {
00031 DeviceLocation *dev = *it;
00032
00033 if (dev)
00034 dev->Release();
00035 }
00036
00037 m_devices.clear();
00038 }
00039
00040 BackendSelection::Decision BackendSelection::Prompt(
00041 DatabaseParams *dbParams, Configuration *pConfig)
00042 {
00043 Decision ret = kCancelConfigure;
00044 MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
00045 if (!mainStack)
00046 return ret;
00047
00048 BackendSelection *backendSettings =
00049 new BackendSelection(mainStack, dbParams, pConfig, true);
00050
00051 if (backendSettings->Create())
00052 {
00053 mainStack->AddScreen(backendSettings, false);
00054 qApp->exec();
00055 ret = backendSettings->m_backendDecision;
00056 mainStack->PopScreen(backendSettings, false);
00057 }
00058 else
00059 delete backendSettings;
00060
00061 return ret;
00062 }
00063
00064 bool BackendSelection::Create(void)
00065 {
00066 if (!LoadWindowFromXML("config-ui.xml", "backendselection", this))
00067 return false;
00068
00069 m_backendList = dynamic_cast<MythUIButtonList*>(GetChild("backends"));
00070 m_saveButton = dynamic_cast<MythUIButton*>(GetChild("save"));
00071 m_cancelButton = dynamic_cast<MythUIButton*>(GetChild("cancel"));
00072 m_manualButton = dynamic_cast<MythUIButton*>(GetChild("manual"));
00073
00074
00075 connect(m_backendList, SIGNAL(itemClicked(MythUIButtonListItem *)),
00076 SLOT(Accept(MythUIButtonListItem *)));
00077
00078
00079 connect(m_manualButton, SIGNAL(Clicked()), SLOT(Manual()));
00080 connect(m_cancelButton, SIGNAL(Clicked()), SLOT(Cancel()));
00081 connect(m_saveButton, SIGNAL(Clicked()), SLOT(Accept()));
00082
00083 BuildFocusList();
00084 LoadInBackground();
00085
00086 return true;
00087 }
00088
00089 void BackendSelection::Accept(MythUIButtonListItem *item)
00090 {
00091 if (!item)
00092 return;
00093
00094 DeviceLocation *dev = qVariantValue<DeviceLocation *>(item->GetData());
00095
00096 if (!dev)
00097 Cancel();
00098
00099 if (ConnectBackend(dev))
00100 {
00101 if (m_pConfig)
00102 {
00103 if (m_pinCode.length())
00104 m_pConfig->SetValue(kDefaultPIN, m_pinCode);
00105 m_pConfig->SetValue(kDefaultUSN, m_USN);
00106 m_pConfig->Save();
00107 }
00108 Close(kAcceptConfigure);
00109 }
00110 }
00111
00112 void BackendSelection::Accept(void)
00113 {
00114 MythUIButtonListItem *item = m_backendList->GetItemCurrent();
00115
00116 if (!item)
00117 return;
00118
00119 Accept(item);
00120 }
00121
00122
00123 void BackendSelection::AddItem(DeviceLocation *dev)
00124 {
00125 if (!dev)
00126 return;
00127
00128 QString USN = dev->m_sUSN;
00129
00130 m_mutex.lock();
00131
00132
00133 if (m_devices.find(USN) == m_devices.end())
00134 {
00135 dev->AddRef();
00136 m_devices.insert(USN, dev);
00137
00138 m_mutex.unlock();
00139
00140 InfoMap infomap;
00141 dev->GetDeviceDetail(infomap, true);
00142
00143
00144 infomap["version"] = infomap["modelnumber"].section('.', 0, 1);
00145
00146 MythUIButtonListItem *item;
00147 item = new MythUIButtonListItem(m_backendList, infomap["modelname"],
00148 qVariantFromValue(dev));
00149 item->SetTextFromMap(infomap);
00150
00151 bool protoMatch = (infomap["protocolversion"] == MYTH_PROTO_VERSION);
00152
00153 QString status = "good";
00154 if (!protoMatch)
00155 status = "protocolmismatch";
00156
00157
00158
00159
00160 if (infomap["modelname"].isEmpty())
00161 status = "blocked";
00162
00163 item->DisplayState(status, "connection");
00164
00165 bool needPin = dev->NeedSecurityPin();
00166 item->DisplayState(needPin ? "yes" : "no", "securitypin");
00167 }
00168 else
00169 m_mutex.unlock();
00170
00171 dev->Release();
00172 }
00173
00178 bool BackendSelection::ConnectBackend(DeviceLocation *dev)
00179 {
00180 QString error;
00181 QString message;
00182 UPnPResultCode stat;
00183
00184 m_USN = dev->m_sUSN;
00185
00186 MythXMLClient client( dev->m_sLocation );
00187
00188 stat = client.GetConnectionInfo(m_pinCode, m_DBparams, message);
00189
00190 QString backendName = dev->GetFriendlyName(true);
00191
00192 if (backendName == "<Unknown>")
00193 backendName = dev->m_sLocation;
00194
00195 switch (stat)
00196 {
00197 case UPnPResult_Success:
00198 LOG(VB_UPNP, LOG_INFO,
00199 QString("ConnectBackend() - success. New hostname: %1")
00200 .arg(m_DBparams->dbHostName));
00201 return true;
00202
00203 case UPnPResult_HumanInterventionRequired:
00204 LOG(VB_GENERAL, LOG_ERR, QString("Need Human: %1").arg(message));
00205 ShowOkPopup(message);
00206
00207 if (TryDBfromURL("", dev->m_sLocation))
00208 return true;
00209
00210 break;
00211
00212 case UPnPResult_ActionNotAuthorized:
00213 LOG(VB_GENERAL, LOG_ERR,
00214 QString("Access denied for %1. Wrong PIN?")
00215 .arg(backendName));
00216 PromptForPassword();
00217 break;
00218
00219 default:
00220 LOG(VB_GENERAL, LOG_ERR,
00221 QString("GetConnectionInfo() failed for %1 : %2")
00222 .arg(backendName).arg(message));
00223 ShowOkPopup(message);
00224 }
00225
00226
00227 SetFocusWidget(m_backendList);
00228 return false;
00229 }
00230
00231 void BackendSelection::Cancel(void)
00232 {
00233 Close(kCancelConfigure);
00234 }
00235
00236 void BackendSelection::Load(void)
00237 {
00238 SSDP::Instance()->AddListener(this);
00239 SSDP::Instance()->PerformSearch(gBackendURI);
00240 }
00241
00242 void BackendSelection::Init(void)
00243 {
00244 SSDPCacheEntries *pEntries = SSDPCache::Instance()->Find(gBackendURI);
00245 if (pEntries)
00246 {
00247 EntryMap ourMap;
00248 pEntries->GetEntryMap(ourMap);
00249 pEntries->Release();
00250
00251 EntryMap::const_iterator it;
00252 for (it = ourMap.begin(); it != ourMap.end(); ++it)
00253 AddItem(*it);
00254 }
00255 }
00256
00257 void BackendSelection::Manual(void)
00258 {
00259 Close(kManualConfigure);
00260 }
00261
00262 void BackendSelection::RemoveItem(QString USN)
00263 {
00264 m_mutex.lock();
00265
00266 ItemMap::iterator it = m_devices.find(USN);
00267
00268 if (it != m_devices.end())
00269 {
00270 DeviceLocation *dev = *it;
00271
00272 if (dev)
00273 dev->Release();
00274
00275 m_devices.erase(it);
00276 }
00277
00278 m_mutex.unlock();
00279 }
00280
00281 bool BackendSelection::TryDBfromURL(const QString &error, QString URL)
00282 {
00283 if (ShowOkPopup(error + tr("Shall I attempt to connect to this"
00284 " host with default database parameters?")))
00285 {
00286 URL.remove("http://");
00287 URL.remove(QRegExp("[:/].*"));
00288 m_DBparams->dbHostName = URL;
00289 return true;
00290 }
00291
00292 return false;
00293 }
00294
00295 void BackendSelection::customEvent(QEvent *event)
00296 {
00297 if (((MythEvent::Type)(event->type())) == MythEvent::MythEventMessage)
00298 {
00299 MythEvent *me = (MythEvent *)event;
00300 QString message = me->Message();
00301 QString URI = me->ExtraData(0);
00302 QString URN = me->ExtraData(1);
00303 QString URL = me->ExtraData(2);
00304
00305
00306 LOG(VB_UPNP, LOG_DEBUG,
00307 QString("BackendSelection::customEvent(%1, %2, %3, %4)")
00308 .arg(message).arg(URI).arg(URN).arg(URL));
00309
00310 if (message.startsWith("SSDP_ADD") &&
00311 URI.startsWith("urn:schemas-mythtv-org:device:MasterMediaServer:"))
00312 {
00313 DeviceLocation *devLoc = SSDP::Instance()->Find(URI, URN);
00314 if (devLoc)
00315 AddItem(devLoc);
00316 }
00317 else if (message.startsWith("SSDP_REMOVE"))
00318 {
00319
00320
00321 RemoveItem(URN);
00322 }
00323 }
00324 else if (event->type() == DialogCompletionEvent::kEventType)
00325 {
00326 DialogCompletionEvent *dce = dynamic_cast<DialogCompletionEvent*>(event);
00327
00328 if (!dce)
00329 return;
00330
00331 QString resultid = dce->GetId();
00332
00333 if (resultid == "password")
00334 {
00335 m_pinCode = dce->GetResultText();
00336 Accept();
00337 }
00338 }
00339 }
00340
00341 void BackendSelection::PromptForPassword(void)
00342 {
00343 QString message = tr("Please enter the backend access PIN");
00344
00345 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
00346
00347 MythTextInputDialog *pwDialog = new MythTextInputDialog(popupStack,
00348 message,
00349 FilterNone,
00350 true);
00351
00352 if (pwDialog->Create())
00353 {
00354 pwDialog->SetReturnEvent(this, "password");
00355 popupStack->AddScreen(pwDialog);
00356 }
00357 else
00358 delete pwDialog;
00359 }
00360
00361 void BackendSelection::Close(Decision d)
00362 {
00363 m_backendDecision = d;
00364
00365 if (m_exitOnFinish)
00366 qApp->quit();
00367 else
00368 MythScreenType::Close();
00369 }