MythTV  master
soapclient.cpp
Go to the documentation of this file.
1 // Program Name: soapclient.cpp
3 // Created : Mar. 19, 2007
4 //
5 // Purpose : SOAP client base class
6 //
7 // Copyright (c) 2007 David Blain <dblain@mythtv.org>
8 //
9 // Licensed under the GPL v2 or later, see LICENSE for details
10 //
12 
13 #include <QBuffer>
14 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
15 #include <QStringConverter>
16 #endif
17 
18 #include "soapclient.h"
19 
22 
23 #include "httprequest.h"
24 #include "upnp.h"
25 
26 #define LOC QString("SOAPClient: ")
27 
35  QString sNamespace,
36  QString sControlPath) :
37  m_url(std::move(url)), m_sNamespace(std::move(sNamespace)),
38  m_sControlPath(std::move(sControlPath))
39 {
40 }
41 
42 
47 bool SOAPClient::Init(const QUrl &url,
48  const QString &sNamespace,
49  const QString &sControlPath)
50 {
51  bool ok = true;
52  if (sNamespace.isEmpty())
53  {
54  ok = false;
55  LOG(VB_GENERAL, LOG_ERR, LOC + "Init() given blank namespace");
56  }
57 
58  QUrl test(url);
59  test.setPath(sControlPath);
60  if (!test.isValid())
61  {
62  ok = false;
63  LOG(VB_GENERAL, LOG_ERR, LOC +
64  QString("Init() given invalid control URL %1")
65  .arg(test.toString()));
66  }
67 
68  if (ok)
69  {
70  m_url = url;
71  m_sNamespace = sNamespace;
72  m_sControlPath = sControlPath;
73  }
74  else
75  {
76  m_url = QUrl();
77  m_sNamespace.clear();
78  m_sControlPath.clear();
79  }
80 
81  return ok;
82 }
83 
86  const QString &sName, const QDomNode &baseNode) const
87 {
88  QStringList parts = sName.split('/', Qt::SkipEmptyParts);
89  return FindNodeInternal(parts, baseNode);
90 }
91 
94  QStringList &sParts, const QDomNode &curNode) const
95 {
96  if (sParts.empty())
97  return curNode;
98 
99  QString sName = sParts.front();
100  sParts.pop_front();
101 
102  QDomNode child = curNode.namedItem(sName);
103 
104  if (child.isNull() )
105  sParts.clear();
106 
107  return FindNodeInternal(sParts, child);
108 }
109 
113  const QDomNode &node, const QString &sName, int nDefault) const
114 {
115  QString sValue = GetNodeValue(node, sName, QString::number(nDefault));
116  return sValue.toInt();
117 }
118 
122  const QDomNode &node, const QString &sName, bool bDefault) const
123 {
124  QString sDefault = (bDefault) ? "true" : "false";
125  QString sValue = GetNodeValue(node, sName, sDefault);
126  if (sValue.isEmpty())
127  return bDefault;
128 
129  char ret = sValue[0].toLatin1();
130  switch (ret)
131  {
132  case 't': case 'T': case 'y': case 'Y': case '1':
133  return true;
134  case 'f': case 'F': case 'n': case 'N': case '0':
135  return false;
136  default:
137  return bDefault;
138  }
139 }
140 
144  const QDomNode &node, const QString &sName, const QString &sDefault) const
145 {
146  if (node.isNull())
147  return sDefault;
148 
149  QString sValue = "";
150  QDomNode valNode = FindNode(sName, node);
151 
152  if (!valNode.isNull())
153  {
154  // -=>TODO: Assumes first child is Text Node.
155 
156  QDomText oText = valNode.firstChild().toText();
157 
158  if (!oText.isNull())
159  sValue = oText.nodeValue();
160 
161  return QUrl::fromPercentEncoding(sValue.toUtf8());
162  }
163 
164  return sDefault;
165 }
166 
183 QDomDocument SOAPClient::SendSOAPRequest(const QString &sMethod,
184  QStringMap &list,
185  int &nErrCode,
186  QString &sErrDesc)
187 {
188  QUrl url(m_url);
189 
190  QString path = m_sControlPath;
191  path.append("/");
192  path.append(sMethod);
193 
194  // Service url port is 6 less than upnp port (see MediaServer::Init)
195  url.setPort(m_url.port() - 6);
196 
197  nErrCode = UPnPResult_Success;
198  sErrDesc = "";
199 
200  QDomDocument xmlResult;
201  if (m_sNamespace.isEmpty())
202  {
204  sErrDesc = "No namespace given";
205  return xmlResult;
206  }
207 
208  // --------------------------------------------------------------
209  // Add appropriate headers
210  // --------------------------------------------------------------
211  QHash<QByteArray, QByteArray> headers;
212 
213  headers.insert("Content-Type", "text/xml; charset=\"utf-8\"");
214  QString soapHeader = QString("\"%1#%2\"").arg(m_sNamespace, sMethod);
215  headers.insert("SOAPACTION", soapHeader.toUtf8());
216  headers.insert("User-Agent", "Mozilla/9.876 (X11; U; Linux 2.2.12-20 i686, en) "
217  "Gecko/25250101 Netscape/5.432b1");
218  // --------------------------------------------------------------
219  // Build request payload
220  // --------------------------------------------------------------
221 
222  QByteArray aBuffer;
223  QUrlQuery query;
224  for (QStringMap::iterator it = list.begin(); it != list.end(); ++it)
225  {
226  query.addQueryItem(it.key(),*it);
227  }
228 
229  url.setPath(path);
230  url.setQuery(query);
231 
232  // --------------------------------------------------------------
233  // Perform Request
234  // --------------------------------------------------------------
235 
236  LOG(VB_UPNP, LOG_DEBUG,
237  QString("SOAPClient(%1) sending:\n %2").arg(url.toString() /*, aBuffer.constData()*/ ));
238 
239  QString sXml;
240 
241  if (!GetMythDownloadManager()->postAuth(url.toString(), &aBuffer, nullptr, nullptr, &headers))
242  {
243  LOG(VB_GENERAL, LOG_ERR, QString("SOAPClient::SendSOAPRequest: request failed: %1")
244  .arg(url.toString()));
245  }
246  else
247  {
248  sXml = QString(aBuffer);
249  }
250 
251  // --------------------------------------------------------------
252  // Parse response
253  // --------------------------------------------------------------
254 
255  LOG(VB_UPNP, LOG_DEBUG, "SOAPClient response:\n" +
256  QString("%1\n").arg(sXml));
257 
258  // TODO handle timeout without response correctly.
259 
260  list.clear();
261 
262  QDomDocument doc;
263 #if QT_VERSION < QT_VERSION_CHECK(6,5,0)
264  int ErrLineNum = 0;
265 
266  if (!doc.setContent(sXml, true, &sErrDesc, &ErrLineNum))
267  {
269  LOG(VB_UPNP, LOG_ERR,
270  QString("SendSOAPRequest(%1) - Invalid response from %2. Error %3: %4. Response: %5")
271  .arg(sMethod, url.toString(),
272  QString::number(nErrCode), sErrDesc, sXml));
273  return xmlResult;
274  }
275 #else
276  auto parseResult = doc.setContent(sXml,QDomDocument::ParseOption::UseNamespaceProcessing);
277  if (!parseResult)
278  {
280  LOG(VB_UPNP, LOG_ERR,
281  QString("SendSOAPRequest(%1) - Invalid response from %2. Error %3: %4. Response: %5")
282  .arg(sMethod, url.toString(),
283  QString::number(nErrCode), parseResult.errorMessage, sXml));
284  return xmlResult;
285  }
286 #endif
287  return doc;
288 }
289 
SOAPClient::GetNodeValue
int GetNodeValue(const QDomNode &node, const QString &sName, int nDefault) const
Gets the named value using QDomNode as the baseNode in the search, returns default if it is not found...
Definition: soapclient.cpp:112
SOAPClient::m_url
QUrl m_url
Definition: soapclient.h:67
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
SOAPClient::Init
bool Init(const QUrl &url, const QString &sNamespace, const QString &sControlPath)
SOAPClient Initializer.
Definition: soapclient.cpp:47
upnp.h
mythlogging.h
QStringMap
QMap< QString, QString > QStringMap
Definition: upnputil.h:32
SOAPClient::m_sControlPath
QString m_sControlPath
Definition: soapclient.h:69
SOAPClient::FindNodeInternal
QDomNode FindNodeInternal(QStringList &sParts, const QDomNode &curNode) const
This is an internal function used to implement FindNode.
Definition: soapclient.cpp:93
UPnPResult_Success
@ UPnPResult_Success
Definition: upnp.h:37
soapclient.h
LOC
#define LOC
Definition: soapclient.cpp:26
SOAPClient::FindNode
QDomNode FindNode(const QString &sName, const QDomNode &baseNode) const
Used by GeNodeValue() methods to find the named node.
Definition: soapclient.cpp:85
SOAPClient::m_sNamespace
QString m_sNamespace
Definition: soapclient.h:68
SOAPClient::SendSOAPRequest
QDomDocument SendSOAPRequest(const QString &sMethod, QStringMap &list, int &nErrCode, QString &sErrDesc)
Actually sends the sMethod action to the command URL specified in the constructor (url+[/]+sControlPa...
Definition: soapclient.cpp:183
mythdownloadmanager.h
culrcscrapers.music163.lyricsScraper.headers
dictionary headers
Definition: lyricsScraper.py:19
httprequest.h
UPnPResult_MythTV_NoNamespaceGiven
@ UPnPResult_MythTV_NoNamespaceGiven
Definition: upnp.h:86
SOAPClient::SOAPClient
SOAPClient()=default
Empty SOAPClient constructor.
UPnPResult_MythTV_XmlParseError
@ UPnPResult_MythTV_XmlParseError
Definition: upnp.h:87
GetMythDownloadManager
MythDownloadManager * GetMythDownloadManager(void)
Gets the pointer to the MythDownloadManager singleton.
Definition: mythdownloadmanager.cpp:146