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