Trim whitespaces on jquery.sortableTable.js and add a copyright line
[phpmyadmin.git] / export.php
blobdb6121c804b0b9cc985252a76d2cbbb68b0455be
1 <?php
2 /* vim: set expandtab sw=4 ts=4 sts=4: */
3 /**
4 * Main export handling code
6 * @package PhpMyAdmin
7 */
8 declare(strict_types=1);
10 use PhpMyAdmin\Core;
11 use PhpMyAdmin\DatabaseInterface;
12 use PhpMyAdmin\Encoding;
13 use PhpMyAdmin\Export;
14 use PhpMyAdmin\Plugins;
15 use PhpMyAdmin\Plugins\ExportPlugin;
16 use PhpMyAdmin\Relation;
17 use PhpMyAdmin\Response;
18 use PhpMyAdmin\Sanitize;
19 use PhpMyAdmin\SqlParser\Parser;
20 use PhpMyAdmin\SqlParser\Statements\SelectStatement;
21 use PhpMyAdmin\SqlParser\Utils\Misc;
22 use PhpMyAdmin\Url;
23 use PhpMyAdmin\Util;
25 if (! defined('ROOT_PATH')) {
26 define('ROOT_PATH', __DIR__ . DIRECTORY_SEPARATOR);
29 global $db, $sql_query;
31 include_once ROOT_PATH . 'libraries/common.inc.php';
33 /** @var Response $response */
34 $response = $containerBuilder->get(Response::class);
36 /** @var DatabaseInterface $dbi */
37 $dbi = $containerBuilder->get(DatabaseInterface::class);
39 $header = $response->getHeader();
40 $scripts = $header->getScripts();
41 $scripts->addFile('export_output.js');
43 /** @var Export $export */
44 $export = $containerBuilder->get('export');
46 //check if it's the GET request to check export time out
47 if (isset($_GET['check_time_out'])) {
48 if (isset($_SESSION['pma_export_error'])) {
49 $err = $_SESSION['pma_export_error'];
50 unset($_SESSION['pma_export_error']);
51 echo "timeout";
52 } else {
53 echo "success";
55 exit;
58 /**
59 * Sets globals from $_POST
61 * - Please keep the parameters in order of their appearance in the form
62 * - Some of these parameters are not used, as the code below directly
63 * verifies from the superglobal $_POST or $_REQUEST
64 * TODO: this should be removed to avoid passing user input to GLOBALS
65 * without checking
67 $post_params = [
68 'db',
69 'table',
70 'what',
71 'single_table',
72 'export_type',
73 'export_method',
74 'quick_or_custom',
75 'db_select',
76 'table_select',
77 'table_structure',
78 'table_data',
79 'limit_to',
80 'limit_from',
81 'allrows',
82 'lock_tables',
83 'output_format',
84 'filename_template',
85 'maxsize',
86 'remember_template',
87 'charset',
88 'compression',
89 'as_separate_files',
90 'knjenc',
91 'xkana',
92 'htmlword_structure_or_data',
93 'htmlword_null',
94 'htmlword_columns',
95 'mediawiki_headers',
96 'mediawiki_structure_or_data',
97 'mediawiki_caption',
98 'pdf_structure_or_data',
99 'odt_structure_or_data',
100 'odt_relation',
101 'odt_comments',
102 'odt_mime',
103 'odt_columns',
104 'odt_null',
105 'codegen_structure_or_data',
106 'codegen_format',
107 'excel_null',
108 'excel_removeCRLF',
109 'excel_columns',
110 'excel_edition',
111 'excel_structure_or_data',
112 'yaml_structure_or_data',
113 'ods_null',
114 'ods_structure_or_data',
115 'ods_columns',
116 'json_structure_or_data',
117 'json_pretty_print',
118 'json_unicode',
119 'xml_structure_or_data',
120 'xml_export_events',
121 'xml_export_functions',
122 'xml_export_procedures',
123 'xml_export_tables',
124 'xml_export_triggers',
125 'xml_export_views',
126 'xml_export_contents',
127 'texytext_structure_or_data',
128 'texytext_columns',
129 'texytext_null',
130 'phparray_structure_or_data',
131 'sql_include_comments',
132 'sql_header_comment',
133 'sql_dates',
134 'sql_relation',
135 'sql_mime',
136 'sql_use_transaction',
137 'sql_disable_fk',
138 'sql_compatibility',
139 'sql_structure_or_data',
140 'sql_create_database',
141 'sql_drop_table',
142 'sql_procedure_function',
143 'sql_create_table',
144 'sql_create_view',
145 'sql_create_trigger',
146 'sql_view_current_user',
147 'sql_if_not_exists',
148 'sql_or_replace_view',
149 'sql_auto_increment',
150 'sql_backquotes',
151 'sql_truncate',
152 'sql_delayed',
153 'sql_ignore',
154 'sql_type',
155 'sql_insert_syntax',
156 'sql_max_query_size',
157 'sql_hex_for_binary',
158 'sql_utc_time',
159 'sql_drop_database',
160 'sql_views_as_tables',
161 'sql_metadata',
162 'csv_separator',
163 'csv_enclosed',
164 'csv_escaped',
165 'csv_terminated',
166 'csv_null',
167 'csv_removeCRLF',
168 'csv_columns',
169 'csv_structure_or_data',
170 // csv_replace should have been here but we use it directly from $_POST
171 'latex_caption',
172 'latex_structure_or_data',
173 'latex_structure_caption',
174 'latex_structure_continued_caption',
175 'latex_structure_label',
176 'latex_relation',
177 'latex_comments',
178 'latex_mime',
179 'latex_columns',
180 'latex_data_caption',
181 'latex_data_continued_caption',
182 'latex_data_label',
183 'latex_null',
184 'aliases',
187 foreach ($post_params as $one_post_param) {
188 if (isset($_POST[$one_post_param])) {
189 $GLOBALS[$one_post_param] = $_POST[$one_post_param];
193 $table = $GLOBALS['table'];
195 PhpMyAdmin\Util::checkParameters(['what', 'export_type']);
197 // sanitize this parameter which will be used below in a file inclusion
198 $what = Core::securePath($_POST['what']);
200 // export class instance, not array of properties, as before
201 /** @var ExportPlugin $export_plugin */
202 $export_plugin = Plugins::getPlugin(
203 "export",
204 $what,
205 'libraries/classes/Plugins/Export/',
207 'export_type' => $export_type,
208 'single_table' => isset($single_table),
212 // Check export type
213 if (empty($export_plugin)) {
214 Core::fatalError(__('Bad type!'));
218 * valid compression methods
220 $compression_methods = [];
221 if ($GLOBALS['cfg']['ZipDump'] && function_exists('gzcompress')) {
222 $compression_methods[] = 'zip';
224 if ($GLOBALS['cfg']['GZipDump'] && function_exists('gzencode')) {
225 $compression_methods[] = 'gzip';
229 * init and variable checking
231 $compression = '';
232 $onserver = false;
233 $save_on_server = false;
234 $buffer_needed = false;
235 $back_button = '';
236 $refreshButton = '';
237 $save_filename = '';
238 $file_handle = '';
239 $err_url = '';
240 $filename = '';
241 $separate_files = '';
243 // Is it a quick or custom export?
244 if (isset($_POST['quick_or_custom'])
245 && $_POST['quick_or_custom'] == 'quick'
247 $quick_export = true;
248 } else {
249 $quick_export = false;
252 if ($_POST['output_format'] == 'astext') {
253 $asfile = false;
254 } else {
255 $asfile = true;
256 if (isset($_POST['as_separate_files'])
257 && ! empty($_POST['as_separate_files'])
259 if (isset($_POST['compression'])
260 && ! empty($_POST['compression'])
261 && $_POST['compression'] == 'zip'
263 $separate_files = $_POST['as_separate_files'];
266 if (in_array($_POST['compression'], $compression_methods)) {
267 $compression = $_POST['compression'];
268 $buffer_needed = true;
270 if (($quick_export && ! empty($_POST['quick_export_onserver']))
271 || (! $quick_export && ! empty($_POST['onserver']))
273 if ($quick_export) {
274 $onserver = $_POST['quick_export_onserver'];
275 } else {
276 $onserver = $_POST['onserver'];
278 // Will we save dump on server?
279 $save_on_server = ! empty($cfg['SaveDir']) && $onserver;
284 * If we are sending the export file (as opposed to just displaying it
285 * as text), we have to bypass the usual PhpMyAdmin\Response mechanism
287 if (isset($_POST['output_format']) && $_POST['output_format'] == 'sendit' && ! $save_on_server) {
288 $response->disable();
289 //Disable all active buffers (see: ob_get_status(true) at this point)
290 do {
291 if (ob_get_length() > 0 || ob_get_level() > 0) {
292 $hasBuffer = ob_end_clean();
293 } else {
294 $hasBuffer = false;
296 } while ($hasBuffer);
299 $tables = [];
300 // Generate error url and check for needed variables
301 if ($export_type == 'server') {
302 $err_url = 'server_export.php' . Url::getCommon();
303 } elseif ($export_type == 'database' && strlen($db) > 0) {
304 $err_url = 'db_export.php' . Url::getCommon(['db' => $db]);
305 // Check if we have something to export
306 if (isset($table_select)) {
307 $tables = $table_select;
308 } else {
309 $tables = [];
311 } elseif ($export_type == 'table' && strlen($db) > 0 && strlen($table) > 0) {
312 $err_url = 'tbl_export.php' . Url::getCommon(
314 'db' => $db,
315 'table' => $table,
318 } else {
319 Core::fatalError(__('Bad parameters!'));
322 // Merge SQL Query aliases with Export aliases from
323 // export page, Export page aliases are given more
324 // preference over SQL Query aliases.
325 $parser = new Parser($sql_query);
326 $aliases = [];
327 if (! empty($parser->statements[0])
328 && ($parser->statements[0] instanceof SelectStatement)
330 $aliases = Misc::getAliases($parser->statements[0], $db);
332 if (! empty($_POST['aliases'])) {
333 $aliases = $export->mergeAliases($aliases, $_POST['aliases']);
334 $_SESSION['tmpval']['aliases'] = $_POST['aliases'];
338 * Increase time limit for script execution and initializes some variables
340 Util::setTimeLimit();
341 if (! empty($cfg['MemoryLimit'])) {
342 ini_set('memory_limit', $cfg['MemoryLimit']);
344 register_shutdown_function([$export, 'shutdown']);
345 // Start with empty buffer
346 $dump_buffer = '';
347 $dump_buffer_len = 0;
349 // Array of dump_buffers - used in separate file exports
350 $dump_buffer_objects = [];
352 // We send fake headers to avoid browser timeout when buffering
353 $time_start = time();
355 // Defines the default <CR><LF> format.
356 // For SQL always use \n as MySQL wants this on all platforms.
357 if ($what == 'sql') {
358 $crlf = "\n";
359 } else {
360 $crlf = PHP_EOL;
363 $output_kanji_conversion = Encoding::canConvertKanji();
365 // Do we need to convert charset?
366 $output_charset_conversion = $asfile
367 && Encoding::isSupported()
368 && isset($charset) && $charset != 'utf-8';
370 // Use on the fly compression?
371 $GLOBALS['onfly_compression'] = $GLOBALS['cfg']['CompressOnFly']
372 && $compression == 'gzip';
373 if ($GLOBALS['onfly_compression']) {
374 $GLOBALS['memory_limit'] = $export->getMemoryLimit();
377 // Generate filename and mime type if needed
378 if ($asfile) {
379 if (empty($remember_template)) {
380 $remember_template = '';
382 list($filename, $mime_type) = $export->getFilenameAndMimetype(
383 $export_type,
384 $remember_template,
385 $export_plugin,
386 $compression,
387 $filename_template
389 } else {
390 $mime_type = '';
393 // Open file on server if needed
394 if ($save_on_server) {
395 list($save_filename, $message, $file_handle) = $export->openFile(
396 $filename,
397 $quick_export
400 // problem opening export file on server?
401 if (! empty($message)) {
402 $export->showPage($db, $table, $export_type);
404 } else {
406 * Send headers depending on whether the user chose to download a dump file
407 * or not
409 if ($asfile) {
410 // Download
411 // (avoid rewriting data containing HTML with anchors and forms;
412 // this was reported to happen under Plesk)
413 ini_set('url_rewriter.tags', '');
414 $filename = Sanitize::sanitizeFilename($filename);
416 Core::downloadHeader($filename, $mime_type);
417 } else {
418 // HTML
419 if ($export_type == 'database') {
420 $num_tables = count($tables);
421 if ($num_tables === 0) {
422 $message = PhpMyAdmin\Message::error(
423 __('No tables found in database.')
425 $active_page = 'db_export.php';
426 include ROOT_PATH . 'db_export.php';
427 exit;
430 list($html, $back_button, $refreshButton) = $export->getHtmlForDisplayedExportHeader(
431 $export_type,
432 $db,
433 $table
435 echo $html;
436 unset($html);
437 } // end download
440 /** @var Relation $relation */
441 $relation = $containerBuilder->get('relation');
443 // Fake loop just to allow skip of remain of this code by break, I'd really
444 // need exceptions here :-)
445 do {
446 // Re - initialize
447 $dump_buffer = '';
448 $dump_buffer_len = 0;
450 // Add possibly some comments to export
451 if (! $export_plugin->exportHeader()) {
452 break;
455 // Will we need relation & co. setup?
456 $do_relation = isset($GLOBALS[$what . '_relation']);
457 $do_comments = isset($GLOBALS[$what . '_include_comments'])
458 || isset($GLOBALS[$what . '_comments']);
459 $do_mime = isset($GLOBALS[$what . '_mime']);
460 if ($do_relation || $do_comments || $do_mime) {
461 $cfgRelation = $relation->getRelationsParam();
464 // Include dates in export?
465 $do_dates = isset($GLOBALS[$what . '_dates']);
467 $whatStrucOrData = $GLOBALS[$what . '_structure_or_data'];
470 * Builds the dump
472 if ($export_type == 'server') {
473 if (! isset($db_select)) {
474 $db_select = '';
476 $export->exportServer(
477 $db_select,
478 $whatStrucOrData,
479 $export_plugin,
480 $crlf,
481 $err_url,
482 $export_type,
483 $do_relation,
484 $do_comments,
485 $do_mime,
486 $do_dates,
487 $aliases,
488 $separate_files
490 } elseif ($export_type == 'database') {
491 if (! isset($table_structure) || ! is_array($table_structure)) {
492 $table_structure = [];
494 if (! isset($table_data) || ! is_array($table_data)) {
495 $table_data = [];
497 if (! empty($_POST['structure_or_data_forced'])) {
498 $table_structure = $tables;
499 $table_data = $tables;
501 if (isset($lock_tables)) {
502 $export->lockTables($db, $tables, "READ");
503 try {
504 $export->exportDatabase(
505 $db,
506 $tables,
507 $whatStrucOrData,
508 $table_structure,
509 $table_data,
510 $export_plugin,
511 $crlf,
512 $err_url,
513 $export_type,
514 $do_relation,
515 $do_comments,
516 $do_mime,
517 $do_dates,
518 $aliases,
519 $separate_files
521 } finally {
522 $export->unlockTables();
524 } else {
525 $export->exportDatabase(
526 $db,
527 $tables,
528 $whatStrucOrData,
529 $table_structure,
530 $table_data,
531 $export_plugin,
532 $crlf,
533 $err_url,
534 $export_type,
535 $do_relation,
536 $do_comments,
537 $do_mime,
538 $do_dates,
539 $aliases,
540 $separate_files
543 } else {
544 // We export just one table
545 // $allrows comes from the form when "Dump all rows" has been selected
546 if (! isset($allrows)) {
547 $allrows = '';
549 if (! isset($limit_to)) {
550 $limit_to = '0';
552 if (! isset($limit_from)) {
553 $limit_from = '0';
555 if (isset($lock_tables)) {
556 try {
557 $export->lockTables($db, [$table], "READ");
558 $export->exportTable(
559 $db,
560 $table,
561 $whatStrucOrData,
562 $export_plugin,
563 $crlf,
564 $err_url,
565 $export_type,
566 $do_relation,
567 $do_comments,
568 $do_mime,
569 $do_dates,
570 $allrows,
571 $limit_to,
572 $limit_from,
573 $sql_query,
574 $aliases
576 } finally {
577 $export->unlockTables();
579 } else {
580 $export->exportTable(
581 $db,
582 $table,
583 $whatStrucOrData,
584 $export_plugin,
585 $crlf,
586 $err_url,
587 $export_type,
588 $do_relation,
589 $do_comments,
590 $do_mime,
591 $do_dates,
592 $allrows,
593 $limit_to,
594 $limit_from,
595 $sql_query,
596 $aliases
600 if (! $export_plugin->exportFooter()) {
601 break;
603 } while (false);
604 // End of fake loop
606 if ($save_on_server && ! empty($message)) {
607 $export->showPage($db, $table, $export_type);
611 * Send the dump as a file...
613 if (empty($asfile)) {
614 echo $export->getHtmlForDisplayedExportFooter($back_button, $refreshButton);
615 return;
616 } // end if
618 // Convert the charset if required.
619 if ($output_charset_conversion) {
620 $dump_buffer = Encoding::convertString(
621 'utf-8',
622 $GLOBALS['charset'],
623 $dump_buffer
627 // Compression needed?
628 if ($compression) {
629 if (! empty($separate_files)) {
630 $dump_buffer = $export->compress(
631 $dump_buffer_objects,
632 $compression,
633 $filename
635 } else {
636 $dump_buffer = $export->compress($dump_buffer, $compression, $filename);
640 /* If we saved on server, we have to close file now */
641 if ($save_on_server) {
642 $message = $export->closeFile(
643 $file_handle,
644 $dump_buffer,
645 $save_filename
647 $export->showPage($db, $table, $export_type);
648 } else {
649 echo $dump_buffer;