| 1 | <?php
 | 
  
    | 2 | /**
 | 
  
    | 3 |  * @category        ISTeasy
 | 
  
    | 4 |  * @package         DatabaseSearchEngine 1
 | 
  
    | 5 |  * @author          Werner von der Decken
 | 
  
    | 6 |  * @copyright       2011, ISTeasy-project
 | 
  
    | 7 |  * @license         http://www.gnu.org/licenses/gpl.html
 | 
  
    | 8 |  * @version         $Id: DseTwo.php 2 2017-07-02 15:14:29Z Manuela $
 | 
  
    | 9 |  * @filesource      $HeadURL: svn://isteam.dynxs.de/wb/2.10.x/trunk/framework/DseTwo.php $
 | 
  
    | 10 |  * @description     Searchengine to browse whoole database for text.
 | 
  
    | 11 |  *                  Black- or whitelist is possible
 | 
  
    | 12 |  *                  min requirements: PHP 5.3.6, mySQL 5.1
 | 
  
    | 13 |  *                  this is a authorisised GPL-lizensed derivate from the original
 | 
  
    | 14 |  *                  ISTeasy class DseOne which is available under a cc-by-sa-3.0 license
 | 
  
    | 15 | */
 | 
  
    | 16 | /* -------------------------------------------------------- */
 | 
  
    | 17 | // Must include code to stop this file being accessed directly
 | 
  
    | 18 | if(!defined('WB_PATH')) {
 | 
  
    | 19 |     require_once(dirname(__FILE__).'/globalExceptionHandler.php');
 | 
  
    | 20 |     throw new IllegalFileException();
 | 
  
    | 21 | }
 | 
  
    | 22 | /* -------------------------------------------------------- */
 | 
  
    | 23 | 
 | 
  
    | 24 | class DseTwo {
 | 
  
    | 25 | 
 | 
  
    | 26 |     const USE_ALL       = 0;
 | 
  
    | 27 |     const USE_BLACKLIST = 1;
 | 
  
    | 28 |     const USE_WHITELIST = 2;
 | 
  
    | 29 | 
 | 
  
    | 30 |     const RETURN_UNUSED = 0;
 | 
  
    | 31 |     const RETURN_USED   = 1;
 | 
  
    | 32 |     /**
 | 
  
    | 33 |      *
 | 
  
    | 34 |      * @var res database handle
 | 
  
    | 35 |      */
 | 
  
    | 36 |     private $_db;
 | 
  
    | 37 |     /**
 | 
  
    | 38 |      * @var object Database object
 | 
  
    | 39 |      */
 | 
  
    | 40 |     private $_oDb = null;
 | 
  
    | 41 |     /**
 | 
  
    | 42 |      *
 | 
  
    | 43 |      * @var string prefix of tables to search for
 | 
  
    | 44 |      */
 | 
  
    | 45 |     private $_TablePrefix;
 | 
  
    | 46 |     /**
 | 
  
    | 47 |      *
 | 
  
    | 48 |      * @var string name of the database
 | 
  
    | 49 |      */
 | 
  
    | 50 |     private $_db_name;
 | 
  
    | 51 |     /**
 | 
  
    | 52 |      *
 | 
  
    | 53 |      * @var array list of unneeded tables.fields
 | 
  
    | 54 |      */
 | 
  
    | 55 |     private $_ControllList;
 | 
  
    | 56 |     private $_ControllListTyp;
 | 
  
    | 57 |     private $_ControllListTypen = array('All','BlackList','WhiteList');
 | 
  
    | 58 | 
 | 
  
    | 59 |     private $_Queries;
 | 
  
    | 60 |     private $_BasePath = '';
 | 
  
    | 61 |     private $_CachePath = '';
 | 
  
    | 62 |     private $_TCacheFile = '';
 | 
  
    | 63 |     private $_DCachePrefix = '';
 | 
  
    | 64 |     private $_bUseCache = true;
 | 
  
    | 65 |     /**
 | 
  
    | 66 |      *
 | 
  
    | 67 |      * @param object $database global database object
 | 
  
    | 68 |      */
 | 
  
    | 69 |     public function __construct()
 | 
  
    | 70 |     {
 | 
  
    | 71 |         $this->_oDb = $GLOBALS['database'];
 | 
  
    | 72 |         $this->_ControllList = array();
 | 
  
    | 73 |         $this->_TCacheFile = 'Ie'.__CLASS__.'CacheTables';
 | 
  
    | 74 |         $this->_DCachePrefix = 'Ie'.__CLASS__.'CacheDir';
 | 
  
    | 75 |         $this->_Queries = array();
 | 
  
    | 76 |     }
 | 
  
    | 77 |     /**
 | 
  
    | 78 |      *
 | 
  
    | 79 |      * @param string $name name of the property
 | 
  
    | 80 |      *        (db_handle, db_name, table_prefix, base_dir, cache_dir, use_cache)
 | 
  
    | 81 |      * @param mixed $value value of the property
 | 
  
    | 82 |      */
 | 
  
    | 83 |     public function  __set($name, $value) {
 | 
  
    | 84 | 
 | 
  
    | 85 |         switch(strtolower($name)):
 | 
  
    | 86 |             case 'db_handle':
 | 
  
    | 87 |                 if($value) { $this->_db = $value; }
 | 
  
    | 88 |                 break;
 | 
  
    | 89 |             case 'db_name':
 | 
  
    | 90 |                 if($value != '') { $this->_db_name = $value; }
 | 
  
    | 91 |                 break;
 | 
  
    | 92 |             case 'table_prefix':
 | 
  
    | 93 |                 if($value != '') { $this->_TablePrefix = $value; }
 | 
  
    | 94 |                 break;
 | 
  
    | 95 |             case 'base_dir':
 | 
  
    | 96 |                 if($value != '') {
 | 
  
    | 97 |                     $this->_BasePath = rtrim(str_replace('\\', '/', $value) , '/');
 | 
  
    | 98 |                 }
 | 
  
    | 99 |                 break;
 | 
  
    | 100 |             case 'cache_dir':
 | 
  
    | 101 |                 $value = rtrim(str_replace('\\', '/', $value) , '/');
 | 
  
    | 102 |                 if(!is_dir($value)) {
 | 
  
    | 103 |                     if(!mkdir($value, 0777, true)) {
 | 
  
    | 104 |                         $this->_CachePath = '';
 | 
  
    | 105 |                         $this->_bUseCache = false;
 | 
  
    | 106 |                         break;
 | 
  
    | 107 |                     }
 | 
  
    | 108 |                 }
 | 
  
    | 109 |                 if(is_writable($value)) {
 | 
  
    | 110 |                     $this->_CachePath = $value;
 | 
  
    | 111 |                     $this->_bUseCache = true;
 | 
  
    | 112 |                 }else {
 | 
  
    | 113 |                     $this->_CachePath = '';
 | 
  
    | 114 |                     $this->_bUseCache = false;
 | 
  
    | 115 |                 }
 | 
  
    | 116 |                 break;
 | 
  
    | 117 |             default:
 | 
  
    | 118 |                 throw new InvalidArgumentException( __CLASS__.'::'.$name );
 | 
  
    | 119 |                 break;
 | 
  
    | 120 |         endswitch;
 | 
  
    | 121 |     }
 | 
  
    | 122 | 
 | 
  
    | 123 |     /**
 | 
  
    | 124 |      * delete all table cache files
 | 
  
    | 125 |      */
 | 
  
    | 126 |     public function clearCache()
 | 
  
    | 127 |     {
 | 
  
    | 128 |         foreach($this->_ControllListTypen as $type) {
 | 
  
    | 129 |             $cFile = $this->_CachePath.'/'.$this->_TCacheFile.$type;
 | 
  
    | 130 |             if(file_exists($cFile)) { @unlink($cFile); }
 | 
  
    | 131 |         }
 | 
  
    | 132 |     }
 | 
  
    | 133 |     /**
 | 
  
    | 134 |      *
 | 
  
    | 135 |      * @param string $blacklist path/filename of the blacklist
 | 
  
    | 136 |      * @param int $type const USE_NO_LIST / USE_BLACKLIST / USE_WHITELIST
 | 
  
    | 137 |      * @return bool false if no or empty list is available
 | 
  
    | 138 |      */
 | 
  
    | 139 |     public function addControllList($sControllList, $type = self::USE_BLACKLIST)
 | 
  
    | 140 |     {
 | 
  
    | 141 |         $this->_ControllList = array();
 | 
  
    | 142 |         $this->_ControllListTyp = $type;
 | 
  
    | 143 |         if(is_readable($sControllList)) {
 | 
  
    | 144 |             if(($list = file($sControllList, FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES)) !== false)
 | 
  
    | 145 |             {
 | 
  
    | 146 |                 $list = preg_grep('/^\s*?[^#;]/', $list);
 | 
  
    | 147 |                 $this->_ControllList = preg_replace('/^\s*?(.*)\s*?$/', $this->_TablePrefix.'$1', $list);
 | 
  
    | 148 |                 unset($list);
 | 
  
    | 149 |             }
 | 
  
    | 150 |         }else {
 | 
  
    | 151 |             $this->_ControllListTyp = self::USE_ALL;
 | 
  
    | 152 |         }
 | 
  
    | 153 |         if(($type == self::USE_BLACKLIST) && (sizeof($this->_ControllList) > 0)) {
 | 
  
    | 154 |             $this->_ControllListTyp = self::USE_ALL;
 | 
  
    | 155 |         }
 | 
  
    | 156 |         return (sizeof($this->_ControllList) > 0);
 | 
  
    | 157 |     }
 | 
  
    | 158 |     /**
 | 
  
    | 159 |      *
 | 
  
    | 160 |      * @param string $sDirToSearch directory to scan (relative to base_dir)
 | 
  
    | 161 |      * @param integer $bRetunMode select matching or unmatching files
 | 
  
    | 162 |      * @return array list of matching files
 | 
  
    | 163 |      */
 | 
  
    | 164 |     public function getMatchesFromDir($sDirToSearch, $bRetunMode = self::RETURN_USED)
 | 
  
    | 165 |     {
 | 
  
    | 166 |         $aResultFileList = array();
 | 
  
    | 167 |         $aNewFileList = array();
 | 
  
    | 168 |         $sDirToSearch = trim(str_replace('\\', '/', $sDirToSearch) , '/');
 | 
  
    | 169 |         $sPathToSearch = $this->_BasePath.'/'.$sDirToSearch;
 | 
  
    | 170 |         $sCacheFile = $this->_DCachePrefix.$bRetunMode.urlencode('/'.$sDirToSearch);
 | 
  
    | 171 |         $sCacheFile = $this->_CachePath.'/'.$sCacheFile;
 | 
  
    | 172 |         if(sizeof($this->_Queries) <= 0) { $this->_getTableQueries(); }
 | 
  
    | 173 |         // read fileList from directory
 | 
  
    | 174 |         try{
 | 
  
    | 175 |             foreach( new DirectoryIterator($sPathToSearch) as $fileinfo ) {
 | 
  
    | 176 |             // at first collect all files from target directory
 | 
  
    | 177 |                 $fileName = $fileinfo->getFilename();
 | 
  
    | 178 |                 if(($fileinfo->isFile()) &&
 | 
  
    | 179 |                    (!$fileinfo->isDot()) &&
 | 
  
    | 180 |                    ($fileinfo->getFilename() != 'index.php')) {
 | 
  
    | 181 |                    $aNewFileList[] = $fileinfo->getFilename();
 | 
  
    | 182 |                 }
 | 
  
    | 183 |             }
 | 
  
    | 184 |         }catch(UnexpectedValueException $e) {}
 | 
  
    | 185 |         // make checksum of current directory
 | 
  
    | 186 |         $bCacheValid = false;
 | 
  
    | 187 |         if($this->_bUseCache) {
 | 
  
    | 188 |             $checkSum = crc32(serialize($aNewFileList));
 | 
  
    | 189 |             if(is_readable($sCacheFile)){
 | 
  
    | 190 |             // read cachefile if available
 | 
  
    | 191 |                 $aResultFileList = unserialize(file_get_contents($sCacheFile));
 | 
  
    | 192 |                 if($checkSum == array_shift($aResultFileList)) {
 | 
  
    | 193 |                 // compare new checksum against checksum from cachefile
 | 
  
    | 194 |                     $bCacheValid = true;
 | 
  
    | 195 |                 }
 | 
  
    | 196 |             }
 | 
  
    | 197 |         }
 | 
  
    | 198 |         if(!$bCacheValid) {
 | 
  
    | 199 |         // skip this loop if valid cache is available
 | 
  
    | 200 |             $aResultFileList = array();
 | 
  
    | 201 |             while (list( , $sFilename) = each($aNewFileList)) {
 | 
  
    | 202 |                 // iterate all tables and search for filename
 | 
  
    | 203 |                 if( $this->_getMatch($sDirToSearch.'/'.$sFilename) !== false) {
 | 
  
    | 204 |                     if($bRetunMode == self::RETURN_USED) { $aResultFileList[] = $sFilename; }
 | 
  
    | 205 |                 }else {
 | 
  
    | 206 |                     if($bRetunMode == self::RETURN_UNUSED) { $aResultFileList[] = $sFilename; }
 | 
  
    | 207 |                 }
 | 
  
    | 208 |             }
 | 
  
    | 209 |             // calculate new checksum
 | 
  
    | 210 |             $newCheckSum = crc32(serialize($aResultFileList));
 | 
  
    | 211 |             // add checksum to array
 | 
  
    | 212 |             array_unshift($aResultFileList,  $newCheckSum);
 | 
  
    | 213 |             // try to write serialized array into new cachefile
 | 
  
    | 214 |             if(file_put_contents($sCacheFile, serialize($aResultFileList)) === false) {
 | 
  
    | 215 |                 throw new RuntimeException();
 | 
  
    | 216 |             }
 | 
  
    | 217 |             // remove checksum again
 | 
  
    | 218 |             array_shift($aResultFileList);
 | 
  
    | 219 |         }
 | 
  
    | 220 |         unset($aNewFileList);
 | 
  
    | 221 |         return $aResultFileList;
 | 
  
    | 222 |     }
 | 
  
    | 223 |     /**
 | 
  
    | 224 |      *
 | 
  
    | 225 |      * @param <type> $sFilename
 | 
  
    | 226 |      * @return bool true if file found in db
 | 
  
    | 227 |      */
 | 
  
    | 228 |     private function _getMatch($sFilename)
 | 
  
    | 229 |     {
 | 
  
    | 230 |         $result = 0;
 | 
  
    | 231 |         $sFilename = str_replace('_', '\_', $sFilename);
 | 
  
    | 232 |         $sSearch = '%'.str_replace('/', '_', $sFilename).'%';
 | 
  
    | 233 |         while (list( , $sQuery) = each($this->_Queries)) {
 | 
  
    | 234 |             $sql = sprintf($sQuery, $sSearch);
 | 
  
    | 235 |             if( ($res = $this->_oDb->query($sql)) ) {
 | 
  
    | 236 |                 if( ($result = intval($res->fetchRow(MYSQL_ASSOC))) > 0 )  { break; }
 | 
  
    | 237 |             }
 | 
  
    | 238 |         }
 | 
  
    | 239 |         return ($result != 0);
 | 
  
    | 240 |     }
 | 
  
    | 241 |     /**
 | 
  
    | 242 |      *
 | 
  
    | 243 |      */
 | 
  
    | 244 |     private function _getTableQueries()
 | 
  
    | 245 |     {
 | 
  
    | 246 |         if($this->_bUseCache) {
 | 
  
    | 247 |         // try to read queries from cace
 | 
  
    | 248 |             $sCacheFile = $this->_CachePath.'/'.$this->_TCacheFile.$this->_ControllListTypen[$this->_ControllListTyp];
 | 
  
    | 249 |             try {
 | 
  
    | 250 |                 if(is_readable($sCacheFile)) {
 | 
  
    | 251 |                     $this->_Queries = unserialize(file_get_contents($sCacheFile));
 | 
  
    | 252 |                 }
 | 
  
    | 253 |             }catch(Exception $e) {
 | 
  
    | 254 |                 $this->_Queries = array();
 | 
  
    | 255 |             }
 | 
  
    | 256 |         }
 | 
  
    | 257 |         if(sizeof($this->_Queries) > 0) { return; } // queries alreade loaded from cache
 | 
  
    | 258 |         $TP = str_replace('_','\_', $this->_TablePrefix);
 | 
  
    | 259 |         $sql  = 'SELECT TABLE_NAME `table`, COLUMN_NAME `column` ';
 | 
  
    | 260 |         $sql .= 'FROM INFORMATION_SCHEMA.COLUMNS ';
 | 
  
    | 261 |         $sql .= 'WHERE `table_schema` = \''.$this->_db_name.'\' AND ';
 | 
  
    | 262 |         $sql .=        '`table_name` LIKE \''.$TP.'%\' AND ';
 | 
  
    | 263 |         $sql .=        '(`data_type` LIKE \'%text\' OR ';
 | 
  
    | 264 |         $sql .=           '(`data_type` = \'varchar\' AND `character_maximum_length` > 20)';
 | 
  
    | 265 |         $sql .=        ')' ;
 | 
  
    | 266 |         $sql .= 'ORDER BY `table`, `column`';
 | 
  
    | 267 |         if(($res = $this->_oDb->query($sql))) {
 | 
  
    | 268 |             $lastTable = '';
 | 
  
    | 269 |             $aOrStatements = array();
 | 
  
    | 270 |             $sPrefix = '';
 | 
  
    | 271 |             while($rec = $res->fetchRow(MYSQL_ASSOC))
 | 
  
    | 272 |             { // loop through all found tables/fields
 | 
  
    | 273 |                 $sTableColumn = $rec['table'].'.'.$rec['column'];
 | 
  
    | 274 |                 switch($this->_ControllListTyp):
 | 
  
    | 275 |                 // test against controll list
 | 
  
    | 276 |                     case self::USE_BLACKLIST:
 | 
  
    | 277 |                         $needRecord = true;
 | 
  
    | 278 |                         if(in_array($rec['table'], $this->_ControllList) ||
 | 
  
    | 279 |                            in_array($sTableColumn, $this->_ControllList))
 | 
  
    | 280 |                         {
 | 
  
    | 281 |                             $needRecord = false;
 | 
  
    | 282 |                         }
 | 
  
    | 283 |                         break;
 | 
  
    | 284 |                     case self::USE_WHITELIST:
 | 
  
    | 285 |                         $needRecord = false;
 | 
  
    | 286 |                         if(in_array($rec['table'], $this->_ControllList) ||
 | 
  
    | 287 |                            in_array($sTableColumn, $this->_ControllList))
 | 
  
    | 288 |                         {
 | 
  
    | 289 |                             $needRecord = true;
 | 
  
    | 290 |                         }
 | 
  
    | 291 |                         break;
 | 
  
    | 292 |                     default: // self::USE_ALL
 | 
  
    | 293 |                         $needRecord = true;
 | 
  
    | 294 |                         break;
 | 
  
    | 295 |                 endswitch;
 | 
  
    | 296 |                 if($needRecord) {
 | 
  
    | 297 |                     if($lastTable != $rec['table']) {
 | 
  
    | 298 |                         if(sizeof($aOrStatements)!= 0){
 | 
  
    | 299 |                         // close previous table
 | 
  
    | 300 |                             $this->_Queries[] = $sPrefix.implode(') OR (', $aOrStatements).')';
 | 
  
    | 301 |                         }
 | 
  
    | 302 |                     // start a new table
 | 
  
    | 303 |                         $sPrefix = 'SELECT COUNT(*) `count` FROM `'.$rec['table'].'` WHERE( ';
 | 
  
    | 304 |                         $aOrStatements = array();
 | 
  
    | 305 |                         $lastTable = $rec['table'];
 | 
  
    | 306 |                     }
 | 
  
    | 307 |                     // add table.column to query
 | 
  
    | 308 |                     $aOrStatements[] = '`'.$rec['table'].'`.`'.$rec['column'].'` LIKE \'%1$s\'';
 | 
  
    | 309 |                 }
 | 
  
    | 310 |             }
 | 
  
    | 311 |             if(sizeof($aOrStatements)!= 0){
 | 
  
    | 312 |             // close last table
 | 
  
    | 313 |                 $this->_Queries[] = $sPrefix.implode(') OR (', $aOrStatements).')';
 | 
  
    | 314 |             }
 | 
  
    | 315 |             unset($res);
 | 
  
    | 316 |         }
 | 
  
    | 317 |         if($this->_bUseCache) {
 | 
  
    | 318 |         // try to write queries into the cache
 | 
  
    | 319 |             if(file_put_contents($sCacheFile, serialize($this->_Queries)) === false) {
 | 
  
    | 320 |                 throw new RuntimeException('unable to write file ['.$sCacheFile.']');
 | 
  
    | 321 |             }
 | 
  
    | 322 |         }
 | 
  
    | 323 |     }
 | 
  
    | 324 | 
 | 
  
    | 325 | }
 |