Convert from NULL macro to nullptr.
[shibboleth/cpp-xmltooling.git] / xmltooling / soap / impl / SOAPClient.cpp
1 /*
2  *  Copyright 2001-2010 Internet2
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /**
18  * SOAPClient.cpp
19  * 
20  * Implements SOAP 1.1 messaging over a transport.
21  */
22
23 #include "internal.h"
24 #include "exceptions.h"
25 #include "logging.h"
26 #include "soap/HTTPSOAPTransport.h"
27 #include "soap/SOAP.h"
28 #include "soap/SOAPClient.h"
29 #include "util/XMLHelper.h"
30 #include "validation/ValidatorSuite.h"
31
32 #include <sstream>
33
34 using namespace soap11;
35 using namespace xmltooling::logging;
36 using namespace xmltooling;
37 using namespace xercesc;
38 using namespace std;
39
40 SOAPTransport::SOAPTransport()
41 {
42 }
43
44 SOAPTransport::~SOAPTransport()
45 {
46 }
47
48 bool SOAPTransport::setProviderOption(const char* provider, const char* option, const char* value)
49 {
50     return false;
51 }
52
53 bool SOAPTransport::setCacheTag(string* cacheTag)
54 {
55     return false;
56 }
57
58 void SOAPTransport::send(istream* in)
59 {
60     if (!in)
61         throw IOException("SOAP transport does not support an empty request body.");
62     return send(*in);
63 }
64
65 long SOAPTransport::getStatusCode() const
66 {
67     return 0;
68 }
69
70 HTTPSOAPTransport::HTTPSOAPTransport()
71 {
72 }
73
74 HTTPSOAPTransport::~HTTPSOAPTransport()
75 {
76 }
77
78 SOAPClient::SOAPClient(bool validate) : m_validate(validate), m_transport(nullptr)
79 {
80 }
81
82 SOAPClient::~SOAPClient()
83 {
84     delete m_transport;
85 }
86
87 void SOAPClient::setValidating(bool validate)
88 {
89     m_validate = validate;
90 }
91
92 void SOAPClient::reset()
93 {
94     delete m_transport;
95     m_transport=nullptr;
96 }
97
98 void SOAPClient::send(const Envelope& env, const SOAPTransport::Address& addr)
99 {
100     // Prepare a transport object.
101     const char* pch = addr.m_endpoint ? strchr(addr.m_endpoint,':') : nullptr;
102     if (!pch)
103         throw IOException("SOAP endpoint was not a URL.");
104     string scheme(addr.m_endpoint, pch-addr.m_endpoint);
105     m_transport = XMLToolingConfig::getConfig().SOAPTransportManager.newPlugin(scheme.c_str(), addr);
106     prepareTransport(*m_transport);
107     
108     Category& log = Category::getInstance(XMLTOOLING_LOGCAT".SOAPClient");
109     if (log.isDebugEnabled())
110         log.debugStream() << "marshalled envelope:\n" << env << logging::eol;
111     
112     // Serialize envelope.
113     stringstream s;
114     s << env;
115     
116     // Send to peer.
117     m_transport->send(s);
118 }
119
120 Envelope* SOAPClient::receive()
121 {
122     if (!m_transport)
123         throw IOException("No call is active.");
124     
125     // If we can get the stream, then the call is still active.
126     istream& out = m_transport->receive();
127     if (!out)
128         return nullptr;    // nothing yet
129     
130     // Check content type.
131     string s = m_transport->getContentType();
132     if (s.find("text/xml") == string::npos)
133         throw IOException("Incorrect content type ($1) for SOAP response.", params(1,s.c_str() ? s.c_str() : "none"));
134     
135     // Parse and bind the document into an XMLObject.
136     DOMDocument* doc = (m_validate ? XMLToolingConfig::getConfig().getValidatingParser()
137         : XMLToolingConfig::getConfig().getParser()).parse(out); 
138     XercesJanitor<DOMDocument> janitor(doc);
139
140     Category& log = Category::getInstance(XMLTOOLING_LOGCAT".SOAPClient");
141     if (log.isDebugEnabled()) {
142 #ifdef XMLTOOLING_LOG4SHIB
143         log.debugStream() << "received XML:\n" << *(doc->getDocumentElement()) << logging::eol;
144 #else
145         string buf;
146         XMLHelper::serialize(doc->getDocumentElement(), buf);
147         log.debugStream() << "received XML:\n" << buf << logging::eol;
148 #endif
149     }
150     
151     auto_ptr<XMLObject> xmlObject(XMLObjectBuilder::buildOneFromElement(doc->getDocumentElement(), true));
152     janitor.release();
153
154     SchemaValidators.validate(xmlObject.get());
155
156     Envelope* env = dynamic_cast<Envelope*>(xmlObject.get());
157     if (!env)
158         throw IOException("Response was not a SOAP 1.1 Envelope.");
159
160     Body* body = env->getBody();
161     if (body && body->hasChildren()) {
162         //Check for a Fault.
163         const Fault* fault = dynamic_cast<Fault*>(body->getUnknownXMLObjects().front());
164         if (fault && handleFault(*fault))
165             throw IOException("SOAP client detected a Fault.");
166     }
167
168     xmlObject.release();
169     return env;
170 }
171
172 void SOAPClient::prepareTransport(SOAPTransport& transport)
173 {
174 }
175
176 bool SOAPClient::handleFault(const Fault& fault)
177 {
178     const xmltooling::QName* code = (fault.getFaultcode() ? fault.getFaultcode()->getCode() : nullptr);
179     auto_ptr_char str((fault.getFaultstring() ? fault.getFaultstring()->getString() : nullptr));
180     Category::getInstance(XMLTOOLING_LOGCAT".SOAPClient").error(
181         "SOAP client detected a Fault: (%s) (%s)",
182         (code ? code->toString().c_str() : "no code"),
183         (str.get() ? str.get() : "no message")
184         );
185     return true;
186 }