2 * Copyright 2001-2007 Internet2
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * AbstractSPRequest.cpp
20 * Abstract base for SPRequest implementations
24 #include "AbstractSPRequest.h"
25 #include "Application.h"
26 #include "ServiceProvider.h"
27 #include "SessionCache.h"
29 using namespace shibsp;
30 using namespace opensaml;
31 using namespace xmltooling;
34 AbstractSPRequest::AbstractSPRequest()
35 : m_sp(NULL), m_mapper(NULL), m_app(NULL), m_sessionTried(false), m_session(NULL),
36 m_log(&Category::getInstance(SHIBSP_LOGCAT".SPRequest")), m_parser(NULL)
38 m_sp=SPConfig::getConfig().getServiceProvider();
42 AbstractSPRequest::~AbstractSPRequest()
53 RequestMapper::Settings AbstractSPRequest::getRequestSettings() const
58 // Map request to application and content settings.
59 m_mapper=m_sp->getRequestMapper();
61 return m_settings = m_mapper->getSettings(*this);
65 const Application& AbstractSPRequest::getApplication() const
68 // Now find the application from the URL settings
69 m_app=m_sp->getApplication(getRequestSettings().first->getString("applicationId").second);
71 throw ConfigurationException("Unable to map request to application settings, check configuration.");
76 Session* AbstractSPRequest::getSession(bool checkTimeout, bool ignoreAddress, bool cache) const
78 // Only attempt this once.
79 if (cache && m_sessionTried)
82 m_sessionTried = true;
84 // Get session ID from cookie.
85 const Application& app = getApplication();
86 pair<string,const char*> shib_cookie = app.getCookieNameProps("_shibsession_");
87 const char* session_id = getCookie(shib_cookie.first.c_str());
88 if (!session_id || !*session_id)
91 // Need address checking and timeout settings.
93 if (checkTimeout || !ignoreAddress) {
94 const PropertySet* props=app.getPropertySet("Sessions");
97 pair<bool,unsigned int> p=props->getUnsignedInt("timeout");
101 pair<bool,bool> pcheck=props->getBool("consistentAddress");
103 ignoreAddress = !pcheck.second;
107 // The cache will either silently pass a session or NULL back, or throw an exception out.
108 Session* session = getServiceProvider().getSessionCache()->find(
109 session_id, app, ignoreAddress ? NULL : getRemoteAddr().c_str(), checkTimeout ? &timeout : NULL
116 static char _x2c(const char *what)
\r
118 register char digit;
\r
120 digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
\r
122 digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));
\r
126 void AbstractSPRequest::setRequestURI(const char* uri)
128 // Fix for bug 574, secadv 20061002
\r
129 // Unescape URI up to query string delimiter by looking for %XX escapes.
\r
130 // Adapted from Apache's util.c, ap_unescape_url function.
\r
137 else if (*uri == ';') {
\r
138 // If this is Java being stupid, skip everything up to the query string, if any.
\r
139 if (!strncmp(uri, ";jsessionid=", 12)) {
\r
140 if (uri = strchr(uri, '?'))
\r
148 else if (*uri != '%') {
\r
153 if (!isxdigit(*uri) || !isxdigit(*(uri+1)))
\r
154 throw ConfigurationException("Bad request, contained unsupported encoded characters.");
\r
155 m_uri += _x2c(uri);
\r
163 const char* AbstractSPRequest::getRequestURL() const
166 // Compute the full target URL
167 int port = getPort();
168 const char* scheme = getScheme();
169 m_url = string(scheme) + "://" + getHostname();
170 if ((!strcmp(scheme,"http") && port!=80) || (!strcmp(scheme,"https") && port!=443)) {
171 ostringstream portstr;
173 m_url += ":" + portstr.str();
177 return m_url.c_str();
180 const char* AbstractSPRequest::getParameter(const char* name) const
183 m_parser=new CGIParser(*this);
185 pair<CGIParser::walker,CGIParser::walker> bounds=m_parser->getParameters(name);
186 return (bounds.first==bounds.second) ? NULL : bounds.first->second;
189 vector<const char*>::size_type AbstractSPRequest::getParameters(const char* name, vector<const char*>& values) const
192 m_parser=new CGIParser(*this);
194 pair<CGIParser::walker,CGIParser::walker> bounds=m_parser->getParameters(name);
195 while (bounds.first!=bounds.second) {
196 values.push_back(bounds.first->second);
199 return values.size();
202 const char* AbstractSPRequest::getCookie(const char* name) const
204 if (m_cookieMap.empty()) {
205 string cookies=getHeader("Cookie");
207 string::size_type pos=0,cname,namelen,val,vallen;
208 while (pos !=string::npos && pos < cookies.length()) {
209 while (isspace(cookies[pos])) pos++;
211 pos=cookies.find_first_of("=",pos);
212 if (pos == string::npos)
216 if (pos==cookies.length())
219 pos=cookies.find_first_of(";",pos);
220 if (pos != string::npos) {
223 m_cookieMap.insert(make_pair(cookies.substr(cname,namelen),cookies.substr(val,vallen)));
226 m_cookieMap.insert(make_pair(cookies.substr(cname,namelen),cookies.substr(val)));
229 map<string,string>::const_iterator lookup=m_cookieMap.find(name);
230 return (lookup==m_cookieMap.end()) ? NULL : lookup->second.c_str();
233 const char* AbstractSPRequest::getHandlerURL(const char* resource) const
236 resource = getRequestURL();
238 if (!m_handlerURL.empty() && resource && !strcmp(getRequestURL(),resource))
239 return m_handlerURL.c_str();
241 #ifdef HAVE_STRCASECMP
242 if (!resource || (strncasecmp(resource,"http://",7) && strncasecmp(resource,"https://",8)))
244 if (!resource || (strnicmp(resource,"http://",7) && strnicmp(resource,"https://",8)))
246 throw ConfigurationException("Target resource was not an absolute URL.");
249 const char* handler=NULL;
250 const PropertySet* props=m_app->getPropertySet("Sessions");
252 pair<bool,bool> p=props->getBool("handlerSSL");
255 pair<bool,const char*> p2=props->getString("handlerURL");
260 // Should never happen...
261 if (!handler || (*handler!='/' && strncmp(handler,"http:",5) && strncmp(handler,"https:",6)))
262 throw ConfigurationException(
263 "Invalid handlerURL property ($1) in Application ($2)",
264 params(2, handler ? handler : "null", m_app->getId())
267 // The "handlerURL" property can be in one of three formats:
269 // 1) a full URI: http://host/foo/bar
270 // 2) a hostless URI: http:///foo/bar
271 // 3) a relative path: /foo/bar
273 // # Protocol Host Path
274 // 1 handler handler handler
275 // 2 handler resource handler
276 // 3 resource resource handler
278 // note: if ssl_only is true, make sure the protocol is https
280 const char* path = NULL;
282 // Decide whether to use the handler or the resource for the "protocol"
284 if (*handler != '/') {
292 // break apart the "protocol" string into protocol, host, and "the rest"
293 const char* colon=strchr(prot,':');
295 const char* slash=strchr(colon,'/');
299 // Compute the actual protocol and store in member.
301 m_handlerURL.assign("https://");
303 m_handlerURL.assign(prot, colon-prot);
305 // create the "host" from either the colon/slash or from the target string
306 // If prot == handler then we're in either #1 or #2, else #3.
307 // If slash == colon then we're in #2.
308 if (prot != handler || slash == colon) {
309 colon = strchr(resource, ':');
310 colon += 3; // Get past the ://
311 slash = strchr(colon, '/');
313 string host(colon, (slash ? slash-colon : strlen(colon)));
315 // Build the handler URL
316 m_handlerURL += host + path;
317 return m_handlerURL.c_str();
320 void AbstractSPRequest::log(SPLogLevel level, const std::string& msg) const
322 reinterpret_cast<Category*>(m_log)->log(
323 (level == SPDebug ? Priority::DEBUG :
324 (level == SPInfo ? Priority::INFO :
325 (level == SPWarn ? Priority::WARN :
326 (level == SPError ? Priority::ERROR : Priority::CRIT)))),
331 bool AbstractSPRequest::isPriorityEnabled(SPLogLevel level) const
333 return reinterpret_cast<Category*>(m_log)->isPriorityEnabled(
334 (level == SPDebug ? Priority::DEBUG :
335 (level == SPInfo ? Priority::INFO :
336 (level == SPWarn ? Priority::WARN :
337 (level == SPError ? Priority::ERROR : Priority::CRIT))))