Lower some log msgs.
[shibboleth/cpp-sp.git] / shib-target / shib-mlp.cpp
1 /*
2  * The Shibboleth License, Version 1.
3  * Copyright (c) 2002
4  * University Corporation for Advanced Internet Development, Inc.
5  * All rights reserved
6  *
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  *
11  * Redistributions of source code must retain the above copyright notice, this
12  * list of conditions and the following disclaimer.
13  *
14  * Redistributions in binary form must reproduce the above copyright notice,
15  * this list of conditions and the following disclaimer in the documentation
16  * and/or other materials provided with the distribution, if any, must include
17  * the following acknowledgment: "This product includes software developed by
18  * the University Corporation for Advanced Internet Development
19  * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement
20  * may appear in the software itself, if and wherever such third-party
21  * acknowledgments normally appear.
22  *
23  * Neither the name of Shibboleth nor the names of its contributors, nor
24  * Internet2, nor the University Corporation for Advanced Internet Development,
25  * Inc., nor UCAID may be used to endorse or promote products derived from this
26  * software without specific prior written permission. For written permission,
27  * please contact shibboleth@shibboleth.org
28  *
29  * Products derived from this software may not be called Shibboleth, Internet2,
30  * UCAID, or the University Corporation for Advanced Internet Development, nor
31  * may Shibboleth appear in their name, without prior written permission of the
32  * University Corporation for Advanced Internet Development.
33  *
34  *
35  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
36  * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
38  * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK
39  * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE.
40  * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY
41  * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT,
42  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
43  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
44  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
47  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48  */
49
50 /*
51  * shib-mlp.cpp -- The ShibTarget Markup Language processor
52  *
53  * Created by:  Derek Atkins <derek@ihtfp.com>
54  *
55  * $Id$
56  */
57
58 #include "internal.h"
59
60 #include <sstream>
61 #include <ctype.h>
62 #include <xercesc/util/XercesDefs.hpp>
63 #include <log4cpp/Category.hh>
64
65 using namespace std;
66 using namespace log4cpp;
67 using namespace saml;
68 using namespace shibboleth;
69 using namespace shibtarget;
70
71 class shibtarget::ShibMLPPriv {
72 public:
73   ShibMLPPriv();
74   ~ShibMLPPriv() {}
75   log4cpp::Category *log;
76 };  
77
78 ShibMLPPriv::ShibMLPPriv() : log(&(log4cpp::Category::getInstance("shibtarget.ShibMLP"))) {}
79
80 static void trimspace (string& s)
81 {
82   int end = s.size() - 1, start = 0;
83
84   // Trim stuff on right.
85   while (end > 0 && !isgraph(s[end])) end--;
86
87   // Trim stuff on left.
88   while (start < end && !isgraph(s[start])) start++;
89
90   // Modify the string.
91   s = s.substr(start, end - start + 1);
92 }
93
94 ShibMLP::ShibMLP()
95 {
96   m_priv = new ShibMLPPriv ();
97 }
98
99 ShibMLP::~ShibMLP ()
100 {
101   delete m_priv;
102 }
103
104 const char* ShibMLP::run(const string& is, const IPropertySet* props, std::string* output)
105 {
106   // Create a timestamp
107   time_t now = time(NULL);
108   insert("now", ctime(&now));
109
110   if (!output)
111     output=&m_generated;
112   const char* line = is.c_str();
113   const char* lastpos = line;
114   const char* thispos;
115
116   m_priv->log->debug("Processing string");
117
118   //
119   // Search for SHIBMLP tags.  These are of the form:
120   //    <shibmlp key/>
121   //    <shibmlpif key> stuff </shibmlpif>
122   //    <shibmlpifnot key> stuff </shibmlpifnot>
123   // Note that there MUST be white-space after "<shibmlp" but
124   // there does not need to be white space between the key and
125   // the close-tag.
126   //
127   while ((thispos = strchr(lastpos, '<')) != NULL) {
128     // save the string up to this token
129     *output += is.substr(lastpos-line, thispos-lastpos);
130
131     // Make sure this token matches our tokens.
132 #ifdef HAVE_STRCASECMP
133     if (!strncasecmp(thispos, "<shibmlp ", 9))
134 #else
135     if (!strnicmp(thispos, "<shibmlp ", 9))
136 #endif
137     {
138         // Save this position off.
139         lastpos = thispos + 9;  // strlen("<shibmlp ")
140     
141         // search for the end-tag
142         if ((thispos = strstr(lastpos, "/>")) != NULL) {
143             string key = is.substr(lastpos-line, thispos-lastpos);
144             trimspace(key);
145     
146             map<string,string>::const_iterator i=m_map.find(key);
147             if (i != m_map.end()) {
148                 *output += i->second;
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                     *output += p.second;
154                 }
155                 else {
156                     static const char* s1 = "<!-- Unknown SHIBMLP key: ";
157                     static const char* s2 = "/>";
158                     *output += s1;
159                     *output += key + s2;
160                 }
161             }
162             lastpos = thispos + 2; // strlen("/>")
163         }
164     }
165 #ifdef HAVE_STRCASECMP
166     else if (!strncasecmp(thispos, "<shibmlpif ", 11))
167 #else
168     else if (!strnicmp(thispos, "<shibmlpif ", 11))
169 #endif
170     {
171         // Save this position off.
172         lastpos = thispos + 11;  // strlen("<shibmlpif ")
173
174         // search for the end of this tag
175         if ((thispos = strchr(lastpos, '>')) != NULL) {
176             string key = is.substr(lastpos-line, thispos-lastpos);
177             trimspace(key);
178             bool eval=false;
179             map<string,string>::const_iterator i=m_map.find(key);
180             if (i != m_map.end() && !i->second.empty()) {
181                 eval=true;
182             }
183             else {
184                 pair<bool,const char*> p=props ? props->getString(key.c_str()) : pair<bool,const char*>(false,NULL);
185                 if (p.first) {
186                     eval=true;
187                 }
188             }
189             lastpos = thispos + 1; // strlen(">")
190             
191             // Search for the closing tag.
192             const char* frontpos=lastpos;
193             while ((thispos = strstr(lastpos, "</")) != NULL) {
194 #ifdef HAVE_STRCASECMP
195                 if (!strncasecmp(thispos, "</shibmlpif>", 12))
196 #else
197                 if (!strnicmp(thispos, "</shibmlpif>", 12))
198 #endif
199                 {
200                     // We found our terminator. Process the string in between.
201                     string segment;
202                     run(is.substr(frontpos-line, thispos-frontpos),props,&segment);
203                     if (eval)
204                         *output += segment;
205                     lastpos = thispos + 12; // strlen("</shibmlpif>")
206                     break;
207                 }
208                 else {
209                     // Skip it.
210                     lastpos = thispos + 2;
211                 }
212             }
213         }
214     }
215 #ifdef HAVE_STRCASECMP
216     else if (!strncasecmp(thispos, "<shibmlpifnot ", 14))
217 #else
218     else if (!strnicmp(thispos, "<shibmlpifnot ", 14))
219 #endif
220     {
221         // Save this position off.
222         lastpos = thispos + 14;  // strlen("<shibmlpifnot ")
223
224         // search for the end of this tag
225         if ((thispos = strchr(lastpos, '>')) != NULL) {
226             string key = is.substr(lastpos-line, thispos-lastpos);
227             trimspace(key);
228             bool eval=false;
229             map<string,string>::const_iterator i=m_map.find(key);
230             if (i != m_map.end() && !i->second.empty()) {
231                 eval=true;
232             }
233             else {
234                 pair<bool,const char*> p=props ? props->getString(key.c_str()) : pair<bool,const char*>(false,NULL);
235                 if (p.first) {
236                     eval=true;
237                 }
238             }
239             lastpos = thispos + 1; // strlen(">")
240             
241             // Search for the closing tag.
242             const char* frontpos=lastpos;
243             while ((thispos = strstr(lastpos, "</")) != NULL) {
244 #ifdef HAVE_STRCASECMP
245                 if (!strncasecmp(thispos, "</shibmlpifnot>", 15))
246 #else
247                 if (!strnicmp(thispos, "</shibmlpifnot>", 15))
248 #endif
249                 {
250                     // We found our terminator. Process the string in between.
251                     string segment;
252                     run(is.substr(frontpos-line, thispos-frontpos),props,&segment);
253                     if (!eval)
254                         *output += segment;
255                     lastpos = thispos + 15; // strlen("</shibmlpifnot>")
256                     break;
257                 }
258                 else {
259                     // Skip it.
260                     lastpos = thispos + 2;
261                 }
262             }
263         }
264     }
265     else {
266       // Skip it.
267       *output += "<";
268       lastpos = thispos + 1;
269     }
270   }
271   *output += is.substr(lastpos-line);
272
273   return output->c_str();
274 }
275
276 const char* ShibMLP::run(istream& is, const IPropertySet* props, std::string* output)
277 {
278   static string eol = "\r\n";
279   string str, line;
280
281   m_priv->log->debug("processing stream");
282
283   while (getline(is, line))
284     str += line + eol;
285
286   return run(str,props,output);
287 }
288
289 void ShibMLP::insert(SAMLException& e)
290 {
291     insert("errorType", e.classname());
292     if (typeid(e)==typeid(ContentTypeException))
293         insert("errorText", "A problem was detected with your identity provider's software configuration.");
294     else
295         insert("errorText", e.getMessage() ? e.getMessage() : "No Message");
296     if (e.getProperty("errorURL"))
297         insert("originErrorURL", e.getProperty("errorURL"));
298     if (e.getProperty("contactName"))
299         insert("originContactName", e.getProperty("contactName"));
300     if (e.getProperty("contactEmail"))
301         insert("originContactEmail", e.getProperty("contactEmail"));
302 }
303
304 void ShibMLP::insert (const std::string& key, const std::string& value)
305 {
306   m_priv->log->debug("inserting %s -> %s", key.c_str(), value.c_str());
307   m_map[key] = value;
308 }