MythTV  master
wsdl.cpp
Go to the documentation of this file.
1 // Program Name: wsdl.cpp
3 // Created : Jan. 19, 2010
4 //
5 // Purpose : WSDL XML Generation Class
6 //
7 // Copyright (c) 2010 David Blain <dblain@mythtv.org>
8 //
9 // Licensed under the GPL v2 or later, see LICENSE for details
10 //
12 
13 #include "wsdl.h"
14 #include "xsd.h"
15 
16 #include "servicehost.h"
17 
19 //
21 
22 bool Wsdl::GetWSDL( HTTPRequest *pRequest )
23 {
24  m_typesToInclude.clear();
25 
26  if (!pRequest->m_mapParams.contains( "raw" ))
27  {
28  appendChild( createProcessingInstruction( "xml-stylesheet",
29  R"(type="text/xsl" href="/xslt/service.xslt")" ));
30  }
31 
32  QDomElement oNode;
33 
34  QString sClassName = m_pServiceHost->GetServiceMetaObject().className();
35  QString sTargetNamespace = "http://mythtv.org";
36 
37  m_oRoot = createElementNS( "http://schemas.xmlsoap.org/wsdl/", "definitions");
38 
39  m_oRoot.setAttribute( "targetNamespace", sTargetNamespace );
40 
41  m_oRoot.setAttribute( "xmlns:soap", "http://schemas.xmlsoap.org/wsdl/soap/" );
42  m_oRoot.setAttribute( "xmlns:xs" , "http://www.w3.org/2001/XMLSchema" );
43  m_oRoot.setAttribute( "xmlns:soap", "http://schemas.xmlsoap.org/wsdl/soap/" );
44  m_oRoot.setAttribute( "xmlns:tns" , sTargetNamespace );
45  m_oRoot.setAttribute( "xmlns:wsaw", "http://www.w3.org/2006/05/addressing/wsdl" );
46 
47  m_oRoot.setAttribute( "name", QString( "%1Services" ).arg( sClassName ) );
48 
49  m_oTypes = createElement( "types" );
51  m_oPortType = createElement( "portType" );
52  m_oBindings = createElement( "binding" );
53  m_oService = createElement( "service" );
54 
55  appendChild( m_oRoot );
56  m_oRoot.appendChild( m_oTypes );
57  m_oRoot.appendChild( m_oPortType );
58  m_oRoot.appendChild( m_oBindings );
59  m_oRoot.appendChild( m_oService );
60 
61  m_oPortType.setAttribute( "name", sClassName );
62 
63  // ----------------------------------------------------------------------
64 
65  QDomElement oImportNode = createElement( "xs:schema" );
66  oImportNode.setAttribute( "targetNamespace" , "http://MythTV.org/Imports" );
67 
68  m_oTypes.appendChild( oImportNode );
69 
70  // ----------------------------------------------------------------------
71 
72  oNode = createElement( "xs:schema" );
73  oNode.setAttribute( "targetNamespace" , sTargetNamespace );
74  oNode.setAttribute( "elementFormDefault", "qualified" );
75 
76  m_oTypes.appendChild( oNode );
77  m_oTypes = oNode;
78 
79  // ----------------------------------------------------------------------
80  // Add Bindings...
81  // ----------------------------------------------------------------------
82 
83  m_oBindings.setAttribute( "name", QString("BasicHttpBinding_%1").arg( sClassName ));
84  m_oBindings.setAttribute( "type", QString( "tns:%1" ).arg( sClassName ));
85 
86  oNode = createElement( "soap:binding" );
87  //oNode.setAttribute( "style" , "document" );
88  oNode.setAttribute( "transport", "http://schemas.xmlsoap.org/soap/http" );
89 
90  m_oBindings.appendChild( oNode );
91 
92  // ----------------------------------------------------------------------
93  // Loop for each method in class
94  // ----------------------------------------------------------------------
95 
96  QMapIterator< QString, MethodInfo > it( m_pServiceHost->GetMethods() );
97 
98  while( it.hasNext())
99  {
100  it.next();
101 
102  MethodInfo oInfo = it.value();
103 
104  QString sRequestTypeName = oInfo.m_sName;
105  QString sResponseTypeName = oInfo.m_sName + "Response";
106 
107  QString sInputMsgName = QString( "%1_%2_InputMessage" )
108  .arg( sClassName,
109  oInfo.m_sName );
110  QString sOutputMsgName = QString( "%1_%2_OutputMessage" )
111  .arg( sClassName,
112  oInfo.m_sName );
113 
114  // ------------------------------------------------------------------
115  // Create PortType Operations
116  // ------------------------------------------------------------------
117 
118  QDomElement oOp = createElement( "operation" );
119 
120  oOp.setAttribute( "name", oInfo.m_sName );
121 
122  // ------------------------------------------------------------------
123  // Add Operation Description
124  // ------------------------------------------------------------------
125 
126  QString sDescription;
127 
128  if ( oInfo.m_eRequestType == RequestTypePost )
129  sDescription = "POST ";
130  else
131  sDescription = "GET ";
132 
134  "description" );
135 
136  oNode = createElement( "documentation" );
137  oNode.appendChild( createTextNode( sDescription ));
138 
139  oOp.appendChild( oNode );
140 
141  // ------------------------------------------------------------------
142  // Create PortType input element
143  // ------------------------------------------------------------------
144 
145  oNode = createElement( "input" );
146  oNode.setAttribute( "wsaw:Action", QString( "%1/%2/%3" )
147  .arg( sTargetNamespace,
148  sClassName,
149  oInfo.m_sName ));
150  oNode.setAttribute( "message" , "tns:" + sInputMsgName );
151 
152  oOp.appendChild( oNode );
153 
154  // ------------------------------------------------------------------
155  // Create PortType output element
156  // ------------------------------------------------------------------
157 
158  oNode = createElement( "output" );
159  oNode.setAttribute( "wsaw:Action", QString( "%1/%2/%3Response" )
160  .arg( sTargetNamespace,
161  sClassName,
162  oInfo.m_sName ));
163  oNode.setAttribute( "message", "tns:" + sOutputMsgName );
164 
165  oOp.appendChild( oNode );
166 
167  m_oPortType.appendChild( oOp );
168 
169  // ------------------------------------------------------------------
170  // Create Messages
171  // ------------------------------------------------------------------
172 
173  QDomElement oMsg = CreateMessage( sInputMsgName, sRequestTypeName );
174 
175  m_oRoot.insertAfter( oMsg, m_oLastMsg );
176  m_oLastMsg = oMsg;
177 
178  // ------------------------------------------------------------------
179  // Create Request Type
180  // ------------------------------------------------------------------
181 
182  m_oTypes.appendChild( CreateMethodType( oInfo, sRequestTypeName ) );
183 
184  // ------------------------------------------------------------------
185  // Create Response message
186  // ------------------------------------------------------------------
187 
188  oMsg = CreateMessage( sOutputMsgName, sResponseTypeName );
189 
190  m_oRoot.insertAfter( oMsg, m_oLastMsg );
191  m_oLastMsg = oMsg;
192 
193  // ------------------------------------------------------------------
194  // Create Response Type
195  // ------------------------------------------------------------------
196 
197  m_oTypes.appendChild( CreateMethodType( oInfo, sResponseTypeName, true ) );
198 
199  // ------------------------------------------------------------------
200  // Create Soap Binding Operations
201  // ------------------------------------------------------------------
202 
203  m_oBindings.appendChild( CreateBindingOperation( oInfo, sClassName ));
204  }
205 
206  // ----------------------------------------------------------------------
207  // Add Service Details
208  // ----------------------------------------------------------------------
209 
210  QString sServiceName = QString( "%1Services" ).arg( sClassName );
211 
212  m_oService.setAttribute( "name", sServiceName );
213 
214  // ------------------------------------------------------------------
215  // Add Service Description
216  // ------------------------------------------------------------------
217 
218  QString sDescription = "Interface Version " +
220  "version" );
221 
222  sDescription += " - " + ReadClassInfo( &m_pServiceHost->GetServiceMetaObject(),
223  "description" );
224 
225  oNode = createElement( "documentation" );
226  oNode.appendChild( createTextNode( sDescription ));
227 
228  m_oService.appendChild( oNode );
229 
230  // ------------------------------------------------------------------
231  // Add Service Port
232  // ------------------------------------------------------------------
233 
234  QDomElement oPort = createElement( "port" );
235 
236  oPort.setAttribute( "name" , QString("BasicHttpBinding_%1" ).arg( sClassName ));
237  oPort.setAttribute( "binding", QString("tns:BasicHttpBinding_%1").arg( sClassName ));
238 
239  oNode = createElement( "soap:address" );
240  oNode.setAttribute( "location", "http://" +
241  pRequest->GetLastHeader( "host" ) + "/" +
243 
244  oPort.appendChild( oNode );
245  m_oService.appendChild( oPort );
246 
247  // ----------------------------------------------------------------------
248  //
249  // ----------------------------------------------------------------------
250 
251  if (m_typesToInclude.count() > 0)
252  {
253  // ------------------------------------------------------------------
254  // Create all needed includes
255  //
256  // <xs:import schemaLocation="<path to dependant schema" namespace="http://mythtv.org"/>
257  // ------------------------------------------------------------------
258 
259  QString sBaseUri = "http://" + pRequest->GetLastHeader( "host" ) + pRequest->m_sBaseUrl + "/xsd";
260 
261  QMap<QString, TypeInfo>::const_iterator it2 = m_typesToInclude.constBegin();
262  while( it2 != m_typesToInclude.constEnd())
263  {
264  QDomElement oIncNode = createElement( "xs:import" );
265  QString sType = it2.key();
266  TypeInfo info = it2.value();
267 
268  sType.remove( "DTC::" );
269 
270  QString sValue = QString( "%1?%2=%3" ).arg( sBaseUri,
271  info.sAttrName,
272  sType);
273 
274  if (!info.sContentType.isEmpty())
275  sValue += "&name=" + info.sContentType;
276 
277  oIncNode.setAttribute( "schemaLocation", sValue );
278  oIncNode.setAttribute( "namespace" , "http://mythtv.org" );
279 
280  oImportNode.appendChild( oIncNode );
281  ++it2;
282  }
283  }
284 
285 
286  // ----------------------------------------------------------------------
287  // Return wsdl doc to caller
288  // ----------------------------------------------------------------------
289 
290  QTextStream os( &(pRequest->m_response) );
291 
292  pRequest->m_eResponseType = ResponseTypeXML;
293 
294  save( os, 0 );
295  return true;
296 }
297 
299 //
301 
303  const QString &sClassName)
304 {
305  // ------------------------------------------------------------------
306  // Create PortType Operation
307  // ------------------------------------------------------------------
308 
309  QDomElement oOp = createElement( "operation" );
310 
311  oOp.setAttribute( "name", oInfo.m_sName );
312 
313  QDomElement oNode = createElement( "soap:operation" );
314  oNode.setAttribute( "soapAction", QString( "http://mythtv.org/%1/%2" )
315  .arg( sClassName,
316  oInfo.m_sName ));
317  oNode.setAttribute( "style" , "document" );
318 
319  oOp.appendChild( oNode );
320 
321  // ------------------------------------------------------------------
322  // Create PortType input element
323  // ------------------------------------------------------------------
324 
325  QDomElement oDirection = createElement( "input" );
326  oNode = createElement( "soap:body" );
327  oNode.setAttribute( "use", "literal" );
328 
329  oDirection.appendChild( oNode );
330  oOp.appendChild( oDirection );
331 
332  if (QString::compare( oInfo.m_oMethod.typeName(), "void", Qt::CaseInsensitive ) != 0)
333  {
334  // Create output element
335 
336  oDirection = createElement( "output" );
337 
338  oNode = createElement( "soap:body" );
339  oNode.setAttribute( "use", "literal" );
340 
341  oDirection.appendChild( oNode );
342  oOp.appendChild( oDirection );
343 
344  }
345 
346  return oOp;
347 }
348 
350 //
352 
353 QDomElement Wsdl::CreateMessage( const QString& sMsgName,
354  const QString& sTypeName )
355 {
356  QDomElement oMsg = createElement( "message" );
357 
358  oMsg.setAttribute( "name", sMsgName );
359 
360  QDomElement oNode = createElement( "part" );
361 
362  oNode.setAttribute( "name" , "parameters" );
363  oNode.setAttribute( "element", "tns:" + sTypeName );
364 
365  oMsg.appendChild( oNode );
366 
367  return oMsg;
368 }
369 
371 //
373 
374 QDomElement Wsdl::CreateMethodType( MethodInfo &oInfo,
375  QString sTypeName,
376  bool bReturnType /* = false */)
377 {
378  QDomElement oElementNode = createElement( "xs:element" );
379 
380  oElementNode.setAttribute( "name", sTypeName );
381 
382  QDomElement oTypeNode = createElement( "xs:complexType" );
383  QDomElement oSeqNode = createElement( "xs:sequence" );
384 
385  oElementNode.appendChild( oTypeNode );
386  oTypeNode .appendChild( oSeqNode );
387 
388  // ------------------------------------------------------------------
389  // Create message for parameters
390  // ------------------------------------------------------------------
391 
392  if (bReturnType)
393  {
394  QDomElement oNode = createElement( "xs:element" );
395 
396  QString sType = oInfo.m_oMethod.typeName();
397 
398  sTypeName.remove( "Response" );
399 
400  oNode.setAttribute( "minOccurs", 0 );
401  oNode.setAttribute( "name" , sTypeName + "Result" );
402  oNode.setAttribute( "nillable" , static_cast<int>(true) ); //-=>TODO: This may need to be determined by sParamType
403 
404  bool bCustomType = IsCustomType( sType );
405 
406  sType = Xsd::ConvertTypeToXSD( sType, bCustomType );
407 
408  QString sPrefix = "xs:";
409 
410  if (bCustomType)
411  {
412  sPrefix = "tns:";
413 
414  sType = AddTypeInfo( sType );
415  }
416 
417  oNode.setAttribute( "type", sPrefix + sType );
418 
419  oSeqNode.appendChild( oNode );
420  }
421  else
422  {
423  QList<QByteArray> paramNames = oInfo.m_oMethod.parameterNames();
424  QList<QByteArray> paramTypes = oInfo.m_oMethod.parameterTypes();
425 
426  for( int nIdx = 0; nIdx < paramNames.length(); nIdx++ )
427  {
428  QString sName = paramNames[ nIdx ];
429  QString sParamType = paramTypes[ nIdx ];
430 
431  bool bCustomType = IsCustomType( sParamType );
432 
433  sParamType = Xsd::ConvertTypeToXSD( sParamType, bCustomType );
434 
435  QString sPrefix = "xs:";
436 
437  if (bCustomType)
438  {
439  sPrefix = "tns:";
440 
441  sParamType = AddTypeInfo( sParamType );
442  }
443 
444  QDomElement oNode = createElement( "xs:element" );
445 
446  oNode.setAttribute( "minOccurs", 0 );
447  oNode.setAttribute( "name" , sName );
448  oNode.setAttribute( "nillable" , static_cast<int>(true) ); //-=>TODO: This may need to be determined by sParamType
449  oNode.setAttribute( "type" , sPrefix + sParamType );
450 
451  oSeqNode.appendChild( oNode );
452  }
453  }
454 
455  return oElementNode;
456 }
457 
459 //
461 
462 bool Wsdl::IsCustomType( const QString &sTypeName )
463 {
464 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
465  int id = QMetaType::type( sTypeName.toUtf8() );
466 #else
467  int id = QMetaType::fromName( sTypeName.toUtf8() ).id();
468 #endif
469 
470  switch( id )
471  {
472  case QMetaType::QStringList:
473  case QMetaType::QVariantList:
474  case QMetaType::QVariantMap:
475  return true;
476 
477  default:
478  // for now, treat QFileInfo as a string. Need to turn into MTOM later.
479  if (id == qMetaTypeId<QFileInfo>())
480  return false;
481  break;
482  }
483 
484  return !(id < QMetaType::User);
485 }
486 
488 //
490 
491 QString Wsdl::AddTypeInfo( QString sType )
492 {
493  QString sCustomAttr = "type";
494 
495  // if sParamType contains "::", then assume it's a enum
496  // (this needs to be looked at again for a better approach)
497 
498  if (sType.contains( "::" ))
499  {
500  sType = sType.replace( "::", "." );
501  sCustomAttr = "enum";
502  }
503 
504  TypeInfo info = { sCustomAttr, QString() };
505 
506  m_typesToInclude.insert( sType, info );
507 
508  return sType;
509 }
510 
512 //
514 
515 QString Wsdl::ReadClassInfo( const QMetaObject *pMeta, const QString &sKey )
516 {
517  int nIdx = -1;
518 
519  if (pMeta)
520  nIdx = pMeta->indexOfClassInfo( sKey.toUtf8() );
521 
522  if (nIdx >=0)
523  return pMeta->classInfo( nIdx ).value();
524 
525  return {};
526 }
Wsdl::m_typesToInclude
QMap< QString, TypeInfo > m_typesToInclude
Definition: wsdl.h:39
HTTPRequest::m_sBaseUrl
QString m_sBaseUrl
Definition: httprequest.h:127
Wsdl::m_oRoot
QDomElement m_oRoot
Definition: wsdl.h:41
HTTPRequest
Definition: httprequest.h:109
xsd.h
TypeInfo
Definition: mythxsd.h:72
RequestTypePost
@ RequestTypePost
Definition: httprequest.h:50
Wsdl::GetWSDL
bool GetWSDL(HTTPRequest *pRequest)
Definition: wsdl.cpp:22
Wsdl::m_oTypes
QDomElement m_oTypes
Definition: wsdl.h:42
Wsdl::m_oBindings
QDomElement m_oBindings
Definition: wsdl.h:45
ServiceHost::GetServiceControlURL
virtual QString GetServiceControlURL()
Definition: servicehost.h:90
TypeInfo::sAttrName
QString sAttrName
Definition: mythxsd.h:72
HTTPRequest::m_mapParams
QStringMap m_mapParams
Definition: httprequest.h:131
Wsdl::m_oPortType
QDomElement m_oPortType
Definition: wsdl.h:44
Wsdl::IsCustomType
static bool IsCustomType(const QString &sTypeName)
Definition: wsdl.cpp:462
ResponseTypeXML
@ ResponseTypeXML
Definition: httprequest.h:78
Xsd::ConvertTypeToXSD
static QString ConvertTypeToXSD(const QString &sType, bool bCustomType=false)
Definition: xsd.cpp:870
MethodInfo
Definition: servicehost.h:34
Wsdl::CreateMethodType
QDomElement CreateMethodType(MethodInfo &oInfo, QString sTypeName, bool bReturnType=false)
Definition: wsdl.cpp:374
MethodInfo::m_eRequestType
HttpRequestType m_eRequestType
Definition: servicehost.h:41
Wsdl::CreateBindingOperation
QDomElement CreateBindingOperation(MethodInfo &oInfo, const QString &sClassName)
Definition: wsdl.cpp:302
Wsdl::m_oLastMsg
QDomElement m_oLastMsg
Definition: wsdl.h:43
Wsdl::CreateMessage
QDomElement CreateMessage(const QString &sMsgName, const QString &sTypeName)
Definition: wsdl.cpp:353
Wsdl::ReadClassInfo
static QString ReadClassInfo(const QMetaObject *pMeta, const QString &sKey)
Definition: wsdl.cpp:515
HTTPRequest::GetLastHeader
QString GetLastHeader(const QString &sType) const
Definition: httprequest.cpp:154
Wsdl::m_pServiceHost
ServiceHost * m_pServiceHost
Definition: wsdl.h:38
HTTPRequest::m_eResponseType
HttpResponseType m_eResponseType
Definition: httprequest.h:149
MethodInfo::m_oMethod
QMetaMethod m_oMethod
Definition: servicehost.h:40
TypeInfo::sContentType
QString sContentType
Definition: mythxsd.h:72
ServiceHost::GetServiceMetaObject
const QMetaObject & GetServiceMetaObject()
Definition: servicehost.h:92
servicehost.h
wsdl.h
ServiceHost::GetMethods
const MetaInfoMap & GetMethods()
Definition: servicehost.h:93
Wsdl::m_oService
QDomElement m_oService
Definition: wsdl.h:46
MethodInfo::m_sName
QString m_sName
Definition: servicehost.h:39
HTTPRequest::m_response
QBuffer m_response
Definition: httprequest.h:157
Wsdl::AddTypeInfo
QString AddTypeInfo(QString sType)
Definition: wsdl.cpp:491