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