. */ /** * Diagnostics panel. * * @package Admin\Diag */ if (!defined('txpinterface')) { die('txpinterface is undefined.'); } /** * @ignore */ define("cs", ': '); /** * @ignore */ define("ln", str_repeat('-', 24).n); global $files; $files = check_file_integrity(); if (!$files) { $files = array(); } else { $files = array_keys($files); } if ($event == 'diag') { require_privs('diag'); $step = gps('step'); doDiagnostics(); } /** * Checks if the given Apache module is installed and active. * * @param string $m The module * @return bool|null TRUE on success, NULL or FALSE on error */ function apache_module($m) { $modules = @apache_get_modules(); if (is_array($modules)) { return in_array($m, $modules); } } /** * Verifies temporary directory status. * * This function verifies that the given temporary directory is writeable. * * @param string $dir The directory to check * @return bool|null NULL on error, TRUE on success */ function test_tempdir($dir) { $f = realpath(tempnam($dir, 'txp_')); if (is_file($f)) { @unlink($f); return true; } } /** * Lists all database tables used by the Textpattern core. * * Returned tables include prefixes. * * @return array */ function list_txp_tables() { $table_names = array(PFX.'textpattern'); $rows = getRows("SHOW TABLES LIKE '".PFX."txp\_%'"); foreach ($rows as $row) { $table_names[] = array_shift($row); } return $table_names; } /** * Checks the status of the given database tables. * * @param array $tables The tables to check * @param string $type Check type, either FOR UPGRADE, QUICK, FAST, MEDIUM, EXTENDED, CHANGED * @param bool $warnings If TRUE, displays warnings * @return array An array of table statuses * @example * print_r( * check_tables(list_txp_tables()) * ); */ function check_tables($tables, $type = 'FAST', $warnings = 0) { $msgs = array(); foreach ($tables as $table) { $rs = getRows("CHECK TABLE `$table` $type"); if ($rs) { foreach ($rs as $r) { if ($r['Msg_type'] != 'status' and ($warnings or $r['Msg_type'] != 'warning')) { $msgs[] = $table.cs.$r['Msg_type'].cs.$r['Msg_text']; } } } } return $msgs; } /** * Renders a diagnostics message block. * * @param string $msg The message * @param string $type The message type * @return string HTML * @access private */ function diag_msg_wrap($msg, $type = 'error') { return span($msg, array('class' => $type)); } /** * Outputs a diagnostics report. * * This is the main panel. */ function doDiagnostics() { global $prefs, $files, $txpcfg, $event, $step, $theme, $DB, $txp_using_svn; extract(get_prefs()); $urlparts = parse_url(hu); $mydomain = $urlparts['host']; $is_apache = stristr(serverSet('SERVER_SOFTWARE'), 'Apache') || is_callable('apache_get_version'); $real_doc_root = (isset($_SERVER['DOCUMENT_ROOT'])) ? realpath($_SERVER['DOCUMENT_ROOT']) : ''; // ini_get() returns string values passed via php_value as a string, // not boolean. $is_register_globals = ((strcasecmp(ini_get('register_globals'), 'on') === 0) or (ini_get('register_globals') === '1')); $fail = array(); $now = time(); if (!$txp_using_svn) { // Check for Textpattern updates, at most once every 24 hours. $updateInfo = unserialize(get_pref('last_update_check', '')); if (!$updateInfo || ($now > ($updateInfo['when'] + (60 * 60 * 24)))) { $updates = checkUpdates(); $updateInfo['msg'] = ($updates) ? gTxt($updates['msg'], array('{version}' => $updates['version'])) : ''; $updateInfo['when'] = $now; set_pref('last_update_check', serialize($updateInfo), 'publish', PREF_HIDDEN, 'text_input'); } if (!empty($updateInfo['msg'])) { $fail['textpattern_version_update'] = diag_msg_wrap($updateInfo['msg'], 'information'); } } if (!is_callable('version_compare') || version_compare(PHP_VERSION, REQUIRED_PHP_VERSION, '<')) { $fail['php_version_required'] = diag_msg_wrap(gTxt('php_version_required', array('{version}' => REQUIRED_PHP_VERSION))); } if (@gethostbyname($mydomain) === $mydomain) { $fail['dns_lookup_fails'] = diag_msg_wrap(gTxt('dns_lookup_fails').cs.$mydomain, 'warning'); } if (!@is_dir($path_to_site)) { $fail['path_to_site_inacc'] = diag_msg_wrap(gTxt('path_to_site_inacc').cs.$path_to_site); } if (rtrim($siteurl, '/') != $siteurl) { $fail['site_trailing_slash'] = diag_msg_wrap(gTxt('site_trailing_slash').cs.$path_to_site, 'warning'); } if (!@is_file($path_to_site."/index.php") || !@is_readable($path_to_site."/index.php")) { $fail['index_inaccessible'] = diag_msg_wrap("{$path_to_site}/index.php ".gTxt('is_inaccessible')); } $not_readable = array(); if (!@is_writable($path_to_site.'/'.$img_dir)) { $not_readable[] = diag_msg_wrap(str_replace('{dirtype}', gTxt('img_dir'), gTxt('dir_not_writable')).": {$path_to_site}/{$img_dir}", 'warning'); } if (!@is_writable($file_base_path)) { $not_readable[] = diag_msg_wrap(str_replace('{dirtype}', gTxt('file_base_path'), gTxt('dir_not_writable')).": {$file_base_path}", 'warning'); } if (!@is_writable($tempdir)) { $not_readable[] = diag_msg_wrap(str_replace('{dirtype}', gTxt('tempdir'), gTxt('dir_not_writable')).": {$tempdir}", 'warning'); } if ($not_readable) { $fail['dir_not_writable'] = join(n, $not_readable); } if ($permlink_mode != 'messy' && !$is_apache) { $fail['cleanurl_only_apache'] = diag_msg_wrap(gTxt('cleanurl_only_apache'), 'information'); } if ($permlink_mode != 'messy' and !@is_readable($path_to_site.'/.htaccess')) { $fail['htaccess_missing'] = diag_msg_wrap(gTxt('htaccess_missing')); } if ($permlink_mode != 'messy' and is_callable('apache_get_modules') and !apache_module('mod_rewrite')) { $fail['mod_rewrite_missing'] = diag_msg_wrap(gTxt('mod_rewrite_missing')); } if (!ini_get('file_uploads')) { $fail['file_uploads_disabled'] = diag_msg_wrap(gTxt('file_uploads_disabled'), 'information'); } if (@is_dir(txpath.DS.'setup')) { $fail['setup_still_exists'] = diag_msg_wrap(txpath.DS."setup".DS.' '.gTxt('still_exists'), 'warning'); } if (empty($tempdir)) { $fail['no_temp_dir'] = diag_msg_wrap(gTxt('no_temp_dir'), 'warning'); } if (is_disabled('mail')) { $fail['warn_mail_unavailable'] = diag_msg_wrap(gTxt('warn_mail_unavailable'), 'warning'); } if ($is_register_globals) { $fail['warn_register_globals_or_update'] = diag_msg_wrap(gTxt('warn_register_globals_or_update'), 'warning'); } if ($permlink_mode != 'messy') { $rs = safe_column("name", 'txp_section', "1 = 1"); foreach ($rs as $name) { if ($name and @file_exists($path_to_site.'/'.$name)) { $fail['old_placeholder_exists'] = diag_msg_wrap(gTxt('old_placeholder').": {$path_to_site}/{$name}"); } } } $cs = check_file_integrity(INTEGRITY_REALPATH); if (!$cs) { $cs = array(); } // Files that don't match their checksums. if (!$txp_using_svn and $modified_files = array_keys($cs, INTEGRITY_MODIFIED)) { $fail['modified_files'] = diag_msg_wrap(gTxt('modified_files').cs.n.t.join(', '.n.t, $modified_files), 'warning'); } // Running development code in live mode is not recommended. if (preg_match('/-dev$/', txp_version) and $production_status == 'live') { $fail['dev_version_live'] = diag_msg_wrap(gTxt('dev_version_live'), 'warning'); } // Missing files. if ($missing = array_merge( array_keys($cs, INTEGRITY_MISSING), array_keys($cs, INTEGRITY_NOT_FILE), array_keys($cs, INTEGRITY_NOT_READABLE) )) { $fail['missing_files'] = diag_msg_wrap(gTxt('missing_files').cs.n.t.join(', '.n.t, $missing)); } // Anything might break if arbitrary functions are disabled. if (ini_get('disable_functions')) { $disabled_funcs = array_map('trim', explode(',', ini_get('disable_functions'))); // Commonly disabled functions that we don't need. $disabled_funcs = array_diff($disabled_funcs, array( 'imagefilltoborder', 'escapeshellarg', 'escapeshellcmd', 'exec', 'passthru', 'proc_close', 'proc_get_status', 'proc_nice', 'proc_open', 'proc_terminate', 'shell_exec', 'system', 'popen', 'dl', 'chown', )); if ($disabled_funcs) { $fail['some_php_functions_disabled'] = diag_msg_wrap(gTxt('some_php_functions_disabled').cs.join(', ', $disabled_funcs), 'warning'); } } // Not sure about this one. // if (strncmp(php_sapi_name(), 'cgi', 3) == 0 and ini_get('cgi.rfc2616_headers')) // $fail['cgi_header_config'] = gTxt('cgi_header_config'); $guess_site_url = $_SERVER['HTTP_HOST'].preg_replace('#[/\\\\]$#', '', dirname(dirname($_SERVER['SCRIPT_NAME']))); if ($siteurl and strip_prefix($siteurl, 'www.') != strip_prefix($guess_site_url, 'www.')) { $fail['site_url_mismatch'] = diag_msg_wrap(gTxt('site_url_mismatch').cs.$guess_site_url, 'warning'); } // Test clean URL server vars. if (hu) { if (ini_get('allow_url_fopen') and ($permlink_mode != 'messy')) { $s = md5(uniqid(rand(), true)); ini_set('default_socket_timeout', 10); $pretext_data = @file(hu.$s.'/?txpcleantest=1'); if ($pretext_data) { $pretext_req = trim(@$pretext_data[0]); if ($pretext_req != md5('/'.$s.'/?txpcleantest=1')) { $fail['clean_url_data_failed'] = diag_msg_wrap(gTxt('clean_url_data_failed').cs.txpspecialchars($pretext_req), 'warning'); } } else { $fail['clean_url_test_failed'] = diag_msg_wrap(gTxt('clean_url_test_failed'), 'warning'); } } } if ($tables = list_txp_tables()) { $table_errors = check_tables($tables); if ($table_errors) { $fail['mysql_table_errors'] = diag_msg_wrap(gTxt('mysql_table_errors').cs.n.t.join(', '.n.t, $table_errors)); } } $active_plugins = array(); if ($rows = safe_rows("name, version, code_md5, MD5(code) AS md5", 'txp_plugin', "status > 0")) { foreach ($rows as $row) { $n = $row['name'].'-'.$row['version']; if (strtolower($row['md5']) != strtolower($row['code_md5'])) { $n .= 'm'; } $active_plugins[] = $n; } } $theme_manifest = $theme->manifest(); // Check GD info. if (function_exists('gd_info')) { $gd_info = gd_info(); $gd_support = array(); if ($gd_info['GIF Create Support']) { $gd_support[] = 'GIF'; } // Aside: In PHP 5.3, they chose to add a previously unemployed capital // "E" to the array key. if (!empty($gd_info['JPEG Support']) || !empty($gd_info['JPG Support'])) { $gd_support[] = 'JPG'; } if ($gd_info['PNG Support']) { $gd_support[] = 'PNG'; } if ($gd_support) { $gd_support = join(', ', $gd_support); } else { $gd_support = gTxt('none'); } $gd = gTxt('gd_info', array( '{version}' => $gd_info['GD Version'], '{supported}' => $gd_support, )); } else { $gd = gTxt('gd_unavailable'); } if (realpath($prefs['tempdir']) === realpath($prefs['plugin_cache_dir'])) { $fail['tmp_plugin_paths_match'] = diag_msg_wrap(gTxt('tmp_plugin_paths_match')); } // Database server time. extract(doSpecial(getRow("SELECT @@global.time_zone AS db_global_timezone, @@session.time_zone AS db_session_timezone, NOW() AS db_server_time, UNIX_TIMESTAMP(NOW()) AS db_server_timestamp"))); $db_server_timeoffset = $db_server_timestamp - $now; echo pagetop(gTxt('tab_diagnostics'), ''); echo n.'