67d00a7588ec87cfc8568ba2fb890d45cae80ccf
[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(bool checkTimeout) 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     time_t timeout=0;
95     bool consistent=true;
96     const PropertySet* props=app.getPropertySet("Sessions");
97     if (props) {
98         if (checkTimeout) {
99             pair<bool,unsigned int> p=props->getUnsignedInt("timeout");
100             if (p.first)
101                 timeout = p.second;
102         }
103         pair<bool,bool> pcheck=props->getBool("consistentAddress");
104         if (pcheck.first)
105             consistent = pcheck.second;
106     }
107
108     // The cache will either silently pass a session or NULL back, or throw an exception out.
109     return m_session = getServiceProvider().getSessionCache()->find(
110         session_id, app, consistent ? getRemoteAddr().c_str() : NULL, checkTimeout ? &timeout : NULL
111         );
112 }
113
114 const char* AbstractSPRequest::getRequestURL() const {
115     if (m_url.empty()) {
116         // Compute the full target URL
117         int port = getPort();
118         const char* scheme = getScheme();
119         m_url = string(scheme) + "://" + getHostname();
120         if ((!strcmp(scheme,"http") && port!=80) || (!strcmp(scheme,"https") && port!=443)) { 
121             ostringstream portstr;
122             portstr << port;
123             m_url += ":" + portstr.str();
124         }
125         scheme = getRequestURI();
126         if (scheme)
127             m_url += scheme;
128     }
129     return m_url.c_str();
130 }
131
132 const char* AbstractSPRequest::getParameter(const char* name) const
133 {
134     if (!m_parser)
135         m_parser=new CGIParser(*this);
136     
137     pair<CGIParser::walker,CGIParser::walker> bounds=m_parser->getParameters(name);
138     return (bounds.first==bounds.second) ? NULL : bounds.first->second;
139 }
140
141 vector<const char*>::size_type AbstractSPRequest::getParameters(const char* name, vector<const char*>& values) const
142 {
143     if (!m_parser)
144         m_parser=new CGIParser(*this);
145
146     pair<CGIParser::walker,CGIParser::walker> bounds=m_parser->getParameters(name);
147     while (bounds.first!=bounds.second) {
148         values.push_back(bounds.first->second);
149         ++bounds.first;
150     }
151     return values.size();
152 }
153
154 const char* AbstractSPRequest::getCookie(const char* name) const
155 {
156     if (m_cookieMap.empty()) {
157         string cookies=getHeader("Cookie");
158
159         string::size_type pos=0,cname,namelen,val,vallen;
160         while (pos !=string::npos && pos < cookies.length()) {
161             while (isspace(cookies[pos])) pos++;
162             cname=pos;
163             pos=cookies.find_first_of("=",pos);
164             if (pos == string::npos)
165                 break;
166             namelen=pos-cname;
167             pos++;
168             if (pos==cookies.length())
169                 break;
170             val=pos;
171             pos=cookies.find_first_of(";",pos);
172             if (pos != string::npos) {
173                 vallen=pos-val;
174                 pos++;
175                 m_cookieMap.insert(make_pair(cookies.substr(cname,namelen),cookies.substr(val,vallen)));
176             }
177             else
178                 m_cookieMap.insert(make_pair(cookies.substr(cname,namelen),cookies.substr(val)));
179         }
180     }
181     map<string,string>::const_iterator lookup=m_cookieMap.find(name);
182     return (lookup==m_cookieMap.end()) ? NULL : lookup->second.c_str();
183 }
184
185 const char* AbstractSPRequest::getHandlerURL(const char* resource) const
186 {
187     if (!m_handlerURL.empty() && resource && !strcmp(getRequestURL(),resource))
188         return m_handlerURL.c_str();
189         
190 #ifdef HAVE_STRCASECMP
191     if (!resource || (strncasecmp(resource,"http://",7) && strncasecmp(resource,"https://",8)))
192 #else
193     if (!resource || (strnicmp(resource,"http://",7) && strnicmp(resource,"https://",8)))
194 #endif
195         throw ConfigurationException("Target resource was not an absolute URL.");
196
197     bool ssl_only=false;
198     const char* handler=NULL;
199     const PropertySet* props=m_app->getPropertySet("Sessions");
200     if (props) {
201         pair<bool,bool> p=props->getBool("handlerSSL");
202         if (p.first)
203             ssl_only=p.second;
204         pair<bool,const char*> p2=props->getString("handlerURL");
205         if (p2.first)
206             handler=p2.second;
207     }
208     
209     // Should never happen...
210     if (!handler || (*handler!='/' && strncmp(handler,"http:",5) && strncmp(handler,"https:",6)))
211         throw ConfigurationException(
212             "Invalid handlerURL property ($1) in Application ($2)",
213             params(2, handler ? handler : "null", m_app->getId())
214             );
215
216     // The "handlerURL" property can be in one of three formats:
217     //
218     // 1) a full URI:       http://host/foo/bar
219     // 2) a hostless URI:   http:///foo/bar
220     // 3) a relative path:  /foo/bar
221     //
222     // #  Protocol  Host        Path
223     // 1  handler   handler     handler
224     // 2  handler   resource    handler
225     // 3  resource  resource    handler
226     //
227     // note: if ssl_only is true, make sure the protocol is https
228
229     const char* path = NULL;
230
231     // Decide whether to use the handler or the resource for the "protocol"
232     const char* prot;
233     if (*handler != '/') {
234         prot = handler;
235     }
236     else {
237         prot = resource;
238         path = handler;
239     }
240
241     // break apart the "protocol" string into protocol, host, and "the rest"
242     const char* colon=strchr(prot,':');
243     colon += 3;
244     const char* slash=strchr(colon,'/');
245     if (!path)
246         path = slash;
247
248     // Compute the actual protocol and store in member.
249     if (ssl_only)
250         m_handlerURL.assign("https://");
251     else
252         m_handlerURL.assign(prot, colon-prot);
253
254     // create the "host" from either the colon/slash or from the target string
255     // If prot == handler then we're in either #1 or #2, else #3.
256     // If slash == colon then we're in #2.
257     if (prot != handler || slash == colon) {
258         colon = strchr(resource, ':');
259         colon += 3;      // Get past the ://
260         slash = strchr(colon, '/');
261     }
262     string host(colon, (slash ? slash-colon : strlen(colon)));
263
264     // Build the handler URL
265     m_handlerURL += host + path;
266     return m_handlerURL.c_str();
267 }
268
269 void AbstractSPRequest::log(SPLogLevel level, const std::string& msg) const
270 {
271     reinterpret_cast<Category*>(m_log)->log(
272         (level == SPDebug ? log4cpp::Priority::DEBUG :
273         (level == SPInfo ? log4cpp::Priority::INFO :
274         (level == SPWarn ? log4cpp::Priority::WARN :
275         (level == SPError ? log4cpp::Priority::ERROR : log4cpp::Priority::CRIT)))),
276         msg
277         );
278 }
279
280 bool AbstractSPRequest::isPriorityEnabled(SPLogLevel level) const
281 {
282     return reinterpret_cast<Category*>(m_log)->isPriorityEnabled(
283         (level == SPDebug ? log4cpp::Priority::DEBUG :
284         (level == SPInfo ? log4cpp::Priority::INFO :
285         (level == SPWarn ? log4cpp::Priority::WARN :
286         (level == SPError ? log4cpp::Priority::ERROR : log4cpp::Priority::CRIT))))
287         );
288 }