Switched remaining files to Apache license.
[shibboleth/cpp-sp.git] / shib-target / shib-mlp.cpp
1 /*
2  *  Copyright 2001-2005 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  * shib-mlp.cpp -- The ShibTarget Markup Language processor
19  *
20  * Created by:  Derek Atkins <derek@ihtfp.com>
21  *
22  * $Id$
23  */
24
25 #include "internal.h"
26
27 #include <sstream>
28 #include <ctype.h>
29 #include <xercesc/util/XercesDefs.hpp>
30 #include <log4cpp/Category.hh>
31
32 using namespace std;
33 using namespace log4cpp;
34 using namespace saml;
35 using namespace shibboleth;
36 using namespace shibtarget;
37
38 class shibtarget::ShibMLPPriv {
39 public:
40   ShibMLPPriv();
41   ~ShibMLPPriv() {}
42   log4cpp::Category *log;
43 };  
44
45 ShibMLPPriv::ShibMLPPriv() : log(&(log4cpp::Category::getInstance("shibtarget.ShibMLP"))) {}
46
47 static void trimspace (string& s)
48 {
49   int end = s.size() - 1, start = 0;
50
51   // Trim stuff on right.
52   while (end > 0 && !isgraph(s[end])) end--;
53
54   // Trim stuff on left.
55   while (start < end && !isgraph(s[start])) start++;
56
57   // Modify the string.
58   s = s.substr(start, end - start + 1);
59 }
60
61 ShibMLP::ShibMLP()
62 {
63   m_priv = new ShibMLPPriv ();
64 }
65
66 ShibMLP::~ShibMLP ()
67 {
68   delete m_priv;
69 }
70
71 const char* ShibMLP::run(const string& is, const IPropertySet* props, std::string* output)
72 {
73   // Create a timestamp
74   time_t now = time(NULL);
75   insert("now", ctime(&now));
76
77   if (!output)
78     output=&m_generated;
79   const char* line = is.c_str();
80   const char* lastpos = line;
81   const char* thispos;
82
83   m_priv->log->debug("Processing string");
84
85   //
86   // Search for SHIBMLP tags.  These are of the form:
87   //    <shibmlp key/>
88   //    <shibmlpif key> stuff </shibmlpif>
89   //    <shibmlpifnot key> stuff </shibmlpifnot>
90   // Note that there MUST be white-space after "<shibmlp" but
91   // there does not need to be white space between the key and
92   // the close-tag.
93   //
94   while ((thispos = strchr(lastpos, '<')) != NULL) {
95     // save the string up to this token
96     *output += is.substr(lastpos-line, thispos-lastpos);
97
98     // Make sure this token matches our tokens.
99 #ifdef HAVE_STRCASECMP
100     if (!strncasecmp(thispos, "<shibmlp ", 9))
101 #else
102     if (!strnicmp(thispos, "<shibmlp ", 9))
103 #endif
104     {
105         // Save this position off.
106         lastpos = thispos + 9;  // strlen("<shibmlp ")
107     
108         // search for the end-tag
109         if ((thispos = strstr(lastpos, "/>")) != NULL) {
110             string key = is.substr(lastpos-line, thispos-lastpos);
111             trimspace(key);
112     
113             map<string,string>::const_iterator i=m_map.find(key);
114             if (i != m_map.end()) {
115                 *output += i->second;
116             }
117             else {
118                 pair<bool,const char*> p=props ? props->getString(key.c_str()) : pair<bool,const char*>(false,NULL);
119                 if (p.first) {
120                     *output += p.second;
121                 }
122                 else {
123                     static const char* s1 = "<!-- Unknown SHIBMLP key: ";
124                     static const char* s2 = "/>";
125                     *output += s1;
126                     *output += key + s2;
127                 }
128             }
129             lastpos = thispos + 2; // strlen("/>")
130         }
131     }
132 #ifdef HAVE_STRCASECMP
133     else if (!strncasecmp(thispos, "<shibmlpif ", 11))
134 #else
135     else if (!strnicmp(thispos, "<shibmlpif ", 11))
136 #endif
137     {
138         // Save this position off.
139         lastpos = thispos + 11;  // strlen("<shibmlpif ")
140
141         // search for the end of this tag
142         if ((thispos = strchr(lastpos, '>')) != NULL) {
143             string key = is.substr(lastpos-line, thispos-lastpos);
144             trimspace(key);
145             bool eval=false;
146             map<string,string>::const_iterator i=m_map.find(key);
147             if (i != m_map.end() && !i->second.empty()) {
148                 eval=true;
149             }
150             else {
151                 pair<bool,const char*> p=props ? props->getString(key.c_str()) : pair<bool,const char*>(false,NULL);
152                 if (p.first) {
153                     eval=true;
154                 }
155             }
156             lastpos = thispos + 1; // strlen(">")
157             
158             // Search for the closing tag.
159             const char* frontpos=lastpos;
160             while ((thispos = strstr(lastpos, "</")) != NULL) {
161 #ifdef HAVE_STRCASECMP
162                 if (!strncasecmp(thispos, "</shibmlpif>", 12))
163 #else
164                 if (!strnicmp(thispos, "</shibmlpif>", 12))
165 #endif
166                 {
167                     // We found our terminator. Process the string in between.
168                     string segment;
169                     run(is.substr(frontpos-line, thispos-frontpos),props,&segment);
170                     if (eval)
171                         *output += segment;
172                     lastpos = thispos + 12; // strlen("</shibmlpif>")
173                     break;
174                 }
175                 else {
176                     // Skip it.
177                     lastpos = thispos + 2;
178                 }
179             }
180         }
181     }
182 #ifdef HAVE_STRCASECMP
183     else if (!strncasecmp(thispos, "<shibmlpifnot ", 14))
184 #else
185     else if (!strnicmp(thispos, "<shibmlpifnot ", 14))
186 #endif
187     {
188         // Save this position off.
189         lastpos = thispos + 14;  // strlen("<shibmlpifnot ")
190
191         // search for the end of this tag
192         if ((thispos = strchr(lastpos, '>')) != NULL) {
193             string key = is.substr(lastpos-line, thispos-lastpos);
194             trimspace(key);
195             bool eval=false;
196             map<string,string>::const_iterator i=m_map.find(key);
197             if (i != m_map.end() && !i->second.empty()) {
198                 eval=true;
199             }
200             else {
201                 pair<bool,const char*> p=props ? props->getString(key.c_str()) : pair<bool,const char*>(false,NULL);
202                 if (p.first) {
203                     eval=true;
204                 }
205             }
206             lastpos = thispos + 1; // strlen(">")
207             
208             // Search for the closing tag.
209             const char* frontpos=lastpos;
210             while ((thispos = strstr(lastpos, "</")) != NULL) {
211 #ifdef HAVE_STRCASECMP
212                 if (!strncasecmp(thispos, "</shibmlpifnot>", 15))
213 #else
214                 if (!strnicmp(thispos, "</shibmlpifnot>", 15))
215 #endif
216                 {
217                     // We found our terminator. Process the string in between.
218                     string segment;
219                     run(is.substr(frontpos-line, thispos-frontpos),props,&segment);
220                     if (!eval)
221                         *output += segment;
222                     lastpos = thispos + 15; // strlen("</shibmlpifnot>")
223                     break;
224                 }
225                 else {
226                     // Skip it.
227                     lastpos = thispos + 2;
228                 }
229             }
230         }
231     }
232     else {
233       // Skip it.
234       *output += "<";
235       lastpos = thispos + 1;
236     }
237   }
238   *output += is.substr(lastpos-line);
239
240   return output->c_str();
241 }
242
243 const char* ShibMLP::run(istream& is, const IPropertySet* props, std::string* output)
244 {
245   static string eol = "\r\n";
246   string str, line;
247
248   m_priv->log->debug("processing stream");
249
250   while (getline(is, line))
251     str += line + eol;
252
253   return run(str,props,output);
254 }
255
256 void ShibMLP::insert(SAMLException& e)
257 {
258     insert("errorType", e.classname());
259     if (typeid(e)==typeid(ContentTypeException))
260         insert("errorText", "A problem was detected with your identity provider's software configuration.");
261     else
262         insert("errorText", e.getMessage() ? e.getMessage() : "No Message");
263     if (e.getProperty("errorURL"))
264         insert("originErrorURL", e.getProperty("errorURL"));
265     if (e.getProperty("contactName"))
266         insert("originContactName", e.getProperty("contactName"));
267     const char* email=e.getProperty("contactEmail");
268     if (email) {
269         if (!strncmp(email,"mailto:",7) && strlen(email)>7)
270             insert("originContactEmail", email+7);
271         else
272             insert("originContactEmail", email);
273     }
274 }
275
276 void ShibMLP::insert (const std::string& key, const std::string& value)
277 {
278   m_priv->log->debug("inserting %s -> %s", key.c_str(), value.c_str());
279   m_map[key] = value;
280 }