Index: main/trunk/greenstone2/runtime-src/packages/Makefile.in
===================================================================
--- main/trunk/greenstone2/runtime-src/packages/Makefile.in	(revision 28886)
+++ main/trunk/greenstone2/runtime-src/packages/Makefile.in	(revision 28888)
@@ -29,8 +29,4 @@
 USE_YAZ=@USE_YAZ@
 USE_APACHE_HTTPD=@USE_APACHE_HTTPD@
-
-
-SECURITY=security/libgpg-error-1.12
-#security/libgcrypt-1.6.1
 
 ifeq ($(USE_Z3950), 1)
@@ -74,5 +70,5 @@
 # is assumed that each package will have at least four rules: all, install,
 # clean, and distclean.
-INSTALLDIRS =   $(YAZ) $(D2M) $(CORBA) $(SECURITY)
+INSTALLDIRS =   $(YAZ) $(D2M) $(CORBA)
 PACKAGEDIRS =	$(INSTALLDIRS) $(HTTPD)
 
@@ -133,7 +129,4 @@
 	cd yaz/yaz-2.1.4/src; $(MAKE)  $(MDEFINES) install
 endif
-
-# make install all packages in the security folder
-
 
 # now install everything except apache, since that had already been installed during "all"
Index: main/trunk/greenstone2/runtime-src/src/recpt/Makefile.in
===================================================================
--- main/trunk/greenstone2/runtime-src/src/recpt/Makefile.in	(revision 28886)
+++ main/trunk/greenstone2/runtime-src/src/recpt/Makefile.in	(revision 28888)
@@ -155,4 +155,5 @@
 APACHE_MODULE =
 endif
+
 
 # Extension support
@@ -229,4 +230,5 @@
 	$(RSS_SOURCES) \
 	statusaction.cpp \
+	securitytools.cpp \
 	summarise.cpp \
 	tipaction.cpp \
@@ -278,4 +280,5 @@
 	$(RSS_OBJECTS) \
 	statusaction.o \
+	securitytools.o \
 	summarise.o \
 	tipaction.o \
Index: main/trunk/greenstone2/runtime-src/src/recpt/action.h
===================================================================
--- main/trunk/greenstone2/runtime-src/src/recpt/action.h	(revision 28886)
+++ main/trunk/greenstone2/runtime-src/src/recpt/action.h	(revision 28888)
@@ -34,4 +34,5 @@
 #include "recptproto.h"
 #include "browserclass.h"
+#include "securitytools.h"
 
 #if defined(GSDL_USE_OBJECTSPACE)
Index: main/trunk/greenstone2/runtime-src/src/recpt/basequeryaction.cpp
===================================================================
--- main/trunk/greenstone2/runtime-src/src/recpt/basequeryaction.cpp	(revision 28886)
+++ main/trunk/greenstone2/runtime-src/src/recpt/basequeryaction.cpp	(revision 28888)
@@ -651,5 +651,5 @@
 
     // Display the "this collection is not installed on this system" page
-    disp.setmacro("cvariable", displayclass::defaultpackage, collection);
+    disp.setmacro("cvariable", displayclass::defaultpackage, encodeForHTML(collection));
     disp.setmacro("content", "query", "<p>_textbadcollection_<p>");
 
Index: main/trunk/greenstone2/runtime-src/src/recpt/cgiutils.cpp
===================================================================
--- main/trunk/greenstone2/runtime-src/src/recpt/cgiutils.cpp	(revision 28886)
+++ main/trunk/greenstone2/runtime-src/src/recpt/cgiutils.cpp	(revision 28888)
@@ -44,5 +44,5 @@
 
 // set to false to undo security changes (url-encoding arguments)
-static bool do_safe_cgi_args = true;
+static bool do_safe_cgi_args = false;
 
 static unsigned short hexdigit (unsigned short c) {
Index: main/trunk/greenstone2/runtime-src/src/recpt/pageaction.cpp
===================================================================
--- main/trunk/greenstone2/runtime-src/src/recpt/pageaction.cpp	(revision 28886)
+++ main/trunk/greenstone2/runtime-src/src/recpt/pageaction.cpp	(revision 28888)
@@ -751,5 +751,5 @@
   if (arg_p == "about") {
     if (cinfo == NULL) {
-      disp.setmacro("cvariable", displayclass::defaultpackage, arg_c);
+      disp.setmacro("cvariable", displayclass::defaultpackage, encodeForHTML(arg_c));
       disp.setmacro("content", arg_p, "<p>_textbadcollection_<p>");
       return;
@@ -797,5 +797,5 @@
   
     if (cinfo == NULL) {
-      disp.setmacro("cvariable", displayclass::defaultpackage, arg_c);
+      disp.setmacro("cvariable", displayclass::defaultpackage, encodeForHTML(arg_c));
       disp.setmacro("content", arg_p, "<p>_textbadcollection_<p>");
       return;
@@ -1009,7 +1009,7 @@
   text_t &arg_p = args["p"];
 
-  textout << outconvert << disp << ("_" + arg_p + ":header_\n")
-	  << ("_" + arg_p + ":content_\n")
-	  << ("_" + arg_p + ":footer_\n");
+  textout << outconvert << disp << ("_" + encodeForHTML(arg_p) + ":header_\n")
+	  << ("_" + encodeForHTML(arg_p) + ":content_\n")
+	  << ("_" + encodeForHTML(arg_p) + ":footer_\n");
 
   return true;
Index: main/trunk/greenstone2/runtime-src/src/recpt/queryaction.cpp
===================================================================
--- main/trunk/greenstone2/runtime-src/src/recpt/queryaction.cpp	(revision 28886)
+++ main/trunk/greenstone2/runtime-src/src/recpt/queryaction.cpp	(revision 28888)
@@ -1010,5 +1010,5 @@
 
     // Display the "this collection is not installed on this system" page
-    disp.setmacro("cvariable", displayclass::defaultpackage, main_collection);
+    disp.setmacro("cvariable", displayclass::defaultpackage, encodeForHTML(main_collection));
     disp.setmacro("content", "query", "<p>_textbadcollection_<p>");
 
@@ -1394,5 +1394,8 @@
 	}
 
-	disp.setmacro ("decodedcompressedoptions", displayclass::defaultpackage, dm_safe(compressedoptions));
+	text_t macrovalue = dm_safe(compressedoptions);
+	disp.setmacro ("decodedcompressedoptions", displayclass::defaultpackage, macrovalue);
+	disp.setmacro ("decodedcompressedoptionsAttrsafe", displayclass::defaultpackage, encodeForHTMLAttr(macrovalue));
+	
       }
     } // form search
Index: main/trunk/greenstone2/runtime-src/src/recpt/receptionist.cpp
===================================================================
--- main/trunk/greenstone2/runtime-src/src/recpt/receptionist.cpp	(revision 28886)
+++ main/trunk/greenstone2/runtime-src/src/recpt/receptionist.cpp	(revision 28888)
@@ -36,4 +36,5 @@
 #include "gsdltimes.h"
 #include "OIDtools.h"
+#include "securitytools.h"
 #include <assert.h>
 #include <time.h>
@@ -1485,5 +1486,8 @@
     compressedoptions = to_uni(compressedoptions);
   }
-  disp.setmacro ("decodedcompressedoptions", displayclass::defaultpackage, dm_safe(compressedoptions)); 
+
+  text_t dmacrovalue = dm_safe(compressedoptions);
+  disp.setmacro ("decodedcompressedoptions", displayclass::defaultpackage, dmacrovalue); 
+  disp.setmacro ("decodedcompressedoptionsAttrsafe", displayclass::defaultpackage, encodeForHTMLAttr(dmacrovalue));
 
 #if defined (__WIN32__)
@@ -1495,4 +1499,7 @@
   cgiargsclass::const_iterator argsend = args.end();
   while (argshere != argsend) {
+
+    text_t macrovalue = (*argshere).second.value; // and stays like that if ((*argshere).first == "hp")
+
     if (((*argshere).first == "q") ||
 	((*argshere).first == "qa") ||
@@ -1502,13 +1509,37 @@
 	((*argshere).first == "qpl") ||
 	((*argshere).first == "qr") ||
-	((*argshere).first == "q2"))
+	((*argshere).first == "q2")) {
+
       // need to escape special characters from query string
-      disp.setmacro ("cgiarg" + (*argshere).first, 
-		     displayclass::defaultpackage, html_safe((*argshere).second.value));
-    else if ((*argshere).first == "hp") {
-      disp.setmacro ("cgiarg" + (*argshere).first, displayclass::defaultpackage, (*argshere).second.value);
-    } else {
-      disp.setmacro ("cgiarg" + (*argshere).first, displayclass::defaultpackage, dm_safe((*argshere).second.value));
-    }
+      macrovalue = html_safe(macrovalue);
+
+    } else  if ((*argshere).first == "hp") {
+      if(!isValidURLProtocol(macrovalue)) {
+	macrovalue = encodeForURL(macrovalue); // URL has invalid protocol like javascript:, so URL encode it
+      }
+    }
+    else {
+      macrovalue = dm_safe(macrovalue);
+    }   
+
+    // set the default value for the macro
+    disp.setmacro ("cgiarg" + (*argshere).first, displayclass::defaultpackage, macrovalue);
+
+    // set macros for the encoded versions of the same value. Uses the functions in securitytools.h
+    // https://www.owasp.org/index.php/XSS_%28Cross_Site_Scripting%29_Prevention_Cheat_Sheet
+
+    text_t htmlsafe = encodeForHTML(macrovalue);
+    text_t attrsafe = encodeForHTMLAttr(macrovalue);
+    text_t urlsafe = encodeForURL(macrovalue);
+    text_t jssafe = encodeForJavascript(macrovalue);
+    text_t csssafe = encodeForCSS(macrovalue);
+
+    disp.setmacro ("cgiarg" + (*argshere).first + "Htmlsafe", displayclass::defaultpackage, htmlsafe);    
+    disp.setmacro ("cgiarg" + (*argshere).first + "Attrsafe", displayclass::defaultpackage, attrsafe);
+    disp.setmacro ("cgiarg" + (*argshere).first + "Jssafe", displayclass::defaultpackage, jssafe);
+    disp.setmacro ("cgiarg" + (*argshere).first + "Csssafe", displayclass::defaultpackage, csssafe);
+    disp.setmacro ("cgiarg" + (*argshere).first + "Urlsafe", displayclass::defaultpackage, urlsafe);
+    
+
     ++argshere;
   }
Index: main/trunk/greenstone2/runtime-src/src/recpt/securitytools.cpp
===================================================================
--- main/trunk/greenstone2/runtime-src/src/recpt/securitytools.cpp	(revision 28888)
+++ main/trunk/greenstone2/runtime-src/src/recpt/securitytools.cpp	(revision 28888)
@@ -0,0 +1,399 @@
+/**********************************************************************
+ *
+ * securitytools.cpp -- a C++ port of the required functions from the OWASP ESAPI for Java
+ * Copyright (C) 2014  The New Zealand Digital Library Project
+ *
+ * A component of the Greenstone digital library software
+ * from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *********************************************************************/
+/*
+ * https://www.owasp.org/index.php/XSS_%28Cross_Site_Scripting%29_Prevention_Cheat_Sheet
+ * http://owasp-esapi-cplusplus.googlecode.com/svn/trunk/doc/html/classesapi_1_1_encoder.html
+ * http://owasp-esapi-cplusplus.googlecode.com/svn/trunk/doc/html/classesapi_1_1_default_encoder.html
+ * http://owasp-esapi-cplusplus.googlecode.com/svn/trunk/doc/html/_default_encoder_8cpp_source.html
+ * 
+ * The OWASP-ESAPI for C++'s online API is outdated/different to the actual method definitions in the
+ * downloaded version of the code at runtime-src/packages/security/installed/include
+ * Further, most of the necessary methods in the ESAPI-for-C++ have not been implemented yet.
+ * The ESAPI-for-C, whose code is more complete, doesn't seem to have the same structure as the Java version.
+ *
+ * As a consequence, this file now contains custom Greenstone C++ functions that port the Java versions of the 
+ * required methods from the OWASP for Java API at http://code.google.com/p/owasp-esapi-java/. 
+ * In particular the codecs and the DefaultEncoder at 
+ * http://code.google.com/p/owasp-esapi-java/source/browse/trunk/src/main/java/org/owasp/esapi/#esapi%2Fcodecs
+ * http://code.google.com/p/owasp-esapi-java/source/browse/trunk/src/main/java/org/owasp/esapi/#esapi%2Freference%253Fstate%253Dclosed
+*/
+
+//http://www.cplusplus.com/reference/cctype/isalnum/
+#include <ctype.h>
+#include <stdio.h>
+#include "securitytools.h"
+
+static bool security_on = true;
+
+// function prototype - unused. Using ctype::isalphanum(int) instead
+bool isAlphaNumeric(const unsigned short c);
+
+/*  
+    Rule 5 of the OWASP XSS cheat sheet states: 
+    https://www.owasp.org/index.php/XSS_%28Cross_Site_Scripting%29_Prevention_Cheat_Sheet#RULE_.235_-_URL_Escape_Before_Inserting_Untrusted_Data_into_HTML_URL_Parameter_Values
+    
+    WARNING: Do not encode complete or relative URL's with URL encoding! If untrusted input is meant to be placed into 
+    href, src or other URL-based attributes, it should be validated to make sure it does not point to an unexpected 
+    protocol, especially Javascript links. URL's should then be encoded based on the context of display like any other
+    piece of data. For example, user driven URL's in HREF links should be attribute encoded. For example:
+
+    String userURL = request.getParameter( "userURL" )
+    boolean isValidURL = ESAPI.validator().isValidInput("URLContext", userURL, "URL", 255, false); 
+    if (isValidURL) {  
+    <a href="<%=encoder.encodeForHTMLAttribute(userURL)%>">link</a>
+    }
+
+    The following function is simpler than theirs as it only looks for any "javascript:" in the string. If present, it 
+    returns false and the string should get URL encoded. Otherwise, the protocol is assumed to be valid and it returns true.
+*/
+bool isValidURLProtocol(const text_t& url) {
+  text_t::const_iterator here = url.begin();
+  text_t::const_iterator end = url.end();
+
+  if(findword(here, end, "javascript:") != end) {
+    return true;
+  }
+  return false;
+}
+
+
+text_t encodeForHTMLAttr(const text_t& in, const text_t& immuneChars) {
+  text_t out;
+  text_t::const_iterator here = in.begin();
+  text_t::const_iterator end = in.end();
+  while (here != end) {
+    out += encodeForHTML(immuneChars, *here); // IMMUNE_HTMLATTR by default
+    ++here;
+  }
+  return out;
+}
+  
+text_t encodeForHTML(const text_t& in, const text_t& immuneChars) {
+  text_t out;
+  text_t::const_iterator here = in.begin();
+  text_t::const_iterator end = in.end();
+  while (here != end) {
+    out += encodeForHTML(immuneChars, *here); // IMMUNE_HTML by default
+    ++here;
+  }
+  return out;
+}
+
+text_t encodeForCSS(const text_t& in, const text_t& immuneChars) {
+  text_t out;
+  text_t::const_iterator here = in.begin();
+  text_t::const_iterator end = in.end();
+  while (here != end) {
+    out += encodeForCSS(immuneChars, *here); // IMMUNE_CSS by default
+    ++here;
+  }
+  return out;
+}
+
+
+text_t encodeForURL(const text_t& in, const text_t& immuneChars) {
+  text_t out;
+  text_t::const_iterator here = in.begin();
+  text_t::const_iterator end = in.end();
+  while (here != end) {
+    out += encodeForURL(immuneChars, *here); // IMMUNE_URL by default
+    ++here;
+  }
+  return out;
+}
+
+text_t encodeForJavascript(const text_t& in, const text_t& immuneChars) {
+  text_t out;
+  text_t::const_iterator here = in.begin();
+  text_t::const_iterator end = in.end();
+  while (here != end) {
+    out += encodeForJavascript(immuneChars, *here); // IMMUNE_JAVASCRIPT by default
+    ++here;
+  }
+  return out;
+}
+
+text_t encodeForMySQL(const text_t& in, const text_t& immuneChars, const SQLMode mode) {
+  text_t out;
+  text_t::const_iterator here = in.begin();
+  text_t::const_iterator end = in.end();
+  while (here != end) {
+    out += encodeForMySQL(immuneChars, *here, mode); // IMMUNE_SQL and STANDARD SQLMode by default
+    ++here;
+  }
+  return out;
+}
+
+
+/*
+The encodeForURL() here function follows the same rules as Java's URLEncoder, since that is called
+by the OWASP-for-Java code when the OWASP project wishes to encode strings for URL contexts:
+http://docs.oracle.com/javase/6/docs/api/java/net/URLEncoder.html
+
+When encoding a String, the following rules apply:
+
+    The alphanumeric characters "a" through "z", "A" through "Z" and "0" through "9" remain the same.
+    The special characters ".", "-", "*", and "_" remain the same.
+    The space character " " is converted into a plus sign "+".
+    All other characters are unsafe and are first converted into one or more bytes using some encoding scheme. Then each byte is represented by the 3-character string "%xy", where xy is the two-digit hexadecimal representation of the byte. The recommended encoding scheme to use is UTF-8. However, for compatibility reasons, if an encoding is not specified, then the default encoding of the platform is used. 
+
+*/
+text_t encodeForURL(const text_t& immuneChars, const unsigned short in) {
+  
+  text_t result = "";
+  text_t::const_iterator here = immuneChars.begin();
+  text_t::const_iterator end = immuneChars.end();
+
+  // Check if the character is in the list of chars immune to encoding
+  if(findchar(here, end, in) != end) {
+    result.push_back(in);
+  }
+
+  else if(isalnum((int)in)) {
+    result.push_back(in);
+  }
+
+  // for URLs, space becomes +
+  else if(in == ' ' ) {
+    result.push_back('+');
+  }  
+
+  // all other chars converted to hexadecimal %XY
+  else {
+    char hex_char[4];
+    sprintf(hex_char,"%%%02X",in);
+    result = text_t(hex_char); // result += hex_char;
+  }
+
+  return result;
+}
+
+// encodes for both HTML and HTML attributes. 
+// The chars in the immuneChars array determines which of the two this is
+// See http://code.google.com/p/owasp-esapi-java/source/browse/trunk/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java
+text_t encodeForHTML(const text_t& immuneChars, const unsigned short in) {
+  
+  text_t result = "";
+  text_t::const_iterator here = immuneChars.begin();
+  text_t::const_iterator end = immuneChars.end();
+
+  // Check if the character is in the list of chars immune to encoding
+  if(findchar(here, end, in) != end) {
+    result.push_back(in);
+  }
+
+  else if(isalnum((int)in)) {
+    result.push_back(in);
+  }
+
+  // check for illegal characters
+  // http://code.google.com/p/owasp-esapi-java/source/browse/trunk/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java
+  // 0x1f is the unit separator, an invisible character, 0x7f is the ascii control code for delete, not sure about 0x9f
+  // Encode all these as the UTF-8 replacement char ufffd, which is used to replace an unknown or unrepresentable character
+
+  else if ( ( in <= 0x1f && in != '\t' && in != '\n' && in != '\r' ) || ( in >= 0x7f && in <= 0x9f ) ) {
+    result = "&#x" + REPLACEMENT_HEX + ";";
+    // Let's entity encode this instead of returning it
+    //c = REPLACEMENT_CHAR;
+  }  
+
+  // all other chars are to be converted to hexadecimal AB, then return the hex entity, which is of the form &#xAB;
+  else {
+    char hex_char[3];
+    sprintf(hex_char,"%02X",in);
+    result = "&#x" + text_t(hex_char) + ";";
+  }
+
+  return result;
+}
+
+// http://code.google.com/p/owasp-esapi-java/source/browse/trunk/src/main/java/org/owasp/esapi/codecs/CSSCodec.java
+// return the hex and end in whitespace to terminate
+text_t encodeForCSS(const text_t& immuneChars, const unsigned short in) {
+  
+  text_t result = "";
+  text_t::const_iterator here = immuneChars.begin();
+  text_t::const_iterator end = immuneChars.end();
+
+  // Check if the character is in the list of chars immune to encoding
+  if(findchar(here, end, in) != end) {
+    result.push_back(in);
+  }
+
+  else if(isalnum((int)in)) {
+    result.push_back(in);
+  }
+
+  // all other chars converted to hexadecimal AB, then return the hex entity, which is of the form &#xAB;
+  else {
+    char hex_char[3];
+    sprintf(hex_char,"%02X",in);    
+    // return the hex and end in whitespace to terminate
+    result = "\\" + text_t(hex_char) + " ";
+  }
+
+  return result;
+}
+
+// http://code.google.com/p/owasp-esapi-java/source/browse/trunk/src/main/java/org/owasp/esapi/codecs/JavaScriptCodec.java
+text_t encodeForJavascript(const text_t& immuneChars, const unsigned short in) {
+  
+  text_t result = "";
+  text_t::const_iterator start = immuneChars.begin();
+  text_t::const_iterator end = immuneChars.end();
+
+  // Check if the character is in the list of chars immune to encoding
+  if(findchar(start, end, in) != end) {
+    result.push_back(in);
+  }
+
+  else if(isalnum((int)in)) {
+    result.push_back(in);
+  }
+
+  // Do not use these shortcuts as they can be used to break out of a context
+  // if ( ch == 0x00 ) return "\\0";
+  // if ( ch == 0x08 ) return "\\b";
+  // if ( ch == 0x09 ) return "\\t";
+  // if ( ch == 0x0a ) return "\\n";
+  // if ( ch == 0x0b ) return "\\v";
+  // if ( ch == 0x0c ) return "\\f";
+  // if ( ch == 0x0d ) return "\\r";
+  // if ( ch == 0x22 ) return "\\\"";
+  // if ( ch == 0x27 ) return "\\'";
+  // if ( ch == 0x5c ) return "\\\\";
+
+
+  // encode up to 256 with hexadecimal \\xHH, otherwise encode with \\uHHHH
+  else {
+
+    // encode up to 256 with \\xHH
+    if(in < 256) {
+      char hex_char[3];
+      sprintf(hex_char,"%02X",in);
+      result = "\\x" + text_t(hex_char);
+    }
+    // otherwise encode with \\uHHHH
+    else {
+      char hex_char[5];
+      sprintf(hex_char,"%04X",in);
+      result = "\\u" + text_t(hex_char);
+    }
+    
+  }
+
+  return result;
+}
+
+
+/* 
+http://code.google.com/p/owasp-esapi-java/source/browse/trunk/src/main/java/org/owasp/esapi/codecs/MySQLCodec.java
+ Defense Option 3 of https://www.owasp.org/index.php/SQL_Injection_Prevention_Cheat_Sheet
+ which states: 
+ "This technique works like this. Each DBMS supports one or more character escaping schemes 
+ specific to certain kinds of queries. If you then escape all user supplied input using the 
+ proper escaping scheme for the database you are using, the DBMS will not confuse that input 
+ with SQL code written by the developer, thus avoiding any possible SQL injection vulnerabilities."
+
+http://www.php.net/manual/en/mysqli.real-escape-string.php
+http://www.php.net/manual/en/function.mysql-real-escape-string.php
+http://www.php.net/manual/en/function.sqlite-escape-string.php
+http://stackoverflow.com/questions/8838913/difference-between-mysql-sqlite-etc-databases
+http://stackoverflow.com/questions/633245/sql-escape-with-sqlite-in-c-sharp
+
+*/
+text_t encodeForMySQL(const text_t& immuneChars, const unsigned short in, const SQLMode mode) {
+  
+  text_t result = "";
+  text_t::const_iterator start = immuneChars.begin();
+  text_t::const_iterator end = immuneChars.end();
+
+  // Check if the character is in the list of chars immune to encoding
+  if(findchar(start, end, in) != end) {
+    result.push_back(in);
+  }
+
+  else if(isalnum((int)in)) {
+    result.push_back(in);
+  }
+
+  // switch( mode ) {
+  // case ANSI: return encodeCharacterANSI( c );
+  // case STANDARD: return encodeCharacterMySQL( c );
+  // }
+
+  if(mode == STANDARD) { // encodeCharacterMySQL: Encode a character suitable for MySQL
+
+    if ( in == 0x00 ) result = "\\0";
+    else if ( in == 0x08 ) result = "\\b";
+    else if ( in == 0x09 ) result = "\\t";
+    else if ( in == 0x0a ) result = "\\n";
+    else if ( in == 0x0d ) result = "\\r";
+    else if ( in == 0x1a ) result = "\\Z";
+    else if ( in == 0x22 ) result = "\\\"";
+    else if ( in == 0x25 ) result = "\\%";
+    else if ( in == 0x27 ) result = "\\'";
+    else if ( in == 0x5c ) result = "\\\\";
+    else if ( in == 0x5f ) result = "\\_";
+    else { 
+      result = "\\";
+      result.push_back(in);
+    }
+
+  } else { // mode is ANSI, encodeCharacterANSI:
+
+    /* Encode for ANSI SQL. 
+     Apostrophe is encoded
+     Bug ###: In ANSI Mode Strings can also be passed in using the quotation. 
+     In ANSI_QUOTES mode a quotation is considered to be an identifier, thus 
+     cannot be used at all in a value and will be dropped completely. 
+     returns a string encoded to standards of MySQL running in ANSI mode
+    */
+
+    if ( in == '\'' ) result = "\'\'";
+    else if ( in == '\"' ) result = "";
+    else result.push_back(in);
+    
+  }
+
+  return result;
+}
+
+// See Codec.hex[] initialization and Codec.getHexForNonAlphanumeric(c) and Codec.toHex(c)
+// http://code.google.com/p/owasp-esapi-java/source/browse/trunk/src/main/java/org/owasp/esapi/codecs/Codec.java
+// See Integer.toHexString()
+// http://docs.oracle.com/javase/6/docs/api/java/lang/Integer.html#toHexString%28int%29
+// http://stackoverflow.com/questions/3370004/what-is-static-block-in-c-or-c
+bool isAlphaNumeric(const unsigned short c) {
+  if(c >= 0xFF) {   // >= 256 need no further checking, it is not alphanumeric
+    return false;
+  }
+  // alphanumeric: 0 - 9 || A - Z || a - z
+  if ( c >= 0x30 && c <= 0x39 || c >= 0x41 && c <= 0x5A || c >= 0x61 && c <= 0x7A ) {
+    return true;
+  }
+  // < 255, but not alphanumeric
+  return false;
+}
Index: main/trunk/greenstone2/runtime-src/src/recpt/securitytools.h
===================================================================
--- main/trunk/greenstone2/runtime-src/src/recpt/securitytools.h	(revision 28888)
+++ main/trunk/greenstone2/runtime-src/src/recpt/securitytools.h	(revision 28888)
@@ -0,0 +1,40 @@
+#ifndef SECURITYTOOLS_H
+#define SECURITYTOOLS_H
+
+#include "text_t.h"
+
+// enums start numbering at 0 unless otherwise specified
+enum SQLMode { STANDARD, ANSI }; // public static enum Mode { ANSI(1),STANDARD(0); ...}
+
+
+// two bytes
+static const text_t REPLACEMENT_HEX = "fffd";
+//static const unsigned short REPLACEMENT_CHAR = '\ufffd';
+static const text_t IMMUNE_CSS = "";
+static const text_t IMMUNE_HTML = ",.-_ ";
+static const text_t IMMUNE_HTMLATTR = ",.-_";
+static const text_t IMMUNE_JAVASCRIPT = ",._";
+static const text_t IMMUNE_URL = "*.-_"; // See http://docs.oracle.com/javase/6/docs/api/java/net/URLEncoder.html
+static const text_t IMMUNE_SQL = " ";
+
+
+// a very simple version of esapi's Validator.isValidInput()
+bool isValidURLProtocol(const text_t& url);
+
+// String conversion
+text_t encodeForHTML(const text_t& input, const text_t& immuneChars=IMMUNE_HTML);
+text_t encodeForURL(const text_t& input, const text_t& immuneChars=IMMUNE_URL);
+text_t encodeForJavascript(const text_t& input, const text_t& immuneChars=IMMUNE_JAVASCRIPT);
+text_t encodeForHTMLAttr(const text_t& input, const text_t& immuneChars=IMMUNE_HTMLATTR);
+text_t encodeForCSS(const text_t& input, const text_t& immuneChars=IMMUNE_CSS);
+text_t encodeForMySQL(const text_t& input, const text_t& immuneChars=IMMUNE_SQL, const SQLMode mode=STANDARD);
+
+// Character conversions
+text_t encodeForHTML(const text_t& immuneChars, const unsigned short input);
+text_t encodeForURL(const text_t& immuneChars, const unsigned short input);
+text_t encodeForJavascript(const text_t& immuneChars, const unsigned short input);
+text_t encodeForCSS(const text_t& immuneChars, const unsigned short input);
+text_t encodeForMySQL(const text_t& immuneChars, const unsigned short input, const SQLMode mode);
+
+
+#endif
Index: main/trunk/greenstone2/runtime-src/src/recpt/sqlqueryaction.cpp
===================================================================
--- main/trunk/greenstone2/runtime-src/src/recpt/sqlqueryaction.cpp	(revision 28886)
+++ main/trunk/greenstone2/runtime-src/src/recpt/sqlqueryaction.cpp	(revision 28888)
@@ -309,6 +309,8 @@
 	  compressedoptions = to_uni(compressedoptions);
 	}
-
-	disp.setmacro ("decodedcompressedoptions", displayclass::defaultpackage, dm_safe(compressedoptions));
+	
+	text_t dmacrovalue = dm_safe(compressedoptions);
+	disp.setmacro ("decodedcompressedoptions", displayclass::defaultpackage, dmacrovalue);
+	disp.setmacro ("decodedcompressedoptionsAttrsafe", displayclass::defaultpackage, encodeForHTMLAttr(dmacrovalue));
       }
     } // form search
@@ -317,8 +319,4 @@
     logout << "ERROR (sqlqueryaction::get_formatted_query_string): querytype not defined\n";
   }
-
-
-
-
 
 }
