a3e654f86811beaac085310f9a4b4fe3fe3fa345
[shibboleth/cpp-sp.git] / shibsp / handler / impl / FormSessionInitiator.cpp
1 /**
2  * Licensed to the University Corporation for Advanced Internet
3  * Development, Inc. (UCAID) under one or more contributor license
4  * agreements. See the NOTICE file distributed with this work for
5  * additional information regarding copyright ownership.
6  *
7  * UCAID licenses this file to you under the Apache License,
8  * Version 2.0 (the "License"); you may not use this file except
9  * in compliance with the License. You may obtain a copy of the
10  * License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17  * either express or implied. See the License for the specific
18  * language governing permissions and limitations under the License.
19  */
20
21 /**
22  * FormSessionInitiator.cpp
23  * 
24  * HTML form-based IdP discovery.
25  */
26
27 #include "internal.h"
28 #include "Application.h"
29 #include "exceptions.h"
30 #include "handler/AbstractHandler.h"
31 #include "handler/SessionInitiator.h"
32 #include "util/TemplateParameters.h"
33
34 #include <fstream>
35 #include <xmltooling/XMLToolingConfig.h>
36 #include <xmltooling/util/PathResolver.h>
37 #include <xmltooling/util/URLEncoder.h>
38
39 using namespace shibsp;
40 using namespace opensaml;
41 using namespace xmltooling;
42 using namespace std;
43
44 namespace shibsp {
45
46 #if defined (_MSC_VER)
47     #pragma warning( push )
48     #pragma warning( disable : 4250 )
49 #endif
50
51     class SHIBSP_DLLLOCAL FormSessionInitiator : public SessionInitiator, public AbstractHandler
52     {
53     public:
54         FormSessionInitiator(const DOMElement* e, const char* appId)
55             : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".SessionInitiator.Form")), m_template(getString("template").second) {
56             if (!m_template)
57                 throw ConfigurationException("Form SessionInitiator requires a template property.");
58         }
59         virtual ~FormSessionInitiator() {}
60         
61         pair<bool,long> run(SPRequest& request, string& entityID, bool isHandler=true) const;
62
63     private:
64         const char* m_template;
65     };
66
67 #if defined (_MSC_VER)
68     #pragma warning( pop )
69 #endif
70
71     SessionInitiator* SHIBSP_DLLLOCAL FormSessionInitiatorFactory(const pair<const DOMElement*,const char*>& p)
72     {
73         return new FormSessionInitiator(p.first, p.second);
74     }
75
76 };
77
78 pair<bool,long> FormSessionInitiator::run(SPRequest& request, string& entityID, bool isHandler) const
79 {
80     if (!checkCompatibility(request, isHandler))
81         return make_pair(false,0L);
82
83     string target;
84     pair<bool,const char*> prop;
85     const Application& app=request.getApplication();
86
87     if (isHandler) {
88         prop = getString("target", request);
89         if (prop.first)
90             target = prop.second;
91         recoverRelayState(app, request, request, target, false);
92     }
93     else {
94         // Check for a hardwired target value in the map or handler.
95         prop = getString("target", request, HANDLER_PROPERTY_MAP|HANDLER_PROPERTY_FIXED);
96         if (prop.first)
97             target = prop.second;
98         else
99             target = request.getRequestURL();
100     }
101
102     // Compute the return URL. We start with a self-referential link.
103     string returnURL=request.getHandlerURL(target.c_str());
104     pair<bool,const char*> thisloc = getString("Location");
105     if (thisloc.first)
106         returnURL += thisloc.second;
107
108     if (isHandler) {
109         // We may already have RelayState set if we looped back here,
110         // but we've turned it back into a resource by this point, so if there's
111         // a target on the URL, reset to that value.
112         prop.second = request.getParameter("target");
113         if (prop.second && *prop.second)
114             target = prop.second;
115     }
116
117     preserveRelayState(app, request, target);
118
119     request.setContentType("text/html");
120     request.setResponseHeader("Expires","Wed, 01 Jan 1997 12:00:00 GMT");
121     request.setResponseHeader("Cache-Control","private,no-store,no-cache,max-age=0");
122     string fname(m_template);
123     ifstream infile(XMLToolingConfig::getConfig().getPathResolver()->resolve(fname, PathResolver::XMLTOOLING_CFG_FILE).c_str());
124     if (!infile)
125         throw ConfigurationException("Unable to access HTML template ($1).", params(1, m_template));
126     TemplateParameters tp;
127     tp.m_request = &request;
128     tp.setPropertySet(app.getPropertySet("Errors"));
129     tp.m_map["action"] = returnURL;
130     if (!target.empty())
131         tp.m_map["target"] = target;
132     stringstream str;
133     XMLToolingConfig::getConfig().getTemplateEngine()->run(infile, str, tp);
134     return make_pair(true,request.sendResponse(str));
135 }