Export.php 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * function for the main export logic
  5. *
  6. * @package PhpMyAdmin
  7. */
  8. namespace PhpMyAdmin;
  9. use PhpMyAdmin\Core;
  10. use PhpMyAdmin\Encoding;
  11. use PhpMyAdmin\Message;
  12. use PhpMyAdmin\Plugins;
  13. use PhpMyAdmin\Plugins\ExportPlugin;
  14. use PhpMyAdmin\Sanitize;
  15. use PhpMyAdmin\Table;
  16. use PhpMyAdmin\Url;
  17. use PhpMyAdmin\Util;
  18. use PhpMyAdmin\ZipExtension;
  19. /**
  20. * PhpMyAdmin\Export class
  21. *
  22. * @package PhpMyAdmin
  23. */
  24. class Export
  25. {
  26. /**
  27. * Sets a session variable upon a possible fatal error during export
  28. *
  29. * @return void
  30. */
  31. public static function shutdown()
  32. {
  33. $error = error_get_last();
  34. if ($error != null && mb_strpos($error['message'], "execution time")) {
  35. //set session variable to check if there was error while exporting
  36. $_SESSION['pma_export_error'] = $error['message'];
  37. }
  38. }
  39. /**
  40. * Detect ob_gzhandler
  41. *
  42. * @return bool
  43. */
  44. public static function isGzHandlerEnabled()
  45. {
  46. return in_array('ob_gzhandler', ob_list_handlers());
  47. }
  48. /**
  49. * Detect whether gzencode is needed; it might not be needed if
  50. * the server is already compressing by itself
  51. *
  52. * @return bool Whether gzencode is needed
  53. */
  54. public static function gzencodeNeeded()
  55. {
  56. /*
  57. * We should gzencode only if the function exists
  58. * but we don't want to compress twice, therefore
  59. * gzencode only if transparent compression is not enabled
  60. * and gz compression was not asked via $cfg['OBGzip']
  61. * but transparent compression does not apply when saving to server
  62. */
  63. $chromeAndGreaterThan43 = PMA_USR_BROWSER_AGENT == 'CHROME'
  64. && PMA_USR_BROWSER_VER >= 43; // see bug #4942
  65. if (function_exists('gzencode')
  66. && ((! ini_get('zlib.output_compression')
  67. && ! self::isGzHandlerEnabled())
  68. || $GLOBALS['save_on_server']
  69. || $chromeAndGreaterThan43)
  70. ) {
  71. return true;
  72. }
  73. return false;
  74. }
  75. /**
  76. * Output handler for all exports, if needed buffering, it stores data into
  77. * $dump_buffer, otherwise it prints them out.
  78. *
  79. * @param string $line the insert statement
  80. *
  81. * @return bool Whether output succeeded
  82. */
  83. public static function outputHandler($line)
  84. {
  85. global $time_start, $dump_buffer, $dump_buffer_len, $save_filename;
  86. // Kanji encoding convert feature
  87. if ($GLOBALS['output_kanji_conversion']) {
  88. $line = Encoding::kanjiStrConv(
  89. $line,
  90. $GLOBALS['knjenc'],
  91. isset($GLOBALS['xkana']) ? $GLOBALS['xkana'] : ''
  92. );
  93. }
  94. // If we have to buffer data, we will perform everything at once at the end
  95. if ($GLOBALS['buffer_needed']) {
  96. $dump_buffer .= $line;
  97. if ($GLOBALS['onfly_compression']) {
  98. $dump_buffer_len += strlen($line);
  99. if ($dump_buffer_len > $GLOBALS['memory_limit']) {
  100. if ($GLOBALS['output_charset_conversion']) {
  101. $dump_buffer = Encoding::convertString(
  102. 'utf-8',
  103. $GLOBALS['charset'],
  104. $dump_buffer
  105. );
  106. }
  107. if ($GLOBALS['compression'] == 'gzip'
  108. && self::gzencodeNeeded()
  109. ) {
  110. // as a gzipped file
  111. // without the optional parameter level because it bugs
  112. $dump_buffer = gzencode($dump_buffer);
  113. }
  114. if ($GLOBALS['save_on_server']) {
  115. $write_result = @fwrite($GLOBALS['file_handle'], $dump_buffer);
  116. // Here, use strlen rather than mb_strlen to get the length
  117. // in bytes to compare against the number of bytes written.
  118. if ($write_result != strlen($dump_buffer)) {
  119. $GLOBALS['message'] = Message::error(
  120. __('Insufficient space to save the file %s.')
  121. );
  122. $GLOBALS['message']->addParam($save_filename);
  123. return false;
  124. }
  125. } else {
  126. echo $dump_buffer;
  127. }
  128. $dump_buffer = '';
  129. $dump_buffer_len = 0;
  130. }
  131. } else {
  132. $time_now = time();
  133. if ($time_start >= $time_now + 30) {
  134. $time_start = $time_now;
  135. header('X-pmaPing: Pong');
  136. } // end if
  137. }
  138. } else {
  139. if ($GLOBALS['asfile']) {
  140. if ($GLOBALS['output_charset_conversion']) {
  141. $line = Encoding::convertString(
  142. 'utf-8',
  143. $GLOBALS['charset'],
  144. $line
  145. );
  146. }
  147. if ($GLOBALS['save_on_server'] && mb_strlen($line) > 0) {
  148. $write_result = @fwrite($GLOBALS['file_handle'], $line);
  149. // Here, use strlen rather than mb_strlen to get the length
  150. // in bytes to compare against the number of bytes written.
  151. if (! $write_result
  152. || $write_result != strlen($line)
  153. ) {
  154. $GLOBALS['message'] = Message::error(
  155. __('Insufficient space to save the file %s.')
  156. );
  157. $GLOBALS['message']->addParam($save_filename);
  158. return false;
  159. }
  160. $time_now = time();
  161. if ($time_start >= $time_now + 30) {
  162. $time_start = $time_now;
  163. header('X-pmaPing: Pong');
  164. } // end if
  165. } else {
  166. // We export as file - output normally
  167. echo $line;
  168. }
  169. } else {
  170. // We export as html - replace special chars
  171. echo htmlspecialchars($line);
  172. }
  173. }
  174. return true;
  175. } // end of the 'self::outputHandler()' function
  176. /**
  177. * Returns HTML containing the footer for a displayed export
  178. *
  179. * @param string $back_button the link for going Back
  180. * @param string $refreshButton the link for refreshing page
  181. *
  182. * @return string $html the HTML output
  183. */
  184. public static function getHtmlForDisplayedExportFooter($back_button, $refreshButton)
  185. {
  186. /**
  187. * Close the html tags and add the footers for on-screen export
  188. */
  189. $html = '</textarea>'
  190. . ' </form>'
  191. . '<br />'
  192. // bottom back button
  193. . $back_button
  194. . $refreshButton
  195. . '</div>'
  196. . '<script type="text/javascript">' . "\n"
  197. . '//<![CDATA[' . "\n"
  198. . 'var $body = $("body");' . "\n"
  199. . '$("#textSQLDUMP")' . "\n"
  200. . '.width($body.width() - 50)' . "\n"
  201. . '.height($body.height() - 100);' . "\n"
  202. . '//]]>' . "\n"
  203. . '</script>' . "\n";
  204. return $html;
  205. }
  206. /**
  207. * Computes the memory limit for export
  208. *
  209. * @return int $memory_limit the memory limit
  210. */
  211. public static function getMemoryLimit()
  212. {
  213. $memory_limit = trim(ini_get('memory_limit'));
  214. $memory_limit_num = (int)substr($memory_limit, 0, -1);
  215. $lowerLastChar = strtolower(substr($memory_limit, -1));
  216. // 2 MB as default
  217. if (empty($memory_limit) || '-1' == $memory_limit) {
  218. $memory_limit = 2 * 1024 * 1024;
  219. } elseif ($lowerLastChar == 'm') {
  220. $memory_limit = $memory_limit_num * 1024 * 1024;
  221. } elseif ($lowerLastChar == 'k') {
  222. $memory_limit = $memory_limit_num * 1024;
  223. } elseif ($lowerLastChar == 'g') {
  224. $memory_limit = $memory_limit_num * 1024 * 1024 * 1024;
  225. } else {
  226. $memory_limit = (int)$memory_limit;
  227. }
  228. // Some of memory is needed for other things and as threshold.
  229. // During export I had allocated (see memory_get_usage function)
  230. // approx 1.2MB so this comes from that.
  231. if ($memory_limit > 1500000) {
  232. $memory_limit -= 1500000;
  233. }
  234. // Some memory is needed for compression, assume 1/3
  235. $memory_limit /= 8;
  236. return $memory_limit;
  237. }
  238. /**
  239. * Return the filename and MIME type for export file
  240. *
  241. * @param string $export_type type of export
  242. * @param string $remember_template whether to remember template
  243. * @param ExportPlugin $export_plugin the export plugin
  244. * @param string $compression compression asked
  245. * @param string $filename_template the filename template
  246. *
  247. * @return string[] the filename template and mime type
  248. */
  249. public static function getFilenameAndMimetype(
  250. $export_type, $remember_template, $export_plugin, $compression,
  251. $filename_template
  252. ) {
  253. if ($export_type == 'server') {
  254. if (! empty($remember_template)) {
  255. $GLOBALS['PMA_Config']->setUserValue(
  256. 'pma_server_filename_template',
  257. 'Export/file_template_server',
  258. $filename_template
  259. );
  260. }
  261. } elseif ($export_type == 'database') {
  262. if (! empty($remember_template)) {
  263. $GLOBALS['PMA_Config']->setUserValue(
  264. 'pma_db_filename_template',
  265. 'Export/file_template_database',
  266. $filename_template
  267. );
  268. }
  269. } else {
  270. if (! empty($remember_template)) {
  271. $GLOBALS['PMA_Config']->setUserValue(
  272. 'pma_table_filename_template',
  273. 'Export/file_template_table',
  274. $filename_template
  275. );
  276. }
  277. }
  278. $filename = Util::expandUserString($filename_template);
  279. // remove dots in filename (coming from either the template or already
  280. // part of the filename) to avoid a remote code execution vulnerability
  281. $filename = Sanitize::sanitizeFilename($filename, $replaceDots = true);
  282. // Grab basic dump extension and mime type
  283. // Check if the user already added extension;
  284. // get the substring where the extension would be if it was included
  285. $extension_start_pos = mb_strlen($filename) - mb_strlen(
  286. $export_plugin->getProperties()->getExtension()
  287. ) - 1;
  288. $user_extension = mb_substr(
  289. $filename, $extension_start_pos, mb_strlen($filename)
  290. );
  291. $required_extension = "." . $export_plugin->getProperties()->getExtension();
  292. if (mb_strtolower($user_extension) != $required_extension) {
  293. $filename .= $required_extension;
  294. }
  295. $mime_type = $export_plugin->getProperties()->getMimeType();
  296. // If dump is going to be compressed, set correct mime_type and add
  297. // compression to extension
  298. if ($compression == 'gzip') {
  299. $filename .= '.gz';
  300. $mime_type = 'application/x-gzip';
  301. } elseif ($compression == 'zip') {
  302. $filename .= '.zip';
  303. $mime_type = 'application/zip';
  304. }
  305. return array($filename, $mime_type);
  306. }
  307. /**
  308. * Open the export file
  309. *
  310. * @param string $filename the export filename
  311. * @param boolean $quick_export whether it's a quick export or not
  312. *
  313. * @return array the full save filename, possible message and the file handle
  314. */
  315. public static function openFile($filename, $quick_export)
  316. {
  317. $file_handle = null;
  318. $message = '';
  319. $save_filename = Util::userDir($GLOBALS['cfg']['SaveDir'])
  320. . preg_replace('@[/\\\\]@', '_', $filename);
  321. if (@file_exists($save_filename)
  322. && ((! $quick_export && empty($_REQUEST['onserver_overwrite']))
  323. || ($quick_export
  324. && $_REQUEST['quick_export_onserver_overwrite'] != 'saveitover'))
  325. ) {
  326. $message = Message::error(
  327. __(
  328. 'File %s already exists on server, '
  329. . 'change filename or check overwrite option.'
  330. )
  331. );
  332. $message->addParam($save_filename);
  333. } elseif (@is_file($save_filename) && ! @is_writable($save_filename)) {
  334. $message = Message::error(
  335. __(
  336. 'The web server does not have permission '
  337. . 'to save the file %s.'
  338. )
  339. );
  340. $message->addParam($save_filename);
  341. } elseif (! $file_handle = @fopen($save_filename, 'w')) {
  342. $message = Message::error(
  343. __(
  344. 'The web server does not have permission '
  345. . 'to save the file %s.'
  346. )
  347. );
  348. $message->addParam($save_filename);
  349. }
  350. return array($save_filename, $message, $file_handle);
  351. }
  352. /**
  353. * Close the export file
  354. *
  355. * @param resource $file_handle the export file handle
  356. * @param string $dump_buffer the current dump buffer
  357. * @param string $save_filename the export filename
  358. *
  359. * @return Message $message a message object (or empty string)
  360. */
  361. public static function closeFile($file_handle, $dump_buffer, $save_filename)
  362. {
  363. $write_result = @fwrite($file_handle, $dump_buffer);
  364. fclose($file_handle);
  365. // Here, use strlen rather than mb_strlen to get the length
  366. // in bytes to compare against the number of bytes written.
  367. if (strlen($dump_buffer) > 0
  368. && (! $write_result || $write_result != strlen($dump_buffer))
  369. ) {
  370. $message = new Message(
  371. __('Insufficient space to save the file %s.'),
  372. Message::ERROR,
  373. array($save_filename)
  374. );
  375. } else {
  376. $message = new Message(
  377. __('Dump has been saved to file %s.'),
  378. Message::SUCCESS,
  379. array($save_filename)
  380. );
  381. }
  382. return $message;
  383. }
  384. /**
  385. * Compress the export buffer
  386. *
  387. * @param array|string $dump_buffer the current dump buffer
  388. * @param string $compression the compression mode
  389. * @param string $filename the filename
  390. *
  391. * @return object $message a message object (or empty string)
  392. */
  393. public static function compress($dump_buffer, $compression, $filename)
  394. {
  395. if ($compression == 'zip' && function_exists('gzcompress')) {
  396. $zipExtension = new ZipExtension();
  397. $filename = substr($filename, 0, -4); // remove extension (.zip)
  398. $dump_buffer = $zipExtension->createFile($dump_buffer, $filename);
  399. } elseif ($compression == 'gzip' && self::gzencodeNeeded()) {
  400. // without the optional parameter level because it bugs
  401. $dump_buffer = gzencode($dump_buffer);
  402. }
  403. return $dump_buffer;
  404. }
  405. /**
  406. * Saves the dump_buffer for a particular table in an array
  407. * Used in separate files export
  408. *
  409. * @param string $object_name the name of current object to be stored
  410. * @param boolean $append optional boolean to append to an existing index or not
  411. *
  412. * @return void
  413. */
  414. public static function saveObjectInBuffer($object_name, $append = false)
  415. {
  416. global $dump_buffer_objects, $dump_buffer, $dump_buffer_len;
  417. if (! empty($dump_buffer)) {
  418. if ($append && isset($dump_buffer_objects[$object_name])) {
  419. $dump_buffer_objects[$object_name] .= $dump_buffer;
  420. } else {
  421. $dump_buffer_objects[$object_name] = $dump_buffer;
  422. }
  423. }
  424. // Re - initialize
  425. $dump_buffer = '';
  426. $dump_buffer_len = 0;
  427. }
  428. /**
  429. * Returns HTML containing the header for a displayed export
  430. *
  431. * @param string $export_type the export type
  432. * @param string $db the database name
  433. * @param string $table the table name
  434. *
  435. * @return string[] the generated HTML and back button
  436. */
  437. public static function getHtmlForDisplayedExportHeader($export_type, $db, $table)
  438. {
  439. $html = '<div>';
  440. /**
  441. * Displays a back button with all the $_REQUEST data in the URL
  442. * (store in a variable to also display after the textarea)
  443. */
  444. $back_button = '<p id="export_back_button">[ <a href="';
  445. if ($export_type == 'server') {
  446. $back_button .= 'server_export.php' . Url::getCommon();
  447. } elseif ($export_type == 'database') {
  448. $back_button .= 'db_export.php' . Url::getCommon(array('db' => $db));
  449. } else {
  450. $back_button .= 'tbl_export.php' . Url::getCommon(
  451. array(
  452. 'db' => $db, 'table' => $table
  453. )
  454. );
  455. }
  456. // Convert the multiple select elements from an array to a string
  457. if ($export_type == 'server' && isset($_REQUEST['db_select'])) {
  458. $_REQUEST['db_select'] = implode(",", $_REQUEST['db_select']);
  459. } elseif ($export_type == 'database') {
  460. if (isset($_REQUEST['table_select'])) {
  461. $_REQUEST['table_select'] = implode(",", $_REQUEST['table_select']);
  462. }
  463. if (isset($_REQUEST['table_structure'])) {
  464. $_REQUEST['table_structure'] = implode(
  465. ",",
  466. $_REQUEST['table_structure']
  467. );
  468. } elseif (empty($_REQUEST['structure_or_data_forced'])) {
  469. $_REQUEST['table_structure'] = '';
  470. }
  471. if (isset($_REQUEST['table_data'])) {
  472. $_REQUEST['table_data'] = implode(",", $_REQUEST['table_data']);
  473. } elseif (empty($_REQUEST['structure_or_data_forced'])) {
  474. $_REQUEST['table_data'] = '';
  475. }
  476. }
  477. foreach ($_REQUEST as $name => $value) {
  478. if (!is_array($value)) {
  479. $back_button .= '&amp;' . urlencode($name) . '=' . urlencode($value);
  480. }
  481. }
  482. $back_button .= '&amp;repopulate=1">' . __('Back') . '</a> ]</p>';
  483. $html .= '<br />';
  484. $html .= $back_button;
  485. $refreshButton = '<form id="export_refresh_form" method="POST" action="export.php" class="disableAjax">';
  486. $refreshButton .= '[ <a class="disableAjax" onclick="$(this).parent().submit()">'. __('Refresh') .'</a> ]';
  487. foreach($_POST as $name => $value) {
  488. if (is_array($value)) {
  489. foreach($value as $val) {
  490. $refreshButton .= '<input type="hidden" name="' . urlencode((string) $name) . '[]" value="' . urlencode((string) $val) . '">';
  491. }
  492. }
  493. else {
  494. $refreshButton .= '<input type="hidden" name="' . urlencode((string) $name) . '" value="' . urlencode((string) $value) . '">';
  495. }
  496. }
  497. $refreshButton .= '</form>';
  498. $html .= $refreshButton
  499. . '<br />'
  500. . '<form name="nofunction">'
  501. . '<textarea name="sqldump" cols="50" rows="30" '
  502. . 'id="textSQLDUMP" wrap="OFF">';
  503. return array($html, $back_button, $refreshButton);
  504. }
  505. /**
  506. * Export at the server level
  507. *
  508. * @param string $db_select the selected databases to export
  509. * @param string $whatStrucOrData structure or data or both
  510. * @param ExportPlugin $export_plugin the selected export plugin
  511. * @param string $crlf end of line character(s)
  512. * @param string $err_url the URL in case of error
  513. * @param string $export_type the export type
  514. * @param bool $do_relation whether to export relation info
  515. * @param bool $do_comments whether to add comments
  516. * @param bool $do_mime whether to add MIME info
  517. * @param bool $do_dates whether to add dates
  518. * @param array $aliases alias information for db/table/column
  519. * @param string $separate_files whether it is a separate-files export
  520. *
  521. * @return void
  522. */
  523. public static function exportServer(
  524. $db_select, $whatStrucOrData, $export_plugin, $crlf, $err_url,
  525. $export_type, $do_relation, $do_comments, $do_mime, $do_dates,
  526. array $aliases, $separate_files
  527. ) {
  528. if (! empty($db_select)) {
  529. $tmp_select = implode($db_select, '|');
  530. $tmp_select = '|' . $tmp_select . '|';
  531. }
  532. // Walk over databases
  533. foreach ($GLOBALS['dblist']->databases as $current_db) {
  534. if (isset($tmp_select)
  535. && mb_strpos(' ' . $tmp_select, '|' . $current_db . '|')
  536. ) {
  537. $tables = $GLOBALS['dbi']->getTables($current_db);
  538. self::exportDatabase(
  539. $current_db, $tables, $whatStrucOrData, $tables, $tables,
  540. $export_plugin, $crlf, $err_url, $export_type, $do_relation,
  541. $do_comments, $do_mime, $do_dates, $aliases,
  542. $separate_files == 'database' ? $separate_files : ''
  543. );
  544. if ($separate_files == 'server') {
  545. self::saveObjectInBuffer($current_db);
  546. }
  547. }
  548. } // end foreach database
  549. }
  550. /**
  551. * Export at the database level
  552. *
  553. * @param string $db the database to export
  554. * @param array $tables the tables to export
  555. * @param string $whatStrucOrData structure or data or both
  556. * @param array $table_structure whether to export structure for each table
  557. * @param array $table_data whether to export data for each table
  558. * @param ExportPlugin $export_plugin the selected export plugin
  559. * @param string $crlf end of line character(s)
  560. * @param string $err_url the URL in case of error
  561. * @param string $export_type the export type
  562. * @param bool $do_relation whether to export relation info
  563. * @param bool $do_comments whether to add comments
  564. * @param bool $do_mime whether to add MIME info
  565. * @param bool $do_dates whether to add dates
  566. * @param array $aliases Alias information for db/table/column
  567. * @param string $separate_files whether it is a separate-files export
  568. *
  569. * @return void
  570. */
  571. public static function exportDatabase(
  572. $db, array $tables, $whatStrucOrData, array $table_structure, array $table_data,
  573. $export_plugin, $crlf, $err_url, $export_type, $do_relation,
  574. $do_comments, $do_mime, $do_dates, array $aliases, $separate_files
  575. ) {
  576. $db_alias = !empty($aliases[$db]['alias'])
  577. ? $aliases[$db]['alias'] : '';
  578. if (! $export_plugin->exportDBHeader($db, $db_alias)) {
  579. return;
  580. }
  581. if (! $export_plugin->exportDBCreate($db, $export_type, $db_alias)) {
  582. return;
  583. }
  584. if ($separate_files == 'database') {
  585. self::saveObjectInBuffer('database', true);
  586. }
  587. if (($GLOBALS['sql_structure_or_data'] == 'structure'
  588. || $GLOBALS['sql_structure_or_data'] == 'structure_and_data')
  589. && isset($GLOBALS['sql_procedure_function'])
  590. ) {
  591. $export_plugin->exportRoutines($db, $aliases);
  592. if ($separate_files == 'database') {
  593. self::saveObjectInBuffer('routines');
  594. }
  595. }
  596. $views = array();
  597. foreach ($tables as $table) {
  598. $_table = new Table($table, $db);
  599. // if this is a view, collect it for later;
  600. // views must be exported after the tables
  601. $is_view = $_table->isView();
  602. if ($is_view) {
  603. $views[] = $table;
  604. }
  605. if (($whatStrucOrData == 'structure'
  606. || $whatStrucOrData == 'structure_and_data')
  607. && in_array($table, $table_structure)
  608. ) {
  609. // for a view, export a stand-in definition of the table
  610. // to resolve view dependencies (only when it's a single-file export)
  611. if ($is_view) {
  612. if ($separate_files == ''
  613. && isset($GLOBALS['sql_create_view'])
  614. && ! $export_plugin->exportStructure(
  615. $db, $table, $crlf, $err_url, 'stand_in',
  616. $export_type, $do_relation, $do_comments,
  617. $do_mime, $do_dates, $aliases
  618. )
  619. ) {
  620. break;
  621. }
  622. } elseif (isset($GLOBALS['sql_create_table'])) {
  623. $table_size = $GLOBALS['maxsize'];
  624. // Checking if the maximum table size constrain has been set
  625. // And if that constrain is a valid number or not
  626. if ($table_size !== '' && is_numeric($table_size)) {
  627. // This obtains the current table's size
  628. $query = 'SELECT data_length + index_length
  629. from information_schema.TABLES
  630. WHERE table_schema = "' . $GLOBALS['dbi']->escapeString($db) . '"
  631. AND table_name = "' . $GLOBALS['dbi']->escapeString($table) . '"';
  632. $size = $GLOBALS['dbi']->fetchValue($query);
  633. //Converting the size to MB
  634. $size = ($size / 1024) / 1024;
  635. if ($size > $table_size) {
  636. continue;
  637. }
  638. }
  639. if (! $export_plugin->exportStructure(
  640. $db, $table, $crlf, $err_url, 'create_table',
  641. $export_type, $do_relation, $do_comments,
  642. $do_mime, $do_dates, $aliases
  643. )) {
  644. break;
  645. }
  646. }
  647. }
  648. // if this is a view or a merge table, don't export data
  649. if (($whatStrucOrData == 'data' || $whatStrucOrData == 'structure_and_data')
  650. && in_array($table, $table_data)
  651. && ! ($is_view)
  652. ) {
  653. $tableObj = new Table($table, $db);
  654. $nonGeneratedCols = $tableObj->getNonGeneratedColumns(true);
  655. $local_query = 'SELECT ' . implode(', ', $nonGeneratedCols)
  656. . ' FROM ' . Util::backquote($db)
  657. . '.' . Util::backquote($table);
  658. if (! $export_plugin->exportData(
  659. $db, $table, $crlf, $err_url, $local_query, $aliases
  660. )) {
  661. break;
  662. }
  663. }
  664. // this buffer was filled, we save it and go to the next one
  665. if ($separate_files == 'database') {
  666. self::saveObjectInBuffer('table_' . $table);
  667. }
  668. // now export the triggers (needs to be done after the data because
  669. // triggers can modify already imported tables)
  670. if (isset($GLOBALS['sql_create_trigger']) && ($whatStrucOrData == 'structure'
  671. || $whatStrucOrData == 'structure_and_data')
  672. && in_array($table, $table_structure)
  673. ) {
  674. if (! $export_plugin->exportStructure(
  675. $db, $table, $crlf, $err_url, 'triggers',
  676. $export_type, $do_relation, $do_comments,
  677. $do_mime, $do_dates, $aliases
  678. )) {
  679. break;
  680. }
  681. if ($separate_files == 'database') {
  682. self::saveObjectInBuffer('table_' . $table, true);
  683. }
  684. }
  685. }
  686. if (isset($GLOBALS['sql_create_view'])) {
  687. foreach ($views as $view) {
  688. // no data export for a view
  689. if ($whatStrucOrData == 'structure'
  690. || $whatStrucOrData == 'structure_and_data'
  691. ) {
  692. if (! $export_plugin->exportStructure(
  693. $db, $view, $crlf, $err_url, 'create_view',
  694. $export_type, $do_relation, $do_comments,
  695. $do_mime, $do_dates, $aliases
  696. )) {
  697. break;
  698. }
  699. if ($separate_files == 'database') {
  700. self::saveObjectInBuffer('view_' . $view);
  701. }
  702. }
  703. }
  704. }
  705. if (! $export_plugin->exportDBFooter($db)) {
  706. return;
  707. }
  708. // export metadata related to this db
  709. if (isset($GLOBALS['sql_metadata'])) {
  710. // Types of metadata to export.
  711. // In the future these can be allowed to be selected by the user
  712. $metadataTypes = self::getMetadataTypes();
  713. $export_plugin->exportMetadata($db, $tables, $metadataTypes);
  714. if ($separate_files == 'database') {
  715. self::saveObjectInBuffer('metadata');
  716. }
  717. }
  718. if ($separate_files == 'database') {
  719. self::saveObjectInBuffer('extra');
  720. }
  721. if (($GLOBALS['sql_structure_or_data'] == 'structure'
  722. || $GLOBALS['sql_structure_or_data'] == 'structure_and_data')
  723. && isset($GLOBALS['sql_procedure_function'])
  724. ) {
  725. $export_plugin->exportEvents($db);
  726. if ($separate_files == 'database') {
  727. self::saveObjectInBuffer('events');
  728. }
  729. }
  730. }
  731. /**
  732. * Export at the table level
  733. *
  734. * @param string $db the database to export
  735. * @param string $table the table to export
  736. * @param string $whatStrucOrData structure or data or both
  737. * @param ExportPlugin $export_plugin the selected export plugin
  738. * @param string $crlf end of line character(s)
  739. * @param string $err_url the URL in case of error
  740. * @param string $export_type the export type
  741. * @param bool $do_relation whether to export relation info
  742. * @param bool $do_comments whether to add comments
  743. * @param bool $do_mime whether to add MIME info
  744. * @param bool $do_dates whether to add dates
  745. * @param string $allrows whether "dump all rows" was ticked
  746. * @param string $limit_to upper limit
  747. * @param string $limit_from starting limit
  748. * @param string $sql_query query for which exporting is requested
  749. * @param array $aliases Alias information for db/table/column
  750. *
  751. * @return void
  752. */
  753. public static function exportTable(
  754. $db, $table, $whatStrucOrData, $export_plugin, $crlf, $err_url,
  755. $export_type, $do_relation, $do_comments, $do_mime, $do_dates,
  756. $allrows, $limit_to, $limit_from, $sql_query, array $aliases
  757. ) {
  758. $db_alias = !empty($aliases[$db]['alias'])
  759. ? $aliases[$db]['alias'] : '';
  760. if (! $export_plugin->exportDBHeader($db, $db_alias)) {
  761. return;
  762. }
  763. if (isset($allrows)
  764. && $allrows == '0'
  765. && $limit_to > 0
  766. && $limit_from >= 0
  767. ) {
  768. $add_query = ' LIMIT '
  769. . (($limit_from > 0) ? $limit_from . ', ' : '')
  770. . $limit_to;
  771. } else {
  772. $add_query = '';
  773. }
  774. $_table = new Table($table, $db);
  775. $is_view = $_table->isView();
  776. if ($whatStrucOrData == 'structure'
  777. || $whatStrucOrData == 'structure_and_data'
  778. ) {
  779. if ($is_view) {
  780. if (isset($GLOBALS['sql_create_view'])) {
  781. if (! $export_plugin->exportStructure(
  782. $db, $table, $crlf, $err_url, 'create_view',
  783. $export_type, $do_relation, $do_comments,
  784. $do_mime, $do_dates, $aliases
  785. )) {
  786. return;
  787. }
  788. }
  789. } elseif (isset($GLOBALS['sql_create_table'])) {
  790. if (! $export_plugin->exportStructure(
  791. $db, $table, $crlf, $err_url, 'create_table',
  792. $export_type, $do_relation, $do_comments,
  793. $do_mime, $do_dates, $aliases
  794. )) {
  795. return;
  796. }
  797. }
  798. }
  799. // If this is an export of a single view, we have to export data;
  800. // for example, a PDF report
  801. // if it is a merge table, no data is exported
  802. if ($whatStrucOrData == 'data'
  803. || $whatStrucOrData == 'structure_and_data'
  804. ) {
  805. if (! empty($sql_query)) {
  806. // only preg_replace if needed
  807. if (! empty($add_query)) {
  808. // remove trailing semicolon before adding a LIMIT
  809. $sql_query = preg_replace('%;\s*$%', '', $sql_query);
  810. }
  811. $local_query = $sql_query . $add_query;
  812. $GLOBALS['dbi']->selectDb($db);
  813. } else {
  814. // Data is exported only for Non-generated columns
  815. $tableObj = new Table($table, $db);
  816. $nonGeneratedCols = $tableObj->getNonGeneratedColumns(true);
  817. $local_query = 'SELECT ' . implode(', ', $nonGeneratedCols)
  818. . ' FROM ' . Util::backquote($db)
  819. . '.' . Util::backquote($table) . $add_query;
  820. }
  821. if (! $export_plugin->exportData(
  822. $db, $table, $crlf, $err_url, $local_query, $aliases
  823. )) {
  824. return;
  825. }
  826. }
  827. // now export the triggers (needs to be done after the data because
  828. // triggers can modify already imported tables)
  829. if (isset($GLOBALS['sql_create_trigger']) && ($whatStrucOrData == 'structure'
  830. || $whatStrucOrData == 'structure_and_data')
  831. ) {
  832. if (! $export_plugin->exportStructure(
  833. $db, $table, $crlf, $err_url, 'triggers',
  834. $export_type, $do_relation, $do_comments,
  835. $do_mime, $do_dates, $aliases
  836. )) {
  837. return;
  838. }
  839. }
  840. if (! $export_plugin->exportDBFooter($db)) {
  841. return;
  842. }
  843. if (isset($GLOBALS['sql_metadata'])) {
  844. // Types of metadata to export.
  845. // In the future these can be allowed to be selected by the user
  846. $metadataTypes = self::getMetadataTypes();
  847. $export_plugin->exportMetadata($db, $table, $metadataTypes);
  848. }
  849. }
  850. /**
  851. * Loads correct page after doing export
  852. *
  853. * @param string $db the database name
  854. * @param string $table the table name
  855. * @param string $export_type Export type
  856. *
  857. * @return void
  858. */
  859. public static function showPage($db, $table, $export_type)
  860. {
  861. global $cfg;
  862. if ($export_type == 'server') {
  863. $active_page = 'server_export.php';
  864. include_once 'server_export.php';
  865. } elseif ($export_type == 'database') {
  866. $active_page = 'db_export.php';
  867. include_once 'db_export.php';
  868. } else {
  869. $active_page = 'tbl_export.php';
  870. include_once 'tbl_export.php';
  871. }
  872. exit();
  873. }
  874. /**
  875. * Merge two alias arrays, if array1 and array2 have
  876. * conflicting alias then array2 value is used if it
  877. * is non empty otherwise array1 value.
  878. *
  879. * @param array $aliases1 first array of aliases
  880. * @param array $aliases2 second array of aliases
  881. *
  882. * @return array resultant merged aliases info
  883. */
  884. public static function mergeAliases(array $aliases1, array $aliases2)
  885. {
  886. // First do a recursive array merge
  887. // on aliases arrays.
  888. $aliases = array_merge_recursive($aliases1, $aliases2);
  889. // Now, resolve conflicts in aliases, if any
  890. foreach ($aliases as $db_name => $db) {
  891. // If alias key is an array then
  892. // it is a merge conflict.
  893. if (isset($db['alias']) && is_array($db['alias'])) {
  894. $val1 = $db['alias'][0];
  895. $val2 = $db['alias'][1];
  896. // Use aliases2 alias if non empty
  897. $aliases[$db_name]['alias']
  898. = empty($val2) ? $val1 : $val2;
  899. }
  900. if (!isset($db['tables'])) {
  901. continue;
  902. }
  903. foreach ($db['tables'] as $tbl_name => $tbl) {
  904. if (isset($tbl['alias']) && is_array($tbl['alias'])) {
  905. $val1 = $tbl['alias'][0];
  906. $val2 = $tbl['alias'][1];
  907. // Use aliases2 alias if non empty
  908. $aliases[$db_name]['tables'][$tbl_name]['alias']
  909. = empty($val2) ? $val1 : $val2;
  910. }
  911. if (!isset($tbl['columns'])) {
  912. continue;
  913. }
  914. foreach ($tbl['columns'] as $col => $col_as) {
  915. if (isset($col_as) && is_array($col_as)) {
  916. $val1 = $col_as[0];
  917. $val2 = $col_as[1];
  918. // Use aliases2 alias if non empty
  919. $aliases[$db_name]['tables'][$tbl_name]['columns'][$col]
  920. = empty($val2) ? $val1 : $val2;
  921. }
  922. };
  923. };
  924. }
  925. return $aliases;
  926. }
  927. /**
  928. * Locks tables
  929. *
  930. * @param string $db database name
  931. * @param array $tables list of table names
  932. * @param string $lockType lock type; "[LOW_PRIORITY] WRITE" or "READ [LOCAL]"
  933. *
  934. * @return mixed result of the query
  935. */
  936. public static function lockTables($db, array $tables, $lockType = "WRITE")
  937. {
  938. $locks = array();
  939. foreach ($tables as $table) {
  940. $locks[] = Util::backquote($db) . "."
  941. . Util::backquote($table) . " " . $lockType;
  942. }
  943. $sql = "LOCK TABLES " . implode(", ", $locks);
  944. return $GLOBALS['dbi']->tryQuery($sql);
  945. }
  946. /**
  947. * Releases table locks
  948. *
  949. * @return mixed result of the query
  950. */
  951. public static function unlockTables()
  952. {
  953. return $GLOBALS['dbi']->tryQuery("UNLOCK TABLES");
  954. }
  955. /**
  956. * Returns all the metadata types that can be exported with a database or a table
  957. *
  958. * @return string[] metadata types.
  959. */
  960. public static function getMetadataTypes()
  961. {
  962. return array(
  963. 'column_info',
  964. 'table_uiprefs',
  965. 'tracking',
  966. 'bookmark',
  967. 'relation',
  968. 'table_coords',
  969. 'pdf_pages',
  970. 'savedsearches',
  971. 'central_columns',
  972. 'export_templates',
  973. );
  974. }
  975. /**
  976. * Returns the checked clause, depending on the presence of key in array
  977. *
  978. * @param string $key the key to look for
  979. * @param array $array array to verify
  980. *
  981. * @return string the checked clause
  982. */
  983. public static function getCheckedClause($key, array $array)
  984. {
  985. if (in_array($key, $array)) {
  986. return ' checked="checked"';
  987. }
  988. return '';
  989. }
  990. /**
  991. * get all the export options and verify
  992. * call and include the appropriate Schema Class depending on $export_type
  993. *
  994. * @param string $export_type format of the export
  995. *
  996. * @return void
  997. */
  998. public static function processExportSchema($export_type)
  999. {
  1000. /**
  1001. * default is PDF, otherwise validate it's only letters a-z
  1002. */
  1003. if (! isset($export_type) || ! preg_match('/^[a-zA-Z]+$/', $export_type)) {
  1004. $export_type = 'pdf';
  1005. }
  1006. // sanitize this parameter which will be used below in a file inclusion
  1007. $export_type = Core::securePath($export_type);
  1008. // get the specific plugin
  1009. /* @var $export_plugin SchemaPlugin */
  1010. $export_plugin = Plugins::getPlugin(
  1011. "schema",
  1012. $export_type,
  1013. 'libraries/classes/Plugins/Schema/'
  1014. );
  1015. // Check schema export type
  1016. if (! isset($export_plugin)) {
  1017. Core::fatalError(__('Bad type!'));
  1018. }
  1019. $GLOBALS['dbi']->selectDb($GLOBALS['db']);
  1020. $export_plugin->exportSchema($GLOBALS['db']);
  1021. }
  1022. }