VS10 solution files, convert from NULL macro to nullptr.
[shibboleth/sp.git] / shibsp / handler / impl / RemotedHandler.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  * RemotedHandler.cpp
19  * 
20  * Base class for handlers that need SP request/response layer to be remoted. 
21  */
22
23 #include "internal.h"
24 #include "Application.h"
25 #include "exceptions.h"
26 #include "ServiceProvider.h"
27 #include "SPRequest.h"
28 #include "handler/RemotedHandler.h"
29
30 #include <algorithm>
31 #include <xmltooling/unicode.h>
32
33 #ifndef SHIBSP_LITE
34 # include "util/CGIParser.h"
35 # include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
36 # include <xsec/enc/XSECCryptoException.hpp>
37 # include <xsec/framework/XSECException.hpp>
38 # include <xsec/framework/XSECProvider.hpp>
39 #endif
40
41 using namespace shibsp;
42 using namespace opensaml;
43 using namespace xmltooling;
44 using namespace xercesc;
45 using namespace std;
46
47 #ifndef SHIBSP_LITE
48 namespace shibsp {
49     class SHIBSP_DLLLOCAL RemotedRequest : public virtual HTTPRequest 
50     {
51         DDF& m_input;
52         mutable CGIParser* m_parser;
53         mutable vector<XSECCryptoX509*> m_certs;
54     public:
55         RemotedRequest(DDF& input) : m_input(input), m_parser(nullptr) {}
56         virtual ~RemotedRequest() {
57             for_each(m_certs.begin(), m_certs.end(), xmltooling::cleanup<XSECCryptoX509>());
58             delete m_parser;
59         }
60
61         // GenericRequest
62         const char* getScheme() const {
63             return m_input["scheme"].string();
64         }
65         const char* getHostname() const {
66             return m_input["hostname"].string();
67         }
68         int getPort() const {
69             return m_input["port"].integer();
70         }
71         std::string getContentType() const {
72             DDF s = m_input["content_type"];
73             return s.string() ? s.string() : "";
74         }
75         long getContentLength() const {
76             return m_input["content_length"].integer();
77         }
78         const char* getRequestBody() const {
79             return m_input["body"].string();
80         }
81
82         const char* getParameter(const char* name) const;
83         std::vector<const char*>::size_type getParameters(const char* name, std::vector<const char*>& values) const;
84         
85         std::string getRemoteUser() const {
86             DDF s = m_input["remote_user"];
87             return s.string() ? s.string() : "";
88         }
89         std::string getRemoteAddr() const {
90             DDF s = m_input["client_addr"];
91             return s.string() ? s.string() : "";
92         }
93
94         const std::vector<XSECCryptoX509*>& getClientCertificates() const;
95         
96         // HTTPRequest
97         const char* getMethod() const {
98             return m_input["method"].string();
99         }
100         const char* getRequestURI() const {
101             return m_input["uri"].string();
102         }
103         const char* getRequestURL() const {
104             return m_input["url"].string();
105         }
106         const char* getQueryString() const {
107             return m_input["query"].string();
108         }
109         std::string getHeader(const char* name) const {
110             DDF s = m_input["headers"][name];
111             return s.string() ? s.string() : "";
112         }
113     };
114
115     class SHIBSP_DLLLOCAL RemotedResponse : public virtual HTTPResponse 
116     {
117         DDF& m_output;
118     public:
119         RemotedResponse(DDF& output) : m_output(output) {}
120         virtual ~RemotedResponse() {}
121        
122         // GenericResponse
123         long sendResponse(std::istream& inputStream, long status);
124         
125         // HTTPResponse
126         void setResponseHeader(const char* name, const char* value);
127         long sendRedirect(const char* url);
128     };
129 }
130
131 const char* RemotedRequest::getParameter(const char* name) const
132 {
133     if (!m_parser)
134         m_parser=new CGIParser(*this);
135     
136     pair<CGIParser::walker,CGIParser::walker> bounds=m_parser->getParameters(name);
137     return (bounds.first==bounds.second) ? nullptr : bounds.first->second;
138 }
139
140 std::vector<const char*>::size_type RemotedRequest::getParameters(const char* name, std::vector<const char*>& values) const
141 {
142     if (!m_parser)
143         m_parser=new CGIParser(*this);
144
145     pair<CGIParser::walker,CGIParser::walker> bounds=m_parser->getParameters(name);
146     while (bounds.first!=bounds.second) {
147         values.push_back(bounds.first->second);
148         ++bounds.first;
149     }
150     return values.size();
151 }
152
153 const std::vector<XSECCryptoX509*>& RemotedRequest::getClientCertificates() const
154 {
155     if (m_certs.empty()) {
156         DDF certs = m_input["certificates"];
157         DDF cert = certs.first();
158         while (cert.string()) {
159             try {
160                 auto_ptr<XSECCryptoX509> x509(XSECPlatformUtils::g_cryptoProvider->X509());
161                 if (strstr(cert.string(), "BEGIN"))
162                     x509->loadX509PEM(cert.string(), cert.strlen());
163                 else
164                     x509->loadX509Base64Bin(cert.string(), cert.strlen());
165                 m_certs.push_back(x509.release());
166             }
167             catch(XSECException& e) {
168                 auto_ptr_char temp(e.getMsg());
169                 Category::getInstance(SHIBSP_LOGCAT".SPRequest").error("XML-Security exception loading client certificate: %s", temp.get());
170             }
171             catch(XSECCryptoException& e) {
172                 Category::getInstance(SHIBSP_LOGCAT".SPRequest").error("XML-Security exception loading client certificate: %s", e.getMsg());
173             }
174             cert = certs.next();
175         }
176     }
177     return m_certs;
178 }
179
180 long RemotedResponse::sendResponse(std::istream& in, long status)
181 {
182     string msg;
183     char buf[1024];
184     while (in) {
185         in.read(buf,1024);
186         msg.append(buf,in.gcount());
187     }
188     if (!m_output.isstruct())
189         m_output.structure();
190     m_output.addmember("response.data").string(msg.c_str());
191     m_output.addmember("response.status").integer(status);
192     return status;
193 }
194
195 void RemotedResponse::setResponseHeader(const char* name, const char* value)
196 {
197     if (!m_output.isstruct())
198         m_output.structure();
199     DDF hdrs = m_output["headers"];
200     if (hdrs.isnull())
201         hdrs = m_output.addmember("headers").list();
202     DDF h = DDF(name).string(value);
203     hdrs.add(h);
204 }
205
206 long RemotedResponse::sendRedirect(const char* url)
207 {
208     if (!m_output.isstruct())
209         m_output.structure();
210     m_output.addmember("redirect").unsafe_string(url);
211     return HTTPResponse::XMLTOOLING_HTTP_STATUS_MOVED;
212 }
213
214 #endif
215
216 void RemotedHandler::setAddress(const char* address)
217 {
218     if (!m_address.empty())
219         throw ConfigurationException("Cannot register a remoting address twice for the same Handler.");
220     m_address = address;
221     SPConfig& conf = SPConfig::getConfig();
222     if (!conf.isEnabled(SPConfig::InProcess)) {
223         ListenerService* listener = conf.getServiceProvider()->getListenerService(false);
224         if (listener)
225             listener->regListener(m_address.c_str(),this);
226         else
227             Category::getInstance(SHIBSP_LOGCAT".Handler").info("no ListenerService available, handler remoting disabled");
228     }
229 }
230
231 RemotedHandler::RemotedHandler()
232 {
233 }
234
235 RemotedHandler::~RemotedHandler()
236 {
237     SPConfig& conf = SPConfig::getConfig();
238     ListenerService* listener=conf.getServiceProvider()->getListenerService(false);
239     if (listener && conf.isEnabled(SPConfig::OutOfProcess) && !conf.isEnabled(SPConfig::InProcess))
240         listener->unregListener(m_address.c_str(),this);
241 }
242
243 DDF RemotedHandler::wrap(const SPRequest& request, const vector<string>* headers, bool certs) const
244 {
245     DDF in = DDF(m_address.c_str()).structure();
246     in.addmember("application_id").string(request.getApplication().getId());
247     in.addmember("scheme").string(request.getScheme());
248     in.addmember("hostname").unsafe_string(request.getHostname());
249     in.addmember("port").integer(request.getPort());
250     in.addmember("content_type").string(request.getContentType().c_str());
251     in.addmember("content_length").integer(request.getContentLength());
252     in.addmember("body").string(request.getRequestBody());
253     in.addmember("remote_user").string(request.getRemoteUser().c_str());
254     in.addmember("client_addr").string(request.getRemoteAddr().c_str());
255     in.addmember("method").string(request.getMethod());
256     in.addmember("uri").unsafe_string(request.getRequestURI());
257     in.addmember("url").unsafe_string(request.getRequestURL());
258     in.addmember("query").string(request.getQueryString());
259
260     if (headers) {
261         string hdr;
262         DDF hin = in.addmember("headers").structure();
263         for (vector<string>::const_iterator h = headers->begin(); h!=headers->end(); ++h) {
264             hdr = request.getHeader(h->c_str());
265             if (!hdr.empty())
266                 hin.addmember(h->c_str()).string(hdr.c_str());
267         }
268     }
269
270     if (certs) {
271 #ifndef SHIBSP_LITE
272         const vector<XSECCryptoX509*>& xvec = request.getClientCertificates();
273         if (!xvec.empty()) {
274             DDF clist = in.addmember("certificates").list();
275             for (vector<XSECCryptoX509*>::const_iterator x = xvec.begin(); x!=xvec.end(); ++x) {
276                 DDF x509 = DDF(nullptr).string((*x)->getDEREncodingSB().rawCharBuffer());
277                 clist.add(x509);
278             }
279         }
280 #else
281         const vector<string>& xvec = request.getClientCertificates();
282         if (!xvec.empty()) {
283             DDF clist = in.addmember("certificates").list();
284             for (vector<string>::const_iterator x = xvec.begin(); x!=xvec.end(); ++x) {
285                 DDF x509 = DDF(nullptr).string(x->c_str());
286                 clist.add(x509);
287             }
288         }
289 #endif
290     }
291
292     return in;
293 }
294
295 pair<bool,long> RemotedHandler::unwrap(SPRequest& request, DDF& out) const
296 {
297     DDF h = out["headers"];
298     DDF hdr = h.first();
299     while (hdr.isstring()) {
300 #ifdef HAVE_STRCASECMP
301         if (!strcasecmp(hdr.name(), "Content-Type"))
302 #else
303         if (!stricmp(hdr.name(), "Content-Type"))
304 #endif
305             request.setContentType(hdr.string());
306         else
307             request.setResponseHeader(hdr.name(), hdr.string());
308         hdr = h.next();
309     }
310     h = out["redirect"];
311     if (h.isstring())
312         return make_pair(true, request.sendRedirect(h.string()));
313     h = out["response"];
314     if (h.isstruct()) {
315         istringstream s(h["data"].string());
316         return make_pair(true, request.sendResponse(s, h["status"].integer()));
317     }
318     return make_pair(false,0L);
319 }
320
321 HTTPRequest* RemotedHandler::getRequest(DDF& in) const
322 {
323 #ifndef SHIBSP_LITE
324     return new RemotedRequest(in);
325 #else
326     throw ConfigurationException("Cannot process message using lite version of shibsp library.");
327 #endif
328 }
329
330 HTTPResponse* RemotedHandler::getResponse(DDF& out) const
331 {
332 #ifndef SHIBSP_LITE
333     return new RemotedResponse(out);
334 #else
335     throw ConfigurationException("Cannot process message using lite version of shibsp library.");
336 #endif
337 }