Mercurial > hg > vamp-website
comparison forum/Sources/Subs-Admin.php @ 76:e3e11437ecea website
Add forum code
author | Chris Cannam |
---|---|
date | Sun, 07 Jul 2013 11:25:48 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
75:72f59aa7e503 | 76:e3e11437ecea |
---|---|
1 <?php | |
2 | |
3 /** | |
4 * Simple Machines Forum (SMF) | |
5 * | |
6 * @package SMF | |
7 * @author Simple Machines http://www.simplemachines.org | |
8 * @copyright 2011 Simple Machines | |
9 * @license http://www.simplemachines.org/about/smf/license.php BSD | |
10 * | |
11 * @version 2.0 | |
12 */ | |
13 | |
14 if (!defined('SMF')) | |
15 die('Hacking attempt...'); | |
16 | |
17 /* This file contains functions that are specifically done by administrators. | |
18 The most important function in this file for mod makers happens to be the | |
19 updateSettingsFile() function, but it shouldn't be used often anyway. | |
20 | |
21 void getServerVersions(array checkFor) | |
22 - get a list of versions that are currently installed on the server. | |
23 | |
24 void getFileVersions(array versionOptions) | |
25 - get detailed version information about the physical SMF files on the | |
26 server. | |
27 - the input parameter allows to set whether to include SSI.php and | |
28 whether the results should be sorted. | |
29 - returns an array containing information on source files, templates | |
30 and language files found in the default theme directory (grouped by | |
31 language). | |
32 | |
33 void updateSettingsFile(array config_vars) | |
34 - updates the Settings.php file with the changes in config_vars. | |
35 - expects config_vars to be an associative array, with the keys as the | |
36 variable names in Settings.php, and the values the varaible values. | |
37 - does not escape or quote values. | |
38 - preserves case, formatting, and additional options in file. | |
39 - writes nothing if the resulting file would be less than 10 lines | |
40 in length (sanity check for read lock.) | |
41 | |
42 void updateAdminPreferences() | |
43 - saves the admins current preferences to the database. | |
44 | |
45 void emailAdmins(string $template, array $replacements = array(), additional_recipients = array()) | |
46 - loads all users who are admins or have the admin forum permission. | |
47 - uses the email template and replacements passed in the parameters. | |
48 - sends them an email. | |
49 | |
50 bool updateLastDatabaseError() | |
51 - attempts to use the backup file first, to store the last database error | |
52 - and only update Settings.php if the first was successful. | |
53 | |
54 */ | |
55 | |
56 function getServerVersions($checkFor) | |
57 { | |
58 global $txt, $db_connection, $_PHPA, $smcFunc, $memcached, $modSettings; | |
59 | |
60 loadLanguage('Admin'); | |
61 | |
62 $versions = array(); | |
63 | |
64 // Is GD available? If it is, we should show version information for it too. | |
65 if (in_array('gd', $checkFor) && function_exists('gd_info')) | |
66 { | |
67 $temp = gd_info(); | |
68 $versions['gd'] = array('title' => $txt['support_versions_gd'], 'version' => $temp['GD Version']); | |
69 } | |
70 | |
71 // Now lets check for the Database. | |
72 if (in_array('db_server', $checkFor)) | |
73 { | |
74 db_extend(); | |
75 if (!isset($db_connection) || $db_connection === false) | |
76 trigger_error('getServerVersions(): you need to be connected to the database in order to get its server version', E_USER_NOTICE); | |
77 else | |
78 { | |
79 $versions['db_server'] = array('title' => sprintf($txt['support_versions_db'], $smcFunc['db_title']), 'version' => ''); | |
80 $versions['db_server']['version'] = $smcFunc['db_get_version'](); | |
81 } | |
82 } | |
83 | |
84 // If we're using memcache we need the server info. | |
85 if (empty($memcached) && function_exists('memcache_get') && isset($modSettings['cache_memcached']) && trim($modSettings['cache_memcached']) != '') | |
86 get_memcached_server(); | |
87 | |
88 // Check to see if we have any accelerators installed... | |
89 if (in_array('mmcache', $checkFor) && defined('MMCACHE_VERSION')) | |
90 $versions['mmcache'] = array('title' => 'Turck MMCache', 'version' => MMCACHE_VERSION); | |
91 if (in_array('eaccelerator', $checkFor) && defined('EACCELERATOR_VERSION')) | |
92 $versions['eaccelerator'] = array('title' => 'eAccelerator', 'version' => EACCELERATOR_VERSION); | |
93 if (in_array('phpa', $checkFor) && isset($_PHPA)) | |
94 $versions['phpa'] = array('title' => 'ionCube PHP-Accelerator', 'version' => $_PHPA['VERSION']); | |
95 if (in_array('apc', $checkFor) && extension_loaded('apc')) | |
96 $versions['apc'] = array('title' => 'Alternative PHP Cache', 'version' => phpversion('apc')); | |
97 if (in_array('memcache', $checkFor) && function_exists('memcache_set')) | |
98 $versions['memcache'] = array('title' => 'Memcached', 'version' => empty($memcached) ? '???' : memcache_get_version($memcached)); | |
99 if (in_array('xcache', $checkFor) && function_exists('xcache_set')) | |
100 $versions['xcache'] = array('title' => 'XCache', 'version' => XCACHE_VERSION); | |
101 if (in_array('php', $checkFor)) | |
102 $versions['php'] = array('title' => 'PHP', 'version' => PHP_VERSION); | |
103 | |
104 if (in_array('server', $checkFor)) | |
105 $versions['server'] = array('title' => $txt['support_versions_server'], 'version' => $_SERVER['SERVER_SOFTWARE']); | |
106 | |
107 return $versions; | |
108 } | |
109 | |
110 // Search through source, theme and language files to determine their version. | |
111 function getFileVersions(&$versionOptions) | |
112 { | |
113 global $boarddir, $sourcedir, $settings; | |
114 | |
115 // Default place to find the languages would be the default theme dir. | |
116 $lang_dir = $settings['default_theme_dir'] . '/languages'; | |
117 | |
118 $version_info = array( | |
119 'file_versions' => array(), | |
120 'default_template_versions' => array(), | |
121 'template_versions' => array(), | |
122 'default_language_versions' => array(), | |
123 ); | |
124 | |
125 // Find the version in SSI.php's file header. | |
126 if (!empty($versionOptions['include_ssi']) && file_exists($boarddir . '/SSI.php')) | |
127 { | |
128 $fp = fopen($boarddir . '/SSI.php', 'rb'); | |
129 $header = fread($fp, 4096); | |
130 fclose($fp); | |
131 | |
132 // The comment looks rougly like... that. | |
133 if (preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $header, $match) == 1) | |
134 $version_info['file_versions']['SSI.php'] = $match[1]; | |
135 // Not found! This is bad. | |
136 else | |
137 $version_info['file_versions']['SSI.php'] = '??'; | |
138 } | |
139 | |
140 // Do the paid subscriptions handler? | |
141 if (!empty($versionOptions['include_subscriptions']) && file_exists($boarddir . '/subscriptions.php')) | |
142 { | |
143 $fp = fopen($boarddir . '/subscriptions.php', 'rb'); | |
144 $header = fread($fp, 4096); | |
145 fclose($fp); | |
146 | |
147 // Found it? | |
148 if (preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $header, $match) == 1) | |
149 $version_info['file_versions']['subscriptions.php'] = $match[1]; | |
150 // If we haven't how do we all get paid? | |
151 else | |
152 $version_info['file_versions']['subscriptions.php'] = '??'; | |
153 } | |
154 | |
155 // Load all the files in the Sources directory, except this file and the redirect. | |
156 $sources_dir = dir($sourcedir); | |
157 while ($entry = $sources_dir->read()) | |
158 { | |
159 if (substr($entry, -4) === '.php' && !is_dir($sourcedir . '/' . $entry) && $entry !== 'index.php') | |
160 { | |
161 // Read the first 4k from the file.... enough for the header. | |
162 $fp = fopen($sourcedir . '/' . $entry, 'rb'); | |
163 $header = fread($fp, 4096); | |
164 fclose($fp); | |
165 | |
166 // Look for the version comment in the file header. | |
167 if (preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $header, $match) == 1) | |
168 $version_info['file_versions'][$entry] = $match[1]; | |
169 // It wasn't found, but the file was... show a '??'. | |
170 else | |
171 $version_info['file_versions'][$entry] = '??'; | |
172 } | |
173 } | |
174 $sources_dir->close(); | |
175 | |
176 // Load all the files in the default template directory - and the current theme if applicable. | |
177 $directories = array('default_template_versions' => $settings['default_theme_dir']); | |
178 if ($settings['theme_id'] != 1) | |
179 $directories += array('template_versions' => $settings['theme_dir']); | |
180 | |
181 foreach ($directories as $type => $dirname) | |
182 { | |
183 $this_dir = dir($dirname); | |
184 while ($entry = $this_dir->read()) | |
185 { | |
186 if (substr($entry, -12) == 'template.php' && !is_dir($dirname . '/' . $entry)) | |
187 { | |
188 // Read the first 768 bytes from the file.... enough for the header. | |
189 $fp = fopen($dirname . '/' . $entry, 'rb'); | |
190 $header = fread($fp, 768); | |
191 fclose($fp); | |
192 | |
193 // Look for the version comment in the file header. | |
194 if (preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $header, $match) == 1) | |
195 $version_info[$type][$entry] = $match[1]; | |
196 // It wasn't found, but the file was... show a '??'. | |
197 else | |
198 $version_info[$type][$entry] = '??'; | |
199 } | |
200 } | |
201 $this_dir->close(); | |
202 } | |
203 | |
204 // Load up all the files in the default language directory and sort by language. | |
205 $this_dir = dir($lang_dir); | |
206 while ($entry = $this_dir->read()) | |
207 { | |
208 if (substr($entry, -4) == '.php' && $entry != 'index.php' && !is_dir($lang_dir . '/' . $entry)) | |
209 { | |
210 // Read the first 768 bytes from the file.... enough for the header. | |
211 $fp = fopen($lang_dir . '/' . $entry, 'rb'); | |
212 $header = fread($fp, 768); | |
213 fclose($fp); | |
214 | |
215 // Split the file name off into useful bits. | |
216 list ($name, $language) = explode('.', $entry); | |
217 | |
218 // Look for the version comment in the file header. | |
219 if (preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*' . preg_quote($name, '~') . '(?:[\s]{2}|\*/)~i', $header, $match) == 1) | |
220 $version_info['default_language_versions'][$language][$name] = $match[1]; | |
221 // It wasn't found, but the file was... show a '??'. | |
222 else | |
223 $version_info['default_language_versions'][$language][$name] = '??'; | |
224 } | |
225 } | |
226 $this_dir->close(); | |
227 | |
228 // Sort the file versions by filename. | |
229 if (!empty($versionOptions['sort_results'])) | |
230 { | |
231 ksort($version_info['file_versions']); | |
232 ksort($version_info['default_template_versions']); | |
233 ksort($version_info['template_versions']); | |
234 ksort($version_info['default_language_versions']); | |
235 | |
236 // For languages sort each language too. | |
237 foreach ($version_info['default_language_versions'] as $language => $dummy) | |
238 ksort($version_info['default_language_versions'][$language]); | |
239 } | |
240 return $version_info; | |
241 } | |
242 | |
243 // Update the Settings.php file. | |
244 function updateSettingsFile($config_vars) | |
245 { | |
246 global $boarddir, $cachedir; | |
247 | |
248 // When is Settings.php last changed? | |
249 $last_settings_change = filemtime($boarddir . '/Settings.php'); | |
250 | |
251 // Load the file. Break it up based on \r or \n, and then clean out extra characters. | |
252 $settingsArray = trim(file_get_contents($boarddir . '/Settings.php')); | |
253 if (strpos($settingsArray, "\n") !== false) | |
254 $settingsArray = explode("\n", $settingsArray); | |
255 elseif (strpos($settingsArray, "\r") !== false) | |
256 $settingsArray = explode("\r", $settingsArray); | |
257 else | |
258 return; | |
259 | |
260 // Make sure we got a good file. | |
261 if (count($config_vars) == 1 && isset($config_vars['db_last_error'])) | |
262 { | |
263 $temp = trim(implode("\n", $settingsArray)); | |
264 if (substr($temp, 0, 5) != '<?php' || substr($temp, -2) != '?' . '>') | |
265 return; | |
266 if (strpos($temp, 'sourcedir') === false || strpos($temp, 'boarddir') === false || strpos($temp, 'cookiename') === false) | |
267 return; | |
268 } | |
269 | |
270 // Presumably, the file has to have stuff in it for this function to be called :P. | |
271 if (count($settingsArray) < 10) | |
272 return; | |
273 | |
274 foreach ($settingsArray as $k => $dummy) | |
275 $settingsArray[$k] = strtr($dummy, array("\r" => '')) . "\n"; | |
276 | |
277 for ($i = 0, $n = count($settingsArray); $i < $n; $i++) | |
278 { | |
279 // Don't trim or bother with it if it's not a variable. | |
280 if (substr($settingsArray[$i], 0, 1) != '$') | |
281 continue; | |
282 | |
283 $settingsArray[$i] = trim($settingsArray[$i]) . "\n"; | |
284 | |
285 // Look through the variables to set.... | |
286 foreach ($config_vars as $var => $val) | |
287 { | |
288 if (strncasecmp($settingsArray[$i], '$' . $var, 1 + strlen($var)) == 0) | |
289 { | |
290 $comment = strstr(substr($settingsArray[$i], strpos($settingsArray[$i], ';')), '#'); | |
291 $settingsArray[$i] = '$' . $var . ' = ' . $val . ';' . ($comment == '' ? '' : "\t\t" . rtrim($comment)) . "\n"; | |
292 | |
293 // This one's been 'used', so to speak. | |
294 unset($config_vars[$var]); | |
295 } | |
296 } | |
297 | |
298 if (substr(trim($settingsArray[$i]), 0, 2) == '?' . '>') | |
299 $end = $i; | |
300 } | |
301 | |
302 // This should never happen, but apparently it is happening. | |
303 if (empty($end) || $end < 10) | |
304 $end = count($settingsArray) - 1; | |
305 | |
306 // Still more? Add them at the end. | |
307 if (!empty($config_vars)) | |
308 { | |
309 if (trim($settingsArray[$end]) == '?' . '>') | |
310 $settingsArray[$end++] = ''; | |
311 else | |
312 $end++; | |
313 | |
314 foreach ($config_vars as $var => $val) | |
315 $settingsArray[$end++] = '$' . $var . ' = ' . $val . ';' . "\n"; | |
316 $settingsArray[$end] = '?' . '>'; | |
317 } | |
318 else | |
319 $settingsArray[$end] = trim($settingsArray[$end]); | |
320 | |
321 // Sanity error checking: the file needs to be at least 12 lines. | |
322 if (count($settingsArray) < 12) | |
323 return; | |
324 | |
325 // Try to avoid a few pitfalls: | |
326 // like a possible race condition, | |
327 // or a failure to write at low diskspace | |
328 | |
329 // Check before you act: if cache is enabled, we can do a simple test | |
330 // Can we even write things on this filesystem? | |
331 if ((empty($cachedir) || !file_exists($cachedir)) && file_exists($boarddir . '/cache')) | |
332 $cachedir = $boarddir . '/cache'; | |
333 $test_fp = @fopen($cachedir . '/settings_update.tmp', "w+"); | |
334 if ($test_fp) | |
335 { | |
336 fclose($test_fp); | |
337 | |
338 $test_fp = @fopen($cachedir . '/settings_update.tmp', 'r+'); | |
339 $written_bytes = fwrite($test_fp, "test"); | |
340 fclose($test_fp); | |
341 @unlink($cachedir . '/settings_update.tmp'); | |
342 | |
343 if ($written_bytes !== strlen("test")) | |
344 { | |
345 // Oops. Low disk space, perhaps. Don't mess with Settings.php then. | |
346 // No means no. :P | |
347 return; | |
348 } | |
349 } | |
350 | |
351 // Protect me from what I want! :P | |
352 clearstatcache(); | |
353 if (filemtime($boarddir . '/Settings.php') === $last_settings_change) | |
354 { | |
355 // You asked for it... | |
356 // Blank out the file - done to fix a oddity with some servers. | |
357 $fp = @fopen($boarddir . '/Settings.php', 'w'); | |
358 | |
359 // Is it even writable, though? | |
360 if ($fp) | |
361 { | |
362 fclose($fp); | |
363 | |
364 $fp = fopen($boarddir . '/Settings.php', 'r+'); | |
365 foreach ($settingsArray as $line) | |
366 fwrite($fp, strtr($line, "\r", '')); | |
367 fclose($fp); | |
368 } | |
369 } | |
370 } | |
371 | |
372 function updateAdminPreferences() | |
373 { | |
374 global $options, $context, $smcFunc, $settings, $user_info; | |
375 | |
376 // This must exist! | |
377 if (!isset($context['admin_preferences'])) | |
378 return false; | |
379 | |
380 // This is what we'll be saving. | |
381 $options['admin_preferences'] = serialize($context['admin_preferences']); | |
382 | |
383 // Just check we haven't ended up with something theme exclusive somehow. | |
384 $smcFunc['db_query']('', ' | |
385 DELETE FROM {db_prefix}themes | |
386 WHERE id_theme != {int:default_theme} | |
387 AND variable = {string:admin_preferences}', | |
388 array( | |
389 'default_theme' => 1, | |
390 'admin_preferences' => 'admin_preferences', | |
391 ) | |
392 ); | |
393 | |
394 // Update the themes table. | |
395 $smcFunc['db_insert']('replace', | |
396 '{db_prefix}themes', | |
397 array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'), | |
398 array($user_info['id'], 1, 'admin_preferences', $options['admin_preferences']), | |
399 array('id_member', 'id_theme', 'variable') | |
400 ); | |
401 | |
402 // Make sure we invalidate any cache. | |
403 cache_put_data('theme_settings-' . $settings['theme_id'] . ':' . $user_info['id'], null, 0); | |
404 } | |
405 | |
406 // Send all the administrators a lovely email. | |
407 function emailAdmins($template, $replacements = array(), $additional_recipients = array()) | |
408 { | |
409 global $smcFunc, $sourcedir, $language, $modSettings; | |
410 | |
411 // We certainly want this. | |
412 require_once($sourcedir . '/Subs-Post.php'); | |
413 | |
414 // Load all groups which are effectively admins. | |
415 $request = $smcFunc['db_query']('', ' | |
416 SELECT id_group | |
417 FROM {db_prefix}permissions | |
418 WHERE permission = {string:admin_forum} | |
419 AND add_deny = {int:add_deny} | |
420 AND id_group != {int:id_group}', | |
421 array( | |
422 'add_deny' => 1, | |
423 'id_group' => 0, | |
424 'admin_forum' => 'admin_forum', | |
425 ) | |
426 ); | |
427 $groups = array(1); | |
428 while ($row = $smcFunc['db_fetch_assoc']($request)) | |
429 $groups[] = $row['id_group']; | |
430 $smcFunc['db_free_result']($request); | |
431 | |
432 $request = $smcFunc['db_query']('', ' | |
433 SELECT id_member, member_name, real_name, lngfile, email_address | |
434 FROM {db_prefix}members | |
435 WHERE (id_group IN ({array_int:group_list}) OR FIND_IN_SET({raw:group_array_implode}, additional_groups) != 0) | |
436 AND notify_types != {int:notify_types} | |
437 ORDER BY lngfile', | |
438 array( | |
439 'group_list' => $groups, | |
440 'notify_types' => 4, | |
441 'group_array_implode' => implode(', additional_groups) != 0 OR FIND_IN_SET(', $groups), | |
442 ) | |
443 ); | |
444 $emails_sent = array(); | |
445 while ($row = $smcFunc['db_fetch_assoc']($request)) | |
446 { | |
447 // Stick their particulars in the replacement data. | |
448 $replacements['IDMEMBER'] = $row['id_member']; | |
449 $replacements['REALNAME'] = $row['member_name']; | |
450 $replacements['USERNAME'] = $row['real_name']; | |
451 | |
452 // Load the data from the template. | |
453 $emaildata = loadEmailTemplate($template, $replacements, empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']); | |
454 | |
455 // Then send the actual email. | |
456 sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], null, null, false, 1); | |
457 | |
458 // Track who we emailed so we don't do it twice. | |
459 $emails_sent[] = $row['email_address']; | |
460 } | |
461 $smcFunc['db_free_result']($request); | |
462 | |
463 // Any additional users we must email this to? | |
464 if (!empty($additional_recipients)) | |
465 foreach ($additional_recipients as $recipient) | |
466 { | |
467 if (in_array($recipient['email'], $emails_sent)) | |
468 continue; | |
469 | |
470 $replacements['IDMEMBER'] = $recipient['id']; | |
471 $replacements['REALNAME'] = $recipient['name']; | |
472 $replacements['USERNAME'] = $recipient['name']; | |
473 | |
474 // Load the template again. | |
475 $emaildata = loadEmailTemplate($template, $replacements, empty($recipient['lang']) || empty($modSettings['userLanguage']) ? $language : $recipient['lang']); | |
476 | |
477 // Send off the email. | |
478 sendmail($recipient['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 1); | |
479 } | |
480 } | |
481 | |
482 function updateLastDatabaseError() | |
483 { | |
484 global $boarddir; | |
485 | |
486 // Find out this way if we can even write things on this filesystem. | |
487 // In addition, store things first in the backup file | |
488 | |
489 $last_settings_change = @filemtime($boarddir . '/Settings.php'); | |
490 | |
491 // Make sure the backup file is there... | |
492 $file = $boarddir . '/Settings_bak.php'; | |
493 if ((!file_exists($file) || filesize($file) == 0) && !copy($boarddir . '/Settings.php', $file)) | |
494 return false; | |
495 | |
496 // ...and writable! | |
497 if (!is_writable($file)) | |
498 { | |
499 chmod($file, 0755); | |
500 if (!is_writable($file)) | |
501 { | |
502 chmod($file, 0775); | |
503 if (!is_writable($file)) | |
504 { | |
505 chmod($file, 0777); | |
506 if (!is_writable($file)) | |
507 return false; | |
508 } | |
509 } | |
510 } | |
511 | |
512 // Put the new timestamp. | |
513 $data = file_get_contents($file); | |
514 $data = preg_replace('~\$db_last_error = \d+;~', '$db_last_error = ' . time() . ';', $data); | |
515 | |
516 // Open the backup file for writing | |
517 if ($fp = @fopen($file, 'w')) | |
518 { | |
519 // Reset the file buffer. | |
520 set_file_buffer($fp, 0); | |
521 | |
522 // Update the file. | |
523 $t = flock($fp, LOCK_EX); | |
524 $bytes = fwrite($fp, $data); | |
525 flock($fp, LOCK_UN); | |
526 fclose($fp); | |
527 | |
528 // Was it a success? | |
529 // ...only relevant if we're still dealing with the same good ole' settings file. | |
530 clearstatcache(); | |
531 if (($bytes == strlen($data)) && (filemtime($boarddir . '/Settings.php') === $last_settings_change)) | |
532 { | |
533 // This is our new Settings file... | |
534 // At least this one is an atomic operation | |
535 @copy($file, $boarddir . '/Settings.php'); | |
536 return true; | |
537 } | |
538 else | |
539 { | |
540 // Oops. Someone might have been faster | |
541 // or we have no more disk space left, troubles, troubles... | |
542 // Copy the file back and run for your life! | |
543 @copy($boarddir . '/Settings.php', $file); | |
544 } | |
545 } | |
546 | |
547 return false; | |
548 } | |
549 | |
550 ?> |