1 | <?php |
||||
2 | |||||
3 | /** |
||||
4 | * This file contains functions that are specifically done by administrators. |
||||
5 | * |
||||
6 | * Simple Machines Forum (SMF) |
||||
7 | * |
||||
8 | * @package SMF |
||||
9 | * @author Simple Machines https://www.simplemachines.org |
||||
10 | * @copyright 2022 Simple Machines and individual contributors |
||||
11 | * @license https://www.simplemachines.org/about/smf/license.php BSD |
||||
12 | * |
||||
13 | * @version 2.1.2 |
||||
14 | */ |
||||
15 | |||||
16 | use SMF\Cache\CacheApiInterface; |
||||
17 | |||||
18 | if (!defined('SMF')) |
||||
19 | die('No direct access...'); |
||||
20 | |||||
21 | /** |
||||
22 | * Get a list of versions that are currently installed on the server. |
||||
23 | * |
||||
24 | * @param array $checkFor An array of what to check versions for - can contain one or more of 'gd', 'imagemagick', 'db_server', 'phpa', 'memcache', 'php' or 'server' |
||||
25 | * @return array An array of versions (keys are same as what was in $checkFor, values are the versions) |
||||
26 | */ |
||||
27 | function getServerVersions($checkFor) |
||||
28 | { |
||||
29 | global $txt, $db_connection, $sourcedir, $smcFunc, $modSettings; |
||||
30 | |||||
31 | loadLanguage('Admin'); |
||||
32 | loadLanguage('ManageSettings'); |
||||
33 | |||||
34 | $versions = array(); |
||||
35 | |||||
36 | // Is GD available? If it is, we should show version information for it too. |
||||
37 | if (in_array('gd', $checkFor) && function_exists('gd_info')) |
||||
38 | { |
||||
39 | $temp = gd_info(); |
||||
40 | $versions['gd'] = array('title' => $txt['support_versions_gd'], 'version' => $temp['GD Version']); |
||||
41 | } |
||||
42 | |||||
43 | // Why not have a look at ImageMagick? If it's installed, we should show version information for it too. |
||||
44 | if (in_array('imagemagick', $checkFor) && (class_exists('Imagick') || function_exists('MagickGetVersionString'))) |
||||
45 | { |
||||
46 | if (class_exists('Imagick')) |
||||
47 | { |
||||
48 | $temp = New Imagick; |
||||
49 | $temp2 = $temp->getVersion(); |
||||
50 | $im_version = $temp2['versionString']; |
||||
51 | $extension_version = 'Imagick ' . phpversion('Imagick'); |
||||
52 | } |
||||
53 | else |
||||
54 | { |
||||
55 | $im_version = MagickGetVersionString(); |
||||
56 | $extension_version = 'MagickWand ' . phpversion('MagickWand'); |
||||
57 | } |
||||
58 | |||||
59 | // We already know it's ImageMagick and the website isn't needed... |
||||
60 | $im_version = str_replace(array('ImageMagick ', ' https://www.imagemagick.org'), '', $im_version); |
||||
61 | $versions['imagemagick'] = array('title' => $txt['support_versions_imagemagick'], 'version' => $im_version . ' (' . $extension_version . ')'); |
||||
62 | } |
||||
63 | |||||
64 | // Now lets check for the Database. |
||||
65 | if (in_array('db_server', $checkFor)) |
||||
66 | { |
||||
67 | db_extend(); |
||||
68 | if (!isset($db_connection) || $db_connection === false) |
||||
69 | { |
||||
70 | loadLanguage('Errors'); |
||||
71 | trigger_error($txt['get_server_versions_no_database'], E_USER_NOTICE); |
||||
72 | } |
||||
73 | else |
||||
74 | { |
||||
75 | $versions['db_engine'] = array( |
||||
76 | 'title' => sprintf($txt['support_versions_db_engine'], $smcFunc['db_title']), |
||||
77 | 'version' => $smcFunc['db_get_vendor'](), |
||||
78 | ); |
||||
79 | $versions['db_server'] = array( |
||||
80 | 'title' => sprintf($txt['support_versions_db'], $smcFunc['db_title']), |
||||
81 | 'version' => $smcFunc['db_get_version'](), |
||||
82 | ); |
||||
83 | } |
||||
84 | } |
||||
85 | |||||
86 | // Check to see if we have any accelerators installed. |
||||
87 | require_once($sourcedir . '/ManageServer.php'); |
||||
88 | $detected = loadCacheAPIs(); |
||||
89 | |||||
90 | /* @var CacheApiInterface $cache_api */ |
||||
91 | foreach ($detected as $class_name => $cache_api) |
||||
92 | { |
||||
93 | $class_name_txt_key = strtolower($cache_api->getImplementationClassKeyName()); |
||||
94 | |||||
95 | if (in_array($class_name_txt_key, $checkFor)) |
||||
96 | $versions[$class_name_txt_key] = array( |
||||
97 | 'title' => isset($txt[$class_name_txt_key . '_cache']) ? |
||||
98 | $txt[$class_name_txt_key . '_cache'] : $class_name, |
||||
99 | 'version' => $cache_api->getVersion(), |
||||
100 | ); |
||||
101 | } |
||||
102 | |||||
103 | if (in_array('php', $checkFor)) |
||||
104 | $versions['php'] = array( |
||||
105 | 'title' => 'PHP', |
||||
106 | 'version' => PHP_VERSION, |
||||
107 | 'more' => '?action=admin;area=serversettings;sa=phpinfo', |
||||
108 | ); |
||||
109 | |||||
110 | if (in_array('server', $checkFor)) |
||||
111 | $versions['server'] = array( |
||||
112 | 'title' => $txt['support_versions_server'], |
||||
113 | 'version' => $_SERVER['SERVER_SOFTWARE'], |
||||
114 | ); |
||||
115 | |||||
116 | return $versions; |
||||
117 | } |
||||
118 | |||||
119 | /** |
||||
120 | * Search through source, theme and language files to determine their version. |
||||
121 | * Get detailed version information about the physical SMF files on the server. |
||||
122 | * |
||||
123 | * - the input parameter allows to set whether to include SSI.php and whether |
||||
124 | * the results should be sorted. |
||||
125 | * - returns an array containing information on source files, templates and |
||||
126 | * language files found in the default theme directory (grouped by language). |
||||
127 | * |
||||
128 | * @param array &$versionOptions An array of options. Can contain one or more of 'include_ssi', 'include_subscriptions', 'include_tasks' and 'sort_results' |
||||
129 | * @return array An array of file version info. |
||||
130 | */ |
||||
131 | function getFileVersions(&$versionOptions) |
||||
132 | { |
||||
133 | global $boarddir, $sourcedir, $settings, $tasksdir; |
||||
134 | |||||
135 | // Default place to find the languages would be the default theme dir. |
||||
136 | $lang_dir = $settings['default_theme_dir'] . '/languages'; |
||||
137 | |||||
138 | $version_info = array( |
||||
139 | 'file_versions' => array(), |
||||
140 | 'default_template_versions' => array(), |
||||
141 | 'template_versions' => array(), |
||||
142 | 'default_language_versions' => array(), |
||||
143 | 'tasks_versions' => array(), |
||||
144 | ); |
||||
145 | |||||
146 | // Find the version in SSI.php's file header. |
||||
147 | if (!empty($versionOptions['include_ssi']) && file_exists($boarddir . '/SSI.php')) |
||||
148 | { |
||||
149 | $fp = fopen($boarddir . '/SSI.php', 'rb'); |
||||
150 | $header = fread($fp, 4096); |
||||
151 | fclose($fp); |
||||
152 | |||||
153 | // The comment looks rougly like... that. |
||||
154 | if (preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $header, $match) == 1) |
||||
155 | $version_info['file_versions']['SSI.php'] = $match[1]; |
||||
156 | // Not found! This is bad. |
||||
157 | else |
||||
158 | $version_info['file_versions']['SSI.php'] = '??'; |
||||
159 | } |
||||
160 | |||||
161 | // Do the paid subscriptions handler? |
||||
162 | if (!empty($versionOptions['include_subscriptions']) && file_exists($boarddir . '/subscriptions.php')) |
||||
163 | { |
||||
164 | $fp = fopen($boarddir . '/subscriptions.php', 'rb'); |
||||
165 | $header = fread($fp, 4096); |
||||
166 | fclose($fp); |
||||
167 | |||||
168 | // Found it? |
||||
169 | if (preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $header, $match) == 1) |
||||
170 | $version_info['file_versions']['subscriptions.php'] = $match[1]; |
||||
171 | // If we haven't how do we all get paid? |
||||
172 | else |
||||
173 | $version_info['file_versions']['subscriptions.php'] = '??'; |
||||
174 | } |
||||
175 | |||||
176 | // Load all the files in the Sources directory, except this file and the redirect. |
||||
177 | $sources_dir = dir($sourcedir); |
||||
178 | while ($entry = $sources_dir->read()) |
||||
179 | { |
||||
180 | if (substr($entry, -4) === '.php' && !is_dir($sourcedir . '/' . $entry) && $entry !== 'index.php') |
||||
181 | { |
||||
182 | // Read the first 4k from the file.... enough for the header. |
||||
183 | $fp = fopen($sourcedir . '/' . $entry, 'rb'); |
||||
184 | $header = fread($fp, 4096); |
||||
185 | fclose($fp); |
||||
186 | |||||
187 | // Look for the version comment in the file header. |
||||
188 | if (preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $header, $match) == 1) |
||||
189 | $version_info['file_versions'][$entry] = $match[1]; |
||||
190 | // It wasn't found, but the file was... show a '??'. |
||||
191 | else |
||||
192 | $version_info['file_versions'][$entry] = '??'; |
||||
193 | } |
||||
194 | } |
||||
195 | $sources_dir->close(); |
||||
196 | |||||
197 | // Load all the files in the tasks directory. |
||||
198 | if (!empty($versionOptions['include_tasks'])) |
||||
199 | { |
||||
200 | $tasks_dir = dir($tasksdir); |
||||
201 | while ($entry = $tasks_dir->read()) |
||||
202 | { |
||||
203 | if (substr($entry, -4) === '.php' && !is_dir($tasksdir . '/' . $entry) && $entry !== 'index.php') |
||||
204 | { |
||||
205 | // Read the first 4k from the file.... enough for the header. |
||||
206 | $fp = fopen($tasksdir . '/' . $entry, 'rb'); |
||||
207 | $header = fread($fp, 4096); |
||||
208 | fclose($fp); |
||||
209 | |||||
210 | // Look for the version comment in the file header. |
||||
211 | if (preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $header, $match) == 1) |
||||
212 | $version_info['tasks_versions'][$entry] = $match[1]; |
||||
213 | // It wasn't found, but the file was... show a '??'. |
||||
214 | else |
||||
215 | $version_info['tasks_versions'][$entry] = '??'; |
||||
216 | } |
||||
217 | } |
||||
218 | $tasks_dir->close(); |
||||
219 | } |
||||
220 | |||||
221 | // Load all the files in the default template directory - and the current theme if applicable. |
||||
222 | $directories = array('default_template_versions' => $settings['default_theme_dir']); |
||||
223 | if ($settings['theme_id'] != 1) |
||||
224 | $directories += array('template_versions' => $settings['theme_dir']); |
||||
225 | |||||
226 | foreach ($directories as $type => $dirname) |
||||
227 | { |
||||
228 | $this_dir = dir($dirname); |
||||
229 | while ($entry = $this_dir->read()) |
||||
230 | { |
||||
231 | if (substr($entry, -12) == 'template.php' && !is_dir($dirname . '/' . $entry)) |
||||
232 | { |
||||
233 | // Read the first 768 bytes from the file.... enough for the header. |
||||
234 | $fp = fopen($dirname . '/' . $entry, 'rb'); |
||||
235 | $header = fread($fp, 768); |
||||
236 | fclose($fp); |
||||
237 | |||||
238 | // Look for the version comment in the file header. |
||||
239 | if (preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $header, $match) == 1) |
||||
240 | $version_info[$type][$entry] = $match[1]; |
||||
241 | // It wasn't found, but the file was... show a '??'. |
||||
242 | else |
||||
243 | $version_info[$type][$entry] = '??'; |
||||
244 | } |
||||
245 | } |
||||
246 | $this_dir->close(); |
||||
247 | } |
||||
248 | |||||
249 | // Load up all the files in the default language directory and sort by language. |
||||
250 | $this_dir = dir($lang_dir); |
||||
251 | while ($entry = $this_dir->read()) |
||||
252 | { |
||||
253 | if (substr($entry, -4) == '.php' && $entry != 'index.php' && !is_dir($lang_dir . '/' . $entry)) |
||||
254 | { |
||||
255 | // Read the first 768 bytes from the file.... enough for the header. |
||||
256 | $fp = fopen($lang_dir . '/' . $entry, 'rb'); |
||||
257 | $header = fread($fp, 768); |
||||
258 | fclose($fp); |
||||
259 | |||||
260 | // Split the file name off into useful bits. |
||||
261 | list ($name, $language) = explode('.', $entry); |
||||
262 | |||||
263 | // Look for the version comment in the file header. |
||||
264 | if (preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*' . preg_quote($name, '~') . '(?:[\s]{2}|\*/)~i', $header, $match) == 1) |
||||
265 | $version_info['default_language_versions'][$language][$name] = $match[1]; |
||||
266 | // It wasn't found, but the file was... show a '??'. |
||||
267 | else |
||||
268 | $version_info['default_language_versions'][$language][$name] = '??'; |
||||
269 | } |
||||
270 | } |
||||
271 | $this_dir->close(); |
||||
272 | |||||
273 | // Sort the file versions by filename. |
||||
274 | if (!empty($versionOptions['sort_results'])) |
||||
275 | { |
||||
276 | ksort($version_info['file_versions']); |
||||
277 | ksort($version_info['default_template_versions']); |
||||
278 | ksort($version_info['template_versions']); |
||||
279 | ksort($version_info['default_language_versions']); |
||||
280 | ksort($version_info['tasks_versions']); |
||||
281 | |||||
282 | // For languages sort each language too. |
||||
283 | foreach ($version_info['default_language_versions'] as $language => $dummy) |
||||
284 | ksort($version_info['default_language_versions'][$language]); |
||||
285 | } |
||||
286 | return $version_info; |
||||
287 | } |
||||
288 | |||||
289 | /** |
||||
290 | * Describes properties of all known Settings.php variables and other content. |
||||
291 | * Helper for updateSettingsFile(); also called by saveSettings(). |
||||
292 | * |
||||
293 | * @return array Descriptions of all known Settings.php content |
||||
294 | */ |
||||
295 | function get_settings_defs() |
||||
296 | { |
||||
297 | /* |
||||
298 | * A big, fat array to define properties of all the Settings.php variables |
||||
299 | * and other content like code blocks. |
||||
300 | * |
||||
301 | * - String keys are used to identify actual variables. |
||||
302 | * |
||||
303 | * - Integer keys are used for content not connected to any particular |
||||
304 | * variable, such as code blocks or the license block. |
||||
305 | * |
||||
306 | * - The content of the 'text' element is simply printed out, if it is used |
||||
307 | * at all. Use it for comments or to insert code blocks, etc. |
||||
308 | * |
||||
309 | * - The 'default' element, not surprisingly, gives a default value for |
||||
310 | * the variable. |
||||
311 | * |
||||
312 | * - The 'type' element defines the expected variable type or types. If |
||||
313 | * more than one type is allowed, this should be an array listing them. |
||||
314 | * Types should match the possible types returned by gettype(). |
||||
315 | * |
||||
316 | * - If 'raw_default' is true, the default should be printed directly, |
||||
317 | * rather than being handled as a string. Use it if the default contains |
||||
318 | * code, e.g. 'dirname(__FILE__)' |
||||
319 | * |
||||
320 | * - If 'required' is true and a value for the variable is undefined, |
||||
321 | * the update will be aborted. (The only exception is during the SMF |
||||
322 | * installation process.) |
||||
323 | * |
||||
324 | * - If 'auto_delete' is 1 or true and the variable is empty, the variable |
||||
325 | * will be deleted from Settings.php. If 'auto_delete' is 0/false/null, |
||||
326 | * the variable will never be deleted. If 'auto_delete' is 2, behaviour |
||||
327 | * depends on $rebuild: if $rebuild is true, 'auto_delete' == 2 behaves |
||||
328 | * like 'auto_delete' == 1; if $rebuild is false, 'auto_delete' == 2 |
||||
329 | * behaves like 'auto_delete' == 0. |
||||
330 | * |
||||
331 | * - The 'is_password' element indicates that a value is a password. This |
||||
332 | * is used primarily to tell SMF how to interpret input when the value |
||||
333 | * is being set to a new value. |
||||
334 | * |
||||
335 | * - The optional 'search_pattern' element defines a custom regular |
||||
336 | * expression to search for the existing entry in the file. This is |
||||
337 | * primarily useful for code blocks rather than variables. |
||||
338 | * |
||||
339 | * - The optional 'replace_pattern' element defines a custom regular |
||||
340 | * expression to decide where the replacement entry should be inserted. |
||||
341 | * Note: 'replace_pattern' should be avoided unless ABSOLUTELY necessary. |
||||
342 | */ |
||||
343 | $settings_defs = array( |
||||
344 | array( |
||||
345 | 'text' => implode("\n", array( |
||||
346 | '', |
||||
347 | '/**', |
||||
348 | ' * The settings file contains all of the basic settings that need to be present when a database/cache is not available.', |
||||
349 | ' *', |
||||
350 | ' * Simple Machines Forum (SMF)', |
||||
351 | ' *', |
||||
352 | ' * @package SMF', |
||||
353 | ' * @author Simple Machines https://www.simplemachines.org', |
||||
354 | ' * @copyright ' . SMF_SOFTWARE_YEAR . ' Simple Machines and individual contributors', |
||||
355 | ' * @license https://www.simplemachines.org/about/smf/license.php BSD', |
||||
356 | ' *', |
||||
357 | ' * @version ' . SMF_VERSION, |
||||
358 | ' */', |
||||
359 | '', |
||||
360 | )), |
||||
361 | 'search_pattern' => '~/\*\*.*?@package\h+SMF\b.*?\*/\n{0,2}~s', |
||||
362 | ), |
||||
363 | 'maintenance' => array( |
||||
364 | 'text' => implode("\n", array( |
||||
365 | '', |
||||
366 | '########## Maintenance ##########', |
||||
367 | '/**', |
||||
368 | ' * The maintenance "mode"', |
||||
369 | ' * Set to 1 to enable Maintenance Mode, 2 to make the forum untouchable. (you\'ll have to make it 0 again manually!)', |
||||
370 | ' * 0 is default and disables maintenance mode.', |
||||
371 | ' *', |
||||
372 | ' * @var int 0, 1, 2', |
||||
373 | ' * @global int $maintenance', |
||||
374 | ' */', |
||||
375 | )), |
||||
376 | 'default' => 0, |
||||
377 | 'type' => 'integer', |
||||
378 | ), |
||||
379 | 'mtitle' => array( |
||||
380 | 'text' => implode("\n", array( |
||||
381 | '/**', |
||||
382 | ' * Title for the Maintenance Mode message.', |
||||
383 | ' *', |
||||
384 | ' * @var string', |
||||
385 | ' * @global int $mtitle', |
||||
386 | ' */', |
||||
387 | )), |
||||
388 | 'default' => 'Maintenance Mode', |
||||
389 | 'type' => 'string', |
||||
390 | ), |
||||
391 | 'mmessage' => array( |
||||
392 | 'text' => implode("\n", array( |
||||
393 | '/**', |
||||
394 | ' * Description of why the forum is in maintenance mode.', |
||||
395 | ' *', |
||||
396 | ' * @var string', |
||||
397 | ' * @global string $mmessage', |
||||
398 | ' */', |
||||
399 | )), |
||||
400 | 'default' => 'Okay faithful users...we\'re attempting to restore an older backup of the database...news will be posted once we\'re back!', |
||||
401 | 'type' => 'string', |
||||
402 | ), |
||||
403 | 'mbname' => array( |
||||
404 | 'text' => implode("\n", array( |
||||
405 | '', |
||||
406 | '########## Forum Info ##########', |
||||
407 | '/**', |
||||
408 | ' * The name of your forum.', |
||||
409 | ' *', |
||||
410 | ' * @var string', |
||||
411 | ' */', |
||||
412 | )), |
||||
413 | 'default' => 'My Community', |
||||
414 | 'type' => 'string', |
||||
415 | ), |
||||
416 | 'language' => array( |
||||
417 | 'text' => implode("\n", array( |
||||
418 | '/**', |
||||
419 | ' * The default language file set for the forum.', |
||||
420 | ' *', |
||||
421 | ' * @var string', |
||||
422 | ' */', |
||||
423 | )), |
||||
424 | 'default' => 'english', |
||||
425 | 'type' => 'string', |
||||
426 | ), |
||||
427 | 'boardurl' => array( |
||||
428 | 'text' => implode("\n", array( |
||||
429 | '/**', |
||||
430 | ' * URL to your forum\'s folder. (without the trailing /!)', |
||||
431 | ' *', |
||||
432 | ' * @var string', |
||||
433 | ' */', |
||||
434 | )), |
||||
435 | 'default' => 'http://127.0.0.1/smf', |
||||
436 | 'type' => 'string', |
||||
437 | ), |
||||
438 | 'webmaster_email' => array( |
||||
439 | 'text' => implode("\n", array( |
||||
440 | '/**', |
||||
441 | ' * Email address to send emails from. (like [email protected].)', |
||||
442 | ' *', |
||||
443 | ' * @var string', |
||||
444 | ' */', |
||||
445 | )), |
||||
446 | 'default' => '[email protected]', |
||||
447 | 'type' => 'string', |
||||
448 | ), |
||||
449 | 'cookiename' => array( |
||||
450 | 'text' => implode("\n", array( |
||||
451 | '/**', |
||||
452 | ' * Name of the cookie to set for authentication.', |
||||
453 | ' *', |
||||
454 | ' * @var string', |
||||
455 | ' */', |
||||
456 | )), |
||||
457 | 'default' => 'SMFCookie11', |
||||
458 | 'type' => 'string', |
||||
459 | ), |
||||
460 | 'auth_secret' => array( |
||||
461 | 'text' => implode("\n", array( |
||||
462 | '/**', |
||||
463 | ' * Secret key used to create and verify cookies, tokens, etc.', |
||||
464 | ' * Do not change this unless absolutely necessary, and NEVER share it.', |
||||
465 | ' *', |
||||
466 | ' * Note: Changing this will immediately log out all members of your forum', |
||||
467 | ' * and break the token-based links in all previous email notifications,', |
||||
468 | ' * among other possible effects.', |
||||
469 | ' *', |
||||
470 | ' * @var string', |
||||
471 | ' */', |
||||
472 | )), |
||||
473 | 'default' => null, |
||||
474 | 'auto_delete' => 1, |
||||
475 | 'type' => 'string', |
||||
476 | ), |
||||
477 | 'db_type' => array( |
||||
478 | 'text' => implode("\n", array( |
||||
479 | '', |
||||
480 | '########## Database Info ##########', |
||||
481 | '/**', |
||||
482 | ' * The database type', |
||||
483 | ' * Default options: mysql, postgresql', |
||||
484 | ' *', |
||||
485 | ' * @var string', |
||||
486 | ' */', |
||||
487 | )), |
||||
488 | 'default' => 'mysql', |
||||
489 | 'type' => 'string', |
||||
490 | ), |
||||
491 | 'db_port' => array( |
||||
492 | 'text' => implode("\n", array( |
||||
493 | '/**', |
||||
494 | ' * The database port', |
||||
495 | ' * 0 to use default port for the database type', |
||||
496 | ' *', |
||||
497 | ' * @var int', |
||||
498 | ' */', |
||||
499 | )), |
||||
500 | 'default' => 0, |
||||
501 | 'type' => 'integer', |
||||
502 | ), |
||||
503 | 'db_server' => array( |
||||
504 | 'text' => implode("\n", array( |
||||
505 | '/**', |
||||
506 | ' * The server to connect to (or a Unix socket)', |
||||
507 | ' *', |
||||
508 | ' * @var string', |
||||
509 | ' */', |
||||
510 | )), |
||||
511 | 'default' => 'localhost', |
||||
512 | 'required' => true, |
||||
513 | 'type' => 'string', |
||||
514 | ), |
||||
515 | 'db_name' => array( |
||||
516 | 'text' => implode("\n", array( |
||||
517 | '/**', |
||||
518 | ' * The database name', |
||||
519 | ' *', |
||||
520 | ' * @var string', |
||||
521 | ' */', |
||||
522 | )), |
||||
523 | 'default' => 'smf', |
||||
524 | 'required' => true, |
||||
525 | 'type' => 'string', |
||||
526 | ), |
||||
527 | 'db_user' => array( |
||||
528 | 'text' => implode("\n", array( |
||||
529 | '/**', |
||||
530 | ' * Database username', |
||||
531 | ' *', |
||||
532 | ' * @var string', |
||||
533 | ' */', |
||||
534 | )), |
||||
535 | 'default' => 'root', |
||||
536 | 'required' => true, |
||||
537 | 'type' => 'string', |
||||
538 | ), |
||||
539 | 'db_passwd' => array( |
||||
540 | 'text' => implode("\n", array( |
||||
541 | '/**', |
||||
542 | ' * Database password', |
||||
543 | ' *', |
||||
544 | ' * @var string', |
||||
545 | ' */', |
||||
546 | )), |
||||
547 | 'default' => '', |
||||
548 | 'required' => true, |
||||
549 | 'type' => 'string', |
||||
550 | 'is_password' => true, |
||||
551 | ), |
||||
552 | 'ssi_db_user' => array( |
||||
553 | 'text' => implode("\n", array( |
||||
554 | '/**', |
||||
555 | ' * Database user for when connecting with SSI', |
||||
556 | ' *', |
||||
557 | ' * @var string', |
||||
558 | ' */', |
||||
559 | )), |
||||
560 | 'default' => '', |
||||
561 | 'type' => 'string', |
||||
562 | ), |
||||
563 | 'ssi_db_passwd' => array( |
||||
564 | 'text' => implode("\n", array( |
||||
565 | '/**', |
||||
566 | ' * Database password for when connecting with SSI', |
||||
567 | ' *', |
||||
568 | ' * @var string', |
||||
569 | ' */', |
||||
570 | )), |
||||
571 | 'default' => '', |
||||
572 | 'type' => 'string', |
||||
573 | 'is_password' => true, |
||||
574 | ), |
||||
575 | 'db_prefix' => array( |
||||
576 | 'text' => implode("\n", array( |
||||
577 | '/**', |
||||
578 | ' * A prefix to put in front of your table names.', |
||||
579 | ' * This helps to prevent conflicts', |
||||
580 | ' *', |
||||
581 | ' * @var string', |
||||
582 | ' */', |
||||
583 | )), |
||||
584 | 'default' => 'smf_', |
||||
585 | 'required' => true, |
||||
586 | 'type' => 'string', |
||||
587 | ), |
||||
588 | 'db_persist' => array( |
||||
589 | 'text' => implode("\n", array( |
||||
590 | '/**', |
||||
591 | ' * Use a persistent database connection', |
||||
592 | ' *', |
||||
593 | ' * @var bool', |
||||
594 | ' */', |
||||
595 | )), |
||||
596 | 'default' => false, |
||||
597 | 'type' => 'boolean', |
||||
598 | ), |
||||
599 | 'db_error_send' => array( |
||||
600 | 'text' => implode("\n", array( |
||||
601 | '/**', |
||||
602 | ' * Send emails on database connection error', |
||||
603 | ' *', |
||||
604 | ' * @var bool', |
||||
605 | ' */', |
||||
606 | )), |
||||
607 | 'default' => false, |
||||
608 | 'type' => 'boolean', |
||||
609 | ), |
||||
610 | 'db_mb4' => array( |
||||
611 | 'text' => implode("\n", array( |
||||
612 | '/**', |
||||
613 | ' * Override the default behavior of the database layer for mb4 handling', |
||||
614 | ' * null keep the default behavior untouched', |
||||
615 | ' *', |
||||
616 | ' * @var null|bool', |
||||
617 | ' */', |
||||
618 | )), |
||||
619 | 'default' => null, |
||||
620 | 'type' => array('NULL', 'boolean'), |
||||
621 | ), |
||||
622 | 'cache_accelerator' => array( |
||||
623 | 'text' => implode("\n", array( |
||||
624 | '', |
||||
625 | '########## Cache Info ##########', |
||||
626 | '/**', |
||||
627 | ' * Select a cache system. You want to leave this up to the cache area of the admin panel for', |
||||
628 | ' * proper detection of memcached, output_cache, or smf file system', |
||||
629 | ' * (you can add more with a mod).', |
||||
630 | ' *', |
||||
631 | ' * @var string', |
||||
632 | ' */', |
||||
633 | )), |
||||
634 | 'default' => '', |
||||
635 | 'type' => 'string', |
||||
636 | ), |
||||
637 | 'cache_enable' => array( |
||||
638 | 'text' => implode("\n", array( |
||||
639 | '/**', |
||||
640 | ' * The level at which you would like to cache. Between 0 (off) through 3 (cache a lot).', |
||||
641 | ' *', |
||||
642 | ' * @var int', |
||||
643 | ' */', |
||||
644 | )), |
||||
645 | 'default' => 0, |
||||
646 | 'type' => 'integer', |
||||
647 | ), |
||||
648 | 'cache_memcached' => array( |
||||
649 | 'text' => implode("\n", array( |
||||
650 | '/**', |
||||
651 | ' * This is only used for memcache / memcached. Should be a string of \'server:port,server:port\'', |
||||
652 | ' *', |
||||
653 | ' * @var array', |
||||
654 | ' */', |
||||
655 | )), |
||||
656 | 'default' => '', |
||||
657 | 'type' => 'string', |
||||
658 | ), |
||||
659 | 'cachedir' => array( |
||||
660 | 'text' => implode("\n", array( |
||||
661 | '/**', |
||||
662 | ' * This is only for the \'smf\' file cache system. It is the path to the cache directory.', |
||||
663 | ' * It is also recommended that you place this in /tmp/ if you are going to use this.', |
||||
664 | ' *', |
||||
665 | ' * @var string', |
||||
666 | ' */', |
||||
667 | )), |
||||
668 | 'default' => 'dirname(__FILE__) . \'/cache\'', |
||||
669 | 'raw_default' => true, |
||||
670 | 'type' => 'string', |
||||
671 | ), |
||||
672 | 'cachedir_sqlite' => array( |
||||
673 | 'text' => implode("\n", array( |
||||
674 | '/**', |
||||
675 | ' * This is only for SQLite3 cache system. It is the path to the directory where the SQLite3', |
||||
676 | ' * database file will be saved.', |
||||
677 | ' *', |
||||
678 | ' * @var string', |
||||
679 | ' */', |
||||
680 | )), |
||||
681 | 'default' => '', |
||||
682 | 'auto_delete' => 2, |
||||
683 | 'type' => 'string', |
||||
684 | ), |
||||
685 | 'image_proxy_enabled' => array( |
||||
686 | 'text' => implode("\n", array( |
||||
687 | '', |
||||
688 | '########## Image Proxy ##########', |
||||
689 | '# This is done entirely in Settings.php to avoid loading the DB while serving the images', |
||||
690 | '/**', |
||||
691 | ' * Whether the proxy is enabled or not', |
||||
692 | ' *', |
||||
693 | ' * @var bool', |
||||
694 | ' */', |
||||
695 | )), |
||||
696 | 'default' => true, |
||||
697 | 'type' => 'boolean', |
||||
698 | ), |
||||
699 | 'image_proxy_secret' => array( |
||||
700 | 'text' => implode("\n", array( |
||||
701 | '/**', |
||||
702 | ' * Secret key to be used by the proxy', |
||||
703 | ' *', |
||||
704 | ' * @var string', |
||||
705 | ' */', |
||||
706 | )), |
||||
707 | 'default' => 'smfisawesome', |
||||
708 | 'type' => 'string', |
||||
709 | ), |
||||
710 | 'image_proxy_maxsize' => array( |
||||
711 | 'text' => implode("\n", array( |
||||
712 | '/**', |
||||
713 | ' * Maximum file size (in KB) for individual files', |
||||
714 | ' *', |
||||
715 | ' * @var int', |
||||
716 | ' */', |
||||
717 | )), |
||||
718 | 'default' => 5192, |
||||
719 | 'type' => 'integer', |
||||
720 | ), |
||||
721 | 'boarddir' => array( |
||||
722 | 'text' => implode("\n", array( |
||||
723 | '', |
||||
724 | '########## Directories/Files ##########', |
||||
725 | '# Note: These directories do not have to be changed unless you move things.', |
||||
726 | '/**', |
||||
727 | ' * The absolute path to the forum\'s folder. (not just \'.\'!)', |
||||
728 | ' *', |
||||
729 | ' * @var string', |
||||
730 | ' */', |
||||
731 | )), |
||||
732 | 'default' => 'dirname(__FILE__)', |
||||
733 | 'raw_default' => true, |
||||
734 | 'type' => 'string', |
||||
735 | ), |
||||
736 | 'sourcedir' => array( |
||||
737 | 'text' => implode("\n", array( |
||||
738 | '/**', |
||||
739 | ' * Path to the Sources directory.', |
||||
740 | ' *', |
||||
741 | ' * @var string', |
||||
742 | ' */', |
||||
743 | )), |
||||
744 | 'default' => 'dirname(__FILE__) . \'/Sources\'', |
||||
745 | 'raw_default' => true, |
||||
746 | 'type' => 'string', |
||||
747 | ), |
||||
748 | 'packagesdir' => array( |
||||
749 | 'text' => implode("\n", array( |
||||
750 | '/**', |
||||
751 | ' * Path to the Packages directory.', |
||||
752 | ' *', |
||||
753 | ' * @var string', |
||||
754 | ' */', |
||||
755 | )), |
||||
756 | 'default' => 'dirname(__FILE__) . \'/Packages\'', |
||||
757 | 'raw_default' => true, |
||||
758 | 'type' => 'string', |
||||
759 | ), |
||||
760 | 'tasksdir' => array( |
||||
761 | 'text' => implode("\n", array( |
||||
762 | '/**', |
||||
763 | ' * Path to the tasks directory.', |
||||
764 | ' *', |
||||
765 | ' * @var string', |
||||
766 | ' */', |
||||
767 | )), |
||||
768 | 'default' => '$sourcedir . \'/tasks\'', |
||||
769 | 'raw_default' => true, |
||||
770 | 'type' => 'string', |
||||
771 | ), |
||||
772 | array( |
||||
773 | 'text' => implode("\n", array( |
||||
774 | '', |
||||
775 | '# Make sure the paths are correct... at least try to fix them.', |
||||
776 | 'if (!is_dir(realpath($boarddir)) && file_exists(dirname(__FILE__) . \'/agreement.txt\'))', |
||||
777 | ' $boarddir = dirname(__FILE__);', |
||||
778 | 'if (!is_dir(realpath($sourcedir)) && is_dir($boarddir . \'/Sources\'))', |
||||
779 | ' $sourcedir = $boarddir . \'/Sources\';', |
||||
780 | 'if (!is_dir(realpath($tasksdir)) && is_dir($sourcedir . \'/tasks\'))', |
||||
781 | ' $tasksdir = $sourcedir . \'/tasks\';', |
||||
782 | 'if (!is_dir(realpath($packagesdir)) && is_dir($boarddir . \'/Packages\'))', |
||||
783 | ' $packagesdir = $boarddir . \'/Packages\';', |
||||
784 | 'if (!is_dir(realpath($cachedir)) && is_dir($boarddir . \'/cache\'))', |
||||
785 | ' $cachedir = $boarddir . \'/cache\';', |
||||
786 | )), |
||||
787 | 'search_pattern' => '~\n?(#[^\n]+)?(?:\n\h*if\s*\((?:\!file_exists\(\$(?'.'>boarddir|sourcedir|tasksdir|packagesdir|cachedir)\)|\!is_dir\(realpath\(\$(?'.'>boarddir|sourcedir|tasksdir|packagesdir|cachedir)\)\))[^;]+\n\h*\$(?'.'>boarddir|sourcedir|tasksdir|packagesdir|cachedir)[^\n]+;)+~sm', |
||||
788 | ), |
||||
789 | 'db_character_set' => array( |
||||
790 | 'text' => implode("\n", array( |
||||
791 | '', |
||||
792 | '######### Legacy Settings #########', |
||||
793 | '# UTF-8 is now the only character set supported in 2.1.', |
||||
794 | )), |
||||
795 | 'default' => 'utf8', |
||||
796 | 'type' => 'string', |
||||
797 | ), |
||||
798 | 'db_show_debug' => array( |
||||
799 | 'text' => implode("\n", array( |
||||
800 | '', |
||||
801 | '######### Developer Settings #########', |
||||
802 | '# Show debug info.', |
||||
803 | )), |
||||
804 | 'default' => false, |
||||
805 | 'auto_delete' => 2, |
||||
806 | 'type' => 'boolean', |
||||
807 | ), |
||||
808 | array( |
||||
809 | 'text' => implode("\n", array( |
||||
810 | '', |
||||
811 | '########## Error-Catching ##########', |
||||
812 | '# Note: You shouldn\'t touch these settings.', |
||||
813 | 'if (file_exists((isset($cachedir) ? $cachedir : dirname(__FILE__)) . \'/db_last_error.php\'))', |
||||
814 | ' include((isset($cachedir) ? $cachedir : dirname(__FILE__)) . \'/db_last_error.php\');', |
||||
815 | '', |
||||
816 | 'if (!isset($db_last_error))', |
||||
817 | '{', |
||||
818 | ' // File does not exist so lets try to create it', |
||||
819 | ' file_put_contents((isset($cachedir) ? $cachedir : dirname(__FILE__)) . \'/db_last_error.php\', \'<\' . \'?\' . "php\n" . \'$db_last_error = 0;\' . "\n" . \'?\' . \'>\');', |
||||
820 | ' $db_last_error = 0;', |
||||
821 | '}', |
||||
822 | )), |
||||
823 | // Designed to match both 2.0 and 2.1 versions of this code. |
||||
824 | 'search_pattern' => '~\n?#+ Error.Catching #+\n[^\n]*?settings\.\n(?:\$db_last_error = \d{1,11};|if \(file_exists.*?\$db_last_error = 0;(?' . '>\s*}))(?=\n|\?' . '>|$)~s', |
||||
825 | ), |
||||
826 | // Temporary variable used during the upgrade process. |
||||
827 | 'upgradeData' => array( |
||||
828 | 'default' => '', |
||||
829 | 'auto_delete' => 1, |
||||
830 | 'type' => 'string', |
||||
831 | ), |
||||
832 | // This should be removed if found. |
||||
833 | 'db_last_error' => array( |
||||
834 | 'default' => 0, |
||||
835 | 'auto_delete' => 1, |
||||
836 | 'type' => 'integer', |
||||
837 | ), |
||||
838 | ); |
||||
839 | |||||
840 | // Allow mods the option to define comments, defaults, etc., for their settings. |
||||
841 | // Check if function exists, in case we are calling from installer or upgrader. |
||||
842 | if (function_exists('call_integration_hook')) |
||||
843 | call_integration_hook('integrate_update_settings_file', array(&$settings_defs)); |
||||
844 | |||||
845 | return $settings_defs; |
||||
846 | } |
||||
847 | |||||
848 | /** |
||||
849 | * Update the Settings.php file. |
||||
850 | * |
||||
851 | * The most important function in this file for mod makers happens to be the |
||||
852 | * updateSettingsFile() function, but it shouldn't be used often anyway. |
||||
853 | * |
||||
854 | * - Updates the Settings.php file with the changes supplied in config_vars. |
||||
855 | * |
||||
856 | * - Expects config_vars to be an associative array, with the keys as the |
||||
857 | * variable names in Settings.php, and the values the variable values. |
||||
858 | * |
||||
859 | * - Correctly formats the values using smf_var_export(). |
||||
860 | * |
||||
861 | * - Restores standard formatting of the file, if $rebuild is true. |
||||
862 | * |
||||
863 | * - Checks for changes to db_last_error and passes those off to a separate |
||||
864 | * handler. |
||||
865 | * |
||||
866 | * - Creates a backup file and will use it should the writing of the |
||||
867 | * new settings file fail. |
||||
868 | * |
||||
869 | * - Tries to intelligently trim quotes and remove slashes from string values. |
||||
870 | * This is done for backwards compatibility purposes (old versions of this |
||||
871 | * function expected strings to have been manually escaped and quoted). This |
||||
872 | * behaviour can be controlled by the $keep_quotes parameter. |
||||
873 | * |
||||
874 | * MOD AUTHORS: If you are adding a setting to Settings.php, you should use the |
||||
875 | * integrate_update_settings_file hook to define it in get_settings_defs(). |
||||
876 | * |
||||
877 | * @param array $config_vars An array of one or more variables to update. |
||||
878 | * @param bool|null $keep_quotes Whether to strip slashes & trim quotes from string values. Defaults to auto-detection. |
||||
879 | * @param bool $rebuild If true, attempts to rebuild with standard format. Default false. |
||||
880 | * @return bool True on success, false on failure. |
||||
881 | */ |
||||
882 | function updateSettingsFile($config_vars, $keep_quotes = null, $rebuild = false) |
||||
883 | { |
||||
884 | // In this function we intentionally don't declare any global variables. |
||||
885 | // This allows us to work with everything cleanly. |
||||
886 | |||||
887 | static $mtime; |
||||
888 | |||||
889 | // Should we try to unescape the strings? |
||||
890 | if (empty($keep_quotes)) |
||||
891 | { |
||||
892 | foreach ($config_vars as $var => $val) |
||||
893 | { |
||||
894 | if (is_string($val) && ($keep_quotes === false || strpos($val, '\'') === 0 && strrpos($val, '\'') === strlen($val) - 1)) |
||||
895 | $config_vars[$var] = trim(stripcslashes($val), '\''); |
||||
896 | } |
||||
897 | } |
||||
898 | |||||
899 | // Updating the db_last_error, then don't mess around with Settings.php |
||||
900 | if (isset($config_vars['db_last_error'])) |
||||
901 | { |
||||
902 | updateDbLastError($config_vars['db_last_error']); |
||||
903 | |||||
904 | if (count($config_vars) === 1 && empty($rebuild)) |
||||
905 | return true; |
||||
906 | |||||
907 | // Make sure we delete this from Settings.php, if present. |
||||
908 | $config_vars['db_last_error'] = 0; |
||||
909 | } |
||||
910 | |||||
911 | // Rebuilding should not be undertaken lightly, so we're picky about the parameter. |
||||
912 | if (!is_bool($rebuild)) |
||||
0 ignored issues
–
show
introduced
by
![]() |
|||||
913 | $rebuild = false; |
||||
914 | |||||
915 | $mtime = isset($mtime) ? (int) $mtime : (defined('TIME_START') ? TIME_START : $_SERVER['REQUEST_TIME']); |
||||
916 | |||||
917 | /***************** |
||||
918 | * PART 1: Setup * |
||||
919 | *****************/ |
||||
920 | |||||
921 | // Typically Settings.php is in $boarddir, but maybe this is a custom setup... |
||||
922 | foreach (get_included_files() as $settingsFile) |
||||
923 | if (basename($settingsFile) === 'Settings.php') |
||||
924 | break; |
||||
925 | |||||
926 | // Fallback in case Settings.php isn't loaded (e.g. while installing) |
||||
927 | if (basename($settingsFile) !== 'Settings.php') |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
928 | $settingsFile = (!empty($GLOBALS['boarddir']) && @realpath($GLOBALS['boarddir']) ? $GLOBALS['boarddir'] : (!empty($_SERVER['SCRIPT_FILENAME']) ? dirname($_SERVER['SCRIPT_FILENAME']) : dirname(__DIR__))) . '/Settings.php'; |
||||
929 | |||||
930 | // File not found? Attempt an emergency on-the-fly fix! |
||||
931 | if (!file_exists($settingsFile)) |
||||
932 | @touch($settingsFile); |
||||
933 | |||||
934 | // When was Settings.php last changed? |
||||
935 | $last_settings_change = filemtime($settingsFile); |
||||
936 | |||||
937 | // Get the current values of everything in Settings.php. |
||||
938 | $settings_vars = get_current_settings($mtime, $settingsFile); |
||||
939 | |||||
940 | // If Settings.php is empty for some reason, see if we can use the backup. |
||||
941 | if (empty($settings_vars) && file_exists(dirname($settingsFile) . '/Settings_bak.php')) |
||||
942 | $settings_vars = get_current_settings($mtime, dirname($settingsFile) . '/Settings_bak.php'); |
||||
943 | |||||
944 | // False means there was a problem with the file and we can't safely continue. |
||||
945 | if ($settings_vars === false) |
||||
946 | return false; |
||||
947 | |||||
948 | // It works best to set everything afresh. |
||||
949 | $new_settings_vars = array_merge($settings_vars, $config_vars); |
||||
950 | |||||
951 | // Are we using UTF-8? |
||||
952 | $utf8 = isset($GLOBALS['context']['utf8']) ? $GLOBALS['context']['utf8'] : (isset($GLOBALS['utf8']) ? $GLOBALS['utf8'] : (isset($settings_vars['db_character_set']) ? $settings_vars['db_character_set'] === 'utf8' : false)); |
||||
953 | |||||
954 | // Get our definitions for all known Settings.php variables and other content. |
||||
955 | $settings_defs = get_settings_defs(); |
||||
956 | |||||
957 | // If Settings.php is empty or invalid, try to recover using whatever is in $GLOBALS. |
||||
958 | if ($settings_vars === array()) |
||||
959 | { |
||||
960 | foreach ($settings_defs as $var => $setting_def) |
||||
961 | if (isset($GLOBALS[$var])) |
||||
962 | $settings_vars[$var] = $GLOBALS[$var]; |
||||
963 | |||||
964 | $new_settings_vars = array_merge($settings_vars, $config_vars); |
||||
965 | } |
||||
966 | |||||
967 | // During install/upgrade, don't set anything until we're ready for it. |
||||
968 | if (defined('SMF_INSTALLING') && empty($rebuild)) |
||||
969 | { |
||||
970 | foreach ($settings_defs as $var => $setting_def) |
||||
971 | if (!in_array($var, array_keys($new_settings_vars)) && !is_int($var)) |
||||
972 | unset($settings_defs[$var]); |
||||
973 | } |
||||
974 | |||||
975 | /******************************* |
||||
976 | * PART 2: Build substitutions * |
||||
977 | *******************************/ |
||||
978 | |||||
979 | $type_regex = array( |
||||
980 | 'string' => |
||||
981 | '(?:' . |
||||
982 | // match the opening quotation mark... |
||||
983 | '(["\'])' . |
||||
984 | // then any number of other characters or escaped quotation marks... |
||||
985 | '(?:.(?!\\1)|\\\(?=\\1))*.?' . |
||||
986 | // then the closing quotation mark. |
||||
987 | '\\1' . |
||||
988 | // Maybe there's a second string concatenated to this one. |
||||
989 | '(?:\s*\.\s*)*' . |
||||
990 | ')+', |
||||
991 | // Some numeric values might have been stored as strings. |
||||
992 | 'integer' => '["\']?[+-]?\d+["\']?', |
||||
993 | 'double' => '["\']?[+-]?\d+\.\d+([Ee][+-]\d+)?["\']?', |
||||
994 | // Some boolean values might have been stored as integers. |
||||
995 | 'boolean' => '(?i:TRUE|FALSE|(["\']?)[01]\b\\1)', |
||||
996 | 'NULL' => '(?i:NULL)', |
||||
997 | // These use a PCRE subroutine to match nested arrays. |
||||
998 | 'array' => 'array\s*(\((?'.'>[^()]|(?1))*\))', |
||||
999 | 'object' => '\w+::__set_state\(array\s*(\((?'.'>[^()]|(?1))*\))\)', |
||||
1000 | ); |
||||
1001 | |||||
1002 | /* |
||||
1003 | * The substitutions take place in one of two ways: |
||||
1004 | * |
||||
1005 | * 1: The search_pattern regex finds a string in Settings.php, which is |
||||
1006 | * temporarily replaced by a placeholder. Once all the placeholders |
||||
1007 | * have been inserted, each is replaced by the final replacement string |
||||
1008 | * that we want to use. This is the standard method. |
||||
1009 | * |
||||
1010 | * 2: The search_pattern regex finds a string in Settings.php, which is |
||||
1011 | * then deleted by replacing it with an empty placeholder. Then after |
||||
1012 | * all the real placeholders have been dealt with, the replace_pattern |
||||
1013 | * regex finds where to insert the final replacement string that we |
||||
1014 | * want to use. This method is for special cases. |
||||
1015 | */ |
||||
1016 | $prefix = mt_rand() . '-'; |
||||
1017 | $neg_index = -1; |
||||
1018 | $substitutions = array( |
||||
1019 | $neg_index-- => array( |
||||
1020 | 'search_pattern' => '~^\s*<\?(php\b)?\n?~', |
||||
1021 | 'placeholder' => '', |
||||
1022 | 'replace_pattern' => '~^~', |
||||
1023 | 'replacement' => '<' . "?php\n", |
||||
1024 | ), |
||||
1025 | $neg_index-- => array( |
||||
1026 | 'search_pattern' => '~\S\K\s*(\?' . '>)?\s*$~', |
||||
1027 | 'placeholder' => "\n" . md5($prefix . '?' . '>'), |
||||
1028 | 'replacement' => "\n\n?" . '>', |
||||
1029 | ), |
||||
1030 | // Remove the code that redirects to the installer. |
||||
1031 | $neg_index-- => array( |
||||
1032 | 'search_pattern' => '~^if\s*\(file_exists\(dirname\(__FILE__\)\s*\.\s*\'/install\.php\'\)\)\s*(?:({(?'.'>[^{}]|(?1))*})\h*|header(\((?' . '>[^()]|(?2))*\));\n)~m', |
||||
1033 | 'placeholder' => '', |
||||
1034 | ), |
||||
1035 | ); |
||||
1036 | |||||
1037 | if (defined('SMF_INSTALLING')) |
||||
1038 | $substitutions[$neg_index--] = array( |
||||
1039 | 'search_pattern' => '~/\*.*?SMF\s+1\.\d.*?\*/~s', |
||||
1040 | 'placeholder' => '', |
||||
1041 | ); |
||||
1042 | |||||
1043 | foreach ($settings_defs as $var => $setting_def) |
||||
1044 | { |
||||
1045 | $placeholder = md5($prefix . $var); |
||||
1046 | $replacement = ''; |
||||
1047 | |||||
1048 | if (!empty($setting_def['text'])) |
||||
1049 | { |
||||
1050 | // Special handling for the license block: always at the beginning. |
||||
1051 | if (strpos($setting_def['text'], "* @package SMF\n") !== false) |
||||
1052 | { |
||||
1053 | $substitutions[$var]['search_pattern'] = $setting_def['search_pattern']; |
||||
1054 | $substitutions[$var]['placeholder'] = ''; |
||||
1055 | $substitutions[-1]['replacement'] .= $setting_def['text'] . "\n"; |
||||
1056 | } |
||||
1057 | // Special handling for the Error-Catching block: always at the end. |
||||
1058 | elseif (strpos($setting_def['text'], 'Error-Catching') !== false) |
||||
1059 | { |
||||
1060 | $errcatch_var = $var; |
||||
0 ignored issues
–
show
|
|||||
1061 | $substitutions[$var]['search_pattern'] = $setting_def['search_pattern']; |
||||
1062 | $substitutions[$var]['placeholder'] = ''; |
||||
1063 | $substitutions[-2]['replacement'] = "\n" . $setting_def['text'] . $substitutions[-2]['replacement']; |
||||
1064 | } |
||||
1065 | // The text is the whole thing (code blocks, etc.) |
||||
1066 | elseif (is_int($var)) |
||||
1067 | { |
||||
1068 | // Remember the path correcting code for later. |
||||
1069 | if (strpos($setting_def['text'], '# Make sure the paths are correct') !== false) |
||||
1070 | $pathcode_var = $var; |
||||
1071 | |||||
1072 | if (!empty($setting_def['search_pattern'])) |
||||
1073 | $substitutions[$var]['search_pattern'] = $setting_def['search_pattern']; |
||||
1074 | else |
||||
1075 | $substitutions[$var]['search_pattern'] = '~' . preg_quote($setting_def['text'], '~') . '~'; |
||||
1076 | |||||
1077 | $substitutions[$var]['placeholder'] = $placeholder; |
||||
1078 | |||||
1079 | $replacement .= $setting_def['text'] . "\n"; |
||||
1080 | } |
||||
1081 | // We only include comments when rebuilding. |
||||
1082 | elseif (!empty($rebuild)) |
||||
1083 | $replacement .= $setting_def['text'] . "\n"; |
||||
1084 | } |
||||
1085 | |||||
1086 | if (is_string($var)) |
||||
1087 | { |
||||
1088 | // Ensure the value is good. |
||||
1089 | if (in_array($var, array_keys($new_settings_vars))) |
||||
1090 | { |
||||
1091 | // Objects without a __set_state method need a fallback. |
||||
1092 | if (is_object($new_settings_vars[$var]) && !method_exists($new_settings_vars[$var], '__set_state')) |
||||
1093 | { |
||||
1094 | if (method_exists($new_settings_vars[$var], '__toString')) |
||||
1095 | $new_settings_vars[$var] = (string) $new_settings_vars[$var]; |
||||
1096 | else |
||||
1097 | $new_settings_vars[$var] = (array) $new_settings_vars[$var]; |
||||
1098 | } |
||||
1099 | |||||
1100 | // Normalize the type if necessary. |
||||
1101 | if (isset($setting_def['type'])) |
||||
1102 | { |
||||
1103 | $expected_types = (array) $setting_def['type']; |
||||
1104 | $var_type = gettype($new_settings_vars[$var]); |
||||
1105 | |||||
1106 | // Variable is not of an expected type. |
||||
1107 | if (!in_array($var_type, $expected_types)) |
||||
1108 | { |
||||
1109 | // Passed in an unexpected array. |
||||
1110 | if ($var_type == 'array') |
||||
1111 | { |
||||
1112 | $temp = reset($new_settings_vars[$var]); |
||||
1113 | |||||
1114 | // Use the first element if there's only one and it is a scalar. |
||||
1115 | if (count($new_settings_vars[$var]) === 1 && is_scalar($temp)) |
||||
1116 | $new_settings_vars[$var] = $temp; |
||||
1117 | |||||
1118 | // Or keep the old value, if that is good. |
||||
1119 | elseif (isset($settings_vars[$var]) && in_array(gettype($settings_vars[$var]), $expected_types)) |
||||
1120 | $new_settings_vars[$var] = $settings_vars[$var]; |
||||
1121 | |||||
1122 | // Fall back to the default |
||||
1123 | else |
||||
1124 | $new_settings_vars[$var] = $setting_def['default']; |
||||
1125 | } |
||||
1126 | |||||
1127 | // Cast it to whatever type was expected. |
||||
1128 | // Note: the order of the types in this loop matters. |
||||
1129 | foreach (array('boolean', 'integer', 'double', 'string', 'array') as $to_type) |
||||
1130 | { |
||||
1131 | if (in_array($to_type, $expected_types)) |
||||
1132 | { |
||||
1133 | settype($new_settings_vars[$var], $to_type); |
||||
1134 | break; |
||||
1135 | } |
||||
1136 | } |
||||
1137 | } |
||||
1138 | } |
||||
1139 | } |
||||
1140 | // Abort if a required one is undefined (unless we're installing). |
||||
1141 | elseif (!empty($setting_def['required']) && !defined('SMF_INSTALLING')) |
||||
1142 | return false; |
||||
1143 | |||||
1144 | // Create the search pattern. |
||||
1145 | if (!empty($setting_def['search_pattern'])) |
||||
1146 | $substitutions[$var]['search_pattern'] = $setting_def['search_pattern']; |
||||
1147 | else |
||||
1148 | { |
||||
1149 | $var_pattern = array(); |
||||
1150 | |||||
1151 | if (isset($setting_def['type'])) |
||||
1152 | { |
||||
1153 | foreach ((array) $setting_def['type'] as $type) |
||||
1154 | $var_pattern[] = $type_regex[$type]; |
||||
1155 | } |
||||
1156 | |||||
1157 | if (in_array($var, array_keys($config_vars))) |
||||
1158 | { |
||||
1159 | $var_pattern[] = @$type_regex[gettype($config_vars[$var])]; |
||||
1160 | |||||
1161 | if (is_string($config_vars[$var]) && strpos($config_vars[$var], dirname($settingsFile)) === 0) |
||||
1162 | $var_pattern[] = '(?:__DIR__|dirname\(__FILE__\)) . \'' . (preg_quote(str_replace(dirname($settingsFile), '', $config_vars[$var]), '~')) . '\''; |
||||
1163 | } |
||||
1164 | |||||
1165 | if (in_array($var, array_keys($settings_vars))) |
||||
1166 | { |
||||
1167 | $var_pattern[] = @$type_regex[gettype($settings_vars[$var])]; |
||||
1168 | |||||
1169 | if (is_string($settings_vars[$var]) && strpos($settings_vars[$var], dirname($settingsFile)) === 0) |
||||
1170 | $var_pattern[] = '(?:__DIR__|dirname\(__FILE__\)) . \'' . (preg_quote(str_replace(dirname($settingsFile), '', $settings_vars[$var]), '~')) . '\''; |
||||
1171 | } |
||||
1172 | |||||
1173 | if (!empty($setting_def['raw_default']) && $setting_def['default'] !== '') |
||||
1174 | { |
||||
1175 | $var_pattern[] = preg_replace('/\s+/', '\s+', preg_quote($setting_def['default'], '~')); |
||||
1176 | |||||
1177 | if (strpos($setting_def['default'], 'dirname(__FILE__)') !== false) |
||||
1178 | $var_pattern[] = preg_replace('/\s+/', '\s+', preg_quote(str_replace('dirname(__FILE__)', '__DIR__', $setting_def['default']), '~')); |
||||
1179 | |||||
1180 | if (strpos($setting_def['default'], '__DIR__') !== false) |
||||
1181 | $var_pattern[] = preg_replace('/\s+/', '\s+', preg_quote(str_replace('__DIR__', 'dirname(__FILE__)', $setting_def['default']), '~')); |
||||
1182 | } |
||||
1183 | |||||
1184 | $var_pattern = array_unique($var_pattern); |
||||
1185 | |||||
1186 | $var_pattern = count($var_pattern) > 1 ? '(?:' . (implode('|', $var_pattern)) . ')' : $var_pattern[0]; |
||||
1187 | |||||
1188 | $substitutions[$var]['search_pattern'] = '~(?<=^|\s)\h*\$' . preg_quote($var, '~') . '\s*=\s*' . $var_pattern . ';~' . (!empty($utf8) ? 'u' : ''); |
||||
1189 | } |
||||
1190 | |||||
1191 | // Next create the placeholder or replace_pattern. |
||||
1192 | if (!empty($setting_def['replace_pattern'])) |
||||
1193 | $substitutions[$var]['replace_pattern'] = $setting_def['replace_pattern']; |
||||
1194 | else |
||||
1195 | $substitutions[$var]['placeholder'] = $placeholder; |
||||
1196 | |||||
1197 | // Now create the replacement. |
||||
1198 | // A setting to delete. |
||||
1199 | if (!empty($setting_def['auto_delete']) && empty($new_settings_vars[$var])) |
||||
1200 | { |
||||
1201 | if ($setting_def['auto_delete'] === 2 && empty($rebuild) && in_array($var, array_keys($new_settings_vars))) |
||||
1202 | { |
||||
1203 | $replacement .= '$' . $var . ' = ' . ($new_settings_vars[$var] === $setting_def['default'] && !empty($setting_def['raw_default']) ? sprintf($new_settings_vars[$var]) : smf_var_export($new_settings_vars[$var], true)) . ";"; |
||||
0 ignored issues
–
show
The call to
smf_var_export() has too many arguments starting with true .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||
1204 | } |
||||
1205 | else |
||||
1206 | { |
||||
1207 | $replacement = ''; |
||||
1208 | $substitutions[$var]['placeholder'] = ''; |
||||
1209 | |||||
1210 | // This is just for cosmetic purposes. Removes the blank line. |
||||
1211 | $substitutions[$var]['search_pattern'] = str_replace('(?<=^|\s)', '\n?', $substitutions[$var]['search_pattern']); |
||||
1212 | } |
||||
1213 | } |
||||
1214 | // Add this setting's value. |
||||
1215 | elseif (in_array($var, array_keys($new_settings_vars))) |
||||
1216 | { |
||||
1217 | $replacement .= '$' . $var . ' = ' . ($new_settings_vars[$var] === $setting_def['default'] && !empty($setting_def['raw_default']) ? sprintf($new_settings_vars[$var]) : smf_var_export($new_settings_vars[$var], true)) . ";"; |
||||
1218 | } |
||||
1219 | // Fall back to the default value. |
||||
1220 | elseif (isset($setting_def['default'])) |
||||
1221 | { |
||||
1222 | $replacement .= '$' . $var . ' = ' . (!empty($setting_def['raw_default']) ? sprintf($setting_def['default']) : smf_var_export($setting_def['default'], true)) . ';'; |
||||
1223 | } |
||||
1224 | // This shouldn't happen, but we've got nothing. |
||||
1225 | else |
||||
1226 | $replacement .= '$' . $var . ' = null;'; |
||||
1227 | } |
||||
1228 | |||||
1229 | $substitutions[$var]['replacement'] = $replacement; |
||||
1230 | |||||
1231 | // We're done with this one. |
||||
1232 | unset($new_settings_vars[$var]); |
||||
1233 | } |
||||
1234 | |||||
1235 | // Any leftovers to deal with? |
||||
1236 | foreach ($new_settings_vars as $var => $val) |
||||
1237 | { |
||||
1238 | $var_pattern = array(); |
||||
1239 | |||||
1240 | if (in_array($var, array_keys($config_vars))) |
||||
1241 | $var_pattern[] = $type_regex[gettype($config_vars[$var])]; |
||||
1242 | |||||
1243 | if (in_array($var, array_keys($settings_vars))) |
||||
1244 | $var_pattern[] = $type_regex[gettype($settings_vars[$var])]; |
||||
1245 | |||||
1246 | $var_pattern = array_unique($var_pattern); |
||||
1247 | |||||
1248 | $var_pattern = count($var_pattern) > 1 ? '(?:' . (implode('|', $var_pattern)) . ')' : $var_pattern[0]; |
||||
1249 | |||||
1250 | $placeholder = md5($prefix . $var); |
||||
1251 | |||||
1252 | $substitutions[$var]['search_pattern'] = '~(?<=^|\s)\h*\$' . preg_quote($var, '~') . '\s*=\s*' . $var_pattern . ';~' . (!empty($utf8) ? 'u' : ''); |
||||
1253 | $substitutions[$var]['placeholder'] = $placeholder; |
||||
1254 | $substitutions[$var]['replacement'] = '$' . $var . ' = ' . smf_var_export($val, true) . ";"; |
||||
1255 | } |
||||
1256 | |||||
1257 | // During an upgrade, some of the path variables may not have been declared yet. |
||||
1258 | if (defined('SMF_INSTALLING') && empty($rebuild)) |
||||
1259 | { |
||||
1260 | preg_match_all('~^\h*\$(\w+)\s*=\s*~m', $substitutions[$pathcode_var]['replacement'], $matches); |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
1261 | $missing_pathvars = array_diff($matches[1], array_keys($substitutions)); |
||||
1262 | |||||
1263 | if (!empty($missing_pathvars)) |
||||
1264 | { |
||||
1265 | foreach ($missing_pathvars as $var) |
||||
1266 | { |
||||
1267 | $substitutions[$pathcode_var]['replacement'] = preg_replace('~\nif[^\n]+\$' . $var . '[^\n]+\n\h*\$' . $var . ' = [^\n]+~', '', $substitutions[$pathcode_var]['replacement']); |
||||
1268 | } |
||||
1269 | } |
||||
1270 | } |
||||
1271 | |||||
1272 | // It's important to do the numbered ones before the named ones, or messes happen. |
||||
1273 | uksort( |
||||
1274 | $substitutions, |
||||
1275 | function($a, $b) { |
||||
1276 | if (is_int($a) && is_int($b)) |
||||
1277 | return $a > $b ? 1 : ($a < $b ? -1 : 0); |
||||
1278 | elseif (is_int($a)) |
||||
1279 | return -1; |
||||
1280 | elseif (is_int($b)) |
||||
1281 | return 1; |
||||
1282 | else |
||||
1283 | return strcasecmp($b, $a); |
||||
1284 | } |
||||
1285 | ); |
||||
1286 | |||||
1287 | /****************************** |
||||
1288 | * PART 3: Content processing * |
||||
1289 | ******************************/ |
||||
1290 | |||||
1291 | /* 3.a: Get the content of Settings.php and make sure it is good. */ |
||||
1292 | |||||
1293 | // Retrieve the contents of Settings.php and normalize the line endings. |
||||
1294 | $settingsText = trim(strtr(file_get_contents($settingsFile), array("\r\n" => "\n", "\r" => "\n"))); |
||||
1295 | |||||
1296 | // If Settings.php is empty or corrupt for some reason, see if we can recover. |
||||
1297 | if ($settingsText == '' || substr($settingsText, 0, 5) !== '<' . '?php') |
||||
1298 | { |
||||
1299 | // Try restoring from the backup. |
||||
1300 | if (file_exists(dirname($settingsFile) . '/Settings_bak.php')) |
||||
1301 | $settingsText = strtr(file_get_contents(dirname($settingsFile) . '/Settings_bak.php'), array("\r\n" => "\n", "\r" => "\n")); |
||||
1302 | |||||
1303 | // Backup is bad too? Our only option is to create one from scratch. |
||||
1304 | if ($settingsText == '' || substr($settingsText, 0, 5) !== '<' . '?php' || substr($settingsText, -2) !== '?' . '>') |
||||
1305 | { |
||||
1306 | $settingsText = '<' . "?php\n"; |
||||
1307 | foreach ($settings_defs as $var => $setting_def) |
||||
1308 | { |
||||
1309 | if (is_string($var) && !empty($setting_def['text']) && strpos($substitutions[$var]['replacement'], $setting_def['text']) === false) |
||||
1310 | $substitutions[$var]['replacement'] = $setting_def['text'] . "\n" . $substitutions[$var]['replacement']; |
||||
1311 | |||||
1312 | $settingsText .= $substitutions[$var]['replacement'] . "\n"; |
||||
1313 | } |
||||
1314 | $settingsText .= "\n\n?" . '>'; |
||||
1315 | $rebuild = true; |
||||
1316 | } |
||||
1317 | } |
||||
1318 | |||||
1319 | // Settings.php is unlikely to contain any heredocs, but just in case... |
||||
1320 | if (preg_match_all('/<<<([\'"]?)(\w+)\1\R(.*?)\R\h*\2;$/ms', $settingsText, $matches)) |
||||
1321 | { |
||||
1322 | foreach ($matches[0] as $mkey => $heredoc) |
||||
1323 | { |
||||
1324 | if (!empty($matches[1][$mkey]) && $matches[1][$mkey] === '\'') |
||||
1325 | $heredoc_replacements[$heredoc] = var_export($matches[3][$mkey], true) . ';'; |
||||
1326 | else |
||||
1327 | $heredoc_replacements[$heredoc] = '"' . strtr(substr(var_export($matches[3][$mkey], true), 1, -1), array("\\'" => "'", '"' => '\"')) . '";'; |
||||
1328 | } |
||||
1329 | |||||
1330 | $settingsText = strtr($settingsText, $heredoc_replacements); |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
1331 | } |
||||
1332 | |||||
1333 | /* 3.b: Loop through all our substitutions to insert placeholders, etc. */ |
||||
1334 | |||||
1335 | $last_var = null; |
||||
1336 | $bare_settingsText = $settingsText; |
||||
1337 | $force_before_pathcode = array(); |
||||
1338 | foreach ($substitutions as $var => $substitution) |
||||
1339 | { |
||||
1340 | $placeholders[$var] = $substitution['placeholder']; |
||||
1341 | |||||
1342 | if (!empty($substitution['placeholder'])) |
||||
1343 | { |
||||
1344 | $simple_replacements[$substitution['placeholder']] = $substitution['replacement']; |
||||
1345 | } |
||||
1346 | elseif (!empty($substitution['replace_pattern'])) |
||||
1347 | { |
||||
1348 | $replace_patterns[$var] = $substitution['replace_pattern']; |
||||
1349 | $replace_strings[$var] = $substitution['replacement']; |
||||
1350 | } |
||||
1351 | |||||
1352 | if (strpos($substitutions[$pathcode_var]['replacement'], '$' . $var . ' = ') !== false) |
||||
1353 | $force_before_pathcode[] = $var; |
||||
1354 | |||||
1355 | // Look before you leap. |
||||
1356 | preg_match_all($substitution['search_pattern'], $bare_settingsText, $matches); |
||||
1357 | |||||
1358 | if ((is_string($var) || $var === $pathcode_var) && count($matches[0]) !== 1 && $substitution['replacement'] !== '') |
||||
1359 | { |
||||
1360 | // More than one instance of the variable = not good. |
||||
1361 | if (count($matches[0]) > 1) |
||||
1362 | { |
||||
1363 | if (is_string($var)) |
||||
1364 | { |
||||
1365 | // Maybe we can try something more interesting? |
||||
1366 | $sp = substr($substitution['search_pattern'], 1); |
||||
1367 | |||||
1368 | if (strpos($sp, '(?<=^|\s)') === 0) |
||||
1369 | $sp = substr($sp, 9); |
||||
1370 | |||||
1371 | if (strpos($sp, '^') === 0 || strpos($sp, '(?<') === 0) |
||||
1372 | return false; |
||||
1373 | |||||
1374 | // See if we can exclude `if` blocks, etc., to narrow down the matches. |
||||
1375 | // @todo Multiple layers of nested brackets might confuse this. |
||||
1376 | $sp = '~(?:^|//[^\n]+c\n|\*/|[;}]|' . implode('|', array_filter($placeholders)) . ')\s*' . (strpos($sp, '\K') === false ? '\K' : '') . $sp; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
1377 | |||||
1378 | preg_match_all($sp, $settingsText, $matches); |
||||
1379 | } |
||||
1380 | else |
||||
1381 | $sp = $substitution['search_pattern']; |
||||
1382 | |||||
1383 | // Found at least some that are simple assignment statements. |
||||
1384 | if (count($matches[0]) > 0) |
||||
1385 | { |
||||
1386 | // Remove any duplicates. |
||||
1387 | if (count($matches[0]) > 1) |
||||
1388 | $settingsText = preg_replace($sp, '', $settingsText, count($matches[0]) - 1); |
||||
1389 | |||||
1390 | // Insert placeholder for the last one. |
||||
1391 | $settingsText = preg_replace($sp, $substitution['placeholder'], $settingsText, 1); |
||||
1392 | } |
||||
1393 | |||||
1394 | // All instances are inside more complex code structures. |
||||
1395 | else |
||||
1396 | { |
||||
1397 | // Only safe option at this point is to skip it. |
||||
1398 | unset($substitutions[$var], $new_settings_vars[$var], $settings_defs[$var], $simple_replacements[$substitution['placeholder']], $replace_patterns[$var], $replace_strings[$var]); |
||||
1399 | |||||
1400 | continue; |
||||
1401 | } |
||||
1402 | } |
||||
1403 | // No matches found. |
||||
1404 | elseif (count($matches[0]) === 0) |
||||
1405 | { |
||||
1406 | $found = false; |
||||
1407 | $in_c = in_array($var, array_keys($config_vars)); |
||||
1408 | $in_s = in_array($var, array_keys($settings_vars)); |
||||
1409 | |||||
1410 | // Is it in there at all? |
||||
1411 | if (!preg_match('~(^|\s)\$' . preg_quote($var, '~') . '\s*=\s*~', $bare_settingsText)) |
||||
1412 | { |
||||
1413 | // It's defined by Settings.php, but not by code in the file. |
||||
1414 | // Probably done via an include or something. Skip it. |
||||
1415 | if ($in_s) |
||||
1416 | unset($substitutions[$var], $settings_defs[$var]); |
||||
1417 | |||||
1418 | // Admin is explicitly trying to set this one, so we'll handle |
||||
1419 | // it as if it were a new custom setting being added. |
||||
1420 | elseif ($in_c) |
||||
1421 | $new_settings_vars[$var] = $config_vars[$var]; |
||||
1422 | |||||
1423 | continue; |
||||
1424 | } |
||||
1425 | |||||
1426 | // It's in there somewhere, so check if the value changed type. |
||||
1427 | foreach (array('scalar', 'object', 'array') as $type) |
||||
1428 | { |
||||
1429 | // Try all the other scalar types first. |
||||
1430 | if ($type == 'scalar') |
||||
1431 | $sp = '(?:' . (implode('|', array_diff_key($type_regex, array($in_c ? gettype($config_vars[$var]) : ($in_s ? gettype($settings_vars[$var]) : PHP_INT_MAX) => '', 'array' => '', 'object' => '')))) . ')'; |
||||
1432 | |||||
1433 | // Maybe it's an object? (Probably not, but we should check.) |
||||
1434 | elseif ($type == 'object') |
||||
1435 | { |
||||
1436 | if (strpos($settingsText, '__set_state') === false) |
||||
1437 | continue; |
||||
1438 | |||||
1439 | $sp = $type_regex['object']; |
||||
1440 | } |
||||
1441 | |||||
1442 | // Maybe it's an array? |
||||
1443 | else |
||||
1444 | $sp = $type_regex['array']; |
||||
1445 | |||||
1446 | if (preg_match('~(^|\s)\$' . preg_quote($var, '~') . '\s*=\s*' . $sp . '~', $bare_settingsText, $derp)) |
||||
1447 | { |
||||
1448 | $settingsText = preg_replace('~(^|\s)\$' . preg_quote($var, '~') . '\s*=\s*' . $sp . '~', $substitution['placeholder'], $settingsText); |
||||
1449 | $found = true; |
||||
1450 | break; |
||||
1451 | } |
||||
1452 | } |
||||
1453 | |||||
1454 | // Something weird is going on. Better just leave it alone. |
||||
1455 | if (!$found) |
||||
1456 | { |
||||
1457 | // $var? What $var? Never heard of it. |
||||
1458 | unset($substitutions[$var], $new_settings_vars[$var], $settings_defs[$var], $simple_replacements[$substitution['placeholder']], $replace_patterns[$var], $replace_strings[$var]); |
||||
1459 | continue; |
||||
1460 | } |
||||
1461 | } |
||||
1462 | } |
||||
1463 | // Good to go, so insert our placeholder. |
||||
1464 | else |
||||
1465 | $settingsText = preg_replace($substitution['search_pattern'], $substitution['placeholder'], $settingsText); |
||||
1466 | |||||
1467 | // Once the code blocks are done, we want to compare to a version without comments. |
||||
1468 | if (is_int($last_var) && is_string($var)) |
||||
1469 | $bare_settingsText = strip_php_comments($settingsText); |
||||
1470 | |||||
1471 | $last_var = $var; |
||||
1472 | } |
||||
1473 | |||||
1474 | // Rebuilding requires more work. |
||||
1475 | if (!empty($rebuild)) |
||||
1476 | { |
||||
1477 | // Strip out the leading and trailing placeholders to prevent duplication. |
||||
1478 | $settingsText = str_replace(array($substitutions[-1]['placeholder'], $substitutions[-2]['placeholder']), '', $settingsText); |
||||
1479 | |||||
1480 | // Strip out all our standard comments. |
||||
1481 | foreach ($settings_defs as $var => $setting_def) |
||||
1482 | { |
||||
1483 | if (isset($setting_def['text'])) |
||||
1484 | $settingsText = strtr($settingsText, array($setting_def['text'] . "\n" => '', $setting_def['text'] => '',)); |
||||
1485 | } |
||||
1486 | |||||
1487 | // We need to refresh $bare_settingsText at this point. |
||||
1488 | $bare_settingsText = strip_php_comments($settingsText); |
||||
1489 | |||||
1490 | // Fix up whitespace to make comparison easier. |
||||
1491 | foreach ($placeholders as $placeholder) |
||||
1492 | { |
||||
1493 | $bare_settingsText = str_replace(array($placeholder . "\n\n", $placeholder), $placeholder . "\n", $bare_settingsText); |
||||
1494 | } |
||||
1495 | $bare_settingsText = preg_replace('/\h+$/m', '', rtrim($bare_settingsText)); |
||||
1496 | |||||
1497 | /* |
||||
1498 | * Divide the existing content into sections. |
||||
1499 | * The idea here is to make sure we don't mess with the relative position |
||||
1500 | * of any code blocks in the file, since that could break things. Within |
||||
1501 | * each section, however, we'll reorganize the content to match the |
||||
1502 | * default layout as closely as we can. |
||||
1503 | */ |
||||
1504 | $sections = array(array()); |
||||
1505 | $section_num = 0; |
||||
1506 | $trimmed_placeholders = array_filter(array_map('trim', $placeholders)); |
||||
1507 | $newsection_placeholders = array(); |
||||
1508 | $all_custom_content = ''; |
||||
1509 | foreach ($substitutions as $var => $substitution) |
||||
1510 | { |
||||
1511 | if (is_int($var) && ($var === -2 || $var > 0) && isset($trimmed_placeholders[$var]) && strpos($bare_settingsText, $trimmed_placeholders[$var]) !== false) |
||||
1512 | $newsection_placeholders[$var] = $trimmed_placeholders[$var]; |
||||
1513 | } |
||||
1514 | foreach (preg_split('~(?<=' . implode('|', $trimmed_placeholders) . ')|(?=' . implode('|', $trimmed_placeholders) . ')~', $bare_settingsText) as $part) |
||||
1515 | { |
||||
1516 | $part = trim($part); |
||||
1517 | |||||
1518 | if (empty($part)) |
||||
1519 | continue; |
||||
1520 | |||||
1521 | // Build a list of placeholders for this section. |
||||
1522 | if (in_array($part, $trimmed_placeholders) && !in_array($part, $newsection_placeholders)) |
||||
1523 | { |
||||
1524 | $sections[$section_num][] = $part; |
||||
1525 | } |
||||
1526 | // Custom content and newsection_placeholders get their own sections. |
||||
1527 | else |
||||
1528 | { |
||||
1529 | if (!empty($sections[$section_num])) |
||||
1530 | ++$section_num; |
||||
1531 | |||||
1532 | $sections[$section_num][] = $part; |
||||
1533 | |||||
1534 | ++$section_num; |
||||
1535 | |||||
1536 | if (!in_array($part, $trimmed_placeholders)) |
||||
1537 | $all_custom_content .= "\n" . $part; |
||||
1538 | } |
||||
1539 | } |
||||
1540 | |||||
1541 | // And now, rebuild the content! |
||||
1542 | $new_settingsText = ''; |
||||
1543 | $done_defs = array(); |
||||
1544 | $sectionkeys = array_keys($sections); |
||||
1545 | foreach ($sections as $sectionkey => $section) |
||||
1546 | { |
||||
1547 | // Custom content needs to be preserved. |
||||
1548 | if (count($section) === 1 && !in_array($section[0], $trimmed_placeholders)) |
||||
1549 | { |
||||
1550 | $prev_section_end = $sectionkey < 1 ? 0 : strpos($settingsText, end($sections[$sectionkey - 1])) + strlen(end($sections[$sectionkey - 1])); |
||||
1551 | $next_section_start = $sectionkey == end($sectionkeys) ? strlen($settingsText) : strpos($settingsText, $sections[$sectionkey + 1][0]); |
||||
1552 | |||||
1553 | $new_settingsText .= "\n" . substr($settingsText, $prev_section_end, $next_section_start - $prev_section_end) . "\n"; |
||||
1554 | } |
||||
1555 | // Put the placeholders in this section into canonical order. |
||||
1556 | else |
||||
1557 | { |
||||
1558 | $section_parts = array_flip($section); |
||||
1559 | $pathcode_reached = false; |
||||
1560 | foreach ($settings_defs as $var => $setting_def) |
||||
1561 | { |
||||
1562 | if ($var === $pathcode_var) |
||||
1563 | $pathcode_reached = true; |
||||
1564 | |||||
1565 | // Already did this setting, so move on to the next. |
||||
1566 | if (in_array($var, $done_defs)) |
||||
1567 | continue; |
||||
1568 | |||||
1569 | // Stop when we hit a setting definition that will start a later section. |
||||
1570 | if (isset($newsection_placeholders[$var]) && count($section) !== 1) |
||||
1571 | break; |
||||
1572 | |||||
1573 | // Stop when everything in this section is done, unless it's the last. |
||||
1574 | // This helps maintain the relative position of any custom content. |
||||
1575 | if (empty($section_parts) && $sectionkey < (count($sections) - 1)) |
||||
1576 | break; |
||||
1577 | |||||
1578 | $p = trim($substitutions[$var]['placeholder']); |
||||
1579 | |||||
1580 | // Can't do anything with an empty placeholder. |
||||
1581 | if ($p === '') |
||||
1582 | continue; |
||||
1583 | |||||
1584 | // Does this need to be inserted before the path correction code? |
||||
1585 | if (strpos($new_settingsText, trim($substitutions[$pathcode_var]['placeholder'])) !== false && in_array($var, $force_before_pathcode)) |
||||
1586 | { |
||||
1587 | $new_settingsText = strtr($new_settingsText, array($substitutions[$pathcode_var]['placeholder'] => $p . "\n" . $substitutions[$pathcode_var]['placeholder'])); |
||||
1588 | |||||
1589 | $bare_settingsText .= "\n" . $substitutions[$var]['placeholder']; |
||||
1590 | $done_defs[] = $var; |
||||
1591 | unset($section_parts[trim($substitutions[$var]['placeholder'])]); |
||||
1592 | } |
||||
1593 | |||||
1594 | // If it's in this section, add it to the new text now. |
||||
1595 | elseif (in_array($p, $section)) |
||||
1596 | { |
||||
1597 | $new_settingsText .= "\n" . $substitutions[$var]['placeholder']; |
||||
1598 | $done_defs[] = $var; |
||||
1599 | unset($section_parts[trim($substitutions[$var]['placeholder'])]); |
||||
1600 | } |
||||
1601 | |||||
1602 | // Perhaps it is safe to reposition it anyway. |
||||
1603 | elseif (is_string($var) && strpos($new_settingsText, $p) === false && strpos($all_custom_content, '$' . $var) === false) |
||||
1604 | { |
||||
1605 | $new_settingsText .= "\n" . $substitutions[$var]['placeholder']; |
||||
1606 | $done_defs[] = $var; |
||||
1607 | unset($section_parts[trim($substitutions[$var]['placeholder'])]); |
||||
1608 | } |
||||
1609 | |||||
1610 | // If this setting is missing entirely, fix it. |
||||
1611 | elseif (strpos($bare_settingsText, $p) === false) |
||||
1612 | { |
||||
1613 | // Special case if the path code is missing. Put it near the end, |
||||
1614 | // and also anything else that is missing that normally follows it. |
||||
1615 | if (!isset($newsection_placeholders[$pathcode_var]) && $pathcode_reached === true && $sectionkey < (count($sections) - 1)) |
||||
1616 | break; |
||||
1617 | |||||
1618 | $new_settingsText .= "\n" . $substitutions[$var]['placeholder']; |
||||
1619 | $bare_settingsText .= "\n" . $substitutions[$var]['placeholder']; |
||||
1620 | $done_defs[] = $var; |
||||
1621 | unset($section_parts[trim($substitutions[$var]['placeholder'])]); |
||||
1622 | } |
||||
1623 | } |
||||
1624 | } |
||||
1625 | } |
||||
1626 | $settingsText = $new_settingsText; |
||||
1627 | |||||
1628 | // Restore the leading and trailing placeholders as necessary. |
||||
1629 | foreach (array(-1, -2) as $var) |
||||
1630 | { |
||||
1631 | if (!empty($substitutions[$var]['placeholder']) && strpos($settingsText, $substitutions[$var]['placeholder']) === false); |
||||
1632 | { |
||||
1633 | $settingsText = ($var == -1 ? $substitutions[$var]['placeholder'] : '') . $settingsText . ($var == -2 ? $substitutions[$var]['placeholder'] : ''); |
||||
1634 | } |
||||
1635 | } |
||||
1636 | } |
||||
1637 | // Even if not rebuilding, there are a few variables that may need to be moved around. |
||||
1638 | else |
||||
1639 | { |
||||
1640 | $pathcode_pos = strpos($settingsText, $substitutions[$pathcode_var]['placeholder']); |
||||
1641 | |||||
1642 | if ($pathcode_pos !== false) |
||||
1643 | { |
||||
1644 | foreach ($force_before_pathcode as $var) |
||||
1645 | { |
||||
1646 | if (!empty($substitutions[$var]['placeholder']) && strpos($settingsText, $substitutions[$var]['placeholder']) > $pathcode_pos) |
||||
1647 | { |
||||
1648 | $settingsText = strtr($settingsText, array( |
||||
1649 | $substitutions[$var]['placeholder'] => '', |
||||
1650 | $substitutions[$pathcode_var]['placeholder'] => $substitutions[$var]['placeholder'] . "\n" . $substitutions[$pathcode_var]['placeholder'], |
||||
1651 | )); |
||||
1652 | } |
||||
1653 | } |
||||
1654 | } |
||||
1655 | } |
||||
1656 | |||||
1657 | /* 3.c: Replace the placeholders with the final values */ |
||||
1658 | |||||
1659 | // Where possible, perform simple substitutions. |
||||
1660 | $settingsText = strtr($settingsText, $simple_replacements); |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
1661 | |||||
1662 | // Deal with any complicated ones. |
||||
1663 | if (!empty($replace_patterns)) |
||||
1664 | $settingsText = preg_replace($replace_patterns, $replace_strings, $settingsText); |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
1665 | |||||
1666 | // Make absolutely sure that the path correction code is included. |
||||
1667 | if (strpos($settingsText, $substitutions[$pathcode_var]['replacement']) === false) |
||||
1668 | $settingsText = preg_replace('~(?=\n#+ Error.Catching #+)~', "\n" . $substitutions[$pathcode_var]['replacement'] . "\n", $settingsText); |
||||
1669 | |||||
1670 | // If we did not rebuild, do just enough to make sure the thing is viable. |
||||
1671 | if (empty($rebuild)) |
||||
1672 | { |
||||
1673 | // We need to refresh $bare_settingsText again, and remove the code blocks from it. |
||||
1674 | $bare_settingsText = $settingsText; |
||||
1675 | foreach ($substitutions as $var => $substitution) |
||||
1676 | { |
||||
1677 | if (!is_int($var)) |
||||
1678 | break; |
||||
1679 | |||||
1680 | if (isset($substitution['replacement'])) |
||||
1681 | $bare_settingsText = str_replace($substitution['replacement'], '', $bare_settingsText); |
||||
1682 | } |
||||
1683 | $bare_settingsText = strip_php_comments($bare_settingsText); |
||||
1684 | |||||
1685 | // Now insert any defined settings that are missing. |
||||
1686 | $pathcode_reached = false; |
||||
1687 | foreach ($settings_defs as $var => $setting_def) |
||||
1688 | { |
||||
1689 | if ($var === $pathcode_var) |
||||
1690 | $pathcode_reached = true; |
||||
1691 | |||||
1692 | if (is_int($var)) |
||||
1693 | continue; |
||||
1694 | |||||
1695 | // Do nothing if it is already in there. |
||||
1696 | if (preg_match($substitutions[$var]['search_pattern'], $bare_settingsText)) |
||||
1697 | continue; |
||||
1698 | |||||
1699 | // Insert it either before or after the path correction code, whichever is appropriate. |
||||
1700 | if (!$pathcode_reached || in_array($var, $force_before_pathcode)) |
||||
1701 | { |
||||
1702 | $settingsText = preg_replace($substitutions[$pathcode_var]['search_pattern'], $substitutions[$var]['replacement'] . "\n\n$0", $settingsText); |
||||
1703 | } |
||||
1704 | else |
||||
1705 | { |
||||
1706 | $settingsText = preg_replace($substitutions[$pathcode_var]['search_pattern'], "$0\n\n" . $substitutions[$var]['replacement'], $settingsText); |
||||
1707 | } |
||||
1708 | } |
||||
1709 | } |
||||
1710 | |||||
1711 | // If we have any brand new settings to add, do so. |
||||
1712 | foreach ($new_settings_vars as $var => $val) |
||||
1713 | { |
||||
1714 | if (isset($substitutions[$var]) && !preg_match($substitutions[$var]['search_pattern'], $settingsText)) |
||||
1715 | { |
||||
1716 | if (!isset($settings_defs[$var]) && strpos($settingsText, '# Custom Settings #') === false) |
||||
1717 | $settingsText = preg_replace('~(?=\n#+ Error.Catching #+)~', "\n\n######### Custom Settings #########\n", $settingsText); |
||||
1718 | |||||
1719 | $settingsText = preg_replace('~(?=\n#+ Error.Catching #+)~', $substitutions[$var]['replacement'] . "\n", $settingsText); |
||||
1720 | } |
||||
1721 | } |
||||
1722 | |||||
1723 | // This is just cosmetic. Get rid of extra lines of whitespace. |
||||
1724 | $settingsText = preg_replace('~\n\s*\n~', "\n\n", $settingsText); |
||||
1725 | |||||
1726 | /************************************** |
||||
1727 | * PART 4: Check syntax before saving * |
||||
1728 | **************************************/ |
||||
1729 | |||||
1730 | $temp_sfile = tempnam(sm_temp_dir(), md5($prefix . 'Settings.php')); |
||||
1731 | file_put_contents($temp_sfile, $settingsText); |
||||
1732 | |||||
1733 | $result = get_current_settings(filemtime($temp_sfile), $temp_sfile); |
||||
1734 | |||||
1735 | unlink($temp_sfile); |
||||
1736 | |||||
1737 | // If the syntax is borked, try rebuilding to see if that fixes it. |
||||
1738 | if ($result === false) |
||||
0 ignored issues
–
show
|
|||||
1739 | return empty($rebuild) ? updateSettingsFile($config_vars, $keep_quotes, true) : false; |
||||
1740 | |||||
1741 | /****************************************** |
||||
1742 | * PART 5: Write updated settings to file * |
||||
1743 | ******************************************/ |
||||
1744 | |||||
1745 | $success = safe_file_write($settingsFile, $settingsText, dirname($settingsFile) . '/Settings_bak.php', $last_settings_change); |
||||
1746 | |||||
1747 | // Remember this in case updateSettingsFile is called twice. |
||||
1748 | $mtime = filemtime($settingsFile); |
||||
0 ignored issues
–
show
|
|||||
1749 | |||||
1750 | return $success; |
||||
1751 | } |
||||
1752 | |||||
1753 | /** |
||||
1754 | * Retrieves a copy of the current values of all settings defined in Settings.php. |
||||
1755 | * |
||||
1756 | * Importantly, it does this without affecting our actual global variables at all, |
||||
1757 | * and it performs safety checks before acting. The result is an array of the |
||||
1758 | * values as recorded in the settings file. |
||||
1759 | * |
||||
1760 | * @param int $mtime Timestamp of last known good configuration. Defaults to time SMF started. |
||||
1761 | * @param string $settingsFile The settings file. Defaults to SMF's standard Settings.php. |
||||
1762 | * @return array An array of name/value pairs for all the settings in the file. |
||||
1763 | */ |
||||
1764 | function get_current_settings($mtime = null, $settingsFile = null) |
||||
1765 | { |
||||
1766 | $mtime = is_null($mtime) ? (defined('TIME_START') ? TIME_START : $_SERVER['REQUEST_TIME']) : (int) $mtime; |
||||
1767 | |||||
1768 | if (!is_file($settingsFile)) |
||||
0 ignored issues
–
show
It seems like
$settingsFile can also be of type null ; however, parameter $filename of is_file() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
1769 | { |
||||
1770 | foreach (get_included_files() as $settingsFile) |
||||
1771 | if (basename($settingsFile) === 'Settings.php') |
||||
1772 | break; |
||||
1773 | |||||
1774 | if (basename($settingsFile) !== 'Settings.php') |
||||
0 ignored issues
–
show
It seems like
$settingsFile can also be of type null ; however, parameter $path of basename() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
1775 | return false; |
||||
1776 | } |
||||
1777 | |||||
1778 | // If the file has been changed since the last known good configuration, bail out. |
||||
1779 | clearstatcache(); |
||||
1780 | if (filemtime($settingsFile) > $mtime) |
||||
0 ignored issues
–
show
It seems like
$settingsFile can also be of type null ; however, parameter $filename of filemtime() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
1781 | return false; |
||||
1782 | |||||
1783 | // Strip out opening and closing PHP tags. |
||||
1784 | $settingsText = trim(file_get_contents($settingsFile)); |
||||
0 ignored issues
–
show
It seems like
$settingsFile can also be of type null ; however, parameter $filename of file_get_contents() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
1785 | if (substr($settingsText, 0, 5) == '<' . '?php') |
||||
1786 | $settingsText = substr($settingsText, 5); |
||||
1787 | if (substr($settingsText, -2) == '?' . '>') |
||||
1788 | $settingsText = substr($settingsText, 0, -2); |
||||
1789 | |||||
1790 | // Since we're using eval, we need to manually replace these with strings. |
||||
1791 | $settingsText = strtr($settingsText, array( |
||||
1792 | '__FILE__' => var_export($settingsFile, true), |
||||
1793 | '__DIR__' => var_export(dirname($settingsFile), true), |
||||
0 ignored issues
–
show
It seems like
$settingsFile can also be of type null ; however, parameter $path of dirname() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
1794 | )); |
||||
1795 | |||||
1796 | // Prevents warnings about constants that are already defined. |
||||
1797 | $settingsText = preg_replace_callback( |
||||
1798 | '~\bdefine\s*\(\s*(["\'])(\w+)\1~', |
||||
1799 | function ($matches) |
||||
1800 | { |
||||
1801 | return 'define(\'' . md5(mt_rand()) . '\''; |
||||
1802 | }, |
||||
1803 | $settingsText |
||||
1804 | ); |
||||
1805 | |||||
1806 | // Handle eval errors gracefully in both PHP 5 and PHP 7 |
||||
1807 | try |
||||
1808 | { |
||||
1809 | if($settingsText !== '' && @eval($settingsText) === false) |
||||
0 ignored issues
–
show
|
|||||
1810 | throw new ErrorException('eval error'); |
||||
1811 | |||||
1812 | unset($mtime, $settingsFile, $settingsText); |
||||
1813 | $defined_vars = get_defined_vars(); |
||||
1814 | } |
||||
1815 | catch (Throwable $e) {} |
||||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
|
|||||
1816 | catch (ErrorException $e) {} |
||||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
|
|||||
1817 | if (isset($e)) |
||||
1818 | return false; |
||||
1819 | |||||
1820 | return $defined_vars; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
1821 | } |
||||
1822 | |||||
1823 | /** |
||||
1824 | * Writes data to a file, optionally making a backup, while avoiding race conditions. |
||||
1825 | * |
||||
1826 | * @param string $file The filepath of the file where the data should be written. |
||||
1827 | * @param string $data The data to be written to $file. |
||||
1828 | * @param string $backup_file The filepath where the backup should be saved. Default null. |
||||
1829 | * @param int $mtime If modification time of $file is more recent than this Unix timestamp, the write operation will abort. Defaults to time that the script started execution. |
||||
1830 | * @param bool $append If true, the data will be appended instead of overwriting the existing content of the file. Default false. |
||||
1831 | * @return bool Whether the write operation succeeded or not. |
||||
1832 | */ |
||||
1833 | function safe_file_write($file, $data, $backup_file = null, $mtime = null, $append = false) |
||||
1834 | { |
||||
1835 | // Sanity checks. |
||||
1836 | if (!file_exists($file) && !is_dir(dirname($file))) |
||||
1837 | return false; |
||||
1838 | |||||
1839 | if (!is_int($mtime)) |
||||
1840 | $mtime = $_SERVER['REQUEST_TIME']; |
||||
1841 | |||||
1842 | $temp_dir = sm_temp_dir(); |
||||
1843 | |||||
1844 | // Our temp files. |
||||
1845 | $temp_sfile = tempnam($temp_dir, pathinfo($file, PATHINFO_FILENAME) . '.'); |
||||
0 ignored issues
–
show
Are you sure
pathinfo($file, PATHINFO_FILENAME) of type array|string can be used in concatenation ?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
1846 | |||||
1847 | if (!empty($backup_file)) |
||||
1848 | $temp_bfile = tempnam($temp_dir, pathinfo($backup_file, PATHINFO_FILENAME) . '.'); |
||||
0 ignored issues
–
show
Are you sure
pathinfo($backup_file, PATHINFO_FILENAME) of type array|string can be used in concatenation ?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
1849 | |||||
1850 | // We need write permissions. |
||||
1851 | $failed = false; |
||||
1852 | foreach (array($file, $backup_file) as $sf) |
||||
1853 | { |
||||
1854 | if (empty($sf)) |
||||
1855 | continue; |
||||
1856 | |||||
1857 | if (!file_exists($sf)) |
||||
1858 | touch($sf); |
||||
1859 | elseif (!is_file($sf)) |
||||
1860 | $failed = true; |
||||
1861 | |||||
1862 | if (!$failed) |
||||
1863 | $failed = !smf_chmod($sf); |
||||
1864 | } |
||||
1865 | |||||
1866 | // Now let's see if writing to a temp file succeeds. |
||||
1867 | if (!$failed && file_put_contents($temp_sfile, $data, LOCK_EX) !== strlen($data)) |
||||
1868 | $failed = true; |
||||
1869 | |||||
1870 | // Tests passed, so it's time to do the job. |
||||
1871 | if (!$failed) |
||||
1872 | { |
||||
1873 | // Back up the backup, just in case. |
||||
1874 | if (file_exists($backup_file)) |
||||
0 ignored issues
–
show
It seems like
$backup_file can also be of type null ; however, parameter $filename of file_exists() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
1875 | $temp_bfile_saved = @copy($backup_file, $temp_bfile); |
||||
0 ignored issues
–
show
It seems like
$backup_file can also be of type null ; however, parameter $from of copy() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() Comprehensibility
Best Practice
introduced
by
|
|||||
1876 | |||||
1877 | // Make sure no one changed the file while we weren't looking. |
||||
1878 | clearstatcache(); |
||||
1879 | if (filemtime($file) <= $mtime) |
||||
1880 | { |
||||
1881 | // Attempt to open the file. |
||||
1882 | $sfhandle = @fopen($file, 'c'); |
||||
1883 | |||||
1884 | // Let's do this thing! |
||||
1885 | if ($sfhandle !== false) |
||||
1886 | { |
||||
1887 | // Immediately get a lock. |
||||
1888 | flock($sfhandle, LOCK_EX); |
||||
1889 | |||||
1890 | // Make sure the backup works before we do anything more. |
||||
1891 | $temp_sfile_saved = @copy($file, $temp_sfile); |
||||
1892 | |||||
1893 | // Now write our data to the file. |
||||
1894 | if ($temp_sfile_saved) |
||||
1895 | { |
||||
1896 | if (empty($append)) |
||||
1897 | { |
||||
1898 | ftruncate($sfhandle, 0); |
||||
1899 | rewind($sfhandle); |
||||
1900 | } |
||||
1901 | |||||
1902 | $failed = fwrite($sfhandle, $data) !== strlen($data); |
||||
1903 | } |
||||
1904 | else |
||||
1905 | $failed = true; |
||||
1906 | |||||
1907 | // If writing failed, put everything back the way it was. |
||||
1908 | if ($failed) |
||||
1909 | { |
||||
1910 | if (!empty($temp_sfile_saved)) |
||||
1911 | @rename($temp_sfile, $file); |
||||
1912 | |||||
1913 | if (!empty($temp_bfile_saved)) |
||||
1914 | @rename($temp_bfile, $backup_file); |
||||
0 ignored issues
–
show
It seems like
$backup_file can also be of type null ; however, parameter $to of rename() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
1915 | } |
||||
1916 | // It worked, so make our temp backup the new permanent backup. |
||||
1917 | elseif (!empty($backup_file)) |
||||
1918 | @rename($temp_sfile, $backup_file); |
||||
1919 | |||||
1920 | // And we're done. |
||||
1921 | flock($sfhandle, LOCK_UN); |
||||
1922 | fclose($sfhandle); |
||||
1923 | } |
||||
1924 | } |
||||
1925 | } |
||||
1926 | |||||
1927 | // We're done with these. |
||||
1928 | @unlink($temp_sfile); |
||||
1929 | @unlink($temp_bfile); |
||||
1930 | |||||
1931 | if ($failed) |
||||
1932 | return false; |
||||
1933 | |||||
1934 | // Even though on normal installations the filemtime should invalidate any cached version |
||||
1935 | // it seems that there are times it might not. So let's MAKE it dump the cache. |
||||
1936 | if (function_exists('opcache_invalidate')) |
||||
1937 | opcache_invalidate($file, true); |
||||
1938 | |||||
1939 | return true; |
||||
1940 | } |
||||
1941 | |||||
1942 | /** |
||||
1943 | * A wrapper around var_export whose output matches SMF coding conventions. |
||||
1944 | * |
||||
1945 | * @todo Add special handling for objects? |
||||
1946 | * |
||||
1947 | * @param mixed $var The variable to export |
||||
1948 | * @return mixed A PHP-parseable representation of the variable's value |
||||
1949 | */ |
||||
1950 | function smf_var_export($var) |
||||
1951 | { |
||||
1952 | /* |
||||
1953 | * Old versions of updateSettingsFile couldn't handle multi-line values. |
||||
1954 | * Even though technically we can now, we'll keep arrays on one line for |
||||
1955 | * the sake of backwards compatibility. |
||||
1956 | */ |
||||
1957 | if (is_array($var)) |
||||
1958 | { |
||||
1959 | $return = array(); |
||||
1960 | |||||
1961 | foreach ($var as $key => $value) |
||||
1962 | $return[] = var_export($key, true) . ' => ' . smf_var_export($value); |
||||
1963 | |||||
1964 | return 'array(' . implode(', ', $return) . ')'; |
||||
1965 | } |
||||
1966 | |||||
1967 | // For the same reason, replace literal returns and newlines with "\r" and "\n" |
||||
1968 | elseif (is_string($var) && (strpos($var, "\n") !== false || strpos($var, "\r") !== false)) |
||||
1969 | { |
||||
1970 | return strtr( |
||||
1971 | preg_replace_callback( |
||||
1972 | '/[\r\n]+/', |
||||
1973 | function($m) |
||||
1974 | { |
||||
1975 | return '\' . "' . strtr($m[0], array("\r" => '\r', "\n" => '\n')) . '" . \''; |
||||
1976 | }, |
||||
1977 | var_export($var, true) |
||||
1978 | ), |
||||
1979 | array("'' . " => '', " . ''" => '') |
||||
1980 | ); |
||||
1981 | } |
||||
1982 | |||||
1983 | // We typically use lowercase true/false/null. |
||||
1984 | elseif (in_array(gettype($var), array('boolean', 'NULL'))) |
||||
1985 | return strtolower(var_export($var, true)); |
||||
1986 | |||||
1987 | // Nothing special. |
||||
1988 | else |
||||
1989 | return var_export($var, true); |
||||
1990 | }; |
||||
1991 | |||||
1992 | /** |
||||
1993 | * Deletes all PHP comments from a string. |
||||
1994 | * |
||||
1995 | * @param string $code_str A string containing PHP code. |
||||
1996 | * @return string A string of PHP code with no comments in it. |
||||
1997 | */ |
||||
1998 | function strip_php_comments($code_str) |
||||
1999 | { |
||||
2000 | // This is the faster, better way. |
||||
2001 | if (is_callable('token_get_all')) |
||||
2002 | { |
||||
2003 | $tokens = token_get_all($code_str); |
||||
2004 | |||||
2005 | $parts = array(); |
||||
2006 | foreach ($tokens as $token) |
||||
2007 | { |
||||
2008 | if (is_string($token)) |
||||
2009 | $parts[] = $token; |
||||
2010 | else |
||||
2011 | { |
||||
2012 | list($id, $text) = $token; |
||||
2013 | |||||
2014 | switch ($id) { |
||||
2015 | case T_COMMENT: |
||||
2016 | case T_DOC_COMMENT: |
||||
2017 | end($parts); |
||||
2018 | $prev_part = key($parts); |
||||
2019 | |||||
2020 | // For the sake of tider output, trim any horizontal |
||||
2021 | // whitespace that immediately preceded the comment. |
||||
2022 | $parts[$prev_part] = rtrim($parts[$prev_part], "\t "); |
||||
2023 | |||||
2024 | // For 'C' style comments, also trim one preceding |
||||
2025 | // line break, if present. |
||||
2026 | if (strpos($text, '/*') === 0) |
||||
2027 | { |
||||
2028 | if (substr($parts[$prev_part], -2) === "\r\n") |
||||
2029 | $parts[$prev_part] = substr($parts[$prev_part], 0, -2); |
||||
2030 | elseif (in_array(substr($parts[$prev_part], -1), array("\r", "\n"))) |
||||
2031 | $parts[$prev_part] = substr($parts[$prev_part], 0, -1); |
||||
2032 | } |
||||
2033 | |||||
2034 | break; |
||||
2035 | |||||
2036 | default: |
||||
2037 | $parts[] = $text; |
||||
2038 | break; |
||||
2039 | } |
||||
2040 | } |
||||
2041 | } |
||||
2042 | |||||
2043 | $code_str = implode('', $parts); |
||||
2044 | |||||
2045 | return $code_str; |
||||
2046 | } |
||||
2047 | |||||
2048 | // If the tokenizer extension has been disabled, do the job manually. |
||||
2049 | |||||
2050 | // Leave any heredocs alone. |
||||
2051 | if (preg_match_all('/<<<([\'"]?)(\w+)\1?\R(.*?)\R\h*\2;$/ms', $code_str, $matches)) |
||||
2052 | { |
||||
2053 | $heredoc_replacements = array(); |
||||
2054 | |||||
2055 | foreach ($matches[0] as $mkey => $heredoc) |
||||
2056 | $heredoc_replacements[$heredoc] = var_export(md5($matches[3][$mkey]), true) . ';'; |
||||
2057 | |||||
2058 | $code_str = strtr($code_str, $heredoc_replacements); |
||||
2059 | } |
||||
2060 | |||||
2061 | // Split before everything that could possibly delimit a comment or a string. |
||||
2062 | $parts = preg_split('~(?=#+|/(?=/|\*)|\*/|\R|(?<!\\\)[\'"])~m', $code_str); |
||||
2063 | |||||
2064 | $in_string = 0; |
||||
2065 | $in_comment = 0; |
||||
2066 | foreach ($parts as $partkey => $part) |
||||
2067 | { |
||||
2068 | $one_char = substr($part, 0, 1); |
||||
2069 | $two_char = substr($part, 0, 2); |
||||
2070 | $to_remove = 0; |
||||
2071 | |||||
2072 | /* |
||||
2073 | * Meaning of $in_string values: |
||||
2074 | * 0: not in a string |
||||
2075 | * 1: in a single quote string |
||||
2076 | * 2: in a double quote string |
||||
2077 | */ |
||||
2078 | if ($one_char == "'") |
||||
2079 | { |
||||
2080 | if (!empty($in_comment)) |
||||
2081 | $in_string = 0; |
||||
2082 | elseif (in_array($in_string, array(0, 1))) |
||||
2083 | $in_string = ($in_string ^ 1); |
||||
2084 | } |
||||
2085 | elseif ($one_char == '"') |
||||
2086 | { |
||||
2087 | if (!empty($in_comment)) |
||||
2088 | $in_string = 0; |
||||
2089 | elseif (in_array($in_string, array(0, 2))) |
||||
2090 | $in_string = ($in_string ^ 2); |
||||
2091 | } |
||||
2092 | |||||
2093 | /* |
||||
2094 | * Meaning of $in_comment values: |
||||
2095 | * 0: not in a comment |
||||
2096 | * 1: in a single line comment |
||||
2097 | * 2: in a multi-line comment |
||||
2098 | */ |
||||
2099 | elseif ($one_char == '#' || $two_char == '//') |
||||
2100 | { |
||||
2101 | $in_comment = !empty($in_string) ? 0 : (empty($in_comment) ? 1 : $in_comment); |
||||
2102 | |||||
2103 | if ($in_comment == 1) |
||||
2104 | { |
||||
2105 | $parts[$partkey - 1] = rtrim($parts[$partkey - 1], "\t "); |
||||
2106 | |||||
2107 | if (substr($parts[$partkey - 1], -2) === "\r\n") |
||||
2108 | $parts[$partkey - 1] = substr($parts[$partkey - 1], 0, -2); |
||||
2109 | elseif (in_array(substr($parts[$partkey - 1], -1), array("\r", "\n"))) |
||||
2110 | $parts[$partkey - 1] = substr($parts[$partkey - 1], 0, -1); |
||||
2111 | } |
||||
2112 | } |
||||
2113 | elseif ($two_char === "\r\n" || $one_char === "\r" || $one_char === "\n") |
||||
2114 | { |
||||
2115 | if ($in_comment == 1) |
||||
2116 | $in_comment = 0; |
||||
2117 | } |
||||
2118 | elseif ($two_char == '/*') |
||||
2119 | { |
||||
2120 | $in_comment = !empty($in_string) ? 0 : (empty($in_comment) ? 2 : $in_comment); |
||||
2121 | |||||
2122 | if ($in_comment == 2) |
||||
2123 | { |
||||
2124 | $parts[$partkey - 1] = rtrim($parts[$partkey - 1], "\t "); |
||||
2125 | |||||
2126 | if (substr($parts[$partkey - 1], -2) === "\r\n") |
||||
2127 | $parts[$partkey - 1] = substr($parts[$partkey - 1], 0, -2); |
||||
2128 | elseif (in_array(substr($parts[$partkey - 1], -1), array("\r", "\n"))) |
||||
2129 | $parts[$partkey - 1] = substr($parts[$partkey - 1], 0, -1); |
||||
2130 | } |
||||
2131 | } |
||||
2132 | elseif ($two_char == '*/') |
||||
2133 | { |
||||
2134 | if ($in_comment == 2) |
||||
2135 | { |
||||
2136 | $in_comment = 0; |
||||
2137 | |||||
2138 | // Delete the comment closing. |
||||
2139 | $to_remove = 2; |
||||
2140 | } |
||||
2141 | } |
||||
2142 | |||||
2143 | if (empty($in_comment)) |
||||
2144 | $parts[$partkey] = strlen($part) > $to_remove ? substr($part, $to_remove) : ''; |
||||
2145 | else |
||||
2146 | $parts[$partkey] = ''; |
||||
2147 | } |
||||
2148 | |||||
2149 | $code_str = implode('', $parts); |
||||
2150 | |||||
2151 | if (!empty($heredoc_replacements)) |
||||
2152 | $code_str = strtr($code_str, array_flip($heredoc_replacements)); |
||||
2153 | |||||
2154 | return $code_str; |
||||
2155 | } |
||||
2156 | |||||
2157 | /** |
||||
2158 | * Saves the time of the last db error for the error log |
||||
2159 | * - Done separately from updateSettingsFile to avoid race conditions |
||||
2160 | * which can occur during a db error |
||||
2161 | * - If it fails Settings.php will assume 0 |
||||
2162 | * |
||||
2163 | * @param int $time The timestamp of the last DB error |
||||
2164 | * @param bool True If we should update the current db_last_error context as well. This may be useful in cases where the current context needs to know a error was logged since the last check. |
||||
2165 | * @return bool True If we could succesfully put the file or not. |
||||
2166 | */ |
||||
2167 | function updateDbLastError($time, $update = true) |
||||
2168 | { |
||||
2169 | global $boarddir, $cachedir, $db_last_error; |
||||
2170 | |||||
2171 | // Write out the db_last_error file with the error timestamp |
||||
2172 | if (!empty($cachedir) && is_writable($cachedir)) |
||||
2173 | $errorfile = $cachedir . '/db_last_error.php'; |
||||
2174 | |||||
2175 | elseif (file_exists(dirname(__DIR__) . '/cache')) |
||||
2176 | $errorfile = dirname(__DIR__) . '/cache/db_last_error.php'; |
||||
2177 | |||||
2178 | else |
||||
2179 | $errorfile = dirname(__DIR__) . '/db_last_error.php'; |
||||
2180 | |||||
2181 | $result = file_put_contents($errorfile, '<' . '?' . "php\n" . '$db_last_error = ' . $time . ';' . "\n" . '?' . '>', LOCK_EX); |
||||
2182 | |||||
2183 | @touch($boarddir . '/' . 'Settings.php'); |
||||
2184 | |||||
2185 | // Unless requested, we should update $db_last_error as well. |
||||
2186 | if ($update) |
||||
2187 | $db_last_error = $time; |
||||
2188 | |||||
2189 | // We do a loose match here rather than strict (!==) as 0 is also false. |
||||
2190 | return $result != false; |
||||
0 ignored issues
–
show
|
|||||
2191 | } |
||||
2192 | |||||
2193 | /** |
||||
2194 | * Saves the admin's current preferences to the database. |
||||
2195 | */ |
||||
2196 | function updateAdminPreferences() |
||||
2197 | { |
||||
2198 | global $options, $context, $smcFunc, $settings, $user_info; |
||||
2199 | |||||
2200 | // This must exist! |
||||
2201 | if (!isset($context['admin_preferences'])) |
||||
2202 | return false; |
||||
2203 | |||||
2204 | // This is what we'll be saving. |
||||
2205 | $options['admin_preferences'] = $smcFunc['json_encode']($context['admin_preferences']); |
||||
2206 | |||||
2207 | // Just check we haven't ended up with something theme exclusive somehow. |
||||
2208 | $smcFunc['db_query']('', ' |
||||
2209 | DELETE FROM {db_prefix}themes |
||||
2210 | WHERE id_theme != {int:default_theme} |
||||
2211 | AND variable = {string:admin_preferences}', |
||||
2212 | array( |
||||
2213 | 'default_theme' => 1, |
||||
2214 | 'admin_preferences' => 'admin_preferences', |
||||
2215 | ) |
||||
2216 | ); |
||||
2217 | |||||
2218 | // Update the themes table. |
||||
2219 | $smcFunc['db_insert']('replace', |
||||
2220 | '{db_prefix}themes', |
||||
2221 | array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'), |
||||
2222 | array($user_info['id'], 1, 'admin_preferences', $options['admin_preferences']), |
||||
2223 | array('id_member', 'id_theme', 'variable') |
||||
2224 | ); |
||||
2225 | |||||
2226 | // Make sure we invalidate any cache. |
||||
2227 | cache_put_data('theme_settings-' . $settings['theme_id'] . ':' . $user_info['id'], null, 0); |
||||
2228 | } |
||||
2229 | |||||
2230 | /** |
||||
2231 | * Send all the administrators a lovely email. |
||||
2232 | * - loads all users who are admins or have the admin forum permission. |
||||
2233 | * - uses the email template and replacements passed in the parameters. |
||||
2234 | * - sends them an email. |
||||
2235 | * |
||||
2236 | * @param string $template Which email template to use |
||||
2237 | * @param array $replacements An array of items to replace the variables in the template |
||||
2238 | * @param array $additional_recipients An array of arrays of info for additional recipients. Should have 'id', 'email' and 'name' for each. |
||||
2239 | */ |
||||
2240 | function emailAdmins($template, $replacements = array(), $additional_recipients = array()) |
||||
2241 | { |
||||
2242 | global $smcFunc, $sourcedir, $language, $modSettings; |
||||
2243 | |||||
2244 | // We certainly want this. |
||||
2245 | require_once($sourcedir . '/Subs-Post.php'); |
||||
2246 | |||||
2247 | // Load all members which are effectively admins. |
||||
2248 | require_once($sourcedir . '/Subs-Members.php'); |
||||
2249 | $members = membersAllowedTo('admin_forum'); |
||||
2250 | |||||
2251 | // Load their alert preferences |
||||
2252 | require_once($sourcedir . '/Subs-Notify.php'); |
||||
2253 | $prefs = getNotifyPrefs($members, 'announcements', true); |
||||
2254 | |||||
2255 | $request = $smcFunc['db_query']('', ' |
||||
2256 | SELECT id_member, member_name, real_name, lngfile, email_address |
||||
2257 | FROM {db_prefix}members |
||||
2258 | WHERE id_member IN({array_int:members})', |
||||
2259 | array( |
||||
2260 | 'members' => $members, |
||||
2261 | ) |
||||
2262 | ); |
||||
2263 | $emails_sent = array(); |
||||
2264 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||||
2265 | { |
||||
2266 | if (empty($prefs[$row['id_member']]['announcements'])) |
||||
2267 | continue; |
||||
2268 | |||||
2269 | // Stick their particulars in the replacement data. |
||||
2270 | $replacements['IDMEMBER'] = $row['id_member']; |
||||
2271 | $replacements['REALNAME'] = $row['member_name']; |
||||
2272 | $replacements['USERNAME'] = $row['real_name']; |
||||
2273 | |||||
2274 | // Load the data from the template. |
||||
2275 | $emaildata = loadEmailTemplate($template, $replacements, empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']); |
||||
2276 | |||||
2277 | // Then send the actual email. |
||||
2278 | sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], null, $template, $emaildata['is_html'], 1); |
||||
2279 | |||||
2280 | // Track who we emailed so we don't do it twice. |
||||
2281 | $emails_sent[] = $row['email_address']; |
||||
2282 | } |
||||
2283 | $smcFunc['db_free_result']($request); |
||||
2284 | |||||
2285 | // Any additional users we must email this to? |
||||
2286 | if (!empty($additional_recipients)) |
||||
2287 | foreach ($additional_recipients as $recipient) |
||||
2288 | { |
||||
2289 | if (in_array($recipient['email'], $emails_sent)) |
||||
2290 | continue; |
||||
2291 | |||||
2292 | $replacements['IDMEMBER'] = $recipient['id']; |
||||
2293 | $replacements['REALNAME'] = $recipient['name']; |
||||
2294 | $replacements['USERNAME'] = $recipient['name']; |
||||
2295 | |||||
2296 | // Load the template again. |
||||
2297 | $emaildata = loadEmailTemplate($template, $replacements, empty($recipient['lang']) || empty($modSettings['userLanguage']) ? $language : $recipient['lang']); |
||||
2298 | |||||
2299 | // Send off the email. |
||||
2300 | sendmail($recipient['email'], $emaildata['subject'], $emaildata['body'], null, $template, $emaildata['is_html'], 1); |
||||
2301 | } |
||||
2302 | } |
||||
2303 | |||||
2304 | /** |
||||
2305 | * Locates the most appropriate temp directory. |
||||
2306 | * |
||||
2307 | * Systems using `open_basedir` restrictions may receive errors with |
||||
2308 | * `sys_get_temp_dir()` due to misconfigurations on servers. Other |
||||
2309 | * cases sys_temp_dir may not be set to a safe value. Additionally |
||||
2310 | * `sys_get_temp_dir` may use a readonly directory. This attempts to |
||||
2311 | * find a working temp directory that is accessible under the |
||||
2312 | * restrictions and is writable to the web service account. |
||||
2313 | * |
||||
2314 | * Directories checked against `open_basedir`: |
||||
2315 | * |
||||
2316 | * - `sys_get_temp_dir()` |
||||
2317 | * - `upload_tmp_dir` |
||||
2318 | * - `session.save_path` |
||||
2319 | * - `cachedir` |
||||
2320 | * |
||||
2321 | * @return string |
||||
2322 | */ |
||||
2323 | function sm_temp_dir() |
||||
2324 | { |
||||
2325 | global $cachedir; |
||||
2326 | |||||
2327 | static $temp_dir = null; |
||||
2328 | |||||
2329 | // Already did this. |
||||
2330 | if (!empty($temp_dir)) |
||||
2331 | return $temp_dir; |
||||
2332 | |||||
2333 | // Temp Directory options order. |
||||
2334 | $temp_dir_options = array( |
||||
2335 | 0 => 'sys_get_temp_dir', |
||||
2336 | 1 => 'upload_tmp_dir', |
||||
2337 | 2 => 'session.save_path', |
||||
2338 | 3 => 'cachedir' |
||||
2339 | ); |
||||
2340 | |||||
2341 | // Determine if we should detect a restriction and what restrictions that may be. |
||||
2342 | $open_base_dir = ini_get('open_basedir'); |
||||
2343 | $restriction = !empty($open_base_dir) ? explode(':', $open_base_dir) : false; |
||||
2344 | |||||
2345 | // Prevent any errors as we search. |
||||
2346 | $old_error_reporting = error_reporting(0); |
||||
2347 | |||||
2348 | // Search for a working temp directory. |
||||
2349 | foreach ($temp_dir_options as $id_temp => $temp_option) |
||||
2350 | { |
||||
2351 | switch ($temp_option) { |
||||
2352 | case 'cachedir': |
||||
2353 | $possible_temp = rtrim($cachedir, '/'); |
||||
2354 | break; |
||||
2355 | |||||
2356 | case 'session.save_path': |
||||
2357 | $possible_temp = rtrim(ini_get('session.save_path'), '/'); |
||||
2358 | break; |
||||
2359 | |||||
2360 | case 'upload_tmp_dir': |
||||
2361 | $possible_temp = rtrim(ini_get('upload_tmp_dir'), '/'); |
||||
2362 | break; |
||||
2363 | |||||
2364 | default: |
||||
2365 | $possible_temp = sys_get_temp_dir(); |
||||
2366 | break; |
||||
2367 | } |
||||
2368 | |||||
2369 | // Check if we have a restriction preventing this from working. |
||||
2370 | if ($restriction) |
||||
2371 | { |
||||
2372 | foreach ($restriction as $dir) |
||||
2373 | { |
||||
2374 | if (strpos($possible_temp, $dir) !== false && is_writable($possible_temp)) |
||||
2375 | { |
||||
2376 | $temp_dir = $possible_temp; |
||||
2377 | break; |
||||
2378 | } |
||||
2379 | } |
||||
2380 | } |
||||
2381 | // No restrictions, but need to check for writable status. |
||||
2382 | elseif (is_writable($possible_temp)) |
||||
2383 | { |
||||
2384 | $temp_dir = $possible_temp; |
||||
2385 | break; |
||||
2386 | } |
||||
2387 | } |
||||
2388 | |||||
2389 | // Fall back to sys_get_temp_dir even though it won't work, so we have something. |
||||
2390 | if (empty($temp_dir)) |
||||
2391 | $temp_dir = sys_get_temp_dir(); |
||||
2392 | |||||
2393 | // Fix the path. |
||||
2394 | $temp_dir = substr($temp_dir, -1) === '/' ? $temp_dir : $temp_dir . '/'; |
||||
2395 | |||||
2396 | // Put things back. |
||||
2397 | error_reporting($old_error_reporting); |
||||
2398 | |||||
2399 | return $temp_dir; |
||||
2400 | } |
||||
2401 | |||||
2402 | ?> |