MythTV master
backendselect.cpp
Go to the documentation of this file.
1// -*- Mode: c++ -*-
2#include "backendselect.h"
3
4#include <utility>
5
6#include <QEventLoop>
7
10#include "libmythbase/mythversion.h"
17#include "libmythupnp/ssdp.h"
19
21 MythScreenStack *parent,
22 DatabaseParams *params,
23 QString config_filename,
24 bool exitOnFinish
25 ) :
26 MythScreenType(parent, "BackEnd Selection"),
27 m_dbParams(params),
28 m_configFilename(std::move(config_filename)),
29 m_exitOnFinish(exitOnFinish)
30{
31 if (exitOnFinish)
32 {
33 m_loop = new QEventLoop();
34 }
35}
36
38{
40
41 ItemMap::iterator it;
42 for (it = m_devices.begin(); it != m_devices.end(); ++it)
43 {
44 if (*it)
45 (*it)->DecrRef();
46 }
47
48 m_devices.clear();
49
51 {
52 delete m_loop;
53 }
54}
55
57 DatabaseParams *dbParams, const QString& config_filename)
58{
61 if (!mainStack)
62 return ret;
63
64 auto *backendSettings =
65 new BackendSelection(mainStack, dbParams, config_filename, true);
66
67 if (backendSettings->Create())
68 {
69 mainStack->AddScreen(backendSettings, false);
70 backendSettings->m_loop->exec();
71 ret = backendSettings->m_backendDecision;
72 mainStack->PopScreen(backendSettings, false);
73 }
74 else
75 {
76 delete backendSettings;
77 }
78
79 return ret;
80}
81
83{
84 if (!LoadWindowFromXML("config-ui.xml", "backendselection", this))
85 return false;
86
87 m_backendList = dynamic_cast<MythUIButtonList*>(GetChild("backends"));
88 m_saveButton = dynamic_cast<MythUIButton*>(GetChild("save"));
89 m_cancelButton = dynamic_cast<MythUIButton*>(GetChild("cancel"));
90 m_manualButton = dynamic_cast<MythUIButton*>(GetChild("manual"));
91
93 this, qOverload<MythUIButtonListItem *>(&BackendSelection::Accept));
94
98 this, qOverload<>(&BackendSelection::Accept));
99
102
103 return true;
104}
105
107{
108 if (!item)
109 return;
110
111 auto *dev = item->GetData().value<DeviceLocation *>();
112 if (!dev)
113 {
114 Cancel();
115 LOG(VB_GENERAL, LOG_ERR,
116 "Could not get device details from UI element?");
117 return;
118 }
119
120 if (ConnectBackend(dev))
121 {
122 {
123 auto config = XmlConfiguration(m_configFilename);
124 if (!m_pinCode.isEmpty())
126 config.SetValue(XmlConfiguration::kDefaultUSN, m_usn);
127 config.Save();
128 }
130 }
131}
132
134{
136
137 if (!item)
138 return;
139
140 Accept(item);
141}
142
143
145{
146 if (!dev)
147 return;
148
149 QString usn = dev->m_sUSN;
150
151 m_mutex.lock();
152
153 // The devices' USN should be unique. Don't add if it is already there:
154 if (!m_devices.contains(usn))
155 {
156 dev->IncrRef();
157 m_devices.insert(usn, dev);
158
159 m_mutex.unlock();
160
161 InfoMap infomap;
162 dev->GetDeviceDetail(infomap);
163
164 // We only want the version number, not the library version info
165 infomap["version"] = infomap["modelnumber"].section('.', 0, 1);
166
167 auto *item = new MythUIButtonListItem(m_backendList, infomap["modelname"],
168 QVariant::fromValue(dev));
169 item->SetTextFromMap(infomap);
170
171 bool protoMatch = (infomap["protocolversion"] == MYTH_PROTO_VERSION);
172
173 QString status = "good";
174 if (!protoMatch)
175 status = "protocolmismatch";
176
177 // TODO: Not foolproof but if we can't get device details then it's
178 // probably because we could not connect to port 6544 - firewall?
179 // Maybe we can replace this with a more specific check
180 if (infomap["modelname"].isEmpty())
181 status = "blocked";
182
183 item->DisplayState(status, "connection");
184
185 bool needPin = dev->NeedSecurityPin();
186 item->DisplayState(needPin ? "yes" : "no", "securitypin");
187 }
188 else
189 {
190 m_mutex.unlock();
191 }
192}
193
199{
200 QString message;
201
202 m_usn = dev->m_sUSN;
203
204 MythXMLClient client( dev->m_sLocation );
205
206 UPnPResultCode stat = client.GetConnectionInfo(m_pinCode, m_dbParams, message);
207
208 QString backendName = dev->GetFriendlyName();
209
210 if (backendName == "<Unknown>")
211 backendName = dev->m_sLocation;
212
213 switch (stat)
214 {
216 LOG(VB_UPNP, LOG_INFO,
217 QString("ConnectBackend() - success. New hostname: %1")
218 .arg(m_dbParams->m_dbHostName));
219 return true;
220
222 LOG(VB_GENERAL, LOG_ERR, QString("Need Human: %1").arg(message));
223 ShowOkPopup(message);
224
225 if (TryDBfromURL("", dev->m_sLocation))
226 return true;
227
228 break;
229
231 LOG(VB_GENERAL, LOG_ERR,
232 QString("Access denied for %1. Wrong PIN?")
233 .arg(backendName));
235 break;
236
237 default:
238 LOG(VB_GENERAL, LOG_ERR,
239 QString("GetConnectionInfo() failed for %1 : %2")
240 .arg(backendName, message));
241 ShowOkPopup(message);
242 }
243
244 // Back to the list, so the user can choose a different backend:
246 return false;
247}
248
250{
252}
253
255{
256 SSDP::AddListener(this);
258}
259
261{
263 if (pEntries)
264 {
265 EntryMap ourMap;
266 pEntries->GetEntryMap(ourMap);
267 pEntries->DecrRef();
268
269 for (auto * devLoc : std::as_const(ourMap))
270 {
271 AddItem(devLoc);
272 devLoc->DecrRef();
273 }
274 }
275}
276
278{
280}
281
282void BackendSelection::RemoveItem(const QString& USN_)
283{
284 m_mutex.lock();
285
286 ItemMap::iterator it = m_devices.find(USN_);
287
288 if (it != m_devices.end())
289 {
290 if (*it)
291 (*it)->DecrRef();
292 m_devices.erase(it);
293 }
294
295 m_mutex.unlock();
296}
297
298bool BackendSelection::TryDBfromURL(const QString &error, const QString& URL)
299{
300 if (ShowOkPopup(error + tr("Shall I attempt to connect to this"
301 " host with default database parameters?")))
302 {
303 static const QRegularExpression re {"http[s]?://([^:/]+)",
304 QRegularExpression::CaseInsensitiveOption};
305 QRegularExpressionMatch match = re.match(URL);
306 if (match.hasMatch())
307 {
308 m_dbParams->m_dbHostName = match.captured(1);
309 return true;
310 }
311 }
312
313 return false;
314}
315
317{
318 if (event->type() == MythEvent::kMythEventMessage)
319 {
320 auto *me = dynamic_cast<MythEvent *>(event);
321 if (me == nullptr)
322 return;
323
324 const QString& message = me->Message();
325 const QString& URI = me->ExtraData(0);
326 const QString& URN = me->ExtraData(1);
327 const QString& URL = me->ExtraData(2);
328
329
330 LOG(VB_UPNP, LOG_DEBUG,
331 QString("BackendSelection::customEvent(%1, %2, %3, %4)")
332 .arg(message, URI, URN, URL));
333
334 if (message.startsWith("SSDP_ADD") &&
335 URI.startsWith("urn:schemas-mythtv-org:device:MasterMediaServer:"))
336 {
337 DeviceLocation *devLoc = SSDP::Find(URI, URN);
338 if (devLoc)
339 {
340 AddItem(devLoc);
341 devLoc->DecrRef();
342 }
343 }
344 else if (message.startsWith("SSDP_REMOVE"))
345 {
346 //-=>Note: This code will never get executed until
347 // SSDPCache is changed to handle NotifyRemove correctly
348 RemoveItem(URN);
349 }
350 }
351 else if (event->type() == DialogCompletionEvent::kEventType)
352 {
353 auto *dce = dynamic_cast<DialogCompletionEvent*>(event);
354 if (!dce)
355 return;
356
357 QString resultid = dce->GetId();
358
359 if (resultid == "password")
360 {
361 m_pinCode = dce->GetResultText();
362 Accept();
363 }
364 }
365}
366
368{
369 QString message = tr("Please enter the backend access PIN");
370
371 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
372
373 auto *pwDialog = new MythTextInputDialog(popupStack, message,
374 FilterNone, true);
375
376 if (pwDialog->Create())
377 {
378 pwDialog->SetReturnEvent(this, "password");
379 popupStack->AddScreen(pwDialog);
380 }
381 else
382 {
383 delete pwDialog;
384 }
385}
386
388{
390}
391
393{
395
396 if (m_exitOnFinish)
397 m_loop->quit();
398 else
400}
void CloseWithDecision(Decision d)
QString m_configFilename
Definition: backendselect.h:64
MythUIButton * m_cancelButton
Definition: backendselect.h:71
void PromptForPassword(void)
void RemoveItem(const QString &USN)
void Manual(void)
Linked to 'Configure Manually' button.
MythUIButton * m_manualButton
Definition: backendselect.h:69
MythUIButton * m_saveButton
Definition: backendselect.h:70
void Load(void) override
Load data which will ultimately be displayed on-screen or used to determine what appears on-screen (S...
bool Create(void) override
bool TryDBfromURL(const QString &error, const QString &URL)
void AddItem(DeviceLocation *dev)
void Cancel(void)
Linked to 'Cancel' button.
MythUIButtonList * m_backendList
Definition: backendselect.h:68
void customEvent(QEvent *event) override
BackendSelection(MythScreenStack *parent, DatabaseParams *params, QString config_filename, bool exitOnFinish=false)
void Init(void) override
Used after calling Load() to assign data to widgets and other UI initilisation which is prohibited in...
~BackendSelection() override
QEventLoop * m_loop
Definition: backendselect.h:79
Decision m_backendDecision
Definition: backendselect.h:78
bool ConnectBackend(DeviceLocation *dev)
Attempt UPnP connection to a backend device, get its DB details.
DatabaseParams * m_dbParams
Definition: backendselect.h:63
void Close(void) override
static Decision Prompt(DatabaseParams *dbParams, const QString &config_filename)
Structure containing the basic Database parameters.
Definition: mythdbparams.h:11
QString m_dbHostName
database server
Definition: mythdbparams.h:21
void GetDeviceDetail(InfoMap &map)
Definition: upnpdevice.h:308
QString GetFriendlyName(void)
Definition: upnpdevice.h:281
QString m_sLocation
Definition: upnpdevice.h:239
bool NeedSecurityPin(void)
Definition: upnpdevice.h:319
QString m_sUSN
Definition: upnpdevice.h:238
Event dispatched from MythUI modal dialogs to a listening class containing a result of some form.
Definition: mythdialogbox.h:41
static const Type kEventType
Definition: mythdialogbox.h:56
This class is used as a container for messages.
Definition: mythevent.h:17
const QString & Message() const
Definition: mythevent.h:65
static const Type kMythEventMessage
Definition: mythevent.h:79
MythScreenStack * GetMainStack()
MythScreenStack * GetStack(const QString &Stackname)
virtual void PopScreen(MythScreenType *screen=nullptr, bool allowFade=true, bool deleteScreen=true)
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
Screen in which all other widgets are contained and rendered.
void LoadInBackground(const QString &message="")
void BuildFocusList(void)
bool SetFocusWidget(MythUIType *widget=nullptr)
virtual void Close()
Dialog prompting the user to enter a text string.
List widget, displays list items in a variety of themeable arrangements and can trigger signals when ...
MythUIButtonListItem * GetItemCurrent() const
void itemClicked(MythUIButtonListItem *item)
A single button widget.
Definition: mythuibutton.h:22
void Clicked()
MythUIType * GetChild(const QString &name) const
Get a named child of this UIType.
Definition: mythuitype.cpp:138
UPnPResultCode GetConnectionInfo(const QString &sPin, DatabaseParams *pParams, QString &sMsg)
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
virtual int IncrRef(void)
Increments reference count.
void GetEntryMap(EntryMap &map)
Returns a copy of the EntryMap.
Definition: ssdpcache.cpp:87
static SSDPCache * Instance()
Definition: ssdpcache.cpp:285
SSDPCacheEntries * Find(const QString &sURI)
Finds the SSDPCacheEntries in the cache, returns nullptr when absent.
Definition: ssdpcache.cpp:341
static const QString kBackendURI
Definition: ssdp.h:116
static SSDPCacheEntries * Find(const QString &sURI)
Definition: ssdp.h:137
static void RemoveListener(QObject *listener)
Definition: ssdp.h:134
static SSDP * Instance()
Definition: ssdp.cpp:102
void PerformSearch(const QString &sST, std::chrono::seconds timeout=2s)
Definition: ssdp.cpp:251
static void AddListener(QObject *listener)
Definition: ssdp.h:132
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
static const QString kDefaultUSN
Definition: configuration.h:63
static const QString kDefaultPIN
Definition: configuration.h:62
static const iso6937table * d
MythConfirmationDialog * ShowOkPopup(const QString &message, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythMainWindow * GetMythMainWindow(void)
QHash< QString, QString > InfoMap
Definition: mythtypes.h:15
@ FilterNone
def error(message)
Definition: smolt.py:409
STL namespace.
QMap< QString, DeviceLocation * > EntryMap
Key == Unique Service Name (USN)
Definition: ssdpcache.h:34
UPnPResultCode
Definition: upnp.h:34
@ UPnPResult_HumanInterventionRequired
Definition: upnp.h:44
@ UPnPResult_Success
Definition: upnp.h:35
@ UPnPResult_ActionNotAuthorized
Definition: upnp.h:46