status constant * 'error_msg' => string * 'user_row' => array * ) */ function login_db($username, $password, $ip = '', $browser = '', $forwarded_for = '') { global $db, $config; global $request; // Auth plugins get the password untrimmed. // For compatibility we trim() here. $password = trim($password); // do not allow empty password if (!$password) { return array( 'status' => LOGIN_ERROR_PASSWORD, 'error_msg' => 'NO_PASSWORD_SUPPLIED', 'user_row' => array('user_id' => ANONYMOUS), ); } if (!$username) { return array( 'status' => LOGIN_ERROR_USERNAME, 'error_msg' => 'LOGIN_ERROR_USERNAME', 'user_row' => array('user_id' => ANONYMOUS), ); } $username_clean = utf8_clean_string($username); $sql = 'SELECT user_id, username, user_password, user_passchg, user_pass_convert, user_email, user_type, user_login_attempts FROM ' . USERS_TABLE . " WHERE username_clean = '" . $db->sql_escape($username_clean) . "'"; $result = $db->sql_query($sql); $row = $db->sql_fetchrow($result); $db->sql_freeresult($result); if (($ip && !$config['ip_login_limit_use_forwarded']) || ($forwarded_for && $config['ip_login_limit_use_forwarded'])) { $sql = 'SELECT COUNT(*) AS attempts FROM ' . LOGIN_ATTEMPT_TABLE . ' WHERE attempt_time > ' . (time() - (int) $config['ip_login_limit_time']); if ($config['ip_login_limit_use_forwarded']) { $sql .= " AND attempt_forwarded_for = '" . $db->sql_escape($forwarded_for) . "'"; } else { $sql .= " AND attempt_ip = '" . $db->sql_escape($ip) . "' "; } $result = $db->sql_query($sql); $attempts = (int) $db->sql_fetchfield('attempts'); $db->sql_freeresult($result); $attempt_data = array( 'attempt_ip' => $ip, 'attempt_browser' => trim(substr($browser, 0, 149)), 'attempt_forwarded_for' => $forwarded_for, 'attempt_time' => time(), 'user_id' => ($row) ? (int) $row['user_id'] : 0, 'username' => $username, 'username_clean' => $username_clean, ); $sql = 'INSERT INTO ' . LOGIN_ATTEMPT_TABLE . $db->sql_build_array('INSERT', $attempt_data); $result = $db->sql_query($sql); } else { $attempts = 0; } if (!$row) { if ($config['ip_login_limit_max'] && $attempts >= $config['ip_login_limit_max']) { return array( 'status' => LOGIN_ERROR_ATTEMPTS, 'error_msg' => 'LOGIN_ERROR_ATTEMPTS', 'user_row' => array('user_id' => ANONYMOUS), ); } return array( 'status' => LOGIN_ERROR_USERNAME, 'error_msg' => 'LOGIN_ERROR_USERNAME', 'user_row' => array('user_id' => ANONYMOUS), ); } $show_captcha = ($config['max_login_attempts'] && $row['user_login_attempts'] >= $config['max_login_attempts']) || ($config['ip_login_limit_max'] && $attempts >= $config['ip_login_limit_max']); // If there are too much login attempts, we need to check for an confirm image // Every auth module is able to define what to do by itself... if ($show_captcha) { // Visual Confirmation handling if (!class_exists('phpbb_captcha_factory', false)) { global $phpbb_root_path, $phpEx; include ($phpbb_root_path . 'includes/captcha/captcha_factory.' . $phpEx); } $captcha = phpbb_captcha_factory::get_instance($config['captcha_plugin']); $captcha->init(CONFIRM_LOGIN); $vc_response = $captcha->validate($row); if ($vc_response) { return array( 'status' => LOGIN_ERROR_ATTEMPTS, 'error_msg' => 'LOGIN_ERROR_ATTEMPTS', 'user_row' => $row, ); } else { $captcha->reset(); } } // If the password convert flag is set we need to convert it if ($row['user_pass_convert']) { // enable super globals to get literal value // this is needed to prevent unicode normalization $super_globals_disabled = $request->super_globals_disabled(); if ($super_globals_disabled) { $request->enable_super_globals(); } // in phpBB2 passwords were used exactly as they were sent, with addslashes applied $password_old_format = isset($_REQUEST['password']) ? (string) $_REQUEST['password'] : ''; $password_old_format = (!STRIP) ? addslashes($password_old_format) : $password_old_format; $password_new_format = $request->variable('password', '', true); if ($super_globals_disabled) { $request->disable_super_globals(); } if ($password == $password_new_format) { if (!function_exists('utf8_to_cp1252')) { global $phpbb_root_path, $phpEx; include($phpbb_root_path . 'includes/utf/data/recode_basic.' . $phpEx); } // cp1252 is phpBB2's default encoding, characters outside ASCII range might work when converted into that encoding // plain md5 support left in for conversions from other systems. if ((strlen($row['user_password']) == 34 && (phpbb_check_hash(md5($password_old_format), $row['user_password']) || phpbb_check_hash(md5(utf8_to_cp1252($password_old_format)), $row['user_password']))) || (strlen($row['user_password']) == 32 && (md5($password_old_format) == $row['user_password'] || md5(utf8_to_cp1252($password_old_format)) == $row['user_password']))) { $hash = phpbb_hash($password_new_format); // Update the password in the users table to the new format and remove user_pass_convert flag $sql = 'UPDATE ' . USERS_TABLE . ' SET user_password = \'' . $db->sql_escape($hash) . '\', user_pass_convert = 0 WHERE user_id = ' . $row['user_id']; $db->sql_query($sql); $row['user_pass_convert'] = 0; $row['user_password'] = $hash; } else { // Although we weren't able to convert this password we have to // increase login attempt count to make sure this cannot be exploited $sql = 'UPDATE ' . USERS_TABLE . ' SET user_login_attempts = user_login_attempts + 1 WHERE user_id = ' . (int) $row['user_id'] . ' AND user_login_attempts < ' . LOGIN_ATTEMPTS_MAX; $db->sql_query($sql); return array( 'status' => LOGIN_ERROR_PASSWORD_CONVERT, 'error_msg' => 'LOGIN_ERROR_PASSWORD_CONVERT', 'user_row' => $row, ); } } } // Check password ... if (!$row['user_pass_convert'] && phpbb_check_hash($password, $row['user_password'])) { // Check for old password hash... if (strlen($row['user_password']) == 32) { $hash = phpbb_hash($password); // Update the password in the users table to the new format $sql = 'UPDATE ' . USERS_TABLE . " SET user_password = '" . $db->sql_escape($hash) . "', user_pass_convert = 0 WHERE user_id = {$row['user_id']}"; $db->sql_query($sql); $row['user_password'] = $hash; } $sql = 'DELETE FROM ' . LOGIN_ATTEMPT_TABLE . ' WHERE user_id = ' . $row['user_id']; $db->sql_query($sql); if ($row['user_login_attempts'] != 0) { // Successful, reset login attempts (the user passed all stages) $sql = 'UPDATE ' . USERS_TABLE . ' SET user_login_attempts = 0 WHERE user_id = ' . $row['user_id']; $db->sql_query($sql); } // User inactive... if ($row['user_type'] == USER_INACTIVE || $row['user_type'] == USER_IGNORE) { return array( 'status' => LOGIN_ERROR_ACTIVE, 'error_msg' => 'ACTIVE_ERROR', 'user_row' => $row, ); } // Successful login... set user_login_attempts to zero... return array( 'status' => LOGIN_SUCCESS, 'error_msg' => false, 'user_row' => $row, ); } // Password incorrect - increase login attempts $sql = 'UPDATE ' . USERS_TABLE . ' SET user_login_attempts = user_login_attempts + 1 WHERE user_id = ' . (int) $row['user_id'] . ' AND user_login_attempts < ' . LOGIN_ATTEMPTS_MAX; $db->sql_query($sql); // Give status about wrong password... return array( 'status' => ($show_captcha) ? LOGIN_ERROR_ATTEMPTS : LOGIN_ERROR_PASSWORD, 'error_msg' => ($show_captcha) ? 'LOGIN_ERROR_ATTEMPTS' : 'LOGIN_ERROR_PASSWORD', 'user_row' => $row, ); } class='right'>1
diff --git a/phpBB/common.php b/phpBB/common.php index 5c0feb87db..c33e2cbb1f 100644 --- a/phpBB/common.php +++ b/phpBB/common.php @@ -111,6 +111,8 @@ $config = $phpbb_container->get('config'); set_config(null, null, null, $config); set_config_count(null, null, null, $config); +$phpbb_log = $phpbb_container->get('log'); + // load extensions $phpbb_extension_manager = $phpbb_container->get('ext.manager'); $phpbb_subscriber_loader = $phpbb_container->get('event.subscriber_loader'); diff --git a/phpBB/config/services.yml b/phpBB/config/services.yml index 24e80ddaf0..7b2a24b2b3 100644 --- a/phpBB/config/services.yml +++ b/phpBB/config/services.yml @@ -187,6 +187,18 @@ services: tags: - { name: kernel.event_subscriber } + log: + class: phpbb_log + arguments: + - @dbal.conn + - @user + - @auth + - @dispatcher + - %core.root_path% + - %core.adm_relative_path% + - %core.php_ext% + - %tables.log% + notification_manager: class: phpbb_notification_manager arguments: diff --git a/phpBB/config/tables.yml b/phpBB/config/tables.yml index 10db8fbab6..b3093abf0c 100644 --- a/phpBB/config/tables.yml +++ b/phpBB/config/tables.yml @@ -1,6 +1,7 @@ parameters: tables.config: %core.table_prefix%config tables.ext: %core.table_prefix%ext + tables.log: %core.table_prefix%log tables.notification_types: %core.table_prefix%notification_types tables.notifications: %core.table_prefix%notifications tables.user_notifications: %core.table_prefix%user_notifications diff --git a/phpBB/download/file.php b/phpBB/download/file.php index 6537355bb0..91c05586a5 100644 --- a/phpBB/download/file.php +++ b/phpBB/download/file.php @@ -67,6 +67,7 @@ if (isset($_GET['avatar'])) $phpbb_dispatcher = $phpbb_container->get('dispatcher'); $request = $phpbb_container->get('request'); $db = $phpbb_container->get('dbal.conn'); + $phpbb_log = $phpbb_container->get('log'); // Connect to DB if (!@$db->sql_connect($dbhost, $dbuser, $dbpasswd, $dbname, $dbport, false, false)) diff --git a/phpBB/includes/functions.php b/phpBB/includes/functions.php index 6a1f144967..c0fd3918dc 100644 --- a/phpBB/includes/functions.php +++ b/phpBB/includes/functions.php @@ -3582,69 +3582,49 @@ function parse_cfg_file($filename, $lines = false) } /** -* Add log event +* Add log entry +* +* @param string $mode The mode defines which log_type is used and from which log the entry is retrieved +* @param int $forum_id Mode 'mod' ONLY: forum id of the related item, NOT INCLUDED otherwise +* @param int $topic_id Mode 'mod' ONLY: topic id of the related item, NOT INCLUDED otherwise +* @param int $reportee_id Mode 'user' ONLY: user id of the reportee, NOT INCLUDED otherwise +* @param string $log_operation Name of the operation +* @param array $additional_data More arguments can be added, depending on the log_type +* +* @return int|bool Returns the log_id, if the entry was added to the database, false otherwise. +* +* @deprecated Use $phpbb_log->add() instead */ function add_log() { - global $db, $user; - - // In phpBB 3.1.x i want to have logging in a class to be able to control it - // For now, we need a quite hakish approach to circumvent logging for some actions - // @todo implement cleanly - if (!empty($GLOBALS['skip_add_log'])) - { - return false; - } + global $phpbb_log, $user; $args = func_get_args(); + $mode = array_shift($args); - $mode = array_shift($args); - $reportee_id = ($mode == 'user') ? intval(array_shift($args)) : ''; - $forum_id = ($mode == 'mod') ? intval(array_shift($args)) : ''; - $topic_id = ($mode == 'mod') ? intval(array_shift($args)) : ''; - $action = array_shift($args); - $data = (!sizeof($args)) ? '' : serialize($args); - - $sql_ary = array( - 'user_id' => (empty($user->data)) ? ANONYMOUS : $user->data['user_id'], - 'log_ip' => $user->ip, - 'log_time' => time(), - 'log_operation' => $action, - 'log_data' => $data, - ); - + // This looks kind of dirty, but add_log has some additional data before the log_operation + $additional_data = array(); switch ($mode) { case 'admin': - $sql_ary['log_type'] = LOG_ADMIN; + case 'critical': break; - case 'mod': - $sql_ary += array( - 'log_type' => LOG_MOD, - 'forum_id' => $forum_id, - 'topic_id' => $topic_id - ); + $additional_data['forum_id'] = array_shift($args); + $additional_data['topic_id'] = array_shift($args); break; - case 'user': - $sql_ary += array( - 'log_type' => LOG_USERS, - 'reportee_id' => $reportee_id - ); - break; - - case 'critical': - $sql_ary['log_type'] = LOG_CRITICAL; + $additional_data['reportee_id'] = array_shift($args); break; - - default: - return false; } - $db->sql_query('INSERT INTO ' . LOG_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary)); + $log_operation = array_shift($args); + $additional_data = array_merge($additional_data, $args); + + $user_id = (empty($user->data)) ? ANONYMOUS : $user->data['user_id']; + $user_ip = (empty($user->ip)) ? '' : $user->ip; - return $db->sql_nextid(); + return $phpbb_log->add($mode, $user_id, $user_ip, $log_operation, time(), $additional_data); } /** diff --git a/phpBB/includes/functions_admin.php b/phpBB/includes/functions_admin.php index baf107bcda..d273b9fb3a 100644 --- a/phpBB/includes/functions_admin.php +++ b/phpBB/includes/functions_admin.php @@ -2506,273 +2506,32 @@ function cache_moderators() /** * View log -* If $log_count is set to false, we will skip counting all entries in the database. +* +* @param string $mode The mode defines which log_type is used and from which log the entry is retrieved +* @param array &$log The result array with the logs +* @param mixed &$log_count If $log_count is set to false, we will skip counting all entries in the database. +* Otherwise an integer with the number of total matching entries is returned. +* @param int $limit Limit the number of entries that are returned +* @param int $offset Offset when fetching the log entries, f.e. when paginating +* @param mixed $forum_id Restrict the log entries to the given forum_id (can also be an array of forum_ids) +* @param int $topic_id Restrict the log entries to the given topic_id +* @param int $user_id Restrict the log entries to the given user_id +* @param int $log_time Only get log entries newer than the given timestamp +* @param string $sort_by SQL order option, e.g. 'l.log_time DESC' +* @param string $keywords Will only return log entries that have the keywords in log_operation or log_data +* +* @return int Returns the offset of the last valid page, if the specified offset was invalid (too high) */ function view_log($mode, &$log, &$log_count, $limit = 0, $offset = 0, $forum_id = 0, $topic_id = 0, $user_id = 0, $limit_days = 0, $sort_by = 'l.log_time DESC', $keywords = '') { - global $db, $user, $auth, $phpEx, $phpbb_root_path, $phpbb_admin_path; - - $topic_id_list = $reportee_id_list = $is_auth = $is_mod = array(); - - $profile_url = (defined('IN_ADMIN')) ? append_sid("{$phpbb_admin_path}index.$phpEx", 'i=users&mode=overview') : append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=viewprofile'); - - switch ($mode) - { - case 'admin': - $log_type = LOG_ADMIN; - $sql_forum = ''; - break; - - case 'mod': - $log_type = LOG_MOD; - $sql_forum = ''; - - if ($topic_id) - { - $sql_forum = 'AND l.topic_id = ' . (int) $topic_id; - } - else if (is_array($forum_id)) - { - $sql_forum = 'AND ' . $db->sql_in_set('l.forum_id', array_map('intval', $forum_id)); - } - else if ($forum_id) - { - $sql_forum = 'AND l.forum_id = ' . (int) $forum_id; - } - break; - - case 'user': - $log_type = LOG_USERS; - $sql_forum = 'AND l.reportee_id = ' . (int) $user_id; - break; - - case 'users': - $log_type = LOG_USERS; - $sql_forum = ''; - break; - - case 'critical': - $log_type = LOG_CRITICAL; - $sql_forum = ''; - break; - - default: - return; - } - - // Use no preg_quote for $keywords because this would lead to sole backslashes being added - // We also use an OR connection here for spaces and the | string. Currently, regex is not supported for searching (but may come later). - $keywords = preg_split('#[\s|]+#u', utf8_strtolower($keywords), 0, PREG_SPLIT_NO_EMPTY); - $sql_keywords = ''; - - if (!empty($keywords)) - { - $keywords_pattern = array(); - - // Build pattern and keywords... - for ($i = 0, $num_keywords = sizeof($keywords); $i < $num_keywords; $i++) - { - $keywords_pattern[] = preg_quote($keywords[$i], '#'); - $keywords[$i] = $db->sql_like_expression($db->any_char . $keywords[$i] . $db->any_char); - } + global $phpbb_log; - $keywords_pattern = '#' . implode('|', $keywords_pattern) . '#ui'; + $count_logs = ($log_count !== false); - $operations = array(); - foreach ($user->lang as $key => $value) - { - if (substr($key, 0, 4) == 'LOG_' && preg_match($keywords_pattern, $value)) - { - $operations[] = $key; - } - } - - $sql_keywords = 'AND ('; - if (!empty($operations)) - { - $sql_keywords .= $db->sql_in_set('l.log_operation', $operations) . ' OR '; - } - $sql_lower = $db->sql_lower_text('l.log_data'); - $sql_keywords .= "$sql_lower " . implode(" OR $sql_lower ", $keywords) . ')'; - } - - if ($log_count !== false) - { - $sql = 'SELECT COUNT(l.log_id) AS total_entries - FROM ' . LOG_TABLE . ' l, ' . USERS_TABLE . " u - WHERE l.log_type = $log_type - AND l.user_id = u.user_id - AND l.log_time >= $limit_days - $sql_keywords - $sql_forum"; - $result = $db->sql_query($sql); - $log_count = (int) $db->sql_fetchfield('total_entries'); - $db->sql_freeresult($result); - } - - // $log_count may be false here if false was passed in for it, - // because in this case we did not run the COUNT() query above. - // If we ran the COUNT() query and it returned zero rows, return; - // otherwise query for logs below. - if ($log_count === 0) - { - // Save the queries, because there are no logs to display - return 0; - } - - if ($offset >= $log_count) - { - $offset = ($offset - $limit < 0) ? 0 : $offset - $limit; - } - - $sql = "SELECT l.*, u.username, u.username_clean, u.user_colour - FROM " . LOG_TABLE . " l, " . USERS_TABLE . " u - WHERE l.log_type = $log_type - AND u.user_id = l.user_id - " . (($limit_days) ? "AND l.log_time >= $limit_days" : '') . " - $sql_keywords - $sql_forum - ORDER BY $sort_by"; - $result = $db->sql_query_limit($sql, $limit, $offset); - - $i = 0; - $log = array(); - while ($row = $db->sql_fetchrow($result)) - { - if ($row['topic_id']) - { - $topic_id_list[] = $row['topic_id']; - } - - if ($row['reportee_id']) - { - $reportee_id_list[] = $row['reportee_id']; - } + $log = $phpbb_log->get_logs($mode, $count_logs, $limit, $offset, $forum_id, $topic_id, $user_id, $limit_days, $sort_by, $keywords); + $log_count = $phpbb_log->get_log_count(); - $log[$i] = array( - 'id' => $row['log_id'], - - 'reportee_id' => $row['reportee_id'], - 'reportee_username' => '', - 'reportee_username_full'=> '', - - 'user_id' => $row['user_id'], - 'username' => $row['username'], - 'username_full' => get_username_string('full', $row['user_id'], $row['username'], $row['user_colour'], false, $profile_url), - - 'ip' => $row['log_ip'], - 'time' => $row['log_time'], - 'forum_id' => $row['forum_id'], - 'topic_id' => $row['topic_id'], - - 'viewforum' => ($row['forum_id'] && $auth->acl_get('f_read', $row['forum_id'])) ? append_sid("{$phpbb_root_path}viewforum.$phpEx", 'f=' . $row['forum_id']) : false, - 'action' => (isset($user->lang[$row['log_operation']])) ? $user->lang[$row['log_operation']] : '{' . ucfirst(str_replace('_', ' ', $row['log_operation'])) . '}', - ); - - if (!empty($row['log_data'])) - { - $log_data_ary = @unserialize($row['log_data']); - $log_data_ary = ($log_data_ary === false) ? array() : $log_data_ary; - - if (isset($user->lang[$row['log_operation']])) - { - // Check if there are more occurrences of % than arguments, if there are we fill out the arguments array - // It doesn't matter if we add more arguments than placeholders - if ((substr_count($log[$i]['action'], '%') - sizeof($log_data_ary)) > 0) - { - $log_data_ary = array_merge($log_data_ary, array_fill(0, substr_count($log[$i]['action'], '%') - sizeof($log_data_ary), '')); - } - - $log[$i]['action'] = vsprintf($log[$i]['action'], $log_data_ary); - - // If within the admin panel we do not censor text out - if (defined('IN_ADMIN')) - { - $log[$i]['action'] = bbcode_nl2br($log[$i]['action']); - } - else - { - $log[$i]['action'] = bbcode_nl2br(censor_text($log[$i]['action'])); - } - } - else if (!empty($log_data_ary)) - { - $log[$i]['action'] .= '<br />' . implode('', $log_data_ary); - } - - /* Apply make_clickable... has to be seen if it is for good. :/ - // Seems to be not for the moment, reconsider later... - $log[$i]['action'] = make_clickable($log[$i]['action']); - */ - } - - $i++; - } - $db->sql_freeresult($result); - - if (sizeof($topic_id_list)) - { - $topic_id_list = array_unique($topic_id_list); - - // This query is not really needed if move_topics() updates the forum_id field, - // although it's also used to determine if the topic still exists in the database - $sql = 'SELECT topic_id, forum_id - FROM ' . TOPICS_TABLE . ' - WHERE ' . $db->sql_in_set('topic_id', array_map('intval', $topic_id_list)); - $result = $db->sql_query($sql); - - $default_forum_id = 0; - - while ($row = $db->sql_fetchrow($result)) - { - if ($auth->acl_get('f_read', $row['forum_id'])) - { - $is_auth[$row['topic_id']] = $row['forum_id']; - } - - if ($auth->acl_gets('a_', 'm_', $row['forum_id'])) - { - $is_mod[$row['topic_id']] = $row['forum_id']; - } - } - $db->sql_freeresult($result); - - foreach ($log as $key => $row) - { - $log[$key]['viewtopic'] = (isset($is_auth[$row['topic_id']])) ? append_sid("{$phpbb_root_path}viewtopic.$phpEx", 'f=' . $is_auth[$row['topic_id']] . '&t=' . $row['topic_id']) : false; - $log[$key]['viewlogs'] = (isset($is_mod[$row['topic_id']])) ? append_sid("{$phpbb_root_path}mcp.$phpEx", 'i=logs&mode=topic_logs&t=' . $row['topic_id'], true, $user->session_id) : false; - } - } - - if (sizeof($reportee_id_list)) - { - $reportee_id_list = array_unique($reportee_id_list); - $reportee_names_list = array(); - - $sql = 'SELECT user_id, username, user_colour - FROM ' . USERS_TABLE . ' - WHERE ' . $db->sql_in_set('user_id', $reportee_id_list); - $result = $db->sql_query($sql); - - while ($row = $db->sql_fetchrow($result)) - { - $reportee_names_list[$row['user_id']] = $row; - } - $db->sql_freeresult($result); - - foreach ($log as $key => $row) - { - if (!isset($reportee_names_list[$row['reportee_id']])) - { - continue; - } - - $log[$key]['reportee_username'] = $reportee_names_list[$row['reportee_id']]['username']; - $log[$key]['reportee_username_full'] = get_username_string('full', $row['reportee_id'], $reportee_names_list[$row['reportee_id']]['username'], $reportee_names_list[$row['reportee_id']]['user_colour'], false, $profile_url); - } - } - - return $offset; + return $phpbb_log->get_valid_offset(); } /** diff --git a/phpBB/includes/functions_container.php b/phpBB/includes/functions_container.php index a3ed21c35b..106b7d75cc 100644 --- a/phpBB/includes/functions_container.php +++ b/phpBB/includes/functions_container.php @@ -57,6 +57,7 @@ function phpbb_create_install_container($phpbb_root_path, $php_ext) $container = phpbb_create_container(array($core), $phpbb_root_path, $php_ext); $container->setParameter('core.root_path', $phpbb_root_path); + $container->setParameter('core.adm_relative_path', $phpbb_adm_relative_path); $container->setParameter('core.php_ext', $php_ext); $container->setParameter('core.table_prefix', ''); diff --git a/phpBB/includes/functions_user.php b/phpBB/includes/functions_user.php index f05bf76ed8..bc636acabb 100644 --- a/phpBB/includes/functions_user.php +++ b/phpBB/includes/functions_user.php @@ -310,8 +310,10 @@ function user_add($user_row, $cp_data = false) if ($add_group_id) { - // Because these actions only fill the log unneccessarily we skip the add_log() entry with a little hack. :/ - $GLOBALS['skip_add_log'] = true; + global $phpbb_log; + + // Because these actions only fill the log unneccessarily we skip the add_log() entry. + $phpbb_log->disable('admin'); // Add user to "newly registered users" group and set to default group if admin specified so. if ($config['new_member_group_default']) @@ -324,7 +326,7 @@ function user_add($user_row, $cp_data = false) group_user_add($add_group_id, $user_id); } - unset($GLOBALS['skip_add_log']); + $phpbb_log->enable('admin'); } } diff --git a/phpBB/includes/log/interface.php b/phpBB/includes/log/interface.php new file mode 100644 index 0000000000..3b459c9bdf --- /dev/null +++ b/phpBB/includes/log/interface.php @@ -0,0 +1,106 @@ +<?php +/** +* +* @package phpbb_log +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ + exit; +} + +/** +* The interface for the log-system. +* +* @package phpbb_log +*/ +interface phpbb_log_interface +{ + /** + * This function returns the state of the log system. + * + * @param string $type The log type we want to check. Empty to get + * global log status. + * + * @return bool True if log for the type is enabled + */ + public function is_enabled($type = ''); + + /** + * Disable log + * + * This function allows disabling the log system or parts of it, for this + * page call. When add_log is called and the type is disabled, + * the log will not be added to the database. + * + * @param mixed $type The log type we want to disable. Empty to + * disable all logs. Can also be an array of types. + * + * @return null + */ + public function disable($type = ''); + + /** + * Enable log + * + * This function allows re-enabling the log system. + * + * @param mixed $type The log type we want to enable. Empty to + * enable all logs. Can also be an array of types. + * + * @return null + */ + public function enable($type = ''); + + /** + * Adds a log entry to the database + * + * @param string $mode The mode defines which log_type is used and from which log the entry is retrieved + * @param int $user_id User ID of the user + * @param string $log_ip IP address of the user + * @param string $log_operation Name of the operation + * @param int $log_time Timestamp when the log entry was added, if empty time() will be used + * @param array $additional_data More arguments can be added, depending on the log_type + * + * @return int|bool Returns the log_id, if the entry was added to the database, false otherwise. + */ + public function add($mode, $user_id, $log_ip, $log_operation, $log_time = false, $additional_data = array()); + + /** + * Grab the logs from the database + * + * @param string $mode The mode defines which log_type is used and ifrom which log the entry is retrieved + * @param bool $count_logs Shall we count all matching log entries? + * @param int $limit Limit the number of entries that are returned + * @param int $offset Offset when fetching the log entries, f.e. when paginating + * @param mixed $forum_id Restrict the log entries to the given forum_id (can also be an array of forum_ids) + * @param int $topic_id Restrict the log entries to the given topic_id + * @param int $user_id Restrict the log entries to the given user_id + * @param int $log_time Only get log entries newer than the given timestamp + * @param string $sort_by SQL order option, e.g. 'l.log_time DESC' + * @param string $keywords Will only return log entries that have the keywords in log_operation or log_data + * + * @return array The result array with the logs + */ + public function get_logs($mode, $count_logs = true, $limit = 0, $offset = 0, $forum_id = 0, $topic_id = 0, $user_id = 0, $log_time = 0, $sort_by = 'l.log_time DESC', $keywords = ''); + + /** + * Get total log count + * + * @return int Returns the number of matching logs from the last call to get_logs() + */ + public function get_log_count(); + + /** + * Get offset of the last valid page + * + * @return int Returns the offset of the last valid page from the last call to get_logs() + */ + public function get_valid_offset(); +} diff --git a/phpBB/includes/log/log.php b/phpBB/includes/log/log.php new file mode 100644 index 0000000000..7a26858348 --- /dev/null +++ b/phpBB/includes/log/log.php @@ -0,0 +1,739 @@ +<?php +/** +* +* @package phpbb_log +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ + exit; +} + +/** +* This class is used to add entries into the log table. +* +* @package phpbb_log +*/ +class phpbb_log implements phpbb_log_interface +{ + /** + * If set, administrative user profile links will be returned and messages + * will not be censored. + * @var bool + */ + protected $is_in_admin; + + /** + * An array with the disabled log types. Logs of such types will not be + * added when add_log() is called. + * @var array + */ + protected $disabled_types; + + /** + * Keeps the total log count of the last call to get_logs() + * @var int + */ + protected $entry_count; + + /** + * Keeps the offset of the last valid page of the last call to get_logs() + * @var int + */ + protected $last_page_offset; + + /** + * The table we use to store our logs. + * @var string + */ + protected $log_table; + + /** + * Database object + * @var phpbb_db_driver + */ + protected $db; + + /** + * User object + * @var phpbb_user + */ + protected $user; + + /** + * Auth object + * @var phpbb_auth + */ + protected $auth; + + /** + * Event dispatcher object + * @var phpbb_dispatcher + */ + protected $dispatcher; + + /** + * phpBB root path + * @var string + */ + protected $phpbb_root_path; + + /** + * Admin root path + * @var string + */ + protected $phpbb_admin_path; + + /** + * PHP Extension + * @var string + */ + protected $php_ext; + + /** + * Constructor + * + * @param phpbb_db_driver $db Database object + * @param phpbb_user $user User object + * @param phpbb_auth $auth Auth object + * @param phpbb_dispatcher $phpbb_dispatcher Event dispatcher + * @param string $phpbb_root_path Root path + * @param string $relative_admin_path Relative admin root path + * @param string $php_ext PHP Extension + * @param string $log_table Name of the table we use to store our logs + * @return null + */ + public function __construct($db, $user, $auth, $phpbb_dispatcher, $phpbb_root_path, $relative_admin_path, $php_ext, $log_table) + { + $this->db = $db; + $this->user = $user; + $this->auth = $auth; + $this->dispatcher = $phpbb_dispatcher; + $this->phpbb_root_path = $phpbb_root_path; + $this->phpbb_admin_path = $this->phpbb_root_path . $relative_admin_path; + $this->php_ext = $php_ext; + $this->log_table = $log_table; + + /* + * IN_ADMIN is set after the session is created, + * so we need to take ADMIN_START into account as well, otherwise + * it will not work for the phpbb_log object we create in common.php + */ + $this->set_is_admin((defined('ADMIN_START') && ADMIN_START) || (defined('IN_ADMIN') && IN_ADMIN)); + $this->enable(); + } + + /** + * Set is_in_admin in order to return administrative user profile links + * in get_logs() + * + * @param bool $is_in_admin Are we called from within the acp? + * @return null + */ + public function set_is_admin($is_in_admin) + { + $this->is_in_admin = (bool) $is_in_admin; + } + + /** + * Returns the is_in_admin option + * + * @return bool + */ + public function get_is_admin() + { + return $this->is_in_admin; + } + + /** + * Set table name + * + * @param string $log_table Can overwrite the table to use for the logs + * @return null + */ + public function set_log_table($log_table) + { + $this->log_table = $log_table; + } + + /** + * This function returns the state of the log system. + * + * {@inheritDoc} + */ + public function is_enabled($type = '') + { + if ($type == '' || $type == 'all') + { + return !isset($this->disabled_types['all']); + } + return !isset($this->disabled_types[$type]) && !isset($this->disabled_types['all']); + } + + /** + * Disable log + * + * This function allows disabling the log system or parts of it, for this + * page call. When add_log is called and the type is disabled, + * the log will not be added to the database. + * + * {@inheritDoc} + */ + public function disable($type = '') + { + if (is_array($type)) + { + foreach ($type as $disable_type) + { + $this->disable($disable_type); + } + return; + } + + // Empty string is an equivalent for all types. + if ($type == '') + { + $type = 'all'; + } + $this->disabled_types[$type] = true; + } + + /** + * Enable log + * + * This function allows re-enabling the log system. + * + * {@inheritDoc} + */ + public function enable($type = '') + { + if (is_array($type)) + { + foreach ($type as $enable_type) + { + $this->enable($enable_type); + } + return; + } + + if ($type == '' || $type == 'all') + { + $this->disabled_types = array(); + return; + } + unset($this->disabled_types[$type]); + } + + /** + * Adds a log to the database + * + * {@inheritDoc} + */ + public function add($mode, $user_id, $log_ip, $log_operation, $log_time = false, $additional_data = array()) + { + if (!$this->is_enabled($mode)) + { + return false; + } + + if ($log_time == false) + { + $log_time = time(); + } + + $sql_ary = array( + 'user_id' => $user_id, + 'log_ip' => $log_ip, + 'log_time' => $log_time, + 'log_operation' => $log_operation, + ); + + switch ($mode) + { + case 'admin': + $sql_ary += array( + 'log_type' => LOG_ADMIN, + 'log_data' => (!empty($additional_data)) ? serialize($additional_data) : '', + ); + break; + + case 'mod': + $forum_id = (int) $additional_data['forum_id']; + unset($additional_data['forum_id']); + $topic_id = (int) $additional_data['topic_id']; + unset($additional_data['topic_id']); + $sql_ary += array( + 'log_type' => LOG_MOD, + 'forum_id' => $forum_id, + 'topic_id' => $topic_id, + 'log_data' => (!empty($additional_data)) ? serialize($additional_data) : '', + ); + break; + + case 'user': + $reportee_id = (int) $additional_data['reportee_id']; + unset($additional_data['reportee_id']); + + $sql_ary += array( + 'log_type' => LOG_USERS, + 'reportee_id' => $reportee_id, + 'log_data' => (!empty($additional_data)) ? serialize($additional_data) : '', + ); + break; + + case 'critical': + $sql_ary += array( + 'log_type' => LOG_CRITICAL, + 'log_data' => (!empty($additional_data)) ? serialize($additional_data) : '', + ); + break; + } + + /** + * Allows to modify log data before we add it to the database + * + * NOTE: if sql_ary does not contain a log_type value, the entry will + * not be stored in the database. So ensure to set it, if needed. + * + * @event core.add_log + * @var string mode Mode of the entry we log + * @var int user_id ID of the user who triggered the log + * @var string log_ip IP of the user who triggered the log + * @var string log_operation Language key of the log operation + * @var int log_time Timestamp, when the log was added + * @var array additional_data Array with additional log data + * @var array sql_ary Array with log data we insert into the + * database. If sql_ary[log_type] is not set, + * we won't add the entry to the database. + * @since 3.1-A1 + */ + $vars = array('mode', 'user_id', 'log_ip', 'log_operation', 'log_time', 'additional_data', 'sql_ary'); + extract($this->dispatcher->trigger_event('core.add_log', $vars)); + + // We didn't find a log_type, so we don't save it in the database. + if (!isset($sql_ary['log_type'])) + { + return false; + } + + $this->db->sql_query('INSERT INTO ' . $this->log_table . ' ' . $this->db->sql_build_array('INSERT', $sql_ary)); + + return $this->db->sql_nextid(); + } + + /** + * Grab the logs from the database + * + * {@inheritDoc} + */ + public function get_logs($mode, $count_logs = true, $limit = 0, $offset = 0, $forum_id = 0, $topic_id = 0, $user_id = 0, $log_time = 0, $sort_by = 'l.log_time DESC', $keywords = '') + { + $this->entry_count = 0; + $this->last_page_offset = $offset; + + $topic_id_list = $reportee_id_list = array(); + + $profile_url = ($this->get_is_admin() && $this->phpbb_admin_path) ? append_sid("{$this->phpbb_admin_path}index.{$this->php_ext}", 'i=users&mode=overview') : append_sid("{$this->phpbb_root_path}memberlist.{$this->php_ext}", 'mode=viewprofile'); + + switch ($mode) + { + case 'admin': + $log_type = LOG_ADMIN; + $sql_additional = ''; + break; + + case 'mod': + $log_type = LOG_MOD; + $sql_additional = ''; + + if ($topic_id) + { + $sql_additional = 'AND l.topic_id = ' . (int) $topic_id; + } + else if (is_array($forum_id)) + { + $sql_additional = 'AND ' . $this->db->sql_in_set('l.forum_id', array_map('intval', $forum_id)); + } + else if ($forum_id) + { + $sql_additional = 'AND l.forum_id = ' . (int) $forum_id; + } + break; + + case 'user': + $log_type = LOG_USERS; + $sql_additional = 'AND l.reportee_id = ' . (int) $user_id; + break; + + case 'users': + $log_type = LOG_USERS; + $sql_additional = ''; + break; + + case 'critical': + $log_type = LOG_CRITICAL; + $sql_additional = ''; + break; + + default: + $log_type = false; + $sql_additional = ''; + } + + /** + * Overwrite log type and limitations before we count and get the logs + * + * NOTE: if log_type is false, no entries will be returned. + * + * @event core.get_logs_modify_type + * @var string mode Mode of the entries we display + * @var bool count_logs Do we count all matching entries? + * @var int limit Limit the number of entries + * @var int offset Offset when fetching the entries + * @var mixed forum_id Limit entries to the forum_id, + * can also be an array of forum_ids + * @var int topic_id Limit entries to the topic_id + * @var int user_id Limit entries to the user_id + * @var int log_time Limit maximum age of log entries + * @var string sort_by SQL order option + * @var string keywords Will only return entries that have the + * keywords in log_operation or log_data + * @var string profile_url URL to the users profile + * @var int log_type Limit logs to a certain type. If log_type + * is false, no entries will be returned. + * @var string sql_additional Additional conditions for the entries, + * e.g.: 'AND l.forum_id = 1' + * @since 3.1-A1 + */ + $vars = array('mode', 'count_logs', 'limit', 'offset', 'forum_id', 'topic_id', 'user_id', 'log_time', 'sort_by', 'keywords', 'profile_url', 'log_type', 'sql_additional'); + extract($this->dispatcher->trigger_event('core.get_logs_modify_type', $vars)); + + if ($log_type === false) + { + $this->last_page_offset = 0; + return array(); + } + + $sql_keywords = ''; + if (!empty($keywords)) + { + // Get the SQL condition for our keywords + $sql_keywords = $this->generate_sql_keyword($keywords); + } + + if ($count_logs) + { + $sql = 'SELECT COUNT(l.log_id) AS total_entries + FROM ' . LOG_TABLE . ' l, ' . USERS_TABLE . ' u + WHERE l.log_type = ' . (int) $log_type . ' + AND l.user_id = u.user_id + AND l.log_time >= ' . (int) $log_time . " + $sql_keywords + $sql_additional"; + $result = $this->db->sql_query($sql); + $this->entry_count = (int) $this->db->sql_fetchfield('total_entries'); + $this->db->sql_freeresult($result); + + if ($this->entry_count == 0) + { + // Save the queries, because there are no logs to display + $this->last_page_offset = 0; + return array(); + } + + // Return the user to the last page that is valid + while ($this->last_page_offset >= $this->entry_count) + { + $this->last_page_offset = max(0, $this->last_page_offset - $limit); + } + } + + $sql = 'SELECT l.*, u.username, u.username_clean, u.user_colour + FROM ' . LOG_TABLE . ' l, ' . USERS_TABLE . ' u + WHERE l.log_type = ' . (int) $log_type . ' + AND u.user_id = l.user_id + ' . (($log_time) ? 'AND l.log_time >= ' . (int) $log_time : '') . " + $sql_keywords + $sql_additional + ORDER BY $sort_by"; + $result = $this->db->sql_query_limit($sql, $limit, $this->last_page_offset); + + $i = 0; + $log = array(); + while ($row = $this->db->sql_fetchrow($result)) + { + $row['forum_id'] = (int) $row['forum_id']; + if ($row['topic_id']) + { + $topic_id_list[] = (int) $row['topic_id']; + } + + if ($row['reportee_id']) + { + $reportee_id_list[] = (int) $row['reportee_id']; + } + + $log_entry_data = array( + 'id' => (int) $row['log_id'], + + 'reportee_id' => (int) $row['reportee_id'], + 'reportee_username' => '', + 'reportee_username_full'=> '', + + 'user_id' => (int) $row['user_id'], + 'username' => $row['username'], + 'username_full' => get_username_string('full', $row['user_id'], $row['username'], $row['user_colour'], false, $profile_url), + + 'ip' => $row['log_ip'], + 'time' => (int) $row['log_time'], + 'forum_id' => (int) $row['forum_id'], + 'topic_id' => (int) $row['topic_id'], + + 'viewforum' => ($row['forum_id'] && $this->auth->acl_get('f_read', $row['forum_id'])) ? append_sid("{$this->phpbb_root_path}viewforum.{$this->php_ext}", 'f=' . $row['forum_id']) : false, + 'action' => (isset($this->user->lang[$row['log_operation']])) ? $this->user->lang[$row['log_operation']] : '{' . ucfirst(str_replace('_', ' ', $row['log_operation'])) . '}', + ); + + /** + * Modify the entry's data before it is returned + * + * @event core.get_logs_modify_entry_data + * @var array row Entry data from the database + * @var array log_entry_data Entry's data which is returned + * @since 3.1-A1 + */ + $vars = array('row', 'log_entry_data'); + extract($this->dispatcher->trigger_event('core.get_logs_modify_entry_data', $vars)); + + $log[$i] = $log_entry_data; + + if (!empty($row['log_data'])) + { + $log_data_ary = unserialize($row['log_data']); + $log_data_ary = ($log_data_ary !== false) ? $log_data_ary : array(); + + if (isset($this->user->lang[$row['log_operation']])) + { + // Check if there are more occurrences of % than + // arguments, if there are we fill out the arguments + // array. It doesn't matter if we add more arguments than + // placeholders. + if ((substr_count($log[$i]['action'], '%') - sizeof($log_data_ary)) > 0) + { + $log_data_ary = array_merge($log_data_ary, array_fill(0, substr_count($log[$i]['action'], '%') - sizeof($log_data_ary), '')); + } + + $log[$i]['action'] = vsprintf($log[$i]['action'], $log_data_ary); + + // If within the admin panel we do not censor text out + if ($this->get_is_admin()) + { + $log[$i]['action'] = bbcode_nl2br($log[$i]['action']); + } + else + { + $log[$i]['action'] = bbcode_nl2br(censor_text($log[$i]['action'])); + } + } + else if (!empty($log_data_ary)) + { + $log[$i]['action'] .= '<br />' . implode('', $log_data_ary); + } + + /* Apply make_clickable... has to be seen if it is for good. :/ + // Seems to be not for the moment, reconsider later... + $log[$i]['action'] = make_clickable($log[$i]['action']); + */ + } + + $i++; + } + $this->db->sql_freeresult($result); + + /** + * Get some additional data after we got all log entries + * + * @event core.get_logs_get_additional_data + * @var array log Array with all our log entries + * @var array topic_id_list Array of topic ids, for which we + * get the permission data + * @var array reportee_id_list Array of additional user IDs we + * get the username strings for + * @since 3.1-A1 + */ + $vars = array('log', 'topic_id_list', 'reportee_id_list'); + extract($this->dispatcher->trigger_event('core.get_logs_get_additional_data', $vars)); + + if (sizeof($topic_id_list)) + { + $topic_auth = $this->get_topic_auth($topic_id_list); + + foreach ($log as $key => $row) + { + $log[$key]['viewtopic'] = (isset($topic_auth['f_read'][$row['topic_id']])) ? append_sid("{$this->phpbb_root_path}viewtopic.{$this->php_ext}", 'f=' . $topic_auth['f_read'][$row['topic_id']] . '&t=' . $row['topic_id']) : false; + $log[$key]['viewlogs'] = (isset($topic_auth['m_'][$row['topic_id']])) ? append_sid("{$this->phpbb_root_path}mcp.{$this->php_ext}", 'i=logs&mode=topic_logs&t=' . $row['topic_id'], true, $this->user->session_id) : false; + } + } + + if (sizeof($reportee_id_list)) + { + $reportee_data_list = $this->get_reportee_data($reportee_id_list); + + foreach ($log as $key => $row) + { + if (!isset($reportee_data_list[$row['reportee_id']])) + { + continue; + } + + $log[$key]['reportee_username'] = $reportee_data_list[$row['reportee_id']]['username']; + $log[$key]['reportee_username_full'] = get_username_string('full', $row['reportee_id'], $reportee_data_list[$row['reportee_id']]['username'], $reportee_data_list[$row['reportee_id']]['user_colour'], false, $profile_url); + } + } + + return $log; + } + + /** + * Generates a sql condition for the specified keywords + * + * @param string $keywords The keywords the user specified to search for + * + * @return string Returns the SQL condition searching for the keywords + */ + protected function generate_sql_keyword($keywords) + { + // Use no preg_quote for $keywords because this would lead to sole + // backslashes being added. We also use an OR connection here for + // spaces and the | string. Currently, regex is not supported for + // searching (but may come later). + $keywords = preg_split('#[\s|]+#u', utf8_strtolower($keywords), 0, PREG_SPLIT_NO_EMPTY); + $sql_keywords = ''; + + if (!empty($keywords)) + { + $keywords_pattern = array(); + + // Build pattern and keywords... + for ($i = 0, $num_keywords = sizeof($keywords); $i < $num_keywords; $i++) + { + $keywords_pattern[] = preg_quote($keywords[$i], '#'); + $keywords[$i] = $this->db->sql_like_expression($this->db->any_char . $keywords[$i] . $this->db->any_char); + } + + $keywords_pattern = '#' . implode('|', $keywords_pattern) . '#ui'; + + $operations = array(); + foreach ($this->user->lang as $key => $value) + { + if (substr($key, 0, 4) == 'LOG_' && preg_match($keywords_pattern, $value)) + { + $operations[] = $key; + } + } + + $sql_keywords = 'AND ('; + if (!empty($operations)) + { + $sql_keywords .= $this->db->sql_in_set('l.log_operation', $operations) . ' OR '; + } + $sql_lower = $this->db->sql_lower_text('l.log_data'); + $sql_keywords .= " $sql_lower " . implode(" OR $sql_lower ", $keywords) . ')'; + } + + return $sql_keywords; + } + + /** + * Determine whether the user is allowed to read and/or moderate the forum of the topic + * + * @param array $topic_ids Array with the topic ids + * + * @return array Returns an array with two keys 'm_' and 'read_f' which are also an array of topic_id => forum_id sets when the permissions are given. Sample: + * array( + * 'permission' => array( + * topic_id => forum_id + * ), + * ), + */ + protected function get_topic_auth(array $topic_ids) + { + $forum_auth = array('f_read' => array(), 'm_' => array()); + $topic_ids = array_unique($topic_ids); + + $sql = 'SELECT topic_id, forum_id + FROM ' . TOPICS_TABLE . ' + WHERE ' . $this->db->sql_in_set('topic_id', array_map('intval', $topic_ids)); + $result = $this->db->sql_query($sql); + + while ($row = $this->db->sql_fetchrow($result)) + { + $row['topic_id'] = (int) $row['topic_id']; + $row['forum_id'] = (int) $row['forum_id']; + + if ($this->auth->acl_get('f_read', $row['forum_id'])) + { + $forum_auth['f_read'][$row['topic_id']] = $row['forum_id']; + } + + if ($this->auth->acl_gets('a_', 'm_', $row['forum_id'])) + { + $forum_auth['m_'][$row['topic_id']] = $row['forum_id']; + } + } + $this->db->sql_freeresult($result); + + return $forum_auth; + } + + /** + * Get the data for all reportee from the database + * + * @param array $reportee_ids Array with the user ids of the reportees + * + * @return array Returns an array with the reportee data + */ + protected function get_reportee_data(array $reportee_ids) + { + $reportee_ids = array_unique($reportee_ids); + $reportee_data_list = array(); + + $sql = 'SELECT user_id, username, user_colour + FROM ' . USERS_TABLE . ' + WHERE ' . $this->db->sql_in_set('user_id', $reportee_ids); + $result = $this->db->sql_query($sql); + + while ($row = $this->db->sql_fetchrow($result)) + { + $reportee_data_list[$row['user_id']] = $row; + } + $this->db->sql_freeresult($result); + + return $reportee_data_list; + } + + /** + * Get total log count + * + * {@inheritDoc} + */ + public function get_log_count() + { + return ($this->entry_count) ? $this->entry_count : 0; + } + + /** + * Get offset of the last valid log page + * + * {@inheritDoc} + */ + public function get_valid_offset() + { + return ($this->last_page_offset) ? $this->last_page_offset : 0; + } +} diff --git a/phpBB/install/database_update.php b/phpBB/install/database_update.php index 3382e57e76..2ecddf49d4 100644 --- a/phpBB/install/database_update.php +++ b/phpBB/install/database_update.php @@ -123,6 +123,7 @@ $request = $phpbb_container->get('request'); $user = $phpbb_container->get('user'); $auth = $phpbb_container->get('auth'); $db = $phpbb_container->get('dbal.conn'); +$phpbb_log = $phpbb_container->get('log'); // make sure request_var uses this request instance request_var('', 0, false, false, $request); // "dependency injection" for a function diff --git a/phpBB/install/install_install.php b/phpBB/install/install_install.php index 4cc154509c..67e368e34d 100644 --- a/phpBB/install/install_install.php +++ b/phpBB/install/install_install.php @@ -53,7 +53,7 @@ class install_install extends module function main($mode, $sub) { global $lang, $template, $language, $phpbb_root_path, $phpEx; - global $phpbb_container, $cache; + global $phpbb_container, $cache, $phpbb_log; switch ($sub) { @@ -105,8 +105,9 @@ class install_install extends module // Create a normal container now $phpbb_container = phpbb_create_default_container($phpbb_root_path, $phpEx); - // Sets the global $cache variable + // Sets the global variables $cache = $phpbb_container->get('cache'); + $phpbb_log = $phpbb_container->get('log'); $this->build_search_index($mode, $sub); $this->add_modules($mode, $sub); diff --git a/tests/functions_user/group_user_attributes_test.php b/tests/functions_user/group_user_attributes_test.php index f13156c2cc..4336fd894e 100644 --- a/tests/functions_user/group_user_attributes_test.php +++ b/tests/functions_user/group_user_attributes_test.php @@ -125,7 +125,7 @@ class phpbb_functions_user_group_user_attributes_test extends phpbb_database_tes */ public function test_group_user_attributes($description, $user_id, $group_id, $group_row, $expected) { - global $auth, $cache, $db, $phpbb_dispatcher, $user, $phpbb_container; + global $auth, $cache, $db, $phpbb_dispatcher, $user, $phpbb_container, $phpbb_log, $phpbb_root_path, $phpEx; $user->ip = ''; $cache = new phpbb_mock_cache; @@ -141,6 +141,7 @@ class phpbb_functions_user_group_user_attributes_test extends phpbb_database_tes ->method('get') ->with('cache.driver') ->will($this->returnValue($cache_driver)); + $phpbb_log = new phpbb_log($db, $user, $auth, $phpbb_dispatcher, $phpbb_root_path, 'adm/', $phpEx, LOG_TABLE); group_user_attributes('default', $group_id, array($user_id), false, 'group_name', $group_row); diff --git a/tests/log/add_test.php b/tests/log/add_test.php new file mode 100644 index 0000000000..a5f93232f2 --- /dev/null +++ b/tests/log/add_test.php @@ -0,0 +1,88 @@ +<?php +/** +* +* @package testing +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php'; + +class phpbb_log_add_test extends phpbb_database_test_case +{ + public function getDataSet() + { + return $this->createXMLDataSet(dirname(__FILE__) . '/fixtures/empty_log.xml'); + } + + public function test_log_enabled() + { + global $phpbb_root_path, $phpEx, $db, $phpbb_dispatcher; + + $db = $this->new_dbal(); + $cache = new phpbb_mock_cache; + $phpbb_dispatcher = new phpbb_mock_event_dispatcher(); + $user = $this->getMock('phpbb_user'); + $auth = $this->getMock('phpbb_auth'); + + $log = new phpbb_log($db, $user, $auth, $phpbb_dispatcher, $phpbb_root_path, 'adm/', $phpEx, LOG_TABLE); + + $this->assertTrue($log->is_enabled(), 'Initialise failed'); + + $log->disable(); + $this->assertFalse($log->is_enabled(), 'Disable all failed'); + + $log->enable(); + $this->assertTrue($log->is_enabled(), 'Enable all failed'); + + $log->disable('admin'); + $this->assertFalse($log->is_enabled('admin'), 'Disable admin failed'); + $this->assertTrue($log->is_enabled('user'), 'User should be enabled, is disabled'); + $this->assertTrue($log->is_enabled(), 'Disable admin disabled all'); + + $log->enable('admin'); + $this->assertTrue($log->is_enabled('admin'), 'Enable admin failed'); + } + + public function test_log_add() + { + global $phpbb_root_path, $phpEx, $db, $phpbb_dispatcher; + + $db = $this->new_dbal(); + $cache = new phpbb_mock_cache; + $phpbb_dispatcher = new phpbb_mock_event_dispatcher(); + $user = $this->getMock('phpbb_user'); + $auth = $this->getMock('phpbb_auth'); + + $log = new phpbb_log($db, $user, $auth, $phpbb_dispatcher, $phpbb_root_path, 'adm/', $phpEx, LOG_TABLE); + + $mode = 'critical'; + $user_id = ANONYMOUS; + $log_ip = 'user_ip'; + $log_time = time(); + $log_operation = 'LOG_OPERATION'; + $additional_data = array(); + + // Add an entry successful + $this->assertEquals(1, $log->add($mode, $user_id, $log_ip, $log_operation, $log_time)); + + // Disable logging for all types + $log->disable(); + $this->assertFalse($log->add($mode, $user_id, $log_ip, $log_operation, $log_time), 'Disable for all types failed'); + $log->enable(); + + // Disable logging for same type + $log->disable('critical'); + $this->assertFalse($log->add($mode, $user_id, $log_ip, $log_operation, $log_time), 'Disable for same type failed'); + $log->enable(); + + // Disable logging for different type + $log->disable('admin'); + $this->assertEquals(2, $log->add($mode, $user_id, $log_ip, $log_operation, $log_time), 'Disable for different types failed'); + $log->enable(); + + // Invalid mode specified + $this->assertFalse($log->add('mode_does_not_exist', $user_id, $log_ip, $log_operation, $log_time)); + } +} diff --git a/tests/log/fixtures/empty_log.xml b/tests/log/fixtures/empty_log.xml new file mode 100644 index 0000000000..261b6a622a --- /dev/null +++ b/tests/log/fixtures/empty_log.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<dataset> + <table name="phpbb_log"> + <column>log_id</column> + <column>log_type</column> + <column>user_id</column> + <column>forum_id</column> + <column>topic_id</column> + <column>reportee_id</column> + <column>log_ip</column> + <column>log_time</column> + <column>log_operation</column> + <column>log_data</column> + </table> +</dataset> diff --git a/tests/log/fixtures/full_log.xml b/tests/log/fixtures/full_log.xml new file mode 100644 index 0000000000..2ce2643d26 --- /dev/null +++ b/tests/log/fixtures/full_log.xml @@ -0,0 +1,166 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<dataset> + <table name="phpbb_log"> + <column>log_id</column> + <column>log_type</column> + <column>user_id</column> + <column>forum_id</column> + <column>topic_id</column> + <column>reportee_id</column> + <column>log_ip</column> + <column>log_time</column> + <column>log_operation</column> + <column>log_data</column> + <row> + <value>1</value> + <value>0</value> + <value>1</value> + <value>0</value> + <value>0</value> + <value>0</value> + <value>127.0.0.1</value> + <value>1</value> + <value>LOG_INSTALL_INSTALLED</value> + <value>a:1:{i:0;s:9:"3.1.0-dev";}</value> + </row> + <row> + <value>2</value> + <value>0</value> + <value>1</value> + <value>0</value> + <value>0</value> + <value>0</value> + <value>127.0.0.1</value> + <value>1</value> + <value>LOG_KEY_NOT_EXISTS</value> + <value>a:1:{i:0;s:15:"additional_data";}</value> + </row> + <row> + <value>3</value> + <value>2</value> + <value>1</value> + <value>0</value> + <value>0</value> + <value>0</value> + <value>127.0.0.1</value> + <value>1</value> + <value>LOG_CRITICAL</value> + <value>a:1:{i:0;s:13:"critical data";}</value> + </row> + <row> + <value>4</value> + <value>1</value> + <value>1</value> + <value>12</value> + <value>34</value> + <value>0</value> + <value>127.0.0.1</value> + <value>1</value> + <value>LOG_MOD</value> + <value></value> + </row> + <row> + <value>5</value> + <value>1</value> + <value>1</value> + <value>12</value> + <value>45</value> + <value>0</value> + <value>127.0.0.1</value> + <value>1</value> + <value>LOG_MOD</value> + <value></value> + </row> + <row> + <value>6</value> + <value>1</value> + <value>1</value> + <value>23</value> + <value>56</value> + <value>0</value> + <value>127.0.0.1</value> + <value>1</value> + <value>LOG_MOD</value> + <value></value> + </row> + <row> + <value>7</value> + <value>1</value> + <value>1</value> + <value>12</value> + <value>45</value> + <value>0</value> + <value>127.0.0.1</value> + <value>1</value> + <value>LOG_MOD2</value> + <value></value> + </row> + <row> + <value>8</value> + <value>3</value> + <value>1</value> + <value>0</value> + <value>0</value> + <value>2</value> + <value>127.0.0.1</value> + <value>1</value> + <value>LOG_USER</value> + <value>a:1:{i:0;s:5:"admin";}</value> + </row> + <row> + <value>9</value> + <value>3</value> + <value>1</value> + <value>0</value> + <value>0</value> + <value>1</value> + <value>127.0.0.1</value> + <value>1</value> + <value>LOG_USER</value> + <value>a:1:{i:0;s:5:"guest";}</value> + </row> + </table> + <table name="phpbb_users"> + <column>user_id</column> + <column>username</column> + <column>username_clean</column> + <column>user_permissions</column> + <column>user_sig</column> + <column>user_occ</column> + <column>user_interests</column> + <row> + <value>1</value> + <value>Anonymous</value> + <value>Anonymous</value> + <value></value> + <value></value> + <value></value> + <value></value> + </row> + <row> + <value>2</value> + <value>admin</value> + <value>admin</value> + <value></value> + <value></value> + <value></value> + <value></value> + </row> + </table> + <table name="phpbb_topics"> + <column>topic_id</column> + <column>forum_id</column> + <row> + <value>34</value> + <value>12</value> + </row> + <row> + <value>45</value> + <value>12</value> + </row> + <row> + <value>56</value> + <value>23</value> + </row> + </table> +</dataset> diff --git a/tests/log/function_add_log_test.php b/tests/log/function_add_log_test.php new file mode 100644 index 0000000000..864b364862 --- /dev/null +++ b/tests/log/function_add_log_test.php @@ -0,0 +1,193 @@ +<?php +/** +* +* @package testing +* @copyright (c) 2012 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php'; + +class phpbb_log_function_add_log_test extends phpbb_database_test_case +{ + public function getDataSet() + { + return $this->createXMLDataSet(dirname(__FILE__) . '/fixtures/empty_log.xml'); |