Issues (1686)

sources/DumpDatabase.php (6 issues)

1
<?php
2
3
/**
4
 * This file has a single job - database backup.
5
 *
6
 * @package   ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
9
 *
10
 * This file contains code covered by:
11
 * copyright: 2011 Simple Machines (http://www.simplemachines.org)
12
 *
13
 * @version 2.0 dev
14
 *
15
 */
16
17
use ElkArte\Helper\Util;
18
use ElkArte\Http\Headers;
19
20
/**
21
 * Dumps the database.
22
 *
23
 * What it does:
24
 *
25
 * - It writes all the database to standard output.
26
 * - It uses gzip compression if compress is set in the URL/post data.
27
 * - It may possibly time out, and mess up badly if you were relying on it. :P
28
 * - The data dumped depends on whether "struct" and "data" are passed.
29
 * - It is called from ManageMaintenance.controller.php.
30
 */
31
function DumpDatabase2()
32
{
33
	global $db_name, $scripturl, $modSettings, $db_prefix, $db_show_debug;
34
35
	// We'll need a db to dump :P
36
	$database = database();
37
38
	// We don't need debug when dumping the database
39
	$modSettings['disableQueryCheck'] = true;
40
	$db_show_debug = false;
41
42
	// You can't dump nothing!
43
	if (!isset($_REQUEST['struct']) && !isset($_REQUEST['data']))
44
	{
45
		$_REQUEST['data'] = true;
46
	}
47
48
	// Attempt to stop from dying...
49
	detectServer()->setTimeLimit(600);
50
	$time_limit = ini_get('max_execution_time');
51
	$start_time = time();
52
53
	// @todo ... fail on not getting the requested memory?
54
	detectServer()->setMemoryLimit('256M');
55
	$memory_limit = memoryReturnBytes(ini_get('memory_limit')) / 4;
56
	$current_used_memory = 0;
57
	$db_backup = '';
58
	$output_function = 'un_compressed';
59
	$headers = Headers::instance();
60
61
	@ob_end_clean();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for ob_end_clean(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

61
	/** @scrutinizer ignore-unhandled */ @ob_end_clean();

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
62
63
	// Start saving the output... (don't do it otherwise for memory reasons.)
64
	if (isset($_REQUEST['compress']) && function_exists('gzencode'))
65
	{
66
		$output_function = 'gzencode';
67
68
		// Send faked headers so it will just save the compressed output as a gzip.
69
		$headers
70
			->removeHeader('all')
71
			->contentType('application/x-gzip', '')
72
			->header('Accept-Ranges', 'bytes');
73
74
		// The file extension will include .gz...
75
		$extension = '.sql.gz';
76
	}
77
	else
78
	{
79
		// Get rid of the gzipping already being done.
80
		if (!empty($modSettings['enableCompressedOutput']))
81
		{
82
			@ob_end_clean();
83
		}
84
		// If we can, clean anything already sent from the output buffer...
85
		elseif (ob_get_length() != 0)
86
		{
87
			ob_clean();
88
		}
89
90
		// Tell the client to save this file, even though it's text.
91
		$headers
92
			->removeHeader('all')
93
			->contentType('application/octet-stream', '');
94
95
		// This time the extension should just be .sql.
96
		$extension = '.sql';
97
	}
98
99
	// This should turn off the session URL parser.
100
	$scripturl = '';
101
102
	// Send the proper headers to let them download this file.
103
	$filename = $db_name . '-' . (empty($_REQUEST['struct']) ? 'data' : (empty($_REQUEST['data']) ? 'structure' : 'complete')) . '_' . Util::strftime('%Y-%m-%d') . $extension . '"';
104
105
	$headers
106
		->header('Content-Disposition', 'attachment; filename="' . $filename . '"')
107
		->header('Cache-Control', 'private')
108
		->header('Connection', 'close')
109
		->sendHeaders();
110
111
	// This makes things simpler when using it so very very often.
112
	$crlf = "\r\n";
113
114
	// SQL Dump Header.
115
	$db_chunks =
116
		'-- ==========================================================' . $crlf .
117
		'--' . $crlf .
118
		'-- Database dump of tables in `' . $db_name . '`' . $crlf .
119
		'-- ' . standardTime(time(), false) . $crlf .
120
		'--' . $crlf .
121
		'-- ==========================================================' . $crlf .
122
		$crlf;
123
124
	// Get all tables in the database....for our installation
125
	$real_prefix = preg_match('~^(`?)(.+?)\\1\\.(.*?)$~', $db_prefix, $match) === 1 ? $match[3] : $db_prefix;
126
	$tables = $database->list_tables(false, $real_prefix . '%');
0 ignored issues
show
The method list_tables() does not exist on ElkArte\Database\QueryInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to ElkArte\Database\QueryInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

126
	/** @scrutinizer ignore-call */ 
127
 $tables = $database->list_tables(false, $real_prefix . '%');
Loading history...
127
128
	// Dump each table.
129
	foreach ($tables as $tableName)
130
	{
131
		// Are we dumping the structures?
132
		if (isset($_REQUEST['struct']))
133
		{
134
			$db_chunks .=
135
				$crlf .
136
				'--' . $crlf .
137
				'-- Table structure for table `' . $tableName . '`' . $crlf .
138
				'--' . $crlf .
139
				$crlf .
140
				$database->table_sql($tableName) . ';' . $crlf;
0 ignored issues
show
The method table_sql() does not exist on ElkArte\Database\QueryInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

140
				$database->/** @scrutinizer ignore-call */ 
141
               table_sql($tableName) . ';' . $crlf;

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
141
		}
142
		// This is needed to speedup things later
143
		else
144
		{
145
			$database->table_sql($tableName);
146
		}
147
148
		// How about the data?
149
		if (!isset($_REQUEST['data']) || substr($tableName, -10) === 'log_errors')
150
		{
151
			continue;
152
		}
153
154
		$first_round = true;
155
		$close_table = false;
156
157
		// Are there any rows in this table?
158
		while (($get_rows = $database->insert_sql($tableName, $first_round)))
0 ignored issues
show
The method insert_sql() does not exist on ElkArte\Database\QueryInterface. Did you maybe mean insert()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

158
		while (($get_rows = $database->/** @scrutinizer ignore-call */ insert_sql($tableName, $first_round)))

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
159
		{
160
			if (empty($get_rows))
161
			{
162
				break;
163
			}
164
165
			// Time is what we need here!
166
			if (function_exists('apache_reset_timeout'))
167
			{
168
				@apache_reset_timeout();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for apache_reset_timeout(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

168
				/** @scrutinizer ignore-unhandled */ @apache_reset_timeout();

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
169
			}
170
			elseif (!empty($time_limit) && (($start_time + (int) $time_limit - 20) > time()))
171
			{
172
				$start_time = time();
173
				detectServer()->setTimeLimit(150);
174
			}
175
176
			// for the first pass, start the output with a custom line...
177
			if ($first_round)
178
			{
179
				$db_chunks .=
180
					$crlf .
181
					'--' . $crlf .
182
					'-- Dumping data in `' . $tableName . '`' . $crlf .
183
					'--' . $crlf .
184
					$crlf;
185
				$first_round = false;
186
			}
187
188
			$db_chunks .= $get_rows;
189
			$current_used_memory += Util::strlen($db_chunks);
190
191
			$db_backup .= $db_chunks;
192
			unset($db_chunks);
193
			$db_chunks = '';
194
			if ($current_used_memory > $memory_limit)
195
			{
196
				echo $output_function($db_backup);
197
				$current_used_memory = 0;
198
199
				// This is probably redundant
200
				unset($db_backup);
201
				$db_backup = '';
202
			}
203
204
			$close_table = true;
205
		}
206
207
		// No rows to get - skip it.
208
		if ($close_table)
209
		{
210
			$db_backup .=
211
				'-- --------------------------------------------------------' . $crlf;
212
		}
213
	}
214
215
	// write the last line
216
	$db_backup .= $crlf . '-- Done' . $crlf;
217
218
	echo $output_function($db_backup);
219
220
	exit;
0 ignored issues
show
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
221
}
222
223
/**
224
 * Dummy/helper function, it simply returns the string passed as argument
225
 *
226
 * @param string $string - string to uncompress
227
 *
228
 * @return string
229
 */
230
function un_compressed($string = '')
231
{
232
	return $string;
233
}
234