@@ -1,181 +1,181 @@  | 
                                                    ||
| 1 | 1 | <?php  | 
                                                        
| 2 | 2 |  class WhitelistGenerator extends Object implements Flushable { | 
                                                        
| 3 | 3 | |
| 4 | -	public static function generateWhitelist(){ | 
                                                        |
| 5 | - $whitelist = self::generateWhitelistRules();  | 
                                                        |
| 6 | - self::syncCacheFilesystem($whitelist);  | 
                                                        |
| 7 | - }  | 
                                                        |
| 8 | -  | 
                                                        |
| 9 | -	public static function generateWhitelistRules(){ | 
                                                        |
| 10 | - //get all URL rules  | 
                                                        |
| 11 | -		$rules = Config::inst()->get('Director', 'rules'); | 
                                                        |
| 12 | -  | 
                                                        |
| 13 | - $allTopLevelRules = array();  | 
                                                        |
| 14 | -		foreach ($rules as $pattern => $controllerOptions) { | 
                                                        |
| 15 | - //allow for route rules starting with double-slash (//)  | 
                                                        |
| 16 | - $pattern = ltrim($pattern, '/!');  | 
                                                        |
| 17 | -  | 
                                                        |
| 18 | - //match first portion of the URL, either delimited by slash or colon or end-of-line  | 
                                                        |
| 19 | -			if (preg_match('/^(.*?)(\/|:|$)/', $pattern, $matches)){ | 
                                                        |
| 20 | -				if (!empty($matches[1])){ | 
                                                        |
| 21 | - array_push($allTopLevelRules, $matches[1]);  | 
                                                        |
| 22 | - }  | 
                                                        |
| 23 | - }  | 
                                                        |
| 24 | - }  | 
                                                        |
| 25 | -		$filteredRules = array('home'); //add 'home' url, as default | 
                                                        |
| 4 | +    public static function generateWhitelist(){ | 
                                                        |
| 5 | + $whitelist = self::generateWhitelistRules();  | 
                                                        |
| 6 | + self::syncCacheFilesystem($whitelist);  | 
                                                        |
| 7 | + }  | 
                                                        |
| 8 | +  | 
                                                        |
| 9 | +    public static function generateWhitelistRules(){ | 
                                                        |
| 10 | + //get all URL rules  | 
                                                        |
| 11 | +        $rules = Config::inst()->get('Director', 'rules'); | 
                                                        |
| 12 | +  | 
                                                        |
| 13 | + $allTopLevelRules = array();  | 
                                                        |
| 14 | +        foreach ($rules as $pattern => $controllerOptions) { | 
                                                        |
| 15 | + //allow for route rules starting with double-slash (//)  | 
                                                        |
| 16 | + $pattern = ltrim($pattern, '/!');  | 
                                                        |
| 17 | +  | 
                                                        |
| 18 | + //match first portion of the URL, either delimited by slash or colon or end-of-line  | 
                                                        |
| 19 | +            if (preg_match('/^(.*?)(\/|:|$)/', $pattern, $matches)){ | 
                                                        |
| 20 | +                if (!empty($matches[1])){ | 
                                                        |
| 21 | + array_push($allTopLevelRules, $matches[1]);  | 
                                                        |
| 22 | + }  | 
                                                        |
| 23 | + }  | 
                                                        |
| 24 | + }  | 
                                                        |
| 25 | +        $filteredRules = array('home'); //add 'home' url, as default | 
                                                        |
| 26 | 26 | |
| 27 | -		$addToWhitelist = Config::inst()->get('WhitelistGenerator', 'addToWhitelist'); | 
                                                        |
| 28 | -		if ($addToWhitelist && is_array($addToWhitelist)) { | 
                                                        |
| 29 | - $filteredRules = array_merge($filteredRules, $addToWhitelist);  | 
                                                        |
| 30 | - }  | 
                                                        |
| 27 | +        $addToWhitelist = Config::inst()->get('WhitelistGenerator', 'addToWhitelist'); | 
                                                        |
| 28 | +        if ($addToWhitelist && is_array($addToWhitelist)) { | 
                                                        |
| 29 | + $filteredRules = array_merge($filteredRules, $addToWhitelist);  | 
                                                        |
| 30 | + }  | 
                                                        |
| 31 | 31 | |
| 32 | -		foreach($allTopLevelRules as $rule) { | 
                                                        |
| 33 | -			if (strpos($rule, '$') !== false) { | 
                                                        |
| 34 | -				if ($rule === '$Controller') { | 
                                                        |
| 35 | - //special case for Controllers, add all possible controllers  | 
                                                        |
| 36 | - $subControllers = ClassInfo::subclassesFor(new Controller());  | 
                                                        |
| 37 | -  | 
                                                        |
| 38 | -					foreach ($subControllers as $controller){ | 
                                                        |
| 39 | - array_push($filteredRules, $controller); //add the controller name as a link  | 
                                                        |
| 40 | - }  | 
                                                        |
| 41 | -  | 
                                                        |
| 42 | -				} elseif ($rule === '$URLSegment') { | 
                                                        |
| 43 | - $topLevelPagesArray = array(); //temporary array to store top-level pages  | 
                                                        |
| 44 | -  | 
                                                        |
| 45 | - //special case for SiteTree, add all possible top Level Pages  | 
                                                        |
| 46 | -					$topLevelPages = SiteTree::get()->filter('ParentID', 0); | 
                                                        |
| 47 | -  | 
                                                        |
| 48 | -					foreach ($topLevelPages as $page) { | 
                                                        |
| 49 | - $link = $page->RelativeLink();  | 
                                                        |
| 50 | - array_push($topLevelPagesArray, trim($link, '\/ ')); //remove trailing or leading slashes from links  | 
                                                        |
| 51 | - }  | 
                                                        |
| 52 | -  | 
                                                        |
| 53 | - //fetch old top-level pages URLs from the SiteTree_versions table  | 
                                                        |
| 54 | -					if (Config::inst()->get('WhitelistGenerator', 'includeSiteTreeVersions')) { | 
                                                        |
| 55 | - $oldTopLevelPagesArray = self::find_old_top_level_pages($topLevelPagesArray);  | 
                                                        |
| 56 | - }  | 
                                                        |
| 57 | -  | 
                                                        |
| 58 | - $filteredRules = array_merge($filteredRules, $topLevelPagesArray, $oldTopLevelPagesArray);  | 
                                                        |
| 59 | -				} else { | 
                                                        |
| 60 | -					user_error('Unknown wildcard URL match found: '.$rule, E_WARNING); | 
                                                        |
| 61 | - }  | 
                                                        |
| 62 | -			} else { | 
                                                        |
| 63 | - //add the rule to a new list of rules  | 
                                                        |
| 64 | - array_push($filteredRules, $rule);  | 
                                                        |
| 65 | - }  | 
                                                        |
| 66 | -  | 
                                                        |
| 67 | - }  | 
                                                        |
| 68 | -  | 
                                                        |
| 69 | - //filter duplicates (order doesn't matter here, as we are only interested in the first level of the rules)  | 
                                                        |
| 70 | - $filteredRules = array_unique($filteredRules);  | 
                                                        |
| 71 | -  | 
                                                        |
| 72 | -		$removeFromWhitelist = Config::inst()->get('WhitelistGenerator', 'removeFromWhitelist'); | 
                                                        |
| 73 | -		if ($removeFromWhitelist && is_array($removeFromWhitelist)) { | 
                                                        |
| 74 | - $filteredRules = array_merge(array_diff($filteredRules, $removeFromWhitelist));  | 
                                                        |
| 75 | - }  | 
                                                        |
| 76 | -  | 
                                                        |
| 77 | - return $filteredRules;  | 
                                                        |
| 78 | - }  | 
                                                        |
| 79 | -  | 
                                                        |
| 80 | -	protected static function array_delete($array, $element) { | 
                                                        |
| 32 | +        foreach($allTopLevelRules as $rule) { | 
                                                        |
| 33 | +            if (strpos($rule, '$') !== false) { | 
                                                        |
| 34 | +                if ($rule === '$Controller') { | 
                                                        |
| 35 | + //special case for Controllers, add all possible controllers  | 
                                                        |
| 36 | + $subControllers = ClassInfo::subclassesFor(new Controller());  | 
                                                        |
| 37 | +  | 
                                                        |
| 38 | +                    foreach ($subControllers as $controller){ | 
                                                        |
| 39 | + array_push($filteredRules, $controller); //add the controller name as a link  | 
                                                        |
| 40 | + }  | 
                                                        |
| 41 | +  | 
                                                        |
| 42 | +                } elseif ($rule === '$URLSegment') { | 
                                                        |
| 43 | + $topLevelPagesArray = array(); //temporary array to store top-level pages  | 
                                                        |
| 44 | +  | 
                                                        |
| 45 | + //special case for SiteTree, add all possible top Level Pages  | 
                                                        |
| 46 | +                    $topLevelPages = SiteTree::get()->filter('ParentID', 0); | 
                                                        |
| 47 | +  | 
                                                        |
| 48 | +                    foreach ($topLevelPages as $page) { | 
                                                        |
| 49 | + $link = $page->RelativeLink();  | 
                                                        |
| 50 | + array_push($topLevelPagesArray, trim($link, '\/ ')); //remove trailing or leading slashes from links  | 
                                                        |
| 51 | + }  | 
                                                        |
| 52 | +  | 
                                                        |
| 53 | + //fetch old top-level pages URLs from the SiteTree_versions table  | 
                                                        |
| 54 | +                    if (Config::inst()->get('WhitelistGenerator', 'includeSiteTreeVersions')) { | 
                                                        |
| 55 | + $oldTopLevelPagesArray = self::find_old_top_level_pages($topLevelPagesArray);  | 
                                                        |
| 56 | + }  | 
                                                        |
| 57 | +  | 
                                                        |
| 58 | + $filteredRules = array_merge($filteredRules, $topLevelPagesArray, $oldTopLevelPagesArray);  | 
                                                        |
| 59 | +                } else { | 
                                                        |
| 60 | +                    user_error('Unknown wildcard URL match found: '.$rule, E_WARNING); | 
                                                        |
| 61 | + }  | 
                                                        |
| 62 | +            } else { | 
                                                        |
| 63 | + //add the rule to a new list of rules  | 
                                                        |
| 64 | + array_push($filteredRules, $rule);  | 
                                                        |
| 65 | + }  | 
                                                        |
| 66 | +  | 
                                                        |
| 67 | + }  | 
                                                        |
| 68 | +  | 
                                                        |
| 69 | + //filter duplicates (order doesn't matter here, as we are only interested in the first level of the rules)  | 
                                                        |
| 70 | + $filteredRules = array_unique($filteredRules);  | 
                                                        |
| 71 | +  | 
                                                        |
| 72 | +        $removeFromWhitelist = Config::inst()->get('WhitelistGenerator', 'removeFromWhitelist'); | 
                                                        |
| 73 | +        if ($removeFromWhitelist && is_array($removeFromWhitelist)) { | 
                                                        |
| 74 | + $filteredRules = array_merge(array_diff($filteredRules, $removeFromWhitelist));  | 
                                                        |
| 75 | + }  | 
                                                        |
| 76 | +  | 
                                                        |
| 77 | + return $filteredRules;  | 
                                                        |
| 78 | + }  | 
                                                        |
| 79 | +  | 
                                                        |
| 80 | +    protected static function array_delete($array, $element) { | 
                                                        |
| 81 | 81 | $elementArray = array($element);  | 
                                                        
| 82 | - return array_diff($array, $elementArray);  | 
                                                        |
| 83 | - }  | 
                                                        |
| 84 | -  | 
                                                        |
| 85 | - /**  | 
                                                        |
| 86 | - * Sync the list of all top-level routes with the file system whitelist cache  | 
                                                        |
| 87 | - */  | 
                                                        |
| 88 | -	protected static function syncCacheFilesystem($whitelist) { | 
                                                        |
| 89 | -		$dir = BASE_PATH . DIRECTORY_SEPARATOR . Config::inst()->get('WhitelistGenerator', 'dir'); | 
                                                        |
| 90 | -  | 
                                                        |
| 91 | - $whitelistFolderContents = scandir($dir);  | 
                                                        |
| 92 | -  | 
                                                        |
| 93 | - //create list of files to create  | 
                                                        |
| 94 | - $toCreate = array();  | 
                                                        |
| 95 | -		foreach ($whitelist as $listItem){ | 
                                                        |
| 96 | -			if (!in_array($listItem, $whitelistFolderContents)) { | 
                                                        |
| 97 | -				if (!empty($listItem)) {    //don't include empty files, such as the file for / | 
                                                        |
| 98 | - array_push($toCreate, $listItem);  | 
                                                        |
| 99 | - }  | 
                                                        |
| 100 | - }  | 
                                                        |
| 101 | - }  | 
                                                        |
| 102 | -  | 
                                                        |
| 103 | - //create list of files to delete  | 
                                                        |
| 104 | - $toDelete = array();  | 
                                                        |
| 105 | -		foreach ($whitelistFolderContents as $file){ | 
                                                        |
| 106 | -			if (!in_array($file, array('','..','.','.htaccess'))) {    //exclude things that should stay in the folder | 
                                                        |
| 107 | -				if (!in_array($file, $whitelist)) { | 
                                                        |
| 108 | - array_push($toDelete, $file);  | 
                                                        |
| 109 | - }  | 
                                                        |
| 110 | - }  | 
                                                        |
| 111 | - }  | 
                                                        |
| 112 | -  | 
                                                        |
| 113 | - //delete files which are no longer necessary  | 
                                                        |
| 114 | -		foreach ($toDelete as $delete) { | 
                                                        |
| 115 | - unlink($dir . DIRECTORY_SEPARATOR . $delete);  | 
                                                        |
| 116 | - }  | 
                                                        |
| 117 | -  | 
                                                        |
| 118 | - //create new whitelist items as files  | 
                                                        |
| 119 | -		foreach ($toCreate as $create) { | 
                                                        |
| 120 | - touch($dir . DIRECTORY_SEPARATOR . $create);  | 
                                                        |
| 121 | - }  | 
                                                        |
| 122 | - }  | 
                                                        |
| 123 | -  | 
                                                        |
| 124 | - /**  | 
                                                        |
| 125 | - * Does a database query searching through past URLs of top-level pages, returning any URLs previously used for  | 
                                                        |
| 126 | - * pages in the SiteTree. This is to ensure that OldPageRedirector rules still apply correctly. That is, to ensure  | 
                                                        |
| 127 | - * that pages that have been renamed continue to redirect to their current versions, we add the pages' old URLs  | 
                                                        |
| 128 | - * to the whitelist.  | 
                                                        |
| 129 | - * @param $currentTopLevelPages  | 
                                                        |
| 130 | - * @return array URLs of past top-level pages  | 
                                                        |
| 131 | - */  | 
                                                        |
| 132 | -	protected static function find_old_top_level_pages($currentTopLevelPages){ | 
                                                        |
| 133 | - $oldPageURLs = array();  | 
                                                        |
| 134 | -  | 
                                                        |
| 135 | - $queryClass = 'SQLSelect';  | 
                                                        |
| 136 | -		if (!class_exists($queryClass) && class_exists('SQLQuery')){ | 
                                                        |
| 137 | - $queryClass = 'SQLQuery';  | 
                                                        |
| 138 | - }  | 
                                                        |
| 82 | + return array_diff($array, $elementArray);  | 
                                                        |
| 83 | + }  | 
                                                        |
| 84 | +  | 
                                                        |
| 85 | + /**  | 
                                                        |
| 86 | + * Sync the list of all top-level routes with the file system whitelist cache  | 
                                                        |
| 87 | + */  | 
                                                        |
| 88 | +    protected static function syncCacheFilesystem($whitelist) { | 
                                                        |
| 89 | +        $dir = BASE_PATH . DIRECTORY_SEPARATOR . Config::inst()->get('WhitelistGenerator', 'dir'); | 
                                                        |
| 90 | +  | 
                                                        |
| 91 | + $whitelistFolderContents = scandir($dir);  | 
                                                        |
| 92 | +  | 
                                                        |
| 93 | + //create list of files to create  | 
                                                        |
| 94 | + $toCreate = array();  | 
                                                        |
| 95 | +        foreach ($whitelist as $listItem){ | 
                                                        |
| 96 | +            if (!in_array($listItem, $whitelistFolderContents)) { | 
                                                        |
| 97 | +                if (!empty($listItem)) {    //don't include empty files, such as the file for / | 
                                                        |
| 98 | + array_push($toCreate, $listItem);  | 
                                                        |
| 99 | + }  | 
                                                        |
| 100 | + }  | 
                                                        |
| 101 | + }  | 
                                                        |
| 102 | +  | 
                                                        |
| 103 | + //create list of files to delete  | 
                                                        |
| 104 | + $toDelete = array();  | 
                                                        |
| 105 | +        foreach ($whitelistFolderContents as $file){ | 
                                                        |
| 106 | +            if (!in_array($file, array('','..','.','.htaccess'))) {    //exclude things that should stay in the folder | 
                                                        |
| 107 | +                if (!in_array($file, $whitelist)) { | 
                                                        |
| 108 | + array_push($toDelete, $file);  | 
                                                        |
| 109 | + }  | 
                                                        |
| 110 | + }  | 
                                                        |
| 111 | + }  | 
                                                        |
| 112 | +  | 
                                                        |
| 113 | + //delete files which are no longer necessary  | 
                                                        |
| 114 | +        foreach ($toDelete as $delete) { | 
                                                        |
| 115 | + unlink($dir . DIRECTORY_SEPARATOR . $delete);  | 
                                                        |
| 116 | + }  | 
                                                        |
| 117 | +  | 
                                                        |
| 118 | + //create new whitelist items as files  | 
                                                        |
| 119 | +        foreach ($toCreate as $create) { | 
                                                        |
| 120 | + touch($dir . DIRECTORY_SEPARATOR . $create);  | 
                                                        |
| 121 | + }  | 
                                                        |
| 122 | + }  | 
                                                        |
| 123 | +  | 
                                                        |
| 124 | + /**  | 
                                                        |
| 125 | + * Does a database query searching through past URLs of top-level pages, returning any URLs previously used for  | 
                                                        |
| 126 | + * pages in the SiteTree. This is to ensure that OldPageRedirector rules still apply correctly. That is, to ensure  | 
                                                        |
| 127 | + * that pages that have been renamed continue to redirect to their current versions, we add the pages' old URLs  | 
                                                        |
| 128 | + * to the whitelist.  | 
                                                        |
| 129 | + * @param $currentTopLevelPages  | 
                                                        |
| 130 | + * @return array URLs of past top-level pages  | 
                                                        |
| 131 | + */  | 
                                                        |
| 132 | +    protected static function find_old_top_level_pages($currentTopLevelPages){ | 
                                                        |
| 133 | + $oldPageURLs = array();  | 
                                                        |
| 134 | +  | 
                                                        |
| 135 | + $queryClass = 'SQLSelect';  | 
                                                        |
| 136 | +        if (!class_exists($queryClass) && class_exists('SQLQuery')){ | 
                                                        |
| 137 | + $queryClass = 'SQLQuery';  | 
                                                        |
| 138 | + }  | 
                                                        |
| 139 | 139 | |
| 140 | - $query = new $queryClass(  | 
                                                        |
| 141 | - 'DISTINCT (URLSegment)',  | 
                                                        |
| 142 | - 'SiteTree_versions',  | 
                                                        |
| 143 | - array(  | 
                                                        |
| 144 | - 'ParentID = 0',  | 
                                                        |
| 145 | - 'WasPublished = 1',  | 
                                                        |
| 146 | -				'URLSegment NOT IN (\''.implode("','",array_filter($currentTopLevelPages)).'\')' | 
                                                        |
| 147 | - )  | 
                                                        |
| 148 | - );  | 
                                                        |
| 149 | -  | 
                                                        |
| 150 | - $records = $query->execute();  | 
                                                        |
| 151 | -		if ($records) { | 
                                                        |
| 152 | -			foreach($records as $record) { | 
                                                        |
| 153 | - array_push($oldPageURLs, $record['URLSegment']);  | 
                                                        |
| 154 | - }  | 
                                                        |
| 155 | - }  | 
                                                        |
| 156 | -  | 
                                                        |
| 157 | - return $oldPageURLs;  | 
                                                        |
| 158 | - }  | 
                                                        |
| 140 | + $query = new $queryClass(  | 
                                                        |
| 141 | + 'DISTINCT (URLSegment)',  | 
                                                        |
| 142 | + 'SiteTree_versions',  | 
                                                        |
| 143 | + array(  | 
                                                        |
| 144 | + 'ParentID = 0',  | 
                                                        |
| 145 | + 'WasPublished = 1',  | 
                                                        |
| 146 | +                'URLSegment NOT IN (\''.implode("','",array_filter($currentTopLevelPages)).'\')' | 
                                                        |
| 147 | + )  | 
                                                        |
| 148 | + );  | 
                                                        |
| 149 | +  | 
                                                        |
| 150 | + $records = $query->execute();  | 
                                                        |
| 151 | +        if ($records) { | 
                                                        |
| 152 | +            foreach($records as $record) { | 
                                                        |
| 153 | + array_push($oldPageURLs, $record['URLSegment']);  | 
                                                        |
| 154 | + }  | 
                                                        |
| 155 | + }  | 
                                                        |
| 156 | +  | 
                                                        |
| 157 | + return $oldPageURLs;  | 
                                                        |
| 158 | + }  | 
                                                        |
| 159 | 159 | |
| 160 | 160 |      public static function ensureWhitelistFolderExists(){ | 
                                                        
| 161 | -		$dir = BASE_PATH . DIRECTORY_SEPARATOR . Config::inst()->get('WhitelistGenerator', 'dir'); | 
                                                        |
| 162 | -		if (!file_exists($dir)) { | 
                                                        |
| 163 | - mkdir($dir); //create a new whitelist dir  | 
                                                        |
| 164 | - chmod($dir,0777); //make sure it is readable by the web-server user  | 
                                                        |
| 165 | - //create a htaccess file to ensure that the whitelist cache directory is not web-accessible  | 
                                                        |
| 166 | - file_put_contents($dir.DIRECTORY_SEPARATOR.'.htaccess', "Deny from all\n");  | 
                                                        |
| 167 | - }  | 
                                                        |
| 168 | - }  | 
                                                        |
| 169 | -  | 
                                                        |
| 170 | -	public static function clearWhitelist(){ | 
                                                        |
| 171 | -		$dir = BASE_PATH . DIRECTORY_SEPARATOR . Config::inst()->get('WhitelistGenerator', 'dir'); | 
                                                        |
| 172 | -		if (file_exists($dir)) { | 
                                                        |
| 173 | -			array_map('unlink', glob($dir."/*")); | 
                                                        |
| 174 | - }  | 
                                                        |
| 175 | - }  | 
                                                        |
| 176 | -  | 
                                                        |
| 177 | -	public static function flush() { | 
                                                        |
| 178 | - self::ensureWhitelistFolderExists(); //only create folder on flush, not on sitetree changes  | 
                                                        |
| 179 | - self::generateWhitelist();  | 
                                                        |
| 180 | - }  | 
                                                        |
| 161 | +        $dir = BASE_PATH . DIRECTORY_SEPARATOR . Config::inst()->get('WhitelistGenerator', 'dir'); | 
                                                        |
| 162 | +        if (!file_exists($dir)) { | 
                                                        |
| 163 | + mkdir($dir); //create a new whitelist dir  | 
                                                        |
| 164 | + chmod($dir,0777); //make sure it is readable by the web-server user  | 
                                                        |
| 165 | + //create a htaccess file to ensure that the whitelist cache directory is not web-accessible  | 
                                                        |
| 166 | + file_put_contents($dir.DIRECTORY_SEPARATOR.'.htaccess', "Deny from all\n");  | 
                                                        |
| 167 | + }  | 
                                                        |
| 168 | + }  | 
                                                        |
| 169 | +  | 
                                                        |
| 170 | +    public static function clearWhitelist(){ | 
                                                        |
| 171 | +        $dir = BASE_PATH . DIRECTORY_SEPARATOR . Config::inst()->get('WhitelistGenerator', 'dir'); | 
                                                        |
| 172 | +        if (file_exists($dir)) { | 
                                                        |
| 173 | +            array_map('unlink', glob($dir."/*")); | 
                                                        |
| 174 | + }  | 
                                                        |
| 175 | + }  | 
                                                        |
| 176 | +  | 
                                                        |
| 177 | +    public static function flush() { | 
                                                        |
| 178 | + self::ensureWhitelistFolderExists(); //only create folder on flush, not on sitetree changes  | 
                                                        |
| 179 | + self::generateWhitelist();  | 
                                                        |
| 180 | + }  | 
                                                        |
| 181 | 181 | }  |