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