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