Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 70 |
|
0.00% |
0 / 4 |
CRAP | |
0.00% |
0 / 1 |
BrowscapCommand | |
0.00% |
0 / 67 |
|
0.00% |
0 / 4 |
272 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
configure | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
2 | |||
checkCachePermissions | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
30 | |||
execute | |
0.00% |
0 / 35 |
|
0.00% |
0 / 1 |
90 |
1 | <?php |
2 | |
3 | /** |
4 | * Console command: browscap |
5 | * |
6 | * PHP version 8 |
7 | * |
8 | * Copyright (C) The National Library of Finland 2024. |
9 | * |
10 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License version 2, |
12 | * as published by the Free Software Foundation. |
13 | * |
14 | * This program is distributed in the hope that it will be useful, |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | * GNU General Public License for more details. |
18 | * |
19 | * You should have received a copy of the GNU General Public License |
20 | * along with this program; if not, write to the Free Software |
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
22 | * |
23 | * @category VuFind |
24 | * @package Console |
25 | * @author Ere Maijala <ere.maijala@helsinki.fi> |
26 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
27 | * @link https://vufind.org/wiki/development Wiki |
28 | */ |
29 | |
30 | namespace VuFindConsole\Command\Util; |
31 | |
32 | use Exception; |
33 | use Laminas\Cache\Psr\SimpleCache\SimpleCacheDecorator; |
34 | use Laminas\Cache\Storage\StorageInterface; |
35 | use Psr\Log\InvalidArgumentException; |
36 | use Psr\Log\LogLevel; |
37 | use Symfony\Component\Console\Attribute\AsCommand; |
38 | use Symfony\Component\Console\Command\Command; |
39 | use Symfony\Component\Console\Input\InputArgument; |
40 | use Symfony\Component\Console\Input\InputInterface; |
41 | use Symfony\Component\Console\Input\InputOption; |
42 | use Symfony\Component\Console\Logger\ConsoleLogger; |
43 | use Symfony\Component\Console\Output\OutputInterface; |
44 | use VuFind\Cache\Manager as CacheManager; |
45 | use VuFind\Http\GuzzleService; |
46 | |
47 | /** |
48 | * Console command: browscap |
49 | * |
50 | * @category VuFind |
51 | * @package Console |
52 | * @author Ere Maijala <ere.maijala@helsinki.fi> |
53 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
54 | * @link https://vufind.org/wiki/development Wiki |
55 | */ |
56 | #[AsCommand( |
57 | name: 'util/browscap', |
58 | description: 'Browscap Cache Manager' |
59 | )] |
60 | class BrowscapCommand extends Command |
61 | { |
62 | /** |
63 | * Cache manager |
64 | * |
65 | * @var CacheManager |
66 | */ |
67 | protected $cacheManager; |
68 | |
69 | /** |
70 | * Guzzle service |
71 | * |
72 | * @var GuzzleService |
73 | */ |
74 | protected $guzzleService; |
75 | |
76 | /** |
77 | * Constructor |
78 | * |
79 | * @param CacheManager $cacheManager Cache manager |
80 | * @param GuzzleService $guzzleService Guzzle service |
81 | */ |
82 | public function __construct( |
83 | CacheManager $cacheManager, |
84 | GuzzleService $guzzleService |
85 | ) { |
86 | parent::__construct(); |
87 | $this->cacheManager = $cacheManager; |
88 | $this->guzzleService = $guzzleService; |
89 | } |
90 | |
91 | /** |
92 | * Configure the command. |
93 | * |
94 | * @return void |
95 | */ |
96 | protected function configure() |
97 | { |
98 | $this |
99 | ->setHelp('Manages the browscap cache.') |
100 | ->addArgument( |
101 | 'function', |
102 | InputArgument::REQUIRED, |
103 | 'Function to execute. Currently the only supported function is: update' |
104 | ) |
105 | ->addOption( |
106 | 'file-type', |
107 | null, |
108 | InputOption::VALUE_REQUIRED, |
109 | 'Browscap file type (standard, lite or full). See https://browscap.org/ for more information.', |
110 | 'standard' |
111 | ); |
112 | } |
113 | |
114 | /** |
115 | * Display a warning message if generated files are not owned by the Apache user. |
116 | * |
117 | * @param ConsoleLogger $logger Logger (for warning output) |
118 | * @param StorageInterface $browscapCache Browscap cache object |
119 | * |
120 | * @return void |
121 | * |
122 | * @throws Exception |
123 | * @throws InvalidArgumentException |
124 | */ |
125 | protected function checkCachePermissions(ConsoleLogger $logger, StorageInterface $browscapCache) |
126 | { |
127 | // If we have a file cache, let's make sure the created files are consistent |
128 | // with expectations. |
129 | if ($browscapCacheDir = ($browscapCache->getOptions()->cacheDir ?? null)) { |
130 | // Figure out the username of the web server -- there is no perfect way to do |
131 | // this, but if an object cache exists, it was most likely generated by the |
132 | // web server. We'll try to use that, and fall back on the base directory |
133 | // otherwise. |
134 | $baseCacheDir = $this->cacheManager->getCacheDir(false); |
135 | $objectCacheDir = $baseCacheDir . '/objects'; |
136 | $dirToCheck = file_exists($objectCacheDir) ? $objectCacheDir : $baseCacheDir; |
137 | $baseCacheOwner = fileowner($dirToCheck); |
138 | // Look through the browscap cache for files owned by somebody other than the |
139 | // expected web user. |
140 | $dir = opendir($browscapCacheDir); |
141 | while ($file = readdir($dir)) { |
142 | if (fileowner($file) !== $baseCacheOwner) { |
143 | $baseCacheUserDetails = posix_getpwuid($baseCacheOwner); |
144 | $baseCacheUsername = $baseCacheUserDetails['name'] ?? $baseCacheOwner; |
145 | $logger->warning( |
146 | "Generated files are not owned by $baseCacheUsername; " |
147 | . "if persistent login attempts fail, try recursively changing ownership of $browscapCacheDir." |
148 | ); |
149 | return; |
150 | } |
151 | } |
152 | } |
153 | } |
154 | |
155 | /** |
156 | * Run the command. |
157 | * |
158 | * Note that there's also similar functionality in MaintenanceController. |
159 | * |
160 | * @param InputInterface $input Input object |
161 | * @param OutputInterface $output Output object |
162 | * |
163 | * @return int 0 for success |
164 | */ |
165 | protected function execute(InputInterface $input, OutputInterface $output) |
166 | { |
167 | ini_set('memory_limit', '1024M'); |
168 | if ($input->getArgument('function') !== 'update') { |
169 | $output->writeln('<error>Invalid function specified</error>'); |
170 | return Command::FAILURE; |
171 | } |
172 | switch ($input->getOption('file-type')) { |
173 | case 'full': |
174 | $type = \BrowscapPHP\Helper\IniLoaderInterface::PHP_INI_FULL; |
175 | break; |
176 | case 'lite': |
177 | $type = \BrowscapPHP\Helper\IniLoaderInterface::PHP_INI_LITE; |
178 | break; |
179 | case 'standard': |
180 | $type = \BrowscapPHP\Helper\IniLoaderInterface::PHP_INI; |
181 | break; |
182 | default: |
183 | $output->writeln('<error>Invalid file-type specified</error>'); |
184 | return Command::FAILURE; |
185 | } |
186 | |
187 | $browscapCache = $this->cacheManager->getCache('browscap'); |
188 | $cache = new SimpleCacheDecorator($browscapCache); |
189 | $logger = new ConsoleLogger($output, [LogLevel::INFO => OutputInterface::VERBOSITY_NORMAL]); |
190 | $client = $this->guzzleService->createClient(); |
191 | |
192 | $bc = new \BrowscapPHP\BrowscapUpdater($cache, $logger, $client); |
193 | $logger->info('Checking for update...'); |
194 | try { |
195 | $bc->checkUpdate(); |
196 | } catch (\BrowscapPHP\Exception\NoNewVersionException $e) { |
197 | $logger->info('No newer version available.'); |
198 | return Command::SUCCESS; |
199 | } catch (\BrowscapPHP\Exception\NoCachedVersionException $e) { |
200 | $logger->info('No cached version available.'); |
201 | } catch (\Exception $e) { |
202 | // Output the exception and continue (assume we don't have a current version): |
203 | $logger->warning((string)$e); |
204 | } |
205 | $logger->info('Updating browscap cache...'); |
206 | $bc->update($type); |
207 | $logger->info('Update complete.'); |
208 | |
209 | $this->checkCachePermissions($logger, $browscapCache); |
210 | |
211 | return Command::SUCCESS; |
212 | } |
213 | } |