| 
									
										
										
										
											2018-03-07 20:21:36 +01:00
										 |  |  | <?php | 
					
						
							| 
									
										
										
										
											2024-11-25 04:18:55 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-07 20:21:36 +01:00
										 |  |  | /** | 
					
						
							|  |  |  |  * InstallController.php | 
					
						
							| 
									
										
										
										
											2020-01-31 07:32:04 +01:00
										 |  |  |  * Copyright (c) 2019 james@firefly-iii.org | 
					
						
							| 
									
										
										
										
											2018-03-07 20:21:36 +01:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2019-10-02 06:37:26 +02:00
										 |  |  |  * This file is part of Firefly III (https://github.com/firefly-iii). | 
					
						
							| 
									
										
										
										
											2018-03-07 20:21:36 +01:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2019-10-02 06:37:26 +02:00
										 |  |  |  * This program is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  |  * it under the terms of the GNU Affero General Public License as | 
					
						
							|  |  |  |  * published by the Free Software Foundation, either version 3 of the | 
					
						
							|  |  |  |  * License, or (at your option) any later version. | 
					
						
							| 
									
										
										
										
											2018-03-07 20:21:36 +01:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2019-10-02 06:37:26 +02:00
										 |  |  |  * This program is distributed in the hope that it will be useful, | 
					
						
							| 
									
										
										
										
											2018-03-07 20:21:36 +01:00
										 |  |  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							| 
									
										
										
										
											2019-10-02 06:37:26 +02:00
										 |  |  |  * GNU Affero General Public License for more details. | 
					
						
							| 
									
										
										
										
											2018-03-07 20:21:36 +01:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2019-10-02 06:37:26 +02:00
										 |  |  |  * You should have received a copy of the GNU Affero General Public License | 
					
						
							|  |  |  |  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | 
					
						
							| 
									
										
										
										
											2018-03-07 20:21:36 +01:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | declare(strict_types=1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace FireflyIII\Http\Controllers\System; | 
					
						
							| 
									
										
										
										
											2021-03-28 11:46:23 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-27 12:58:43 +02:00
										 |  |  | use Exception; | 
					
						
							| 
									
										
										
										
											2022-12-30 09:28:03 +01:00
										 |  |  | use FireflyIII\Exceptions\FireflyException; | 
					
						
							| 
									
										
										
										
											2018-03-07 20:21:36 +01:00
										 |  |  | use FireflyIII\Http\Controllers\Controller; | 
					
						
							| 
									
										
										
										
											2025-10-05 12:59:43 +02:00
										 |  |  | use FireflyIII\Support\Facades\FireflyConfig; | 
					
						
							| 
									
										
										
										
											2018-08-09 17:50:30 +02:00
										 |  |  | use FireflyIII\Support\Http\Controllers\GetConfigurationData; | 
					
						
							| 
									
										
										
										
											2021-03-28 11:46:23 +02:00
										 |  |  | use Illuminate\Contracts\View\Factory; | 
					
						
							| 
									
										
										
										
											2018-04-27 12:58:43 +02:00
										 |  |  | use Illuminate\Http\JsonResponse; | 
					
						
							| 
									
										
										
										
											2019-03-30 11:03:39 +01:00
										 |  |  | use Illuminate\Http\Request; | 
					
						
							| 
									
										
										
										
											2025-10-05 12:59:43 +02:00
										 |  |  | use Illuminate\Support\Facades\Artisan; | 
					
						
							|  |  |  | use Illuminate\Support\Facades\Cache; | 
					
						
							| 
									
										
										
										
											2021-03-28 11:46:23 +02:00
										 |  |  | use Illuminate\View\View; | 
					
						
							| 
									
										
										
										
											2018-03-07 20:21:36 +01:00
										 |  |  | use Laravel\Passport\Passport; | 
					
						
							| 
									
										
										
										
											2021-03-28 11:46:23 +02:00
										 |  |  | use phpseclib3\Crypt\RSA; | 
					
						
							| 
									
										
										
										
											2025-10-05 13:03:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-24 06:24:17 +02:00
										 |  |  | use function Safe\file_put_contents; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-07 20:21:36 +01:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Class InstallController | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | class InstallController extends Controller | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2018-08-09 17:50:30 +02:00
										 |  |  |     use GetConfigurationData; | 
					
						
							| 
									
										
										
										
											2019-03-30 11:03:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-02 12:56:48 +01:00
										 |  |  |     public const string BASEDIR_ERROR   = 'Firefly III cannot execute the upgrade commands. It is not allowed to because of an open_basedir restriction.'; | 
					
						
							|  |  |  |     public const string FORBIDDEN_ERROR = 'Internal PHP function "proc_close" is disabled for your installation. Auto-migration is not possible.'; | 
					
						
							|  |  |  |     public const string OTHER_ERROR     = 'An unknown error prevented Firefly III from executing the upgrade commands. Sorry.'; | 
					
						
							| 
									
										
										
										
											2020-10-18 16:44:34 +02:00
										 |  |  |     private string $lastError; | 
					
						
							| 
									
										
										
										
											2021-03-28 11:46:23 +02:00
										 |  |  |     private array  $upgradeCommands; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-07 20:21:36 +01:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * InstallController constructor. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function __construct() | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-11-05 08:15:17 +01:00
										 |  |  |         parent::__construct(); | 
					
						
							| 
									
										
										
										
											2018-03-07 20:21:36 +01:00
										 |  |  |         // empty on purpose.
 | 
					
						
							| 
									
										
										
										
											2019-03-30 11:03:39 +01:00
										 |  |  |         $this->upgradeCommands = [ | 
					
						
							| 
									
										
										
										
											2023-04-16 07:33:12 +02:00
										 |  |  |             // there are 5 initial commands
 | 
					
						
							|  |  |  |             // Check 4 places: InstallController, Docker image, UpgradeDatabase, composer.json
 | 
					
						
							|  |  |  |             'migrate'                            => ['--seed' => true, '--force' => true], | 
					
						
							|  |  |  |             'generate-keys'                      => [], // an exception :(
 | 
					
						
							|  |  |  |             'firefly-iii:upgrade-database'       => [], | 
					
						
							|  |  |  |             'firefly-iii:set-latest-version'     => ['--james-is-cool' => true], | 
					
						
							|  |  |  |             'firefly-iii:verify-security-alerts' => [], | 
					
						
							| 
									
										
										
										
											2019-03-30 11:03:39 +01:00
										 |  |  |         ]; | 
					
						
							| 
									
										
										
										
											2020-10-18 16:44:34 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-01 14:43:56 +01:00
										 |  |  |         $this->lastError       = ''; | 
					
						
							| 
									
										
										
										
											2018-03-07 20:21:36 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-04-06 08:10:50 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-07 20:21:36 +01:00
										 |  |  |     /** | 
					
						
							| 
									
										
										
										
											2018-07-22 08:10:16 +02:00
										 |  |  |      * Show index. | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2021-03-28 11:46:23 +02:00
										 |  |  |      * @return Factory|View | 
					
						
							| 
									
										
										
										
											2018-03-07 20:21:36 +01:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2018-03-10 07:17:05 +01:00
										 |  |  |     public function index() | 
					
						
							| 
									
										
										
										
											2018-03-07 20:21:36 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-04-15 10:14:14 +02:00
										 |  |  |         app('view')->share('FF_VERSION', config('firefly.version')); | 
					
						
							| 
									
										
										
										
											2019-03-17 09:06:45 +01:00
										 |  |  |         // index will set FF3 version.
 | 
					
						
							| 
									
										
										
										
											2025-10-02 06:55:09 +02:00
										 |  |  |         FireflyConfig::set('ff3_version', (string) config('firefly.version')); | 
					
						
							| 
									
										
										
										
											2019-08-18 13:01:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-29 14:11:12 +01:00
										 |  |  |         return view('install.index'); | 
					
						
							| 
									
										
										
										
											2018-03-10 07:17:05 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-03-07 20:21:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-30 11:03:39 +01:00
										 |  |  |     public function runCommand(Request $request): JsonResponse | 
					
						
							| 
									
										
										
										
											2018-03-10 07:17:05 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2024-12-22 08:43:12 +01:00
										 |  |  |         $requestIndex = (int) $request->get('index'); | 
					
						
							| 
									
										
										
										
											2019-03-30 11:03:39 +01:00
										 |  |  |         $response     = [ | 
					
						
							|  |  |  |             'hasNextCommand' => false, | 
					
						
							|  |  |  |             'done'           => true, | 
					
						
							|  |  |  |             'previous'       => null, | 
					
						
							|  |  |  |             'error'          => false, | 
					
						
							|  |  |  |             'errorMessage'   => null, | 
					
						
							|  |  |  |         ]; | 
					
						
							| 
									
										
										
										
											2018-03-10 07:17:05 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-29 06:33:43 +01:00
										 |  |  |         app('log')->debug(sprintf('Will now run commands. Request index is %d', $requestIndex)); | 
					
						
							| 
									
										
										
										
											2025-01-04 09:15:39 +01:00
										 |  |  |         $indexes      = array_keys($this->upgradeCommands); | 
					
						
							| 
									
										
										
										
											2023-04-16 07:33:12 +02:00
										 |  |  |         if (array_key_exists($requestIndex, $indexes)) { | 
					
						
							| 
									
										
										
										
											2024-01-01 14:43:56 +01:00
										 |  |  |             $command                    = $indexes[$requestIndex]; | 
					
						
							|  |  |  |             $parameters                 = $this->upgradeCommands[$command]; | 
					
						
							| 
									
										
										
										
											2023-10-29 06:33:43 +01:00
										 |  |  |             app('log')->debug(sprintf('Will now execute command "%s" with parameters', $command), $parameters); | 
					
						
							| 
									
										
										
										
											2023-12-20 19:35:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-30 09:28:03 +01:00
										 |  |  |             try { | 
					
						
							| 
									
										
										
										
											2023-04-15 10:14:14 +02:00
										 |  |  |                 $result = $this->executeCommand($command, $parameters); | 
					
						
							| 
									
										
										
										
											2022-12-30 09:28:03 +01:00
										 |  |  |             } catch (FireflyException $e) { | 
					
						
							| 
									
										
										
										
											2023-10-29 06:32:00 +01:00
										 |  |  |                 app('log')->error($e->getMessage()); | 
					
						
							|  |  |  |                 app('log')->error($e->getTraceAsString()); | 
					
						
							| 
									
										
										
										
											2023-11-28 04:45:07 +01:00
										 |  |  |                 if (str_contains($e->getMessage(), 'open_basedir restriction in effect')) { | 
					
						
							| 
									
										
										
										
											2022-12-30 09:28:03 +01:00
										 |  |  |                     $this->lastError = self::BASEDIR_ERROR; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 $result          = false; | 
					
						
							|  |  |  |                 $this->lastError = sprintf('%s %s', self::OTHER_ERROR, $e->getMessage()); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2020-10-18 16:44:34 +02:00
										 |  |  |             if (false === $result) { | 
					
						
							|  |  |  |                 $response['errorMessage'] = $this->lastError; | 
					
						
							|  |  |  |                 $response['error']        = true; | 
					
						
							| 
									
										
										
										
											2023-12-20 19:35:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-18 16:44:34 +02:00
										 |  |  |                 return response()->json($response); | 
					
						
							| 
									
										
										
										
											2018-04-27 12:58:43 +02:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-04-15 10:14:14 +02:00
										 |  |  |             $response['hasNextCommand'] = array_key_exists($requestIndex + 1, $indexes); | 
					
						
							| 
									
										
										
										
											2020-10-18 16:44:34 +02:00
										 |  |  |             $response['previous']       = $command; | 
					
						
							| 
									
										
										
										
											2018-04-27 12:58:43 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-10-18 16:44:34 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-20 19:35:52 +01:00
										 |  |  |         return response()->json($response); | 
					
						
							| 
									
										
										
										
											2020-10-18 16:44:34 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-06-21 12:34:58 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-20 19:35:52 +01:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * @throws FireflyException | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     private function executeCommand(string $command, array $args): bool | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         app('log')->debug(sprintf('Will now call command %s with args.', $command), $args); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |             if ('generate-keys' === $command) { | 
					
						
							|  |  |  |                 $this->keys(); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if ('generate-keys' !== $command) { | 
					
						
							| 
									
										
										
										
											2025-05-24 16:39:20 +02:00
										 |  |  |                 Artisan::call($command, $args); | 
					
						
							|  |  |  |                 app('log')->debug(Artisan::output()); | 
					
						
							| 
									
										
										
										
											2023-12-20 19:35:52 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2025-05-27 16:53:48 +02:00
										 |  |  |         } catch (Exception $e) { // intentional generic exception
 | 
					
						
							| 
									
										
										
										
											2023-12-20 19:35:52 +01:00
										 |  |  |             throw new FireflyException($e->getMessage(), 0, $e); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         // clear cache as well.
 | 
					
						
							| 
									
										
										
										
											2025-05-24 16:39:20 +02:00
										 |  |  |         Cache::clear(); | 
					
						
							| 
									
										
										
										
											2023-12-20 19:35:52 +01:00
										 |  |  |         app('preferences')->mark(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-02-22 20:11:09 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Create specific RSA keys. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function keys(): void | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $key                      = RSA::createKey(4096); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         [$publicKey, $privateKey] = [ | 
					
						
							|  |  |  |             Passport::keyPath('oauth-public.key'), | 
					
						
							|  |  |  |             Passport::keyPath('oauth-private.key'), | 
					
						
							|  |  |  |         ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (file_exists($publicKey) || file_exists($privateKey)) { | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-24 05:40:20 +02:00
										 |  |  |         file_put_contents($publicKey, (string) $key->getPublicKey()); | 
					
						
							|  |  |  |         file_put_contents($privateKey, $key->toString('PKCS1')); | 
					
						
							| 
									
										
										
										
											2024-02-22 20:11:09 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-03-19 13:23:26 +01:00
										 |  |  | } |