VS10 solution files, convert from NULL macro to nullptr.
[shibboleth/sp.git] / shibsp / AbstractSPRequest.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  * AbstractSPRequest.cpp
19  *
20  * Abstract base for SPRequest implementations.
21  */
22
23 #include "internal.h"
24 #include "exceptions.h"
25 #include "AbstractSPRequest.h"
26 #include "Application.h"
27 #include "ServiceProvider.h"
28 #include "SessionCache.h"
29 #include "util/CGIParser.h"
30
31 using namespace shibsp;
32 using namespace opensaml;
33 using namespace xmltooling;
34 using namespace std;
35
36 SPRequest::SPRequest()
37 {
38 }
39
40 SPRequest::~SPRequest()
41 {
42 }
43
44 string SPRequest::getSecureHeader(const char* name) const
45 {
46     return getHeader(name);
47 }
48
49 void SPRequest::setAuthType(const char* authtype)
50 {
51 }
52
53 AbstractSPRequest::AbstractSPRequest(const char* category)
54     : m_sp(nullptr), m_mapper(nullptr), m_app(nullptr), m_sessionTried(false), m_session(nullptr),
55         m_log(&Category::getInstance(category)), m_parser(nullptr)
56 {
57     m_sp=SPConfig::getConfig().getServiceProvider();
58     m_sp->lock();
59 }
60
61 AbstractSPRequest::~AbstractSPRequest()
62 {
63     if (m_session)
64         m_session->unlock();
65     if (m_mapper)
66         m_mapper->unlock();
67     if (m_sp)
68         m_sp->unlock();
69     delete m_parser;
70 }
71
72 const ServiceProvider& AbstractSPRequest::getServiceProvider() const
73 {
74     return *m_sp;
75 }
76
77 RequestMapper::Settings AbstractSPRequest::getRequestSettings() const
78 {
79     if (!m_mapper) {
80         // Map request to application and content settings.
81         m_mapper=m_sp->getRequestMapper();
82         m_mapper->lock();
83         m_settings = m_mapper->getSettings(*this);
84
85         if (reinterpret_cast<Category*>(m_log)->isDebugEnabled()) {
86             reinterpret_cast<Category*>(m_log)->debug(
87                 "mapped %s to %s", getRequestURL(), m_settings.first->getString("applicationId").second
88                 );
89         }
90     }
91     return m_settings;
92 }
93
94 const Application& AbstractSPRequest::getApplication() const
95 {
96     if (!m_app) {
97         // Now find the application from the URL settings
98         m_app=m_sp->getApplication(getRequestSettings().first->getString("applicationId").second);
99         if (!m_app)
100             throw ConfigurationException("Unable to map request to ApplicationOverride settings, check configuration.");
101     }
102     return *m_app;
103 }
104
105 Session* AbstractSPRequest::getSession(bool checkTimeout, bool ignoreAddress, bool cache)
106 {
107     // Only attempt this once.
108     if (cache && m_sessionTried)
109         return m_session;
110     else if (cache)
111         m_sessionTried = true;
112
113     // Need address checking and timeout settings.
114     time_t timeout=3600;
115     if (checkTimeout || !ignoreAddress) {
116         const PropertySet* props=getApplication().getPropertySet("Sessions");
117         if (props) {
118             if (checkTimeout) {
119                 pair<bool,unsigned int> p=props->getUnsignedInt("timeout");
120                 if (p.first)
121                     timeout = p.second;
122             }
123             pair<bool,bool> pcheck=props->getBool("consistentAddress");
124             if (pcheck.first)
125                 ignoreAddress = !pcheck.second;
126         }
127     }
128
129     // The cache will either silently pass a session or nullptr back, or throw an exception out.
130     Session* session = getServiceProvider().getSessionCache()->find(
131         getApplication(), *this, ignoreAddress ? nullptr : getRemoteAddr().c_str(), checkTimeout ? &timeout : nullptr
132         );
133     if (cache)
134         m_session = session;
135     return session;
136 }
137
138 static char _x2c(const char *what)
139 {
140     register char digit;
141
142     digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
143     digit *= 16;
144     digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));
145     return(digit);
146 }
147
148 void AbstractSPRequest::setRequestURI(const char* uri)
149 {
150     // Fix for bug 574, secadv 20061002
151     // Unescape URI up to query string delimiter by looking for %XX escapes.
152     // Adapted from Apache's util.c, ap_unescape_url function.
153     if (uri) {
154         while (*uri) {
155             if (*uri == '?') {
156                 m_uri += uri;
157                 break;
158             }
159             else if (*uri != '%') {
160                 m_uri += *uri;
161             }
162             else {
163                 ++uri;
164                 if (!isxdigit(*uri) || !isxdigit(*(uri+1)))
165                     throw ConfigurationException("Bad request, contained unsupported encoded characters.");
166                 m_uri += _x2c(uri);
167                 ++uri;
168             }
169             ++uri;
170         }
171     }
172 }
173
174 const char* AbstractSPRequest::getRequestURI() const
175 {
176     return m_uri.c_str();
177 }
178
179 const char* AbstractSPRequest::getRequestURL() const
180 {
181     if (m_url.empty()) {
182         // Compute the full target URL
183         int port = getPort();
184         const char* scheme = getScheme();
185         m_url = string(scheme) + "://" + getHostname();
186         if ((!strcmp(scheme,"http") && port!=80) || (!strcmp(scheme,"https") && port!=443)) {
187             ostringstream portstr;
188             portstr << port;
189             m_url += ":" + portstr.str();
190         }
191         m_url += m_uri;
192     }
193     return m_url.c_str();
194 }
195
196 string AbstractSPRequest::getRemoteAddr() const
197 {
198     pair<bool,const char*> addr = getRequestSettings().first->getString("REMOTE_ADDR");
199     return addr.first ? getHeader(addr.second) : "";
200 }
201
202 const char* AbstractSPRequest::getParameter(const char* name) const
203 {
204     if (!m_parser)
205         m_parser=new CGIParser(*this);
206
207     pair<CGIParser::walker,CGIParser::walker> bounds=m_parser->getParameters(name);
208     return (bounds.first==bounds.second) ? nullptr : bounds.first->second;
209 }
210
211 vector<const char*>::size_type AbstractSPRequest::getParameters(const char* name, vector<const char*>& values) const
212 {
213     if (!m_parser)
214         m_parser=new CGIParser(*this);
215
216     pair<CGIParser::walker,CGIParser::walker> bounds=m_parser->getParameters(name);
217     while (bounds.first!=bounds.second) {
218         values.push_back(bounds.first->second);
219         ++bounds.first;
220     }
221     return values.size();
222 }
223
224 const char* AbstractSPRequest::getHandlerURL(const char* resource) const
225 {
226     if (!resource)
227         resource = getRequestURL();
228
229     if (!m_handlerURL.empty() && resource && !strcmp(getRequestURL(),resource))
230         return m_handlerURL.c_str();
231
232     string stackresource;
233     if (resource && *resource == '/') {
234         // Compute a URL to the root of the site and point resource at constructed string.
235         int port = getPort();
236         const char* scheme = getScheme();
237         stackresource = string(scheme) + "://" + getHostname();
238         if ((!strcmp(scheme,"http") && port!=80) || (!strcmp(scheme,"https") && port!=443)) {
239             ostringstream portstr;
240             portstr << port;
241             stackresource += ":" + portstr.str();
242         }
243         stackresource += resource;
244         resource = stackresource.c_str();
245     }
246
247 #ifdef HAVE_STRCASECMP
248     if (!resource || (strncasecmp(resource,"http://",7) && strncasecmp(resource,"https://",8)))
249 #else
250     if (!resource || (strnicmp(resource,"http://",7) && strnicmp(resource,"https://",8)))
251 #endif
252         throw ConfigurationException("Target resource was not an absolute URL.");
253
254     bool ssl_only=true;
255     const char* handler=nullptr;
256     const PropertySet* props=getApplication().getPropertySet("Sessions");
257     if (props) {
258         pair<bool,bool> p=props->getBool("handlerSSL");
259         if (p.first)
260             ssl_only=p.second;
261         pair<bool,const char*> p2=props->getString("handlerURL");
262         if (p2.first)
263             handler=p2.second;
264     }
265
266     // Should never happen...
267     if (!handler || (*handler!='/' && strncmp(handler,"http:",5) && strncmp(handler,"https:",6)))
268         throw ConfigurationException(
269             "Invalid handlerURL property ($1) in <Sessions> element for Application ($2)",
270             params(2, handler ? handler : "null", m_app->getId())
271             );
272
273     // The "handlerURL" property can be in one of three formats:
274     //
275     // 1) a full URI:       http://host/foo/bar
276     // 2) a hostless URI:   http:///foo/bar
277     // 3) a relative path:  /foo/bar
278     //
279     // #  Protocol  Host        Path
280     // 1  handler   handler     handler
281     // 2  handler   resource    handler
282     // 3  resource  resource    handler
283     //
284     // note: if ssl_only is true, make sure the protocol is https
285
286     const char* path = nullptr;
287
288     // Decide whether to use the handler or the resource for the "protocol"
289     const char* prot;
290     if (*handler != '/') {
291         prot = handler;
292     }
293     else {
294         prot = resource;
295         path = handler;
296     }
297
298     // break apart the "protocol" string into protocol, host, and "the rest"
299     const char* colon=strchr(prot,':');
300     colon += 3;
301     const char* slash=strchr(colon,'/');
302     if (!path)
303         path = slash;
304
305     // Compute the actual protocol and store in member.
306     if (ssl_only)
307         m_handlerURL.assign("https://");
308     else
309         m_handlerURL.assign(prot, colon-prot);
310
311     // create the "host" from either the colon/slash or from the target string
312     // If prot == handler then we're in either #1 or #2, else #3.
313     // If slash == colon then we're in #2.
314     if (prot != handler || slash == colon) {
315         colon = strchr(resource, ':');
316         colon += 3;      // Get past the ://
317         slash = strchr(colon, '/');
318     }
319     string host(colon, (slash ? slash-colon : strlen(colon)));
320
321     // Build the handler URL
322     m_handlerURL += host + path;
323     return m_handlerURL.c_str();
324 }
325
326 void AbstractSPRequest::log(SPLogLevel level, const std::string& msg) const
327 {
328     reinterpret_cast<Category*>(m_log)->log(
329         (level == SPDebug ? Priority::DEBUG :
330         (level == SPInfo ? Priority::INFO :
331         (level == SPWarn ? Priority::WARN :
332         (level == SPError ? Priority::ERROR : Priority::CRIT)))),
333         msg
334         );
335 }
336
337 bool AbstractSPRequest::isPriorityEnabled(SPLogLevel level) const
338 {
339     return reinterpret_cast<Category*>(m_log)->isPriorityEnabled(
340         (level == SPDebug ? Priority::DEBUG :
341         (level == SPInfo ? Priority::INFO :
342         (level == SPWarn ? Priority::WARN :
343         (level == SPError ? Priority::ERROR : Priority::CRIT))))
344         );
345 }