]> BookStack Code Mirror - system-cli/blobdiff - src/Services/MySqlRunner.php
Added escaping for MySQL config options
[system-cli] / src / Services / MySqlRunner.php
index 8a91aadbe629b6c62d229a04102c28519b8d3a56..e1c4a0fbf3529c10aaf64551988ddd5842eeddbe 100644 (file)
@@ -3,6 +3,7 @@
 namespace Cli\Services;
 
 use Exception;
+use Symfony\Component\Process\Exception\ProcessTimedOutException;
 
 class MySqlRunner
 {
@@ -28,41 +29,64 @@ class MySqlRunner
         }
     }
 
+    protected function createOptionsFile(): string
+    {
+        $path = tempnam(sys_get_temp_dir(), 'bs-cli-mysql-opts');
+        $password = str_replace('\\', '\\\\', $this->password);
+        $contents = "[client]\nuser='{$this->user}'\nhost='{$this->host}'\nport={$this->port}\npassword='{$password}'\nprotocol=TCP";
+        file_put_contents($path, $contents);
+        chmod($path, 0600);
+        return $path;
+    }
+
+    protected function getProgramRunnerInstance(): ProgramRunner
+    {
+        return (new ProgramRunner(['mariadb', 'mysql'], '/usr/bin/mysql'));
+    }
+
     public function testConnection(): bool
     {
-        $output = (new ProgramRunner('mysql', '/usr/bin/mysql'))
-            ->withEnvironment(['MYSQL_PWD' => $this->password])
-            ->withTimeout(300)
-            ->withIdleTimeout(300)
-            ->runCapturingStdErr([
-                '-h', $this->host,
-                '-P', $this->port,
-                '-u', $this->user,
-                '--protocol=TCP',
-                $this->database,
-                '-e', "show tables;"
-            ]);
-
-        return !$output;
+        $optionsFile = $this->createOptionsFile();
+
+        try {
+            $stdErr = $this->getProgramRunnerInstance()
+                ->withTimeout(300)
+                ->withIdleTimeout(300)
+                ->runCapturingStdErr([
+                    "--defaults-extra-file={$optionsFile}",
+                    $this->database,
+                    '-e', "show tables;"
+                ]);
+            unlink($optionsFile);
+        } catch (Exception $exception) {
+            unlink($optionsFile);
+            throw $exception;
+        }
+
+        return !$stdErr;
     }
 
     public function importSqlFile(string $sqlFilePath): void
     {
-        $output = (new ProgramRunner('mysql', '/usr/bin/mysql'))
-            ->withEnvironment(['MYSQL_PWD' => $this->password])
-            ->withTimeout(240)
-            ->withIdleTimeout(5)
-            ->runCapturingStdErr([
-                '-h', $this->host,
-                '-P', $this->port,
-                '-u', $this->user,
-                '--protocol=TCP',
-                $this->database,
-                '-e', "source {$sqlFilePath}"
-            ]);
+        $optionsFile = $this->createOptionsFile();
+
+        try {
+            $output = $this->getProgramRunnerInstance()
+                ->withTimeout(300)
+                ->withIdleTimeout(300)
+                ->runCapturingStdErr([
+                    "--defaults-extra-file={$optionsFile}",
+                    $this->database,
+                    '-e', "source {$sqlFilePath}"
+                ]);
+            unlink($optionsFile);
+        } catch (Exception $exception) {
+            unlink($optionsFile);
+            throw $exception;
+        }
 
         if ($output) {
-            throw new Exception("Failed mysql file import with errors:\n" . $output);
+            throw new Exception("Failed mysql file import with errors:\n{$output}");
         }
     }
 
@@ -91,17 +115,15 @@ HEREDOC;
         $errors = "";
         $warnings = "";
         $hasOutput = false;
+        $optionsFile = $this->createOptionsFile();
 
         try {
-            (new ProgramRunner('mysqldump', '/usr/bin/mysqldump'))
-                ->withTimeout(240)
-                ->withIdleTimeout(15)
-                ->withEnvironment(['MYSQL_PWD' => $this->password])
+            (new ProgramRunner(['mariadb-dump', 'mysqldump'], '/usr/bin/mysqldump'))
+                ->withTimeout(300)
+                ->withIdleTimeout(300)
+                ->withAdditionalPathLocation('C:\xampp\mysql\bin')
                 ->runWithoutOutputCallbacks([
-                    '-h', $this->host,
-                    '-P', $this->port,
-                    '-u', $this->user,
-                    '--protocol=TCP',
+                    "--defaults-extra-file={$optionsFile}",
                     '--single-transaction',
                     '--no-tablespaces',
                     $this->database,
@@ -113,13 +135,15 @@ HEREDOC;
                     foreach ($lines as $line) {
                         if (str_starts_with(strtolower($line), 'warning: ')) {
                             $warnings .= $line;
-                        } else {
+                        } else if (!empty(trim($line))) {
                             $errors .= $line . "\n";
                         }
                     }
                 });
+            unlink($optionsFile);
         } catch (\Exception $exception) {
             fclose($file);
+            unlink($optionsFile);
             if ($exception instanceof ProcessTimedOutException) {
                 if (!$hasOutput) {
                     throw new Exception("mysqldump operation timed-out.\nNo data has been received so the connection to your database may have failed.");
@@ -149,4 +173,4 @@ HEREDOC;
 
         return new static($host, $username, $password, $database, $port);
     }
-}
\ No newline at end of file
+}