From 4d6214ec0c50ccbb31dcecd0cc8be7ed32ddc7c6 Mon Sep 17 00:00:00 2001
From: Carsten  Rose <carsten.rose@math.uzh.ch>
Date: Sun, 19 Mar 2017 17:29:43 +0100
Subject: [PATCH] Support.php: backport of ldap_escape() for PHP <5.6.

---
 extension/qfq/qfq/Constants.php      | 25 ++++++++-
 extension/qfq/qfq/helper/Support.php | 78 +++++++++++++++++++++++++++-
 2 files changed, 100 insertions(+), 3 deletions(-)

diff --git a/extension/qfq/qfq/Constants.php b/extension/qfq/qfq/Constants.php
index 072b225be..92cb305fd 100644
--- a/extension/qfq/qfq/Constants.php
+++ b/extension/qfq/qfq/Constants.php
@@ -163,7 +163,7 @@ const ERROR_MISSING_SLAVE_ID_DEFINITION = 1074;
 const ERROR_MISSING_INTL = 1075;
 const ERROR_HTML_TOKEN_TOO_SHORT = 1076;
 const ERROR_MISSING_PRINTF_ARGUMENTS = 1077;
-
+const ERROR_MISSING_DEFINITON = 1078;
 // Subrecord
 const ERROR_SUBRECORD_MISSING_COLUMN_ID = 1100;
 
@@ -242,6 +242,7 @@ const STORE_EMPTY = "E"; // value: '', might helpfull if variable is not defined
 const STORE_SYSTEM = "Y"; // various system values like db connection credentials
 const STORE_EXTRA = 'X'; // Persistent Store: contains arrays! Not Usefull for user. Used by system.
 const STORE_ADDITIONAL_FORM_ELEMENTS = 'A'; // Internal Store to collect FormElements. Typically for 'hidden' elements of radio and checkbox. Helps render those elements at the end of the whole form rendering.
+const STORE_LDAP = 'L';
 
 const STORE_USE_DEFAULT = "FSRVD";
 
@@ -381,17 +382,28 @@ const VAR_FILENAME = 'filename'; // Original filename of an uploaded file.
 const MODE_DB_REGULAR = 'regular';
 const MODE_DB_NO_LOG = 'noLog';
 
-// PHPO class Typeahead
+// PHP class Typeahead
 const TYPEAHEAD_API_QUERY = 'query';  // Name of parameter in API call of typeahead.php?query=...&s=... - See also FE_TYPE_AHEAD_SQL
 const TYPEAHEAD_API_SIP = 'sip';  // Name of parameter in API call of typeahead.php?query=...&s=...
 const TYPEAHEAD_DEFAULT_LIMIT = 20;
 
+const DEFAULT_LDAP_TIME_LIMIT = 3;
+
 const SINGLE_TICK = "'";
 const DOUBLE_TICK = '"';
 
 // TOKEN evaluate
 const TOKEN_ESCAPE_SINGLE_TICK = 's';
 const TOKEN_ESCAPE_DOUBLE_TICK = 'd';
+const TOKEN_LDAP_ESCAPE_FILTER = 'l';
+const TOKEN_LDAP_ESCAPE_DN = 'L';
+
+// Workaround for PHP < 5.6.0
+if (!function_exists('ldap_escape')) {
+    define('LDAP_ESCAPE_FILTER', 0x01);
+    define('LDAP_ESCAPE_DN', 0x02);
+}
+
 const TOKEN_FOUND_IN_STORE_QUERY = 'query';
 
 const RANDOM_LENGTH = 32;
@@ -427,6 +439,9 @@ const SQL_LOG_MODE_ALL = 'all';
 const SQL_LOG_MODE_MODIFY = 'modify';
 const SQL_LOG_MODE_ERROR = 'error';  // write log entry, independent of global setting (e.g. broken Query)
 
+const MODE_LDAP_SINGLE = 'ldapSingle';
+const MODE_LDAP_MULTI = 'ldapMulti';
+
 // api/save.php, api/delete.php, api/load.php
 const API_DELETE_PHP = 'delete.php';
 
@@ -534,10 +549,13 @@ const F_PARAMETER = 'parameter';    // valid for F_ and FE_
 const F_LDAP_SERVER = 'ldapServer';
 const F_LDAP_BASE_DN = 'ldapBaseDn';
 const F_LDAP_SEARCH = 'ldapSearch';
+const F_LDAP_ATTRIBUTES = 'ldapAttributes';
+const F_LDAP_TIME_LIMIT = 'ldapTimeLimit';
 const F_TYPEAHEAD_LIMIT = 'typeAheadLimit';
 const F_TYPEAHEAD_MINLENGTH = 'typeAheadMinLength';
 const F_TYPEAHEAD_LDAP_VALUE_PRINTF = 'typeAheadLdapValuePrintf';
 const F_TYPEAHEAD_LDAP_KEY_PRINTF = 'typeAheadLdapKeyPrintf';
+const F_TYPEAHEAD_LDAP_SEARCH = 'typeAheadLdapSearch';
 
 // FORM_ELEMENT_STATI
 const FE_MODE_SHOW = 'show';
@@ -621,12 +639,15 @@ const FE_BUTTON_CLASS = 'buttonClass';
 const FE_LDAP_SERVER = F_LDAP_SERVER;
 const FE_LDAP_BASE_DN = F_LDAP_BASE_DN;
 const FE_LDAP_SEARCH = F_LDAP_SEARCH;
+const FE_LDAP_ATTRIBUTES = F_LDAP_ATTRIBUTES;
+const FE_LDAP_TIME_LIMIT = F_LDAP_TIME_LIMIT;
 const FE_TYPEAHEAD_LIMIT = F_TYPEAHEAD_LIMIT;
 const FE_TYPEAHEAD_MINLENGTH = F_TYPEAHEAD_MINLENGTH;
 const FE_TYPEAHEAD_SQL = 'typeAheadSql';
 const FE_TYPEAHEAD_LDAP_VALUE_PRINTF = F_TYPEAHEAD_LDAP_VALUE_PRINTF;
 const FE_TYPEAHEAD_LDAP_KEY_PRINTF = F_TYPEAHEAD_LDAP_KEY_PRINTF;
 const FE_TYPEAHEAD_LDAP = 'typeAheadLdap';
+const FE_TYPEAHEAD_LDAP_SEARCH = F_TYPEAHEAD_LDAP_SEARCH;
 const FE_FILL_STORE_LDAP = 'fillStoreLdap';
 const FE_CHARACTER_COUNT_WRAP = 'characterCountWrap';
 const RETYPE_FE_NAME_EXTENSION = 'RETYPE';
diff --git a/extension/qfq/qfq/helper/Support.php b/extension/qfq/qfq/helper/Support.php
index 1fc32d719..1316b06e7 100644
--- a/extension/qfq/qfq/helper/Support.php
+++ b/extension/qfq/qfq/helper/Support.php
@@ -123,7 +123,7 @@ class Support {
         switch (strtolower($type)) {
             case 'size':
             case 'maxlength':
-            // empty or '0' for attributes of type 'size' or 'maxlength' result in unsuable input elements: skip this.
+                // empty or '0' for attributes of type 'size' or 'maxlength' result in unsuable input elements: skip this.
                 if ($value === '' || $value == 0) {
                     return '';
                 }
@@ -703,4 +703,80 @@ class Support {
     public static function falseEmptyToZero($val) {
         return ($val == '' || $val == false) ? '0' : $val;
     }
+
+    /**
+     * TODO: as soon as we don't support PHP 5.6.0 anymore, this local implemention can be removed.
+     * Workaround for PHP < 5.6.0: there is no ldap_escape() - use this code instead.
+     *
+     * @param string $subject The subject string
+     * @param string $ignore Set of characters to leave untouched
+     * @param int $flags Any combination of LDAP_ESCAPE_* flags to indicate the
+     *                   set(s) of characters to escape.
+     * @return string
+     **/
+    public static function ldap_escape($subject, $ignore = '', $flags = 0) {
+
+        if (function_exists('ldap_escape')) {
+
+            return ldap_escape($subject, $ignore, $flags);
+
+        } else {
+
+//            define('LDAP_ESCAPE_FILTER', 0x01);
+//            define('LDAP_ESCAPE_DN',     0x02);
+
+            static $charMaps = array(
+                LDAP_ESCAPE_FILTER => array('\\', '*', '(', ')', "\x00"),
+                LDAP_ESCAPE_DN => array('\\', ',', '=', '+', '<', '>', ';', '"', '#'),
+            );
+            // Pre-process the char maps on first call
+            if (!isset($charMaps[0])) {
+                $charMaps[0] = array();
+                for ($i = 0; $i < 256; $i++) {
+                    $charMaps[0][chr($i)] = sprintf('\\%02x', $i);;
+                }
+                for ($i = 0, $l = count($charMaps[LDAP_ESCAPE_FILTER]); $i < $l; $i++) {
+                    $chr = $charMaps[LDAP_ESCAPE_FILTER][$i];
+                    unset($charMaps[LDAP_ESCAPE_FILTER][$i]);
+                    $charMaps[LDAP_ESCAPE_FILTER][$chr] = $charMaps[0][$chr];
+                }
+                for ($i = 0, $l = count($charMaps[LDAP_ESCAPE_DN]); $i < $l; $i++) {
+                    $chr = $charMaps[LDAP_ESCAPE_DN][$i];
+                    unset($charMaps[LDAP_ESCAPE_DN][$i]);
+                    $charMaps[LDAP_ESCAPE_DN][$chr] = $charMaps[0][$chr];
+                }
+            }
+            // Create the base char map to escape
+            $flags = (int)$flags;
+            $charMap = array();
+            if ($flags & LDAP_ESCAPE_FILTER) {
+                $charMap += $charMaps[LDAP_ESCAPE_FILTER];
+            }
+            if ($flags & LDAP_ESCAPE_DN) {
+                $charMap += $charMaps[LDAP_ESCAPE_DN];
+            }
+            if (!$charMap) {
+                $charMap = $charMaps[0];
+            }
+            // Remove any chars to ignore from the list
+            $ignore = (string)$ignore;
+            for ($i = 0, $l = strlen($ignore); $i < $l; $i++) {
+                unset($charMap[$ignore[$i]]);
+            }
+            // Do the main replacement
+            $result = strtr($subject, $charMap);
+            // Encode leading/trailing spaces if LDAP_ESCAPE_DN is passed
+            if ($flags & LDAP_ESCAPE_DN) {
+                if ($result[0] === ' ') {
+                    $result = '\\20' . substr($result, 1);
+                }
+                if ($result[strlen($result) - 1] === ' ') {
+                    $result = substr($result, 0, -1) . '\\20';
+                }
+            }
+
+            return $result;
+        }
+    }
+
 }
\ No newline at end of file
-- 
GitLab