wire()->addHookAfter('ProcessWire::ready', $this, 'executeAtReady'); return 0; // indicates we will update system version ourselves when ready } public function executeAtReady() { $this->auto = true; if(!$this->update()) return; $this->message( "Details: $this->detailsUrl", Notice::allowMarkup ); $this->updater->saveSystemVersion(17); } /** * Apply the update * * @return bool * @throws WireException * */ public function update() { if(!$this->isApache()) { $this->warning( "Update skipped because Apache not detected. " . "Please see this post for details on how to apply this update manually.", Notice::allowMarkup | Notice::noGroup ); return true; } $blockAll = "\n\n Require all denied\n" . // Apache 2.4+ "\n\n Order allow,deny\n Deny from all\n"; // Prior to Apache 2.4 $denyAll = str_replace("\n", "\n ", $blockAll); // indented blockAll $rules = array( 'all' => array( 'label' => 'block all access', 'content' => ltrim($blockAll), ), 'php' => array( 'label' => 'block all PHP files', 'content' => "$denyAll\n", ), 'rifc' => array( 'label' => 'block some PHP files', 'content' => "$denyAll\n", ) ); $cachePath = $this->wire('config')->paths->cache; $siteRootDir = rtrim($this->wire('config')->paths->site, '/'); $siteRootUrl = rtrim($this->wire('config')->urls->site, '/'); $sitePathRules = array( '/' => 'rifc', '/assets/' => 'php', '/assets/cache/' => 'all', '/assets/backups/' => 'all', '/assets/logs/' => 'all', '/templates/' => 'php', '/modules/' => 'php', ); /** @var WireFileTools $files */ $files = $this->wire('files'); $numErrors = 0; foreach($sitePathRules as $dir => $ruleName) { $rule = $rules[$ruleName]; $header = "# Start ProcessWire:pwb$ruleName (update 17)"; $footer = "# End ProcessWire:pwb$ruleName"; $summary = "# $rule[label] (optional fallback if root .htaccess missing)"; $location = "$siteRootUrl$dir.htaccess"; $content = "$header\n$summary\n$rule[content]\n$footer"; $file = $siteRootDir . $dir . '.htaccess'; $url = $siteRootUrl . $dir . '.htaccess'; $path = dirname($file); if(!is_dir($path)) { if($this->auto) $this->message("Skipped $url (directory not present)"); continue; } if(file_exists($file)) { // existing .htaccess file already present if($this->auto) $this->message("Skipped: $url (already exists)"); continue; } // no .htaccess file currently present $writable = is_writable(dirname($file)); $data = $content; $actionLabel = 'Created'; if(!$writable) { // file not writable, so we will create a temporary file in cache instead (for optional manual copy) $file = $cachePath . str_replace(array('/', "\\", '.', '--'), '-', trim(str_replace($siteRootUrl, 'site', $url), '/')) . '.txt'; $tmpUrl = str_replace($siteRootDir, $siteRootUrl, $file); if(file_exists($file)) { // file already exists so we can skip if($this->auto) $this->message("Ignored: $tmpUrl (already exists)"); continue; } $writable = is_writable(dirname($file)); if($writable) { $this->warning("Unable to write updates to '$url', so writing to '$tmpUrl' instead, in case you want to copy manually."); $data = "# Intended location of this file: $location\n$data"; $actionLabel = 'Created'; } $url = $tmpUrl; } if(!$writable) { if($ruleName !== 'all') { if($this->auto) $this->message("Ignored: $url"); } else { $this->error("Unable to write: $url"); $numErrors++; } continue; } try { if($files->filePutContents($file, "$data\n", LOCK_EX)) { $this->message("$actionLabel: $url"); } else { throw new WireException("Unable to write: $url"); } } catch(\Exception $e) { $this->error($e->getMessage()); $numErrors++; } } return $numErrors === 0; } /** * Are we running under Apache? * * @return bool * */ public function isApache() { $software = isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : ''; if(stripos($software, 'microsoft-iis') !== false) return false; if(stripos($software, 'nginx') !== false) return false; if(stripos($software, 'apache') !== false) return true; if(function_exists('apache_get_version') && stripos(apache_get_version(), 'Apache') !== false) return true; if(function_exists('apache_get_modules') && in_array('mod_rewrite', apache_get_modules())) return true; if(getenv('HTTP_MOD_REWRITE') == 'On') return true; $rootPath = $this->wire('config')->paths->root; if(file_exists($rootPath . 'Web.config') || file_exists($rootPath . 'web.config')) return false; // IIS if(file_exists($rootPath . '.htaccess')) return true; return false; } /** * Is this update useful for this installation? * * @return bool * */ public function isUseful() { if(!$this->isApache()) return false; $f = '.htaccess'; $paths = $this->wire('config')->paths; $assets = $paths->assets; if(!file_exists($assets . $f)) return true; if(!file_exists($assets . "logs/$f")) return true; if(!file_exists($assets . "cache/$f")) return true; if(!file_exists($assets . "backups/$f")) return true; if(!file_exists($paths->templates . $f)) return true; if(!file_exists($paths->site . $f)) return true; if(!file_exists($paths->site . "modules/$f")) return true; return false; } }