MythTV master
wsdl.cpp
Go to the documentation of this file.
1
2// 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
22bool 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
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
353QDomElement 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
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
462bool 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
491QString 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
515QString 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}
HttpResponseType m_eResponseType
Definition: httprequest.h:150
QString GetLastHeader(const QString &sType) const
QString m_sBaseUrl
Definition: httprequest.h:128
QStringMap m_mapParams
Definition: httprequest.h:132
QBuffer m_response
Definition: httprequest.h:158
HttpRequestType m_eRequestType
Definition: servicehost.h:42
QString m_sName
Definition: servicehost.h:40
QMetaMethod m_oMethod
Definition: servicehost.h:41
const MetaInfoMap & GetMethods()
Definition: servicehost.h:94
const QMetaObject & GetServiceMetaObject()
Definition: servicehost.h:93
virtual QString GetServiceControlURL()
Definition: servicehost.h:91
QString AddTypeInfo(QString sType)
Definition: wsdl.cpp:491
QDomElement CreateMessage(const QString &sMsgName, const QString &sTypeName)
Definition: wsdl.cpp:353
QMap< QString, TypeInfo > m_typesToInclude
Definition: wsdl.h:37
bool GetWSDL(HTTPRequest *pRequest)
Definition: wsdl.cpp:22
QDomElement m_oPortType
Definition: wsdl.h:42
QDomElement CreateMethodType(MethodInfo &oInfo, QString sTypeName, bool bReturnType=false)
Definition: wsdl.cpp:374
static QString ReadClassInfo(const QMetaObject *pMeta, const QString &sKey)
Definition: wsdl.cpp:515
static bool IsCustomType(const QString &sTypeName)
Definition: wsdl.cpp:462
QDomElement m_oTypes
Definition: wsdl.h:40
ServiceHost * m_pServiceHost
Definition: wsdl.h:36
QDomElement m_oLastMsg
Definition: wsdl.h:41
QDomElement CreateBindingOperation(MethodInfo &oInfo, const QString &sClassName)
Definition: wsdl.cpp:302
QDomElement m_oRoot
Definition: wsdl.h:39
QDomElement m_oBindings
Definition: wsdl.h:43
QDomElement m_oService
Definition: wsdl.h:44
static QString ConvertTypeToXSD(const QString &sType, bool bCustomType=false)
Definition: xsd.cpp:871
@ ResponseTypeXML
Definition: httprequest.h:79
@ RequestTypePost
Definition: httprequest.h:51
dictionary info
Definition: azlyrics.py:7