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