https://issues.shibboleth.net/jira/browse/SSPCPP-86
[shibboleth/cpp-sp.git] / shib-target / shib-mlp.cpp
index bf43756..45ebfb0 100644 (file)
@@ -1,50 +1,17 @@
 /*
- * The Shibboleth License, Version 1.
- * Copyright (c) 2002
- * University Corporation for Advanced Internet Development, Inc.
- * All rights reserved
+ *  Copyright 2001-2005 Internet2
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution, if any, must include
- * the following acknowledgment: "This product includes software developed by
- * the University Corporation for Advanced Internet Development
- * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement
- * may appear in the software itself, if and wherever such third-party
- * acknowledgments normally appear.
- *
- * Neither the name of Shibboleth nor the names of its contributors, nor
- * Internet2, nor the University Corporation for Advanced Internet Development,
- * Inc., nor UCAID may be used to endorse or promote products derived from this
- * software without specific prior written permission. For written permission,
- * please contact shibboleth@shibboleth.org
- *
- * Products derived from this software may not be called Shibboleth, Internet2,
- * UCAID, or the University Corporation for Advanced Internet Development, nor
- * may Shibboleth appear in their name, without prior written permission of the
- * University Corporation for Advanced Internet Development.
- *
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
- * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK
- * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE.
- * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY
- * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT,
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 /*
  * $Id$
  */
 
-#ifdef WIN32
-# define SHIBTARGET_EXPORTS __declspec(dllexport)
-#endif
+#include "internal.h"
 
-#include "shib-target.h"
+#include <typeinfo>
 #include <sstream>
 #include <ctype.h>
 #include <xercesc/util/XercesDefs.hpp>
 
-#include <log4cpp/Category.hh>
-
 using namespace std;
-using namespace shibtarget;
 using namespace saml;
+using namespace shibboleth;
+using namespace shibtarget;
 
-class shibtarget::ShibMLPPriv {
-public:
-  ShibMLPPriv();
-  ~ShibMLPPriv() {}
-  log4cpp::Category *log;
-};  
-
-ShibMLPPriv::ShibMLPPriv()
+void ShibMLP::html_encode(string& os, const char* start)
 {
-  string ctx = "shibtarget.ShibMLP";
-  log = &(log4cpp::Category::getInstance(ctx));
+    while (start && *start) {
+        switch (*start) {
+            case '<':   os += "&lt;";       break;
+            case '>':   os += "&gt;";       break;
+            case '"':   os += "&quot;";     break;
+            case '#':   os += "&#35;";      break;
+            case '%':   os += "&#37;";      break;
+            case '&':   os += "&#38;";      break;
+            case '\'':  os += "&#39;";      break;
+            case '(':   os += "&#40;";      break;
+            case ')':   os += "&#41;";      break;
+            case ':':   os += "&#58;";      break;
+            case '[':   os += "&#91;";      break;
+            case '\\':  os += "&#92;";      break;
+            case ']':   os += "&#93;";      break;
+            case '`':   os += "&#96;";      break;
+            case '{':   os += "&#123;";     break;
+            case '}':   os += "&#125;";     break;
+            default:    os += *start;
+        }
+        start++;
+    }
 }
 
-
 static void trimspace (string& s)
 {
   int end = s.size() - 1, start = 0;
@@ -98,104 +74,211 @@ static void trimspace (string& s)
   s = s.substr(start, end - start + 1);
 }
 
-ShibMLP::ShibMLP ()
+const char* ShibMLP::run(const string& is, const IPropertySet* props, std::string* output)
 {
-  m_priv = new ShibMLPPriv ();
-
   // Create a timestamp
   time_t now = time(NULL);
-  string now_s = ctime(&now);
-  insert ("now", now_s);
-}
-
-ShibMLP::~ShibMLP ()
-{
-  delete m_priv;
-}
-
-string ShibMLP::run (const string& is) const
-{
-  string res;
+#if defined(HAVE_CTIME_R_2)
+    char timebuf[32];
+    insert("now", ctime_r(&now,timebuf));
+#elif defined(HAVE_CTIME_R_3)
+    char timebuf[32];
+    insert("now", ctime_r(&now,timebuf,sizeof(timebuf)));
+#else
+    insert("now", ctime(&now));
+#endif
 
+  if (!output)
+    output=&m_generated;
   const char* line = is.c_str();
   const char* lastpos = line;
   const char* thispos;
 
-  m_priv->log->info("Processing string");
-
   //
   // Search for SHIBMLP tags.  These are of the form:
-  //   <shibmlp key />
+  //   <shibmlp key/>
+  //    <shibmlpif key> stuff </shibmlpif>
+  //    <shibmlpifnot key> stuff </shibmlpifnot>
   // Note that there MUST be white-space after "<shibmlp" but
   // there does not need to be white space between the key and
   // the close-tag.
   //
-  while ((thispos = strstr(lastpos, "<")) != NULL) {
+  while ((thispos = strchr(lastpos, '<')) != NULL) {
     // save the string up to this token
-    res += is.substr(lastpos-line, thispos-lastpos);
+    *output += is.substr(lastpos-line, thispos-lastpos);
 
-    // Make sure this token matches our token.
-    if (strnicmp (thispos, "<shibmlp ", 9)) {
-      res += "<";
-      lastpos = thispos + 1;
-      continue;
+    // Make sure this token matches our tokens.
+#ifdef HAVE_STRCASECMP
+    if (!strncasecmp(thispos, "<shibmlp ", 9))
+#else
+    if (!strnicmp(thispos, "<shibmlp ", 9))
+#endif
+    {
+        // Save this position off.
+        lastpos = thispos + 9;  // strlen("<shibmlp ")
+    
+        // search for the end-tag
+        if ((thispos = strstr(lastpos, "/>")) != NULL) {
+            string key = is.substr(lastpos-line, thispos-lastpos);
+            trimspace(key);
+    
+            map<string,string>::const_iterator i=m_map.find(key);
+            if (i != m_map.end()) {
+                html_encode(*output,i->second.c_str());
+            }
+            else {
+                pair<bool,const char*> p=props ? props->getString(key.c_str()) : pair<bool,const char*>(false,NULL);
+                if (p.first) {
+                    html_encode(*output,p.second);
+                }
+                else {
+                    static const char* s1 = "<!-- Unknown SHIBMLP key: ";
+                    static const char* s2 = "/>";
+                    *output += s1;
+                    *output += key + s2;
+                }
+            }
+            lastpos = thispos + 2; // strlen("/>")
+        }
     }
-
-    // Save this position off.
-    lastpos = thispos + 9;     // strlen("<shibmlp ")
-
-    // search for the end-tag
-    if ((thispos = strstr(lastpos, "/>")) != NULL) {
-      string key = is.substr(lastpos-line, thispos-lastpos);
-      trimspace(key);
-
-      m_priv->log->debug("found key: \"%s\"", key.c_str());
-
-      map<string,string>::const_iterator i=m_map.find(key);
-      if (i == m_map.end()) {
-       static string s1 = "<!-- Unknown SHIBMLP key: ";
-       static string s2 = "/>";
-       res += s1 + key + s2;
-       m_priv->log->debug("key unknown");
-      } else {
-       res += i->second;
-       m_priv->log->debug("key maps to \"%s\"", i->second.c_str());
-      }
-
-      lastpos = thispos + 2;   // strlen("/>")
+#ifdef HAVE_STRCASECMP
+    else if (!strncasecmp(thispos, "<shibmlpif ", 11))
+#else
+    else if (!strnicmp(thispos, "<shibmlpif ", 11))
+#endif
+    {
+        // Save this position off.
+        lastpos = thispos + 11;  // strlen("<shibmlpif ")
+
+        // search for the end of this tag
+        if ((thispos = strchr(lastpos, '>')) != NULL) {
+            string key = is.substr(lastpos-line, thispos-lastpos);
+            trimspace(key);
+            bool eval=false;
+            map<string,string>::const_iterator i=m_map.find(key);
+            if (i != m_map.end() && !i->second.empty()) {
+                eval=true;
+            }
+            else {
+                pair<bool,const char*> p=props ? props->getString(key.c_str()) : pair<bool,const char*>(false,NULL);
+                if (p.first) {
+                    eval=true;
+                }
+            }
+            lastpos = thispos + 1; // strlen(">")
+            
+            // Search for the closing tag.
+            const char* frontpos=lastpos;
+            while ((thispos = strstr(lastpos, "</")) != NULL) {
+#ifdef HAVE_STRCASECMP
+                if (!strncasecmp(thispos, "</shibmlpif>", 12))
+#else
+                if (!strnicmp(thispos, "</shibmlpif>", 12))
+#endif
+                {
+                    // We found our terminator. Process the string in between.
+                    string segment;
+                    run(is.substr(frontpos-line, thispos-frontpos),props,&segment);
+                    if (eval)
+                        *output += segment;
+                    lastpos = thispos + 12; // strlen("</shibmlpif>")
+                    break;
+                }
+                else {
+                    // Skip it.
+                    lastpos = thispos + 2;
+                }
+            }
+        }
+    }
+#ifdef HAVE_STRCASECMP
+    else if (!strncasecmp(thispos, "<shibmlpifnot ", 14))
+#else
+    else if (!strnicmp(thispos, "<shibmlpifnot ", 14))
+#endif
+    {
+        // Save this position off.
+        lastpos = thispos + 14;  // strlen("<shibmlpifnot ")
+
+        // search for the end of this tag
+        if ((thispos = strchr(lastpos, '>')) != NULL) {
+            string key = is.substr(lastpos-line, thispos-lastpos);
+            trimspace(key);
+            bool eval=false;
+            map<string,string>::const_iterator i=m_map.find(key);
+            if (i != m_map.end() && !i->second.empty()) {
+                eval=true;
+            }
+            else {
+                pair<bool,const char*> p=props ? props->getString(key.c_str()) : pair<bool,const char*>(false,NULL);
+                if (p.first) {
+                    eval=true;
+                }
+            }
+            lastpos = thispos + 1; // strlen(">")
+            
+            // Search for the closing tag.
+            const char* frontpos=lastpos;
+            while ((thispos = strstr(lastpos, "</")) != NULL) {
+#ifdef HAVE_STRCASECMP
+                if (!strncasecmp(thispos, "</shibmlpifnot>", 15))
+#else
+                if (!strnicmp(thispos, "</shibmlpifnot>", 15))
+#endif
+                {
+                    // We found our terminator. Process the string in between.
+                    string segment;
+                    run(is.substr(frontpos-line, thispos-frontpos),props,&segment);
+                    if (!eval)
+                        *output += segment;
+                    lastpos = thispos + 15; // strlen("</shibmlpifnot>")
+                    break;
+                }
+                else {
+                    // Skip it.
+                    lastpos = thispos + 2;
+                }
+            }
+        }
+    }
+    else {
+      // Skip it.
+      *output += "<";
+      lastpos = thispos + 1;
     }
   }
-  res += is.substr(lastpos-line);
+  *output += is.substr(lastpos-line);
 
-  return res;
+  return output->c_str();
 }
 
-string ShibMLP::run (istream& is) const
+const char* ShibMLP::run(istream& is, const IPropertySet* props, std::string* output)
 {
   static string eol = "\r\n";
   string str, line;
 
-  m_priv->log->info("processing stream");
-
   while (getline(is, line))
     str += line + eol;
 
-  return run(str);
+  return run(str,props,output);
 }
 
-void ShibMLP::insert (RPCError& e)
+void ShibMLP::insert(SAMLException& e)
 {
-  insert ("errorType", e.getType());
-  insert ("errorText", e.getText());
-  insert ("errorDesc", e.getDesc());
-  insert ("originErrorURL", e.getOriginErrorURL());
-  insert ("originContactName", e.getOriginContactName());
-  insert ("originContactEmail", e.getOriginContactEmail());
-}
-
-void ShibMLP::insert (const std::string& key, const std::string& value)
-{
-  saml::NDC ndc("insert");
-  m_priv->log->debug("inserting %s -> %s", key.c_str(), value.c_str());
-  m_map[key] = value;
+    insert("errorType", e.classname());
+    if (typeid(e)==typeid(ContentTypeException))
+        insert("errorText", "A problem was detected with your identity provider's software configuration.");
+    else
+        insert("errorText", e.getMessage() ? e.getMessage() : "No Message");
+    if (e.getProperty("errorURL"))
+        insert("originErrorURL", e.getProperty("errorURL"));
+    if (e.getProperty("contactName"))
+        insert("originContactName", e.getProperty("contactName"));
+    const char* email=e.getProperty("contactEmail");
+    if (email) {
+        if (!strncmp(email,"mailto:",7) && strlen(email)>7)
+            insert("originContactEmail", email+7);
+        else
+            insert("originContactEmail", email);
+    }
 }