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