New -lite library and elimination of SAML libraries from modules.
[shibboleth/sp.git] / shibsp / AbstractSPRequest.cpp
1 /*
2  *  Copyright 2001-2007 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 "AbstractSPRequest.h"
25 #include "Application.h"
26 #include "ServiceProvider.h"
27 #include "SessionCache.h"
28
29 #include <log4cpp/Category.hh>
30
31 using namespace shibsp;
32 using namespace opensaml;
33 using namespace xmltooling;
34 using namespace log4cpp;
35 using namespace std;
36
37 AbstractSPRequest::AbstractSPRequest()
38     : m_sp(NULL), m_mapper(NULL), m_app(NULL), m_sessionTried(false), m_session(NULL),
39         m_log(&Category::getInstance(SHIBSP_LOGCAT".SPRequest")), m_parser(NULL)
40 {
41     m_sp=SPConfig::getConfig().getServiceProvider();
42     m_sp->lock();
43 }
44
45 AbstractSPRequest::~AbstractSPRequest()
46 {
47     if (m_session)
48         m_session->unlock();
49     if (m_mapper)
50         m_mapper->unlock();
51     if (m_sp)
52         m_sp->unlock();
53     delete m_parser;
54 }
55
56 RequestMapper::Settings AbstractSPRequest::getRequestSettings() const
57 {
58     if (m_mapper)
59         return m_settings;
60
61     // Map request to application and content settings.
62     m_mapper=m_sp->getRequestMapper();
63     m_mapper->lock();
64     return m_settings = m_mapper->getSettings(*this);
65
66 }
67
68 const Application& AbstractSPRequest::getApplication() const
69 {
70     if (!m_app) {
71         // Now find the application from the URL settings
72         m_app=m_sp->getApplication(getRequestSettings().first->getString("applicationId").second);
73         if (!m_app)
74             throw ConfigurationException("Unable to map request to application settings, check configuration.");
75     }    
76     return *m_app;
77 }
78
79 Session* AbstractSPRequest::getSession() const
80 {
81     // Only attempt this once.
82     if (m_sessionTried)
83         return m_session;
84     m_sessionTried = true;
85
86     // Get session ID from cookie.
87     const Application& app = getApplication();
88     pair<string,const char*> shib_cookie = app.getCookieNameProps("_shibsession_");
89     const char* session_id = getCookie(shib_cookie.first.c_str());
90     if (!session_id || !*session_id)
91         return NULL;
92
93     // Need address checking and timeout settings.
94     int timeout=0;
95     bool consistent=true;
96     const PropertySet* props=app.getPropertySet("Sessions");
97     if (props) {
98         pair<bool,unsigned int> p=props->getUnsignedInt("timeout");
99         if (p.first)
100             timeout = p.second;
101         pair<bool,bool> pcheck=props->getBool("consistentAddress");
102         if (pcheck.first)
103             consistent = pcheck.second;
104     }
105
106     // The cache will either silently pass a session or NULL back, or throw an exception out.
107     return m_session = getServiceProvider().getSessionCache()->find(
108         session_id, app, consistent ? getRemoteAddr().c_str() : NULL, timeout
109         );
110 }
111
112 const char* AbstractSPRequest::getRequestURL() const {
113     if (m_url.empty()) {
114         // Compute the full target URL
115         int port = getPort();
116         const char* scheme = getScheme();
117         m_url = string(scheme) + "://" + getHostname();
118         if ((!strcmp(scheme,"http") && port!=80) || (!strcmp(scheme,"https") && port!=443)) { 
119             ostringstream portstr;
120             portstr << port;
121             m_url += ":" + portstr.str();
122         }
123         scheme = getRequestURI();
124         if (scheme)
125             m_url += scheme;
126     }
127     return m_url.c_str();
128 }
129
130 const char* AbstractSPRequest::getParameter(const char* name) const
131 {
132     if (!m_parser)
133         m_parser=new CGIParser(*this);
134     
135     pair<CGIParser::walker,CGIParser::walker> bounds=m_parser->getParameters(name);
136     return (bounds.first==bounds.second) ? NULL : bounds.first->second;
137 }
138
139 vector<const char*>::size_type AbstractSPRequest::getParameters(const char* name, vector<const char*>& values) const
140 {
141     if (!m_parser)
142         m_parser=new CGIParser(*this);
143
144     pair<CGIParser::walker,CGIParser::walker> bounds=m_parser->getParameters(name);
145     while (bounds.first!=bounds.second) {
146         values.push_back(bounds.first->second);
147         ++bounds.first;
148     }
149     return values.size();
150 }
151
152 const char* AbstractSPRequest::getCookie(const char* name) const
153 {
154     if (m_cookieMap.empty()) {
155         string cookies=getHeader("Cookie");
156
157         string::size_type pos=0,cname,namelen,val,vallen;
158         while (pos !=string::npos && pos < cookies.length()) {
159             while (isspace(cookies[pos])) pos++;
160             cname=pos;
161             pos=cookies.find_first_of("=",pos);
162             if (pos == string::npos)
163                 break;
164             namelen=pos-cname;
165             pos++;
166             if (pos==cookies.length())
167                 break;
168             val=pos;
169             pos=cookies.find_first_of(";",pos);
170             if (pos != string::npos) {
171                 vallen=pos-val;
172                 pos++;
173                 m_cookieMap.insert(make_pair(cookies.substr(cname,namelen),cookies.substr(val,vallen)));
174             }
175             else
176                 m_cookieMap.insert(make_pair(cookies.substr(cname,namelen),cookies.substr(val)));
177         }
178     }
179     map<string,string>::const_iterator lookup=m_cookieMap.find(name);
180     return (lookup==m_cookieMap.end()) ? NULL : lookup->second.c_str();
181 }
182
183 const char* AbstractSPRequest::getHandlerURL(const char* resource) const
184 {
185     if (!m_handlerURL.empty() && resource && !strcmp(getRequestURL(),resource))
186         return m_handlerURL.c_str();
187         
188 #ifdef HAVE_STRCASECMP
189     if (!resource || (strncasecmp(resource,"http://",7) && strncasecmp(resource,"https://",8)))
190 #else
191     if (!resource || (strnicmp(resource,"http://",7) && strnicmp(resource,"https://",8)))
192 #endif
193         throw ConfigurationException("Target resource was not an absolute URL.");
194
195     bool ssl_only=false;
196     const char* handler=NULL;
197     const PropertySet* props=m_app->getPropertySet("Sessions");
198     if (props) {
199         pair<bool,bool> p=props->getBool("handlerSSL");
200         if (p.first)
201             ssl_only=p.second;
202         pair<bool,const char*> p2=props->getString("handlerURL");
203         if (p2.first)
204             handler=p2.second;
205     }
206     
207     // Should never happen...
208     if (!handler || (*handler!='/' && strncmp(handler,"http:",5) && strncmp(handler,"https:",6)))
209         throw ConfigurationException(
210             "Invalid handlerURL property ($1) in Application ($2)",
211             params(2, handler ? handler : "null", m_app->getId())
212             );
213
214     // The "handlerURL" property can be in one of three formats:
215     //
216     // 1) a full URI:       http://host/foo/bar
217     // 2) a hostless URI:   http:///foo/bar
218     // 3) a relative path:  /foo/bar
219     //
220     // #  Protocol  Host        Path
221     // 1  handler   handler     handler
222     // 2  handler   resource    handler
223     // 3  resource  resource    handler
224     //
225     // note: if ssl_only is true, make sure the protocol is https
226
227     const char* path = NULL;
228
229     // Decide whether to use the handler or the resource for the "protocol"
230     const char* prot;
231     if (*handler != '/') {
232         prot = handler;
233     }
234     else {
235         prot = resource;
236         path = handler;
237     }
238
239     // break apart the "protocol" string into protocol, host, and "the rest"
240     const char* colon=strchr(prot,':');
241     colon += 3;
242     const char* slash=strchr(colon,'/');
243     if (!path)
244         path = slash;
245
246     // Compute the actual protocol and store in member.
247     if (ssl_only)
248         m_handlerURL.assign("https://");
249     else
250         m_handlerURL.assign(prot, colon-prot);
251
252     // create the "host" from either the colon/slash or from the target string
253     // If prot == handler then we're in either #1 or #2, else #3.
254     // If slash == colon then we're in #2.
255     if (prot != handler || slash == colon) {
256         colon = strchr(resource, ':');
257         colon += 3;      // Get past the ://
258         slash = strchr(colon, '/');
259     }
260     string host(colon, (slash ? slash-colon : strlen(colon)));
261
262     // Build the handler URL
263     m_handlerURL += host + path;
264     return m_handlerURL.c_str();
265 }
266
267 void AbstractSPRequest::log(SPLogLevel level, const std::string& msg) const
268 {
269     reinterpret_cast<Category*>(m_log)->log(
270         (level == SPDebug ? log4cpp::Priority::DEBUG :
271         (level == SPInfo ? log4cpp::Priority::INFO :
272         (level == SPWarn ? log4cpp::Priority::WARN :
273         (level == SPError ? log4cpp::Priority::ERROR : log4cpp::Priority::CRIT)))),
274         msg
275         );
276 }
277
278 bool AbstractSPRequest::isPriorityEnabled(SPLogLevel level) const
279 {
280     return reinterpret_cast<Category*>(m_log)->isPriorityEnabled(
281         (level == SPDebug ? log4cpp::Priority::DEBUG :
282         (level == SPInfo ? log4cpp::Priority::INFO :
283         (level == SPWarn ? log4cpp::Priority::WARN :
284         (level == SPError ? log4cpp::Priority::ERROR : log4cpp::Priority::CRIT))))
285         );
286 }