X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2FForeignResourceManager.php;h=dddbad53349be99fd8055106a30a11549f11f2af;hb=a469e81971a11b931716897d65e1fa3fdfe378e2;hp=e0d088a9167704c03bfa26eb87239a2f9c1257db;hpb=b8e0ca16aa743581f5fac5cef8bed5ac2bf6e7cb;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/ForeignResourceManager.php b/includes/ForeignResourceManager.php index e0d088a916..dddbad5334 100644 --- a/includes/ForeignResourceManager.php +++ b/includes/ForeignResourceManager.php @@ -30,10 +30,12 @@ class ForeignResourceManager { private $registryFile; private $libDir; private $tmpParentDir; + private $cacheDir; private $infoPrinter; private $errorPrinter; private $verbosePrinter; private $action; + private $registry; /** * @param string $registryFile Path to YAML file @@ -60,8 +62,11 @@ class ForeignResourceManager { // Use a temporary directory under the destination directory instead // of wfTempDir() because PHP's rename() does not work across file - // systems, as the user's /tmp and $IP may be on different filesystems. - $this->tmpParentDir = "{$this->libDir}/.tmp"; + // systems, and the user's /tmp and $IP may be on different filesystems. + $this->tmpParentDir = "{$this->libDir}/.foreign/tmp"; + + $cacheHome = getenv( 'XDG_CACHE_HOME' ) ? realpath( getenv( 'XDG_CACHE_HOME' ) ) : false; + $this->cacheDir = $cacheHome ? "$cacheHome/mw-foreign" : "{$this->libDir}/.foreign/cache"; } /** @@ -69,18 +74,24 @@ class ForeignResourceManager { * @throws Exception */ public function run( $action, $module ) { - if ( !in_array( $action, [ 'update', 'verify', 'make-sri' ] ) ) { - throw new Exception( 'Invalid action parameter.' ); + $actions = [ 'update', 'verify', 'make-sri' ]; + if ( !in_array( $action, $actions ) ) { + $this->error( "Invalid action.\n\nMust be one of " . implode( ', ', $actions ) . '.' ); + return false; } $this->action = $action; - $registry = $this->parseBasicYaml( file_get_contents( $this->registryFile ) ); + $this->registry = $this->parseBasicYaml( file_get_contents( $this->registryFile ) ); if ( $module === 'all' ) { - $modules = $registry; - } elseif ( isset( $registry[ $module ] ) ) { - $modules = [ $module => $registry[ $module ] ]; + $modules = $this->registry; + } elseif ( isset( $this->registry[ $module ] ) ) { + $modules = [ $module => $this->registry[ $module ] ]; } else { - throw new Exception( 'Unknown module name.' ); + $this->error( "Unknown module name.\n\nMust be one of:\n" . + wordwrap( implode( ', ', array_keys( $this->registry ) ), 80 ) . + '.' + ); + return false; } foreach ( $modules as $moduleName => $info ) { @@ -121,8 +132,8 @@ class ForeignResourceManager { } } - $this->cleanUp(); $this->output( "\nDone!\n" ); + $this->cleanUp(); if ( $this->hasErrors ) { // The verify mode should check all modules/files and fail after, not during. return false; @@ -131,7 +142,29 @@ class ForeignResourceManager { return true; } + private function cacheKey( $src, $integrity ) { + $key = basename( $src ) . '_' . substr( $integrity, -12 ); + $key = preg_replace( '/[.\/+?=_-]+/', '_', $key ); + return rtrim( $key, '_' ); + } + + /** @return string|false */ + private function cacheGet( $key ) { + return Wikimedia\quietCall( 'file_get_contents', "{$this->cacheDir}/$key.data" ); + } + + private function cacheSet( $key, $data ) { + wfMkdirParents( $this->cacheDir ); + file_put_contents( "{$this->cacheDir}/$key.data", $data, LOCK_EX ); + } + private function fetch( $src, $integrity ) { + $key = $this->cacheKey( $src, $integrity ); + $data = $this->cacheGet( $key ); + if ( $data ) { + return $data; + } + $req = MWHttpRequest::factory( $src, [ 'method' => 'GET', 'followRedirects' => false ] ); if ( !$req->execute()->isOK() ) { throw new Exception( "Failed to download resource at {$src}" ); @@ -144,15 +177,14 @@ class ForeignResourceManager { $actualIntegrity = $algo . '-' . base64_encode( hash( $algo, $data, true ) ); if ( $integrity === $actualIntegrity ) { $this->verbose( "... passed integrity check for {$src}\n" ); + $this->cacheSet( $key, $data ); + } elseif ( $this->action === 'make-sri' ) { + $this->output( "Integrity for {$src}\n\tintegrity: ${actualIntegrity}\n" ); } else { - if ( $this->action === 'make-sri' ) { - $this->output( "Integrity for {$src}\n\tintegrity: ${actualIntegrity}\n" ); - } else { - throw new Exception( "Integrity check failed for {$src}\n" . - "\tExpected: {$integrity}\n" . - "\tActual: {$actualIntegrity}" - ); - } + throw new Exception( "Integrity check failed for {$src}\n" . + "\tExpected: {$integrity}\n" . + "\tActual: {$actualIntegrity}" + ); } return $data; } @@ -271,6 +303,23 @@ class ForeignResourceManager { private function cleanUp() { wfRecursiveRemoveDir( $this->tmpParentDir ); + + // Prune the cache of files we don't recognise. + $knownKeys = []; + foreach ( $this->registry as $info ) { + if ( $info['type'] === 'file' || $info['type'] === 'tar' ) { + $knownKeys[] = $this->cacheKey( $info['src'], $info['integrity'] ); + } elseif ( $info['type'] === 'multi-file' ) { + foreach ( $info['files'] as $file ) { + $knownKeys[] = $this->cacheKey( $file['src'], $file['integrity'] ); + } + } + } + foreach ( glob( "{$this->cacheDir}/*" ) as $cacheFile ) { + if ( !in_array( basename( $cacheFile, '.data' ), $knownKeys ) ) { + unlink( $cacheFile ); + } + } } /**