REVO2700 libSOAP 1 @ W cFunctions )function SOAP.RPCRequest pUrl, pMethod, pNamespace, pParams, pSoapAction, pHeader, pAuthority, pAuthID
function SOAP.ComplexRequest pUrl, pMethod, pNamespace, pBody, pSoapAction, pHeader
function SOAP.RawRequest pUrl, pBody, pSoapAction
function SOAP.DocRequest pUrl, pBody, pSoapAction
function SOAP.Response
function SOAP.Headers
function SOAP.Envelope
function UnitTest.SOAP.AddNamespace
function SOAP.AddNamespace pSOAPEnvelope, pNamespaceNames, pNamespaceIDs
function SOAP.AddParam pTag, pValue, pType, pNamespace
function SOAP.AddHeader pHeaderType, pHeaderText
function SOAP.AddRPCParam pTag, pValue, pType, pNamespace
function SOAP.Version
function WSDL.ParseSOAPFile pDocPath
function WSDL.ParseSOAPData pDocPath
function WSDL.CreateCPrototype pWSDLArray, pWebServiceRequest
function WSDL.CreatePrototype pWSDLArray, pWebServiceRequest, pKey, pValue
function WSDL.CreatePrototypeFor pWebServiceRequest
function WSDL.CreateServerPrototype pWSDLArray, pWebServiceRequest
function WSDL.CreateServerPrototypes pWSDLFilePath
function WSDL.CreateServicePrototypeFromFile pWSDLFilePath, pWebServiceRequest
function WSDL.CreateServicePrototypesFromFile pWSDLFilePath
function WSDL.EnumerateServicesFromFile pWSDLFilePath
function Schema.EnumerateDataTypesFromFile pWSDLFilePath
function Schema.ParseSchemaFile pDocPath
uSOAPDocEnvelope
<$DOC_BODY/>
uDemoWSDL |
GlobalWeather
cCommands Jon WSDL.ParseSOAP pDocID, @pWSDLArray
on Schema.Parse pDocID, @pWSDLArray
uSOAPEnvelope
<$HEADER>
<$METHOD>
<$PARAMS/>
$METHOD>
--> all handlers
/**
--------------------------------------------
# converted from the old revSOAP library version 1.0
-- Functions that won't work in the old library (missing parameters):
#-- revSoapComplexRequest
#-- revSoapDocRequest
--
-- Public functions here:
--
#-- SOAP.RPCRequest()
#-- SOAP.ComplexRequest()
#-- SOAP.DocRequest()
#-- SOAP.RawRequest()
#-- SOAP.AddParam()
#-- SOAP.AddRPCParam()
#-- SOAP.AddNamespace()
-- Accessor functions --
#-- SOAP.Response()
#-- SOAP.Headers()
#-- SOAP.Envelope()
#-- SOAP.Version()
-- WSDL functions --
#-- WSDL.CreateCPrototype()
#-- WSDL.CreatePrototype()
#-- WSDL.EnumerateServicesFromFile()
#-- WSDL.CreateServicePrototypeFromFile()
#-- WSDL.CreateServicePrototypesFromFile()
#-- WSDL.ParseSOAP
#-- WSDL.ParseSOAPData()
#-- WSDL.ParseSOAPFile()
--
-- History:
2007.10.09 mdw
-- added documentation for the SOAP.RPCRequest function and others
-- added SOAP.RawRequest
-- added SOAP.AddRPCParam, but hopefully it can be ignored.
2009.11.29 mdw
-- added WSDL prototype functions
2010.06.20 mdw
-- added SOAP.AddNamespace() function
-- removed unused parameters from SOAP.AddRPCParam() and SOAP.RawRequest()
-- added XML.StartTag() and XML.EndTag()
2010.06.21 mdw
-- fixed SOAP.AddRawRequest (forgot pAuthID parameter)
2011.10.27 mdw
-- added error checking to WSDL.ParseSOAP() and WSDL.ParseSOAPFile()
-- added WSDL.CreateServerPrototype(), WSDL.CreateServerPrototypes()
2011.12.20 mdw
* reworked wsdl parsing for more redirection for more complex schemas
* added chomp function
2011.12.23 mdw
* SOAP.AddParam can now handle "ArrayOf" types
* made SOAP_AddParam private
2013.02.04 mdw
* Added WSDL.EnumerateServices function to list available services from wsdl
* WSDL.ParseSOAP now can use web-based wsdl file
* Thus WSDL.CreateServicePrototypesFromFile can use either a local wsdl file
* or a remote url as its source.
* Modified WSDL.GetInputParameters and WSDL.CreatePrototype
* to add optional parameters only if not empty
* WSDL.CreatePrototype now uses named parameters instead of mock "tValue"
* WSDL.GetInputParameters and WSDL.GetOutputParameters work with more wsdl schemas.
--------------------------------------------------
-- NOTE: getting web services to work is a tricky process.
-- There's no standardization as to the header format.
-- Wsdl documents are a good place to start,
-- and ngrep is an indispensible tool:
-- http://ngrep.sourceforge.net/
--------------------------------------------------
--------------------------------------------------
-- Example of how to use SOAP.RPCRequest:
--
-- The simplest way to start out (assuming you have a wsdl file) is:
--
-- start using "libSOAP"
-- put WSDL.EnumerateServicesFromFile(pathToFile)
-- stop using "libSOAP"
--
-- or
-- start using "libSOAP"
-- put WSDL.EnumerateServicesFromFile("http://website.domain/file.wsdl")
-- stop using "libSOAP"
--
-- That will list the available web services, one per line.
--
-- To generate a function that calls one of those services, use
--
-- start using "libSOAP"
-- put WSDL.CreateServicePrototypeFromFile(pathToFile, webservice)
-- stop using "libSOAP"
--
-- There are two templates supplied as custom properties
-- of this stack: uSOAPEnvelope and uSOAPDocEnvelope
--------------------------------------------------
--------------------------------------------------
*/
/**
constant kWSURL= "https://webservice.com/ws/activations.asmx"
constant kWSNamespace= "http://authority.com/namespace"
on PerformAction pAction -- private
local tPartnerKey
local tRegCode
local tParams
local tHeader
put empty into field "fldResults"
put empty into field "fldResponse"
put empty into field "fldSOAP"
put field "fldPartnerKey" into tPartnerKey
put field "fldRegCode" into tRegCode
put SOAP.AddParam("ActivationCode", tRegCode, "string", kWSNamespace) into tParams
-- use tHeader if you need https authentication.
-- otherwise leave tHeader empty
put SOAP.AddParam("Key", tPartnerKey, "string") into tHeader
put SOAP.AddParam("AuthenticationHeader", tHeader, "", kWSNamespace) into tHeader
try
put SOAP.RPCRequest(kWSURL, \
pAction, \
kWSNamespace, \
tParams, \
"http://webservice.com/ws/" & pAction, \
tHeader \
) into field "fldResponse"
put SOAP.Response() into field "fldResults"
put SOAP.Envelope() into field "fldSOAP"
catch e
-- this is here for timeouts
answer e
end try
end PerformAction
--------------------------------------------
*/
/**
--------------------------------------------
contents of uSOAPEnvelope custom property:
--------------------------------------------------
<$HEADER>
<$METHOD>
<$PARAMS/>
$METHOD>
--------------------------------------------------
contents of uSOAPDocEnvelope custom property:
--------------------------------------------------
<$DOC_BODY/>
--------------------------------------------
*/
local sSOAPResponse
local sSOAPHeaders
local sSOAPEnvelope
constant kMethodHeader="ns1"
/**
* --------------------------------------------
* SOAP.RPCRequest
*
* SOAP.RPCRequest(Url, Method, Namespace, Params, SoapAction [,pHeader, pAuthority, pAuthID])
*
* @pUrl
* @pMethod
* @pNamespace
* @pParams
* @pSoapAction
* @pHeader : if not empty, this replaces '<$HEADER>' in the SOAP envelope
* @pAuthority : optional additional namespaces
* @pAuthID : optional additional namespace identifiers
* (see SOAP.AddNamespace for namespace details)
*
* load the uSOAPEnvelope custom property of this library stack
* (contains the template for the xml data)
* these parameters need to be supplied in the function call:
*
* $METHOD -- replaced by pMethod
* $PARAMS -- replaced by pParams
* $HEADER -- only if you need https authentication
*
* Usage:
* See above in stack script for full example of usage
* put SOAP.RPCRequest("https://webservice.com/ws/activations.asmx", \
* pAction, \
* "http://authority.com/namespace", \
* tParams, \
* "http://webservice.com/ws/" & pAction, \
* tHeader \
* ) into field "fldResponse"
*
* Leave pHeader empty if you don't need https authentication
*
* pAuthority and pAuthID can be variables containing
* comma-separated lists:
* "http://one.two.com/auths/,http://hello.sailor.com/new/"
* "abc,xyz"
* --------------------------------------------
*/
function SOAP.RPCRequest pUrl, pMethod, pNamespace, pParams, pSoapAction, pHeader, pAuthority, pAuthID
local tHeaders
local tSOAPEnvelope
local tMethod
local tMethodTag
local tHeader
put the uSOAPEnvelope of me into tSOAPEnvelope
--put kMethodHeader & ":" & pMethod into tMethod
put pMethod into tMethod
if pAuthority is not empty and pAuthID is not empty then
put SOAP.AddNamespace(tSOAPEnvelope, pAuthority, pAuthID) into tSOAPEnvelope
put XML.StartTag(tMethod) into tMethodTag
else
--put "<" & tMethod && "xmlns:" & kMethodHeader & "=" & quote & pNamespace & quote & ">" into tMethodTag
put "<" & tMethod && "xmlns" & "=" & quote & pNamespace & quote & ">" into tMethodTag
end if
replace "<$METHOD>" with tMethodTag in tSOAPEnvelope
replace "$METHOD>" with "" & tMethod & ">" in tSOAPEnvelope
chomp pParams
replace "<$PARAMS/>" with pParams in tSOAPEnvelope
if pHeader is not empty then
put SOAP.AddParam("soap:Header", pHeader) into tHeader
--replace "<$HEADER>" & cr with tHeader in tSOAPEnvelope
else
put empty into tHeader
--replace "<$HEADER>" & cr with empty in tSOAPEnvelope
end if
replace "<$HEADER>" & cr with tHeader in tSOAPEnvelope
--put tSOAPEnvelope & cr after msg
return _SOAP.Post(tSOAPEnvelope, pUrl, pSoapAction)
end SOAP.RPCRequest
/**
* --------------------------------------------
* SOAP.ComplexRequest
*
* SOAP.ComplexRequest(pUrl, pMethod, pNamespace, pBody, pSoapAction [, pHeader])
*
* This didn't work in the original library,
* so I don't know what it was intended to do.
*
* I made it substitute for the SOAP.RPCRequest function but with the job of
* filling in the parameters body left to external routines for flexibility.
*
* Leave pHeader empty if you don't need https authentication
*
* @pUrl
* @pMethod
* @pNamespace
* @pBody
* @pSoapAction
* @pHeader
* --------------------------------------------
*/
function SOAP.ComplexRequest pUrl, pMethod, pNamespace, pBody, pSoapAction, pHeader
local tHeaders
local tSOAPEnvelope
local tMethod
local tMethodTag
put the uSOAPEnvelope of me into tSOAPEnvelope
put kMethodHeader & ":" & pMethod into tMethod
put "<" & tMethod && "xmlns:" & kMethodHeader & "=" & quote & pNamespace & quote & ">" into tMethodTag
replace "<$METHOD>" with tMethodTag in tSOAPEnvelope
replace "$METHOD>" with "" & tMethod & ">" in tSOAPEnvelope
replace "<$PARAMS/>" with pBody in tSOAPEnvelope
replace "<$HEADER>" & cr with pHeader in tSOAPEnvelope
return _SOAP.Post(tSOAPEnvelope, pUrl, pSoapAction)
end SOAP.ComplexRequest
/**
* --------------------------------------------
* SOAP.RawRequest
*
* SOAP.RawRequest pUrl, pMethod, pNamespace, pBody, pSoapAction
*
* Same as SOAP.RPCRequest, but with
* all the work left to external handlers.
* Pass the formatted content in pBody.
*
* @pUrl
* @pMethod
* @pNamespace
* @pBody
* @pSoapAction
*
* pMethod and pNamespace are currently not used
* --------------------------------------------
*/
--function SOAP.RawRequest pUrl, pMethod, pNamespace, pBody, pSoapAction
function SOAP.RawRequest pUrl, pBody, pSoapAction
return _SOAP.Post(pBody, pUrl, pSoapAction)
end SOAP.RawRequest
/**
* --------------------------------------------
* SOAP.DocRequest
*
* SOAP.DocRequest pUrl, pMethod, pNamespace, pBody, pSoapAction
*
* Same as SOAP.RPCRequest, but with
* all the work left to external handlers.
* Pass the formatted content in pBody.
*
* @pUrl
* @pBody
* @pSoapAction
* --------------------------------------------
*/
--function SOAP.DocRequest pUrl, pMethod, pNamespace, pBody, pSoapAction
function SOAP.DocRequest pUrl, pBody, pSoapAction
local tSOAPEnvelope
put the uSOAPDocEnvelope of me into tSOAPEnvelope
replace "<$DOC_BODY/>" with pBody in tSOAPEnvelope
return _SOAP.Post(tSOAPEnvelope, pUrl, pSoapAction)
end SOAP.DocRequest
/**
* --------------------------------------------
* SOAP.Response
*
* Accessor function for the sSOAPResponse private variable
* (result of the SOAP action)
*
* Usage:
* get SOAP.Response()
* --------------------------------------------
*/
function SOAP.Response
return sSOAPResponse
end SOAP.Response
/**
* --------------------------------------------
* SOAP.Headers
*
* Accessor function for the sSOAPHeaders private variable
* (result of the SOAP action)
*
* Usage:
* get SOAP.Headers()
* --------------------------------------------
*/
function SOAP.Headers
return sSOAPResponse
end SOAP.Headers
/**
* --------------------------------------------
* SOAP.Envelope
*
* Accessor function for the sSOAPEnvelope private variable
* (result of the SOAP action)
*
* Usage:
* get SOAP.Envelope()
* --------------------------------------------
*/
function SOAP.Envelope
return sSOAPEnvelope
end SOAP.Envelope
/**
* --------------------------------------------
* SOAP.AddNamespace
*
* For cases where an additional namespace is needed
* in the envelope header, this addes another namespace field
* after the existing ones.
*
* @pSOAPEnvelope
* @pNamespaceName : comma-separated list of namespace urls
* @pNamespaceID : comma-separated list of namespace identifiers
*
* Usage:
* put SOAP.AddNamespace(tSOAPEnvelope, "http://www.xyz.com", "xsi") into tSOAPEnvelope
* put SOAP.AddNamespace(tSOAPEnvelope, "http://www.xyz.com,http://hello.sailor.com/hello/", "xsi,new") into tSOAPEnvelope
* --------------------------------------------
*/
function UnitTest.SOAP.AddNamespace
local tReturn
local tAuthorities
local tAuthorityIDs
put "http://hello.sailor.com/unit.test/,http://drwho.42.com" into tAuthorities
put "abc,xyz" into tAuthorityIDs
--put SOAP.AddNamespace(the uSOAPEnvelope of this stack, "http://hello.sailor.com/unit.test/,http://drwho.42.com", "abc,xyz") into tReturn
put SOAP.AddNamespace(the uSOAPEnvelope of this stack, "http://hello.sailor.com/unit.test/", "abc") into tReturn
if "xmlns:abc=" & quote & item 1 of tAuthorities & quote is not in tReturn then
fail "UnitTest.SOAP.AddNamespace"
end if
return tReturn
end UnitTest.SOAP.AddNamespace
private command fail pTest
answer pTest && "failed!"
end fail
function SOAP.AddNamespace pSOAPEnvelope, pNamespaceNames, pNamespaceIDs
local tParam
local tStart, tEnd
local x
-- find the insertion point
get matchchunk(pSOAPEnvelope, "(?Us))", tStart, tEnd)
if pNamespaceNames is not empty then
put cr into tParam
repeat with x=1 to the number of items in pNamespaceNames
put "xmlns:" & item x of pNamespaceIDs & "=" & quote & item x of pNamespaceNames & quote & cr after tParam
-- this should leave tParam with something like
-- xmlns:xsi="http://www.xyz.com"
end repeat
end if
put tParam before char tStart of pSOAPEnvelope
return pSOAPEnvelope
end SOAP.AddNamespace
/**
* --------------------------------------------
* SOAP.AddXMLParam
*
* SOAP.AddXMLParam(tag, value [, type][, Namespace])
*
* @pTag
* @pValue
* @pType : optional type identifier
* @pNamespace : optional namespace identifier
*
* Usage:
*
* put SOAP.AddXMLParam("statenum", tNum, "int", "http://example.com/jack")
* gives:
* tNum
*
* put SOAP.AddXMLParam("statenum", tNum, "int")
* gives:
* tNum
*
* put SOAP.AddXMLParam("statenum", tNum)
* gives:
* tNum
* --------------------------------------------
*/
private function SOAP.AddXMLParam pTag, pValue, pType, pNamespace
local tParam
put "<" & pTag into tParam
-- see if the option namespace parameter is present
if pNamespace is not empty then
put " xmlns=" & quote & pNamespace & quote after tParam
end if
-- see if the optional type parameter is present
if pType is not empty then
if ":" is not in pType then
put "ns1:" before pType
end if
put " xsi:type=" & quote & pType & quote after tParam
end if
put ">" & pValue after tParam
put XML.EndTag(pTag) & cr after tParam
return tParam
end SOAP.AddXMLParam
private function SOAP.AddJsonParam pTag, pValue
local tParam
put q(pTag) & ":" & q(pValue) into tParam
return tParam
end SOAP.AddJsonParam
/**
* --------------------------------------------
* SOAP.AddParam
*
* SOAP.AddParam(tag, value [, type][, Namespace])
*
* @pTag
* @pValue
* @pType : optional type identifier
* @pNamespace : optional namespace identifier
*
* Usage:
*
* put SOAP.AddParam("statenum", tNum, "int", "http://example.com/jack")
* gives:
* tNum
*
* put SOAP.AddParam("statenum", tNum, "int")
* gives:
* tNum
*
* put SOAP.AddParam("statenum", tNum)
* gives:
* tNum
* --------------------------------------------
*/
function SOAP.AddParam pTag, pValue, pType, pNamespace
local tParams
local tArrayParams
local tType
if "ArrayOf" is in pType then
put pType into tType
delete char 1 to 7 of tType
put tolower(tType) into tType
repeat for each line tParam in pValue
put XML.StartTag(tType) & tParam & XML.EndTag(tType) & cr after tArrayParams
end repeat
put SOAP.AddXMLParam(pTag, tArrayParams, pType, pNamespace) after tParams
else
put SOAP.AddXMLParam(pTag, pValue, pType, pNamespace) into tParams
end if
return tParams
end SOAP.AddParam
/**
* --------------------------------------------
* SOAP.AddHeader
*
* @pHeaderType
* @pHeaderText
* --------------------------------------------
*/
function SOAP.AddHeader pHeaderType, pHeaderText
local tHeaders
put pHeaderType & ":" && pHeaderText & cr into tHeaders
return tHeaders
end SOAP.AddHeader
/**
* --------------------------------------------
* SOAP.AddRPCParam
*
* same parameters as SOAP.AddParam, but this one
* just results in
*
*
* 94107
*
*
* I needed this for a non-standard RPC url (http://hamete.org/yafnagnames)
* (no namespace parameter)
* Hopefully it won't be necessary for anything else.
*
* @pTag
* @pValue
* @pType
* @pNamespace
* --------------------------------------------
*/
function SOAP.AddRPCParam pTag, pValue, pType, pNamespace
local tParam
put XML.StartTag(pTag) & cr into tParam
put XML.StartTag("value") after tParam
put XML.StartTag(pType) after tParam
put pValue after tParam
put XML.EndTag(pType) after tParam
put XML.EndTag("value") & cr & XML.EndTag(pTag) & cr after tParam
return tParam
end SOAP.AddRPCParam
/**
* --------------------------------------------
* SOAP.Version
*
* utility to return the current library version
* --------------------------------------------
*/
function SOAP.Version
return the uRIP["version"] of me
end SOAP.Version
--> WSDL handlers
/**
* --------------------------------------------
* WSDL.ParseSOAPFile
*
* Return an array from a WSDL data file
*
* @pDocPath -- path to wsdl file
*
* usage:
* put WSDL.ParseSOAPFile(pathToFile)
* put WSDL.ParseSOAPFile("http://www.website.domain/file.wsdl")
* --------------------------------------------
*/
function WSDL.ParseSOAPFile pDocPath
local tDocID
local tWSDLArray
local tPath
if pDocPath is not empty then
if pDocPath begins with "http" then
put tempname() into tPath
put url(pDocPath) into url("file:"&tPath)
put tPath into pDocPath
end if
put revCreateXMLTreeFromFile(pDocPath, false, true, false) into tDocID
if tDocID is a number then
WSDL.ParseSOAP tDocID, tWSDLArray
else
put "not a valid xml file" into tWSDLArray["error"]
end if
end if
return tWSDLArray
end WSDL.ParseSOAPFile
/**
* --------------------------------------------
* WSDL.ParseSOAPData
*
* Return an array from WSDL data
*
* @pDocPath -- xml data from wsdl file
* --------------------------------------------
*/
function WSDL.ParseSOAPData pDocPath
local tDocID
local tWSDLArray
if pDocPath is not empty then
put revCreateXMLTree(pDocPath, false, true, false) into tDocID
if tDocID is a number then
WSDL.ParseSOAP tDocID, tWSDLArray
else
put "not a valid xml file" into tWSDLArray["error"]
end if
end if
return tWSDLArray
end WSDL.ParseSOAPData
/**
* --------------------------------------------
* WSDL.ParseSOAP
*
* Parse a properly-formatted WSDL file
* to determine the characteristics of a web service provider
*
* @pDocID -- ID of document xml tree
* @pWSDLArray -- pointer to output array
* --------------------------------------------
*/
on WSDL.ParseSOAP pDocID, @pWSDLArray
local tBasePath
local tAttValue
local tSoapBindingPath
local tBindingStyle
local tBindingPath
local tBindingName
local tPortType, tPort
local tOperationList
local tOperationName, tParameterName
local tOperationNames, tPortTypes
local tInputMessage, tInput, tInP
local tPTOpPath
local tNamespace
local tComplexType
local tName, tType
local tMessageParameters
constant kTypeParameters = "/definitions/types/schema/"
put "/definitions/binding" into tBasePath
put "http://schemas.xmlsoap.org/soap/http" into tAttValue
put revXMLMatchingNode( pDocID, tBasePath, "binding", "transport", tAttValue, -1 ) into tSoapBindingPath
-- if tSoapBindingPath is empty then
-- put tBasePath into tSoapBindingPath
-- end if
put WSDL.GetNameSpace(pDocID) into pWSDLArray["namespace"]
-- all we support here is "RPC" and "document"
put revXMLAttribute( pDocID, tSoapBindingPath, "style") into tBindingStyle
if tBindingStyle is not "RPC" then
put "document" into tBindingStyle
end if
if tSoapBindingPath is empty then
put tBasePath into tBindingPath
else
put revXMLParent( pDocID, tSoapBindingPath) into tBindingPath
end if
get revXMLAttribute( pDocID, tBindingPath, "name")
put _stripNSPrefix(it) into tBindingName
put revXMLChildNames( pDocID, "/definitions/", cr,"portType", true) into tPortTypes
repeat for each line tPortType in tPortTypes
put "/definitions/" & tPortType & "/" into tPort
put revXMLChildNames( pDocID, tPort, cr,"operation", true) into tOperationList
-- go through the list of web services
repeat for each line tOperation in tOperationList
-- get the name of the web service
put revXMLAttribute(pDocID, tPort & tOperation, "name") into tOperationName
-- get the input parameters message
put revXMLAttribute(pDocID, tPort & tOperation & "/input", "message") into tParameterName
put WSDL.GetInputParameters(pDocID, tParameterName) into tMessageParameters
put tMessageParameters into pWSDLArray[tOperationName]["inputParams"]
-- get the output parameters message
put revXMLAttribute(pDocID, tPort & tOperation & "/output", "message") into tParameterName
put WSDL.GetOutputParameters(pDocID, tParameterName) into pWSDLArray[tOperationName]["outputParams"]
put tOperationName & cr after tOperationNames
put pWSDLArray["namespace"] into pWSDLArray[tOperationName]["namespace"]
end repeat
end repeat
chomp tOperationNames
put tOperationNames into pWSDLArray["operations"]
put _serviceAddress(pDocID, tBindingName) into pWSDLArray["serviceAddress"]
end WSDL.ParseSOAP
-- only called from ParseSOAP
private function WSDL.GetInputParameters pDocID, pParameterName
local tMessageParameters
local tElementName
local tPTOpPath
local tComplexType
local tInput
local tName, tType, tOptional
constant kTypeParameters = "/definitions/types/schema/"
-- vector through it to get the input parameter list
put revXMLMatchingNode( pDocID, "/definitions/", "message", "name", _stripNSPrefix(pParameterName), -1 ) into tPTOpPath
put revXMLFirstChild(pDocID, tPTOpPath) into tComplexType
-- get the input input element name
put revXMLAttribute(pDocID, tComplexType, "type") into tElementName
if tElementName begins with "xmlerr" then
put revXMLAttribute(pDocID, tComplexType, "element") into tElementName
else
put revXMLAttribute(pDocID, tComplexType, "name") into tName
put revXMLAttribute(pDocID, tComplexType, "type") into tType
end if
put revXMLMatchingNode( pDocID, kTypeParameters, "element", "name", _stripNSPrefix(tElementName), -1 ) into tPTOpPath
put revXMLFirstChild(pDocID, tPTOpPath & "/complexType") & "/" into tComplexType
if tComplexType begins with "xmlerr" then
--put revXMLFirstChild(pDocID, tPTOpPath) into tComplexType
--put revXMLAttribute(pDocID, tPTOpPath, "name") into tType
put tName & tab & tType & tab & tOptional after tMessageParameters
put cr after tMessageParameters
else
put revXMLChildNames(pDocID, tComplexType, cr, "element", true) into tInput
-- loop through the input parameter list
put empty into tMessageParameters
repeat for each line tInputParameter in tInput
put revXMLAttribute(pDocID, tComplexType & tInputParameter, "name") into tName
put revXMLAttribute(pDocID, tComplexType & tInputParameter, "type") into tType
put revXMLAttribute(pDocID, tComplexType & tInputParameter, "minOccurs") into tOptional
put tName & tab & tType & tab & tOptional after tMessageParameters
put cr after tMessageParameters
end repeat
end if
return tMessageParameters
end WSDL.GetInputParameters
private function WSDL.GetOutputParameters pDocID, pParameterName
local tElementName
local tPTOpPath
local tComplexType
local tInput
local tName
constant kTypeParameters = "/definitions/types/schema/"
put revXMLMatchingNode( pDocID, "/definitions/", "message", "name", _stripNSPrefix(pParameterName), -1 ) into tPTOpPath
put revXMLFirstChild(pDocID, tPTOpPath) into tComplexType
-- get the output Response element name
put revXMLAttribute(pDocID, tComplexType, "type") into tElementName
if tElementName begins with "xmlerr" then
put revXMLAttribute(pDocID, tComplexType, "element") into tElementName
else
put revXMLAttribute(pDocID, tComplexType, "name") into tName
end if
put revXMLMatchingNode( pDocID, kTypeParameters, "element", "name", _stripNSPrefix(tElementName), -1 ) into tPTOpPath
-- get the output result element name
put revXMLFirstChild(pDocID, tPTOpPath & "/complexType") & "/" into tComplexType
if tComplexType begins with "xmlerr" then
else
put revXMLChildNames(pDocID, tComplexType, cr, "element", true) into tInput
put revXMLAttribute(pDocID, tComplexType & tInput, "name") into tName
end if
return _stripNSPrefix(tElementName) & "/" & tName
end WSDL.GetOutputParameters
private function WSDL.GetNameSpace pDocID
local tNamespace
put revXMLAttribute( pDocID, "/definitions", "targetNamespace") into tNamespace
return tNamespace
end WSDL.GetNameSpace
/**
* --------------------------------------------
* WSDL.CreateCPrototype
*
* Given a WSDL array (result of WSDL.ParseSOAP) and a service name
* Return a C prototype function for the web service request
*
* @pWSDLArray
* @pWebServiceRequest
* --------------------------------------------
*/
function WSDL.CreateCPrototype pWSDLArray, pWebServiceRequest
local tRequestArray
local tPrototype
local tParams
put pWSDLArray[pWebServiceRequest] into tRequestArray
put tRequestArray["outputParams"] into tParams
repeat for each line tLine in tParams
set the itemdelimiter to "="
-- "type="
put item -1 of tLine after tPrototype
-- parameter name
put space & word 1 of tLine & space after tPrototype
end repeat
put pWebServiceRequest after tPrototype
put "(" after tPrototype
put tRequestArray["inputParams"] into tParams
repeat for each line tLine in tParams
set the itemdelimiter to "="
-- "type="
put item -1 of tLine after tPrototype
-- parameter name
put space & word 1 of tLine & comma after tPrototype
end repeat
put ");" into char -1 of tPrototype
replace cr with comma in tPrototype
return tPrototype
end WSDL.CreateCPrototype
/**
* --------------------------------------------
* WSDL.CreatePrototype
*
* Given a WSDL array (result of WSDL.ParseSOAP) and a service name
* Return a LiveCode prototype function for the web service request
*
* @pWSDLArray
* @pWebServiceRequest
* @pKey : optional https key-value key
* @pValue : optional https key-value value
* --------------------------------------------
*/
function WSDL.CreatePrototype pWSDLArray, pWebServiceRequest, pKey, pValue
local tRequestArray
local tPrototype
local tParams
local tHeader
put pWSDLArray[pWebServiceRequest] into tRequestArray
put tRequestArray["inputParams"] into tParams
put "/**" & cr after tPrototype
put "--" && "webService." & pWebServiceRequest & cr after tPrototype
put "*/" & cr after tPrototype
put "function webService." & pWebServiceRequest & cr after tPrototype
put "local tResult" & cr after tPrototype
put "local tDocID" & cr after tPrototype
put "local tResponse" & cr after tPrototype
put "local tParams" & cr & cr after tPrototype
put "put empty into tParams" & cr after tPrototype
set the itemdelimiter to tab
repeat for each line tLine in tParams
if item -1 of tLine is 0 then
put "if p" & item 1 of tLine && "is not empty then" & cr after tPrototype
end if
put "put SOAP.AddParam(" after tPrototype
put quote & item 1 of tLine & quote & comma after tPrototype
put "p" & item 1 of tLine & comma after tPrototype
if word 3 of line 4 of tPrototype is not empty then
put comma after line 4 of tPrototype
end if
put space & "p" & item 1 of tLine after line 4 of tPrototype
put quote & item 2 of tLine & quote & comma after tPrototype
put quote & pWSDLArray["namespace"] & quote after tPrototype
put ") after tParams" & cr after tPrototype
if item -1 of tLine is 0 then
put "end if" & cr after tPrototype
end if
end repeat
put "put SOAP.RPCRequest(" after tPrototype
put quote & pWSDLArray["serviceAddress"] & quote & comma after tPrototype
put quote & pWebServiceRequest & quote & comma after tPrototype
-- namespace
put quote & pWSDLArray["namespace"] & quote & comma after tPrototype
-- put tRequestArray["inputParams"] into tParams
-- repeat for each line tLine in tParams
-- put word 1 of tLine & comma after tPrototype
-- end repeat
put "tParams" & comma after tPrototype
put quote & pWSDLArray["serviceAddress"] after tPrototype
if char -1 of tPrototype is not "/" then
put "/" after tPrototype
end if
put pWebServiceRequest & quote after tPrototype
-- uncomment the following lines if you need https authentication
-- and adjust the parameters accordingly
if "https" is in pWSDLArray["serviceAddress"] then
-- need a key-value pair here for authentication
--put SOAP.AddParam("authenticationKey", "authenticationValue", "string") into tHeader
--put SOAP.AddParam("AuthenticationHeader", tHeader, "", pWSDLArray["namespace"]) into tHeader
put comma & "authenticationKey" & comma & "authenticationValue" after tPrototype
--put comma & tHeader after tPrototype
end if
put ") into tResult" & cr after tPrototype
put "if tResult is empty then" & cr after tPrototype
put "put SOAP.Response() into tResponse" & cr after tPrototype
put "put revCreateXMLTree(tResponse, false, true, false) into tDocID" & cr after tPrototype
put "put revXMLNodeContents(tDocID," & quote & "/Envelope/Body/" \
& tRequestArray["outputParams"] \
& quote & ") into tResult" & cr after tPrototype
--& pWebServiceRequest & "Response/" & pWebServiceRequest & "Result"
put "end if" & cr after tPrototype
put "return tResult" & cr after tPrototype
put "end webService." & pWebServiceRequest & cr after tPrototype
return tPrototype
end WSDL.CreatePrototype
/**
* --------------------------------------------
* WSDL.CreatePrototypeFor
*
* Return a LiveCode prototype function for the web service request
*
* @pWebServiceRequest : web service to create prototype for
* --------------------------------------------
*/
function WSDL.CreatePrototypeFor pWebServiceRequest
local tWSDLArray
local tPrototype
answer file "where is the wsdl file?"
if it is not empty then
put WSDL.ParseSOAPFile(it) into tWSDLArray
put WSDL.CreateServerPrototype(tWSDLArray, pWebServiceRequest) into tPrototype
end if
return tPrototype
end WSDL.CreatePrototypeFor
/**
* --------------------------------------------
* WSDL.CreateServerPrototype
*
* Given a WSDL array (result of WSDL.ParseSOAP) and a service name
* Return a LiveCode prototype function for the server web service
*
* @pWSDLArray
* @pWebServiceRequest
* --------------------------------------------
*/
function WSDL.CreateServerPrototype pWSDLArray, pWebServiceRequest
local tPrototype
local tInParams
local tOutParam
set the itemdelimiter to tab
put item 1 of pWSDLArray[pWebServiceRequest]["inputParams"] into tInParams
put item 1 of pWSDLArray[pWebServiceRequest]["outputParams"] into tOutParam
if tOutParam is empty then
put "command" && pWebServiceRequest into tPrototype
else
put "function" && pWebServiceRequest into tPrototype
end if
if tInParams is not empty then
put space & "p" & tInParams after tPrototype
end if
put cr after tPrototype
if tOutParam is not empty then
put tab & "local" && "t" & tOutParam & cr & cr after tPrototype
put tab & "return" && "t" & tOutParam & cr after tPrototype
end if
put "end" && pWebServiceRequest & cr after tPrototype
return tPrototype
end WSDL.CreateServerPrototype
/**
* --------------------------------------------
* WSDL.CreateServerPrototypes
*
* Given a path to a wsdl file
* Create the LiveCode prototype functions for all the server web services
*
* @pWSDLFilePath : path to wsdl file
* --------------------------------------------
*/
function WSDL.CreateServerPrototypes pWSDLFilePath
local tXMLData
local tPrototypes
local tWSDLArray
put url("file:" & pWSDLFilePath) into tXMLData
if "xml" is not in line 1 of tXMLData then
put "" before tXMLData
end if
put WSDL.ParseSOAPData(tXMLData) into tWSDLArray
if the keys of tWSDLArray is not empty then
repeat for each line tWebServiceRequest in tWSDLArray["operations"]
put WSDL.CreateServerPrototype(tWSDLArray, tWebServiceRequest) & cr after tPrototypes
end repeat
end if
return tPrototypes
end WSDL.CreateServerPrototypes
/**
* --------------------------------------------
* WSDL.CreateServicePrototypeFromFile
*
* Given a path to a WSDL file and a service name
* Return a prototype function for the web service request
*
* @pWSDLFilePath
* @pWebServiceRequest
* usage:
* put WSDL.CreateServicePrototypeFromFile(pathToFile, webservice)
* put WSDL.CreateServicePrototypeFromFile("http://www.website.domain/file.wsdl", webservice)
* --------------------------------------------
*/
function WSDL.CreateServicePrototypeFromFile pWSDLFilePath, pWebServiceRequest
local tPrototype
local tWSDLArray
put WSDL.ParseSOAPFile (pWSDLFilePath) into tWSDLArray
if the keys of tWSDLArray is not empty then
put WSDL.CreatePrototype(tWSDLArray, pWebServiceRequest) into tPrototype
end if
return tPrototype
end WSDL.CreateServicePrototypeFromFile
/**
* --------------------------------------------
* WSDL.CreateServicePrototypesFromFile
*
* Given a path to a WSDL file
* Return prototype functions for all the web service requests
*
* @pWSDLFilePath : path to wsdl file
*
* usage:
* put WSDL.CreateServicePrototypesFromFile(pathToFile)
* put WSDL.CreateServicePrototypesFromFile("http://www.website.domain/file.wsdl")
* --------------------------------------------
*/
function WSDL.CreateServicePrototypesFromFile pWSDLFilePath
local tPrototypes
local tWSDLArray
local tWebServiceRequest
put WSDL.ParseSOAPFile(pWSDLFilePath) into tWSDLArray
if the keys of tWSDLArray is not empty then
repeat for each line tWebServiceRequest in tWSDLArray["operations"]
put WSDL.CreatePrototype(tWSDLArray, tWebServiceRequest) & cr after tPrototypes
end repeat
end if
return tPrototypes
end WSDL.CreateServicePrototypesFromFile
/**
* --------------------------------------------
* WSDL.EnumerateServicesFromFile
*
* Given a path to a WSDL file
* Return a list of the available web services
*
* @pWSDLFilePath : path to wsdl file
*
* usage:
* put WSDL.EnumerateServicesFromFile(pathToFile)
* put WSDL.EnumerateServicesFromFile("http://www.website.domain/file.wsdl")
* --------------------------------------------
*/
function WSDL.EnumerateServicesFromFile pWSDLFilePath
local tWSDLArray
local tReturn
put WSDL.ParseSOAPFile(pWSDLFilePath) into tWSDLArray
put the keys of tWSDLArray into tReturn
filter tReturn without "operations"
filter tReturn without "namespace"
filter tReturn without "serviceAddress"
return tReturn
end WSDL.EnumerateServicesFromFile
--> Schema handlers
/**
* --------------------------------------------
* Schema.Parse
*
* Parse a properly-formatted Schema file
* to determine the characteristics of a web service provider
*
* @pDocID -- ID of document xml tree
* @pWSDLArray -- pointer to output array
* --------------------------------------------
*/
on Schema.Parse pDocID, @pWSDLArray
local tBasePath
local tAttValue
local tSoapBindingPath
local tBindingStyle
local tBindingPath
local tBindingName
local tPortType, tPort
local tOperationList
local tOperationName, tParameterName
local tOperationNames, tPortTypes
local tInputMessage, tInput, tInP
local tPTOpPath
local tNamespace
local tComplexType
local tName, tType
local tMessageParameters
constant kTypeParameters = "/definitions/types/schema/"
put "/definitions/binding" into tBasePath
put "http://schemas.xmlsoap.org/soap/http" into tAttValue
breakpoint
put revXMLMatchingNode( pDocID, tBasePath, "binding", "transport", tAttValue, -1 ) into tSoapBindingPath
-- if tSoapBindingPath is empty then
-- put tBasePath into tSoapBindingPath
-- end if
put WSDL.GetNameSpace(pDocID) into pWSDLArray["namespace"]
-- all we support here is "RPC" and "document"
put revXMLAttribute( pDocID, tSoapBindingPath, "style") into tBindingStyle
if tBindingStyle is not "RPC" then
put "document" into tBindingStyle
end if
if tSoapBindingPath is empty then
put tBasePath into tBindingPath
else
put revXMLParent( pDocID, tSoapBindingPath) into tBindingPath
end if
get revXMLAttribute( pDocID, tBindingPath, "name")
put _stripNSPrefix(it) into tBindingName
put revXMLChildNames( pDocID, "/definitions/", cr,"portType", true) into tPortTypes
repeat for each line tPortType in tPortTypes
put "/definitions/" & tPortType & "/" into tPort
put revXMLChildNames( pDocID, tPort, cr,"operation", true) into tOperationList
-- go through the list of web services
repeat for each line tOperation in tOperationList
-- get the name of the web service
put revXMLAttribute(pDocID, tPort & tOperation, "name") into tOperationName
-- get the input parameters message
put revXMLAttribute(pDocID, tPort & tOperation & "/input", "message") into tParameterName
put WSDL.GetInputParameters(pDocID, tParameterName) into tMessageParameters
put tMessageParameters into pWSDLArray[tOperationName]["inputParams"]
-- get the output parameters message
put revXMLAttribute(pDocID, tPort & tOperation & "/output", "message") into tParameterName
put WSDL.GetOutputParameters(pDocID, tParameterName) into pWSDLArray[tOperationName]["outputParams"]
put tOperationName & cr after tOperationNames
put pWSDLArray["namespace"] into pWSDLArray[tOperationName]["namespace"]
end repeat
end repeat
chomp tOperationNames
put tOperationNames into pWSDLArray["operations"]
put _serviceAddress(pDocID, tBindingName) into pWSDLArray["serviceAddress"]
end Schema.Parse
/**
* --------------------------------------------
* Schema.EnumerateDataTypesFromFile
*
* Given a path to a WSDL file
* Return a list of the available web services
*
* @pWSDLFilePath : path to wsdl file
*
* usage:
* put Schema.EnumerateServicesFromFile(pathToFile)
* put Schema.EnumerateServicesFromFile("http://www.website.domain/file.wsdl")
* --------------------------------------------
*/
function Schema.EnumerateDataTypesFromFile pWSDLFilePath
local tWSDLArray
local tReturn
put Schema.ParseSchemaFile(pWSDLFilePath) into tWSDLArray
put the keys of tWSDLArray into tReturn
filter tReturn without "operations"
filter tReturn without "namespace"
filter tReturn without "serviceAddress"
return tReturn
end Schema.EnumerateDataTypesFromFile
/**
* --------------------------------------------
* Schema.ParseSchemaFile
*
* Return an array from a WSDL data file
*
* @pDocPath -- path to wsdl file
*
* usage:
* put Schema.ParseSchemaFile(pathToFile)
* put Schema.ParseSchemaFile("http://www.website.domain/file.wsdl")
* --------------------------------------------
*/
function Schema.ParseSchemaFile pDocPath
local tDocID
local tWSDLArray
local tPath
if pDocPath is not empty then
if pDocPath begins with "http" then
put tempname() into tPath
put url(pDocPath) into url("file:"&tPath)
put tPath into pDocPath
end if
put revCreateXMLTreeFromFile(pDocPath, false, true, false) into tDocID
if tDocID is a number then
Schema.Parse tDocID, tWSDLArray
else
put "not a valid xml file" into tWSDLArray["error"]
end if
end if
return tWSDLArray
end Schema.ParseSchemaFile
--> private handlers
/**
* --------------------------------------------
* XML.StartTag
*
* @pTag
* --------------------------------------------
*/
private function XML.StartTag pTag
return "<" & pTag & ">"
end XML.StartTag
/**
* --------------------------------------------
* XML.EndTag
*
* @pTag
* --------------------------------------------
*/
private function XML.EndTag pTag
return "" & pTag & ">"
end XML.EndTag
/**
* --------------------------------------------
* _SOAP.Post
*
* Post the SOAP envelope to the specified url
*
* Usage:
* get SOAP.Post(tSOAPEnvelope, "http://subscriptions.cloudmark.com/JACK/activations.asmx")
*
* @pSOAPEnvelope
* @pUrl
* @pAction
* --------------------------------------------
*/
private function _SOAP.Post pSOAPEnvelope, pUrl, pAction
local tHTTPHeaders
local tAction
-- set the http headers
put SOAP.AddHeader("Accept", "text/xml") after tHTTPHeaders
put SOAP.AddHeader("Accept", "multipart/*") after tHTTPHeaders
put SOAP.AddHeader("Connection", "Keep-Alive") after tHTTPHeaders
--put SOAP.AddHeader("User-Agent", "PHP-SOAP/5.3.6") after tHTTPHeaders
put SOAP.AddHeader("Content-Type", "text/xml; charset=utf-8") after tHTTPHeaders
put SOAP.AddHeader("SOAPAction", quote & pAction & quote) after tHTTPHeaders
put SOAP.AddHeader("Content-length", the length of pSOAPEnvelope) after tHTTPHeaders
set the httpheaders to tHTTPHeaders
-- ignore the fact that we don't have a valid certificate
libUrlSetSSLVerification false
-- save the elements so they can be recovered by the accessor functions
put tHTTPHeaders into sSOAPHeaders
put pSOAPEnvelope into sSOAPEnvelope
-- if there is a field "fldSOAPEnvelope" then
-- put sSOAPHeaders & cr & sSOAPEnvelope into field "fldSOAPEnvelope"
-- end if
-- note that post is a blocking call: may need to set the timeout
post pSOAPEnvelope to url pUrl
put it into sSOAPResponse
return the result
end _SOAP.Post
/**
* --------------------------------------------
* _SOAP.ActionHeaders
*
* Set up the headers common to all SOAP actions
*
* @pSOAPAction
* --------------------------------------------
*/
private function _SOAP.ActionHeaders pSoapAction
local tHeaders
put SOAP.AddHeader("Content-Type", "text/xml") into tHeaders
put SOAP.AddHeader("SOAPAction", quote & pSoapAction & quote) after tHeaders
return tHeaders
end _SOAP.ActionHeaders
/**
* --------------------------------------------
* _serviceAddress
*
* @pDocID
* @pBindingName
* --------------------------------------------
*/
private function _serviceAddress pDocID, pBindingName
local tServiceList
local tServicePath
local tPortList
local tPortPath
local tAddressPath
get revXMLChildNames( pDocID, "definitions", cr, "service", true)
if item 1 of it is "xmlerr" then
put empty into it
end if
put it into tServiceList
repeat for each line tService in tServiceList
put "definitions/" & tService into tServicePath
get revXMLChildNames( pDocID, tServicePath, cr, "port", true)
if item 1 of it is "xmlerr" then
put empty into it
end if
put it into tPortList
repeat for each line tPort in tPortList
put tServicePath & "/" & "port" into tPortPath
get revXMLAttribute( pDocID, tPortPath, "binding")
if _stripNSPrefix(it) = pBindingName then
##get address
put tPortPath & "/" & "address" into tAddressPath
return revXMLAttribute( pDocID, tAddressPath, "location")
end if
end repeat
end repeat
return empty ##only if failed
end _serviceAddress
/**
* --------------------------------------------
* _stripNSPrefix
*
* Strip the namespace prefix from a parameter
* Return just the last item (separated by ":")
* put _stripNSPrefix("SOAP::Transport::HTTP::Client::send_receive: POST")
* results in " POST"
* --------------------------------------------
*/
private function _stripNSPrefix pString
if ":" is in pString then
repeat while ":" is in pString
delete char 1 of pString
end repeat
end if
return pString
end _stripNSPrefix
private command chomp @pText
if char -1 of pText is cr then
delete char -1 of pText
end if
end chomp
private function q pText
return quote & pText & quote
end q
4d
W Tahoma
W Tahoma U Tahoma
U Lucida Grande W Tahoma U
Helvetica U Tahoma U Tahoma AW Source Code Pro U Source Code Pro cPWRBreakpoints uRIP name libSOAPauthor Mark Wieder and Ah Softwareglx2VersionControl
1360003286914version 1.4date
2014.06.10 cREVGeometryCache stackID 1042
cGlx2General cScriptChecksum CompilesForArchive 2cExplicitVariables cHscrollScript 0cLocals (sSOAPResponse
sSOAPHeaders
sSOAPEnvelopecHscrollHandlers 0cHtmlScript !Z =ks+p+-wE'IGXX"ʧr
wArٙ,x50].89@h D"-m}!&eq%FNYUDs!EOE%<;V@:+q0|=Wb54Hq]_Rda̲Ίs1Ot& ;`/e:^湼 ]Ⱥ_^S
iMęBVrynJ>/^GJ#DP]Ɗt"/d"뺬7vz-)5˹W'Bx+6QU6eskoO/LVXVW̫,5,kFvkӌH4ZWє:o=~?N r+1&`(uxF@;F+qh"oEֈIZS)t#`8)`̭2nQ"BU䬼bQ?U\hS:Ϳ*˿{;&*XɅ\iʀ<4㣚Qgz;fhjQFz$H,"`4ĒN5d E
lrھ}yH(aUAc32f^eW01ᣯ5CHpρDzfyz
ֶ A`
ŵlXX(.|畜0
@4粨3ئ,s.Eǣ*@$lFwC]&Ei~TSATXk<3\xڙ4NZ.m ph0pTɪ:.@<y\U#SYmW 94+F8!N,Y6e30Ip!y9Iw +.
94KtrkjK\d`
WF{#},D.k`+F9tBp1(& J,$Y=4`r,ZT)D7Rx7(:q?}*FjM$
9Cаzr]xi=Ip0 D
HYe-A(LdkVHV(
ZJ{yK?4j^*H'L@G,9˧Y,N"cI5E;^Mڅi|ֶ{%vERC_]7?ˁSc1B\b"لzFd_g4(8
;FpdњՌMo c!4
ZTSpX;i|\N,~K"pK0L#$(YEjiQ_#qlI|דmp[Ȳ!پ.vɷ7D&GLiɫ'>z=᧱UЮ7WC0Fk<+(U+sX.9_Ff࣠O4UӂvqsuzR%Q-bw/O^~8|wZS=~K_9?%(Pv 4ӿZvXVYdS#@Un!$ELePkV"/)
WXl@ & aWdiڤd^>GX~\c3Z-m-L`Kd[i`d MhN%`1` TټicXH/6i82dKyR/FW6z
5dIϹ%m0<>Zͬi6mbTy6X.W/qn !6Uִo!Tu5fu&ZS$>;#z̴;Ɋ\5ؽ2{U.C4 oirojv?.EտWRVTw(^;'Tg[{P3=:6fkύb 6]ٻJYCX s搌IH$ɦ#r?U 1A2DdQU'h<`]~WD'Fz^vdlh#['挲&=-/$z1,%+P,]X "p*7PټfLC
ۑN@ @1 'NBmٙw]I PK 㬞Rϰ'#={멫̵#[ՌF $*D0IR? BE)dWFQ#Jz2W&üqk3H5:T J*Vսm=5>ZQ-&8>a\ rf}Wv7.če
0"RExSvW;O SݖNxcAz"/?p#֕Ǟg7[Fh<%ވP\6`"KrCX
ʼKwPŊ"N.訔1p@NX~mlse?:e
j%xzz:`I=p;W}&o\1.1Zz۸>QVYH@.Dl(mZ-?RaR[ѧ!xe,RAUlYSAyBoT=y=y2_u1Vٺ[3:\ž92+`ty)h{~9i] S1
?>Nƴ^그Urpko(?F̚lbzYEѪ$Jb+VoU[ eDس=o(1Ԣ/`U]NjG4n/Y[oP\cx7zHݐ.1
^_W@|& ,N
[Cٝ.'7Xv̍q)Te7"]qjb69xAeP28ztU@%qM-vjXO<VJV>$;Y
b
akQV.?JU^iteS:7; ͒|lͶm>e;%BF0Eyru*bŌm6{1
MYDCY fNF.G]ե nꚑtܰt%;)!^eS |CG'6wXZUΟ n}rx9"HW2>~2 æޡ)wx9KqT֥t] e(C~5o#pнzuW]=Т>Gx'{T<:y?&\'m8o&RB NwbmvYS,+(K(^SwγJGMeF(ȯww\N%?@$fkSE=%h8niX]%/\-^0 똶dL{Y!I EPibCbT(DT8
`:r"bzv |쬄}cg0ra,ݓ
/52ιɚ?#bE{~ju3$s-խUSGNW~)ن 뫅vi$i0֘Q