getParsedBodyParamAsString('charset_of_file', ''); $format = $request->getParsedBodyParamAsString('format', ''); ImportSettings::$importType = $request->getParsedBodyParamAsString('import_type', ''); $GLOBALS['is_js_confirmed'] = $request->getParsedBodyParam('is_js_confirmed'); $GLOBALS['message_to_show'] = $request->getParsedBodyParam('message_to_show'); $GLOBALS['noplugin'] = $request->getParsedBodyParam('noplugin'); ImportSettings::$skipQueries = (int) $request->getParsedBodyParamAsStringOrNull('skip_queries'); ImportSettings::$localImportFile = $request->getParsedBodyParamAsString('local_import_file', ''); $GLOBALS['show_as_php'] = $request->getParsedBodyParam('show_as_php'); // reset import messages for ajax request $_SESSION['Import_message']['message'] = null; $_SESSION['Import_message']['go_back_url'] = null; // default values $GLOBALS['reload'] = false; $GLOBALS['ajax_reload'] = []; $GLOBALS['import_text'] = ''; // Are we just executing plain query or sql file? // (eg. non import, but query box/window run) if (Current::$sqlQuery !== '') { // apply values for parameters /** @var array|null $parameters */ $parameters = $request->getParsedBodyParam('parameters'); if ($request->hasBodyParam('parameterized') && is_array($parameters)) { foreach ($parameters as $parameter => $replacementValue) { if (! is_numeric($replacementValue)) { $replacementValue = $this->dbi->quoteString($replacementValue); } $quoted = preg_quote($parameter, '/'); // making sure that :param does not apply values to :param1 Current::$sqlQuery = preg_replace( '/' . $quoted . '([^a-zA-Z0-9_])/', $replacementValue . '${1}', Current::$sqlQuery, ) ?? ''; // for parameters that appear at the end of the string Current::$sqlQuery = preg_replace( '/' . $quoted . '$/', $replacementValue, Current::$sqlQuery, ) ?? ''; } } // run SQL query $GLOBALS['import_text'] = Current::$sqlQuery; ImportSettings::$importType = 'query'; $format = 'sql'; $_SESSION['sql_from_query_box'] = true; // If there is a request to ROLLBACK when finished. if ($request->hasBodyParam('rollback_query')) { $this->import->handleRollbackRequest($GLOBALS['import_text']); } // refresh navigation and main panels if (preg_match('/^(DROP)\s+(VIEW|TABLE|DATABASE|SCHEMA)\s+/i', Current::$sqlQuery) === 1) { $GLOBALS['reload'] = true; $GLOBALS['ajax_reload']['reload'] = true; } // refresh navigation panel only if (preg_match('/^(CREATE|ALTER)\s+(VIEW|TABLE|DATABASE|SCHEMA)\s+/i', Current::$sqlQuery) === 1) { $GLOBALS['ajax_reload']['reload'] = true; } // do a dynamic reload if table is RENAMED // (by sending the instruction to the AJAX response handler) if ( preg_match( '/^RENAME\s+TABLE\s+(.*?)\s+TO\s+(.*?)($|;|\s)/i', Current::$sqlQuery, $renameTableNames, ) === 1 ) { $GLOBALS['ajax_reload']['reload'] = true; $GLOBALS['ajax_reload']['table_name'] = Util::unQuote($renameTableNames[2]); } Current::$sqlQuery = ''; } elseif ($request->hasBodyParam('id_bookmark')) { // run bookmark ImportSettings::$importType = 'query'; $format = 'sql'; } // If we didn't get any parameters, either user called this directly, or // upload limit has been reached, let's assume the second possibility. $getParams = $request->getQueryParams(); $postParams = $request->getParsedBody(); if ($postParams === [] && $getParams === []) { Current::$message = Message::error( __( 'You probably tried to upload a file that is too large. Please refer ' . 'to %sdocumentation%s for a workaround for this limit.', ), ); Current::$message->addParam('[doc@faq1-16]'); Current::$message->addParam('[/doc]'); // so we can obtain the message $_SESSION['Import_message']['message'] = Current::$message->getDisplay(); $_SESSION['Import_message']['go_back_url'] = UrlParams::$goto; $this->response->setRequestStatus(false); $this->response->addJSON('message', Current::$message); return $this->response->response(); } // Add console message id to response output $consoleMessageId = $request->getParsedBodyParam('console_message_id'); if ($consoleMessageId !== null) { $this->response->addJSON('console_message_id', $consoleMessageId); } $importFormat = ImportFormat::tryFrom($format); if ($importFormat === null) { $this->response->setRequestStatus(false); $this->response->addHTML(Message::error(__('Incorrect format parameter'))->getDisplay()); return $this->response->response(); } if (Current::$table !== '' && Current::$database !== '') { UrlParams::$params = ['db' => Current::$database, 'table' => Current::$table]; } elseif (Current::$database !== '') { UrlParams::$params = ['db' => Current::$database]; } else { UrlParams::$params = []; } // Create error and goto url if (ImportSettings::$importType === 'table') { UrlParams::$goto = Url::getFromRoute('/table/import'); } elseif (ImportSettings::$importType === 'database') { UrlParams::$goto = Url::getFromRoute('/database/import'); } elseif (ImportSettings::$importType === 'server') { UrlParams::$goto = Url::getFromRoute('/server/import'); } elseif (UrlParams::$goto === '' || preg_match('@^index\.php$@i', UrlParams::$goto) !== 1) { if (Current::$table !== '' && Current::$database !== '') { UrlParams::$goto = Url::getFromRoute('/table/structure'); } elseif (Current::$database !== '') { UrlParams::$goto = Url::getFromRoute('/database/structure'); } else { UrlParams::$goto = Url::getFromRoute('/server/sql'); } } $GLOBALS['errorUrl'] = UrlParams::$goto . Url::getCommon(UrlParams::$params, '&'); $_SESSION['Import_message']['go_back_url'] = $GLOBALS['errorUrl']; if (Current::$database !== '') { $this->dbi->selectDb(Current::$database); } Util::setTimeLimit(); $config = Config::getInstance(); if (! empty($config->settings['MemoryLimit'])) { ini_set('memory_limit', $config->settings['MemoryLimit']); } ImportSettings::$timestamp = time(); ImportSettings::$maximumTime = 0; $maxExecutionTime = (int) ini_get('max_execution_time'); if ($request->hasBodyParam('allow_interrupt') && $maxExecutionTime >= 1) { ImportSettings::$maximumTime = $maxExecutionTime - 1; // Give 1 second for phpMyAdmin to exit nicely } // set default values ImportSettings::$timeoutPassed = false; $GLOBALS['error'] = false; ImportSettings::$readMultiply = 1; ImportSettings::$finished = false; ImportSettings::$offset = 0; ImportSettings::$maxSqlLength = 0; Current::$sqlQuery = ''; ImportSettings::$sqlQueryDisabled = false; ImportSettings::$goSql = false; ImportSettings::$executedQueries = 0; ImportSettings::$runQuery = true; ImportSettings::$charsetConversion = false; $resetCharset = false; ImportSettings::$message = 'Sorry an unexpected error happened!'; $GLOBALS['result'] = false; // Bookmark Support: get a query back from bookmark if required $idBookmark = (int) $request->getParsedBodyParamAsStringOrNull('id_bookmark'); $actionBookmark = (int) $request->getParsedBodyParamAsStringOrNull('action_bookmark'); if ($idBookmark !== 0) { switch ($actionBookmark) { case 0: // bookmarked query that have to be run $bookmark = $this->bookmarkRepository->get( $request->hasBodyParam('action_bookmark_all') ? null : $config->selectedServer['user'], $idBookmark, ); if (! $bookmark instanceof Bookmark) { break; } $bookmarkVariables = $request->getParsedBodyParam('bookmark_variable'); if (is_array($bookmarkVariables)) { $GLOBALS['import_text'] = $bookmark->applyVariables($bookmarkVariables); } else { $GLOBALS['import_text'] = $bookmark->getQuery(); } // refresh navigation and main panels if (preg_match('/^(DROP)\s+(VIEW|TABLE|DATABASE|SCHEMA)\s+/i', $GLOBALS['import_text']) === 1) { $GLOBALS['reload'] = true; $GLOBALS['ajax_reload']['reload'] = true; } // refresh navigation panel only if ( preg_match( '/^(CREATE|ALTER)\s+(VIEW|TABLE|DATABASE|SCHEMA)\s+/i', $GLOBALS['import_text'], ) === 1 ) { $GLOBALS['ajax_reload']['reload'] = true; } break; case 1: // bookmarked query that have to be displayed $bookmark = $this->bookmarkRepository->get($config->selectedServer['user'], $idBookmark); if (! $bookmark instanceof Bookmark) { break; } $GLOBALS['import_text'] = $bookmark->getQuery(); if ($request->isAjax()) { Current::$message = Message::success(__('Showing bookmark')); $this->response->setRequestStatus(Current::$message->isSuccess()); $this->response->addJSON('message', Current::$message); $this->response->addJSON('sql_query', $GLOBALS['import_text']); $this->response->addJSON('action_bookmark', $actionBookmark); return $this->response->response(); } ImportSettings::$runQuery = false; break; case 2: // bookmarked query that have to be deleted $bookmark = $this->bookmarkRepository->get($config->selectedServer['user'], $idBookmark); if (! $bookmark instanceof Bookmark) { break; } $bookmark->delete(); if ($request->isAjax()) { Current::$message = Message::success( __('The bookmark has been deleted.'), ); $this->response->setRequestStatus(Current::$message->isSuccess()); $this->response->addJSON('message', Current::$message); $this->response->addJSON('action_bookmark', $actionBookmark); $this->response->addJSON('id_bookmark', $idBookmark); return $this->response->response(); } ImportSettings::$runQuery = false; $GLOBALS['error'] = true; // this is kind of hack to skip processing the query break; } } // Do no run query if we show PHP code if (isset($GLOBALS['show_as_php'])) { ImportSettings::$runQuery = false; ImportSettings::$goSql = true; } // We can not read all at once, otherwise we can run out of memory // Calculate value of the limit $memoryLimit = (string) ini_get('memory_limit'); $memoryLimit = ini_parse_quantity($memoryLimit); // 2 MB as default if ($memoryLimit === 0) { $memoryLimit = 2 * 1024 * 1024; } // In case no memory limit we work on 10MB chunks if ($memoryLimit === -1) { $memoryLimit = 10 * 1024 * 1024; } // Just to be sure, there might be lot of memory needed for uncompression ImportSettings::$readLimit = $memoryLimit / 8; // handle filenames if ( isset($_FILES['import_file']) && is_array($_FILES['import_file']) && isset($_FILES['import_file']['name'], $_FILES['import_file']['tmp_name']) && is_string($_FILES['import_file']['name']) && is_string($_FILES['import_file']['tmp_name']) ) { ImportSettings::$importFile = $_FILES['import_file']['tmp_name']; ImportSettings::$importFileName = $_FILES['import_file']['name']; } if (ImportSettings::$localImportFile !== '' && $config->settings['UploadDir'] !== '') { // sanitize $local_import_file as it comes from a POST ImportSettings::$localImportFile = Core::securePath(ImportSettings::$localImportFile); ImportSettings::$importFile = Util::userDir($config->settings['UploadDir']) . ImportSettings::$localImportFile; /** * Do not allow symlinks to avoid security issues * (user can create symlink to file they can not access, * but phpMyAdmin can). */ if (@is_link(ImportSettings::$importFile)) { ImportSettings::$importFile = 'none'; } } elseif (empty(ImportSettings::$importFile) || ! is_uploaded_file(ImportSettings::$importFile)) { ImportSettings::$importFile = 'none'; } // Do we have file to import? if (ImportSettings::$importFile !== 'none' && ! $GLOBALS['error']) { /** * Handle file compression */ $importHandle = new File(ImportSettings::$importFile); $importHandle->checkUploadedFile(); if ($importHandle->isError()) { /** @var Message $errorMessage */ $errorMessage = $importHandle->getError(); $importHandle->close(); $_SESSION['Import_message']['message'] = $errorMessage->getDisplay(); $this->response->setRequestStatus(false); $this->response->addJSON('message', $errorMessage->getDisplay()); $this->response->addHTML($errorMessage->getDisplay()); return $this->response->response(); } $importHandle->setDecompressContent(true); $importHandle->open(); if ($importHandle->isError()) { /** @var Message $errorMessage */ $errorMessage = $importHandle->getError(); $importHandle->close(); $_SESSION['Import_message']['message'] = $errorMessage->getDisplay(); $this->response->setRequestStatus(false); $this->response->addJSON('message', $errorMessage->getDisplay()); $this->response->addHTML($errorMessage->getDisplay()); return $this->response->response(); } } elseif (! $GLOBALS['error'] && empty($GLOBALS['import_text'])) { Current::$message = Message::error( __( 'No data was received to import. Either no file name was ' . 'submitted, or the file size exceeded the maximum size permitted ' . 'by your PHP configuration. See [doc@faq1-16]FAQ 1.16[/doc].', ), ); $_SESSION['Import_message']['message'] = Current::$message->getDisplay(); $this->response->setRequestStatus(false); $this->response->addJSON('message', Current::$message->getDisplay()); $this->response->addHTML(Current::$message->getDisplay()); return $this->response->response(); } // Convert the file's charset if necessary if ( Encoding::isSupported() && ImportSettings::$charsetOfFile !== '' && ImportSettings::$charsetOfFile !== 'utf-8' ) { ImportSettings::$charsetConversion = true; } elseif (ImportSettings::$charsetOfFile !== '' && ImportSettings::$charsetOfFile !== 'utf-8') { $this->dbi->query('SET NAMES \'' . ImportSettings::$charsetOfFile . '\''); // We can not show query in this case, it is in different charset ImportSettings::$sqlQueryDisabled = true; $resetCharset = true; } // Something to skip? (because timeout has passed) if (! $GLOBALS['error'] && $request->hasBodyParam('skip')) { $originalSkip = $skip = (int) $request->getParsedBodyParamAsStringOrNull('skip'); while ($skip > 0 && ! ImportSettings::$finished) { $this->import->getNextChunk( $importHandle ?? null, min($skip, ImportSettings::$readLimit), ); // Disable read progressivity, otherwise we eat all memory! ImportSettings::$readMultiply = 1; $skip -= ImportSettings::$readLimit; } unset($skip); } // This array contain the data like number of valid sql queries in the statement // and complete valid sql statement (which affected for rows) $queriesToBeExecuted = []; if (! $GLOBALS['error']) { $importPlugin = new ($importFormat->getClassName()); $importPlugin->setImportOptions($request); // Do the real import $defaultFkCheck = ForeignKey::handleDisableCheckInit(); try { $queriesToBeExecuted = $importPlugin->doImport($importHandle ?? null); ForeignKey::handleDisableCheckCleanup($defaultFkCheck); } catch (Throwable $e) { ForeignKey::handleDisableCheckCleanup($defaultFkCheck); throw $e; } } if (isset($importHandle)) { $importHandle->close(); } // Reset charset back, if we did some changes if ($resetCharset) { $this->dbi->query('SET CHARACTER SET ' . $this->dbi->getDefaultCharset()); $this->dbi->setCollation($this->dbi->getDefaultCollation()); } // Show correct message if ($idBookmark !== 0 && $actionBookmark === 2) { Current::$message = Message::success(__('The bookmark has been deleted.')); $GLOBALS['display_query'] = $GLOBALS['import_text']; $GLOBALS['error'] = false; // unset error marker, it was used just to skip processing } elseif ($idBookmark !== 0 && $actionBookmark === 1) { Current::$message = Message::notice(__('Showing bookmark')); } elseif (ImportSettings::$finished && ! $GLOBALS['error']) { // Do not display the query with message, we do it separately $GLOBALS['display_query'] = ';'; if (ImportSettings::$importType !== 'query') { Current::$message = Message::success( '' . _ngettext( 'Import has been successfully finished, %d query executed.', 'Import has been successfully finished, %d queries executed.', ImportSettings::$executedQueries, ) . '', ); Current::$message->addParam(ImportSettings::$executedQueries); if (ImportSettings::$importNotice !== '') { Current::$message->addHtml(ImportSettings::$importNotice); } if (ImportSettings::$localImportFile !== '') { Current::$message->addText('(' . ImportSettings::$localImportFile . ')'); } elseif ( isset($_FILES['import_file']) && is_array($_FILES['import_file']) && isset($_FILES['import_file']['name']) && is_string($_FILES['import_file']['name']) ) { Current::$message->addText('(' . $_FILES['import_file']['name'] . ')'); } } } // Did we hit timeout? Tell it user. if (ImportSettings::$timeoutPassed) { UrlParams::$params['timeout_passed'] = '1'; UrlParams::$params['offset'] = ImportSettings::$offset; if (ImportSettings::$localImportFile !== '') { UrlParams::$params['local_import_file'] = ImportSettings::$localImportFile; } $importUrl = $GLOBALS['errorUrl'] = UrlParams::$goto . Url::getCommon(UrlParams::$params, '&'); Current::$message = Message::error( __( 'Script timeout passed, if you want to finish import,' . ' please %sresubmit the same file%s and import will resume.', ), ); Current::$message->addParamHtml(''); Current::$message->addParamHtml(''); if (ImportSettings::$offset === 0 || (isset($originalSkip) && $originalSkip == ImportSettings::$offset)) { Current::$message->addText( __( 'However on last run no data has been parsed,' . ' this usually means phpMyAdmin won\'t be able to' . ' finish this import unless you increase php time limits.', ), ); } } // if there is any message, copy it into $_SESSION as well, // so we can obtain it by AJAX call if (Current::$message instanceof Message) { $_SESSION['Import_message']['message'] = Current::$message->getDisplay(); } // Parse and analyze the query, for correct db and table name // in case of a query typed in the query window // (but if the query is too large, in case of an imported file, the parser // can choke on it so avoid parsing) $sqlLength = mb_strlen(Current::$sqlQuery); if ($sqlLength <= $config->settings['MaxCharactersInDisplayedSQL']) { [$statementInfo, Current::$database, $tableFromSql] = ParseAnalyze::sqlQuery( Current::$sqlQuery, Current::$database, ); $GLOBALS['reload'] = $statementInfo->flags->reload; ImportSettings::$offset = (int) $statementInfo->flags->offset; if (Current::$table != $tableFromSql && $tableFromSql !== '') { Current::$table = $tableFromSql; } } foreach (ImportSettings::$failedQueries as $die) { Generator::mysqlDie($die['error'], $die['sql'], false, $GLOBALS['errorUrl'], $GLOBALS['error']); } if (ImportSettings::$goSql) { if ($queriesToBeExecuted === []) { $queriesToBeExecuted = [Current::$sqlQuery]; } $htmlOutput = ''; foreach ($queriesToBeExecuted as Current::$sqlQuery) { // parse sql query [$statementInfo, Current::$database, $tableFromSql] = ParseAnalyze::sqlQuery( Current::$sqlQuery, Current::$database, ); ImportSettings::$offset = (int) $statementInfo->flags->offset; $GLOBALS['reload'] = $statementInfo->flags->reload; // Check if User is allowed to issue a 'DROP DATABASE' Statement if ( $this->sql->hasNoRightsToDropDatabase( $statementInfo, $config->settings['AllowUserDropDatabase'], $this->dbi->isSuperUser(), ) ) { Generator::mysqlDie( __('"DROP DATABASE" statements are disabled.'), '', false, $_SESSION['Import_message']['go_back_url'], ); } if (Current::$table != $tableFromSql && $tableFromSql !== '') { Current::$table = $tableFromSql; } $htmlOutput .= $this->sql->executeQueryAndGetQueryResponse( $statementInfo, false, // is_gotofile Current::$database, // db Current::$table, // table '', // sql_query_for_bookmark - see below '', // message_to_show UrlParams::$goto, // goto null, // disp_query null, // disp_message Current::$sqlQuery, null, // complete_query ); } // sql_query_for_bookmark is not included in Sql::executeQueryAndGetQueryResponse // since only one bookmark has to be added for all the queries submitted through // the SQL tab if (! empty($request->getParsedBodyParam('bkm_label')) && ! empty($GLOBALS['import_text'])) { $this->sql->storeTheQueryAsBookmark( Current::$database, $config->selectedServer['user'], $request->getParsedBodyParamAsString('sql_query'), $request->getParsedBodyParamAsString('bkm_label'), $request->hasBodyParam('bkm_replace'), ); } $this->response->addJSON('ajax_reload', $GLOBALS['ajax_reload']); $this->response->addHTML($htmlOutput); return $this->response->response(); } if ($request->hasBodyParam('rollback_query')) { // We rollback because there might be other queries that need to be executed after this, // e.g. creation of a bookmark. $this->dbi->query('ROLLBACK'); ImportSettings::$message .= __('[ROLLBACK occurred.]'); } if ($GLOBALS['result']) { // Save a Bookmark with more than one queries (if Bookmark label given). if (! empty($request->getParsedBodyParam('bkm_label')) && ! empty($GLOBALS['import_text'])) { $relation = new Relation($this->dbi); $this->sql->storeTheQueryAsBookmark( Current::$database, $config->selectedServer['user'], $request->getParsedBodyParamAsString('sql_query'), $request->getParsedBodyParamAsString('bkm_label'), $request->hasBodyParam('bkm_replace'), ); } $this->response->setRequestStatus(true); $this->response->addJSON('message', Message::success(ImportSettings::$message)); $this->response->addJSON( 'sql_query', Generator::getMessage(ImportSettings::$message, Current::$sqlQuery, MessageType::Success), ); } elseif ($GLOBALS['result'] === false) { $this->response->setRequestStatus(false); $this->response->addJSON('message', Message::error(ImportSettings::$message)); } else { /** @psalm-suppress UnresolvableInclude */ include ROOT_PATH . UrlParams::$goto; } return $this->response->response(); } }