<?php
# MantisBT - A PHP based bugtracking system

# MantisBT is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# MantisBT is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with MantisBT.  If not, see <http://www.gnu.org/licenses/>.

/**
 * Logging API
 *
 * Provides functionality to log system events other than bug related history.
 *
 * @package CoreAPI
 * @subpackage LoggingAPI
 * @copyright Copyright 2000 - 2002  Kenzaburo Ito - kenito@300baud.org
 * @copyright Copyright 2002  MantisBT Team - mantisbt-dev@lists.sourceforge.net
 * @link http://www.mantisbt.org
 *
 * @uses config_api.php
 * @uses constant_inc.php
 * @uses event_api.php
 * @uses utility_api.php
 */

require_api( 'config_api.php' );
require_api( 'constant_inc.php' );
require_api( 'event_api.php' );
require_api( 'utility_api.php' );

$g_log_levels = array(
	LOG_EMAIL => 'MAIL',
	LOG_EMAIL_RECIPIENT => 'RECIPIENT',
	LOG_EMAIL_VERBOSE => 'MAIL_VERBOSE',
	LOG_FILTERING => 'FILTER',
	LOG_AJAX => 'AJAX',
	LOG_LDAP => 'LDAP',
	LOG_DATABASE => 'DB',
	LOG_WEBSERVICE => 'WEBSERVICE',
	LOG_PLUGIN => 'PLUGIN',
);

/**
 * Log an event
 * @param integer          $p_level Valid debug log level.
 * @param string|array,... $p_msg   Either a string, or an array structured as (string,execution time).
 * @return void
 */
function log_event( $p_level, $p_msg ) {
	global $g_log_levels;

	# check to see if logging is enabled
	$t_sys_log = config_get_global( 'log_level' );

	if( 0 == ( $t_sys_log & $p_level ) ) {
		return;
	}

	if( is_array( $p_msg ) ) {
		$t_event = $p_msg;
		$t_msg = var_export( $p_msg, true );
	} else {
		if( func_num_args() > 2 ) {
			$t_args = func_get_args();
			array_shift( $t_args ); # skip level
			array_shift( $t_args ); # skip message
			$p_msg = vsprintf( $p_msg, $t_args );
		}
		$t_event = array( $p_msg, 0 );
		$t_msg = $p_msg;
	}

	$t_backtrace = debug_backtrace();
	$t_caller = '';
	if( $p_level == LOG_PLUGIN ) {
		$t_caller .= plugin_get_current() . ' ';
		# remove the trace step for plugin.php::include()
		# so we will consider the included plugin page as the base script
		$t_trace_last = end( $t_backtrace );
		if( isset( $t_trace_last['function'] ) && $t_trace_last['function'] == 'include' ) {
			array_pop( $t_backtrace );
		}
		# If plugin_log_event() was used, remove that trace step, so that we can
		# later retrieve the actual function that started the logging
		# Set here the file and line depending on how this was called
		if( isset( $t_backtrace[1]['function'] ) && $t_backtrace[1]['function'] == 'plugin_log_event' ) {
			$t_caller .= basename( $t_backtrace[1]['file'] );
			$t_caller .= ':' . $t_backtrace[1]['line'];
			unset( $t_backtrace[1] );
			$t_backtrace = array_values( $t_backtrace );
		} else {
			$t_caller .= basename( $t_backtrace[0]['file'] );
			$t_caller .= ':' . $t_backtrace[0]['line'];
		}
	} else {
		$t_caller .= basename( $t_backtrace[0]['file'] );
		$t_caller .= ':' . $t_backtrace[0]['line'];
	}

	# Is this called from another function?
	if( isset( $t_backtrace[1] ) ) {
		if( $p_level == LOG_DATABASE ) {
			if( isset( $t_backtrace[2] ) && $t_backtrace[2]['function'] == 'call_user_func_array' ) {
				$t_caller = basename( $t_backtrace[3]['file'] );
				$t_caller .= ':' . $t_backtrace[3]['line'];
				$t_caller .= ' ' . $t_backtrace[3]['function'] . '()';
			} else if( $t_backtrace[1]['function'] == 'db_query' ) {
				$t_caller = basename( $t_backtrace[1]['file'] );
				$t_caller .= ':' . $t_backtrace[1]['line'];
				if( isset( $t_backtrace[2] ) ) {
					$t_caller .= ' ' . $t_backtrace[2]['function'] . '()';
				} else {
					$t_caller .= ' ' . $t_backtrace[1]['function'] . '()';
				}
			}
		} else {
			if( isset(  $t_backtrace[1]['class'] ) ) {
				$t_caller .= ' ' . $t_backtrace[1]['class'] . '::' . $t_backtrace[1]['function'] . '()';
			} else {
				$t_caller .= ' ' . $t_backtrace[1]['function'] . '()';
			}
		}
	} else {
		# or from a script directly?
		# ignore plugin logs, since its page is included via plugin.php, the script name
		# will always be plugin.php, which is not very useful
		if( $p_level != LOG_PLUGIN ) {
			$t_caller .= ' ' . $_SERVER['SCRIPT_NAME'];
		}
	}

	$t_now = date( config_get_global( 'complete_date_format' ) );
	$t_level = $g_log_levels[$p_level];

	$t_plugin_event = '[' . $t_level . '] ' . $t_msg;
	if( function_exists( 'event_signal' ) ) {
		event_signal( 'EVENT_LOG', array( $t_plugin_event ) );
	}

	$t_log_destination = config_get_global( 'log_destination' );

	if( is_blank( $t_log_destination ) ) {
		$t_destination = '';
	} else {
		$t_result = explode( ':', $t_log_destination, 2 );
		$t_destination = $t_result[0];
		if( isset( $t_result[1] ) ) {
			$t_modifiers = $t_result[1];
		}
	}

	$t_php_event = $t_now . ' ' . $t_level . ' ' . $t_caller . ' ' . $t_msg;

	switch( $t_destination ) {
		case 'none':
			break;
		case 'file':
			error_log( $t_php_event . PHP_EOL, 3, $t_modifiers );
			break;
		case 'page':
			global $g_log_events;
			$g_log_events[] = array( time(), $p_level, $t_event, $t_caller);
			break;
		case 'firebug':
			if( !class_exists( 'FirePHP' ) ) {
				if( file_exists( config_get_global( 'library_path' ) . 'FirePHPCore/FirePHP.class.php' ) ) {
					require_lib( 'FirePHPCore/FirePHP.class.php' );
				}
			}
			if( class_exists( 'FirePHP' ) ) {
				static $s_firephp;
				if( $s_firephp === null ) {
					$s_firephp = FirePHP::getInstance( true );
				}
				# Don't use $t_msg, let FirePHP format the message
				$s_firephp->log( $p_msg, $t_now . ' ' . $t_level );
				return;
			}
			# if firebug is not available, fall through
		default:
			# use default PHP error log settings
			error_log( $t_php_event . PHP_EOL );
			break;
	}

	# If running from command line, echo log event to stdout
	if( $t_destination != 'none' && php_sapi_name() == 'cli' ) {
		echo $t_php_event . PHP_EOL;
	}
}

/**
 * Print logging api output to bottom of html page
 * @return void
 */
function log_print_to_page() {
	if( config_get_global( 'log_destination' ) === 'page' && auth_is_user_authenticated() && access_has_global_level( config_get( 'show_log_threshold' ) ) ) {
		global $g_log_events, $g_log_levels, $g_email_shutdown_processing;

		if( $g_email_shutdown_processing ) {
			email_send_all();
		}

		$t_unique_queries_count = 0;
		$t_total_query_execution_time = 0;
		$t_unique_queries = array();
		$t_total_queries_count = 0;
		$t_total_event_count = count( $g_log_events );


		echo "<div class=\"space-10\"></div>";
		echo "\t<div class=\"row\">\n";
		echo "\t<div class=\"col-xs-12\">\n";

		echo "\t<div class=\"widget-box widget-color-red\">\n";
		echo "\t<div class=\"widget-header widget-header-small\">\n";
		echo "\t<h4 class=\"widget-title lighter\">\n";
		echo "\t<i class=\"ace-icon fa fa-flag-o\"></i>\n";
		echo "Debug Log";
		echo "</h4>\n";
		echo "</div>\n";

		echo "\t<div class=\"widget-body\">\n";

		echo "\n\n<!--Mantis Debug Log Output-->";
		if( $t_total_event_count == 0 ) {
			echo "<!--END Mantis Debug Log Output-->\n\n";
			return;
		}

		echo "<div class=\"widget-main no-padding\">";
		echo "<div class=\"table-responsive\">\n";
		echo "<table class=\"table table-bordered table-condensed table-striped\" id=\"log-event-list\">\n";
		echo "\t<thead>\n";
		echo "\t\t<tr>\n";
		echo "\t\t\t<th class=\"small-caption\">" . lang_get( 'log_page_number' ) . "</th>\n";
		echo "\t\t\t<th class=\"small-caption\">" . lang_get( 'log_page_time' ) . "</th>\n";
		echo "\t\t\t<th class=\"small-caption\">" . lang_get( 'log_page_caller' ) . "</th>\n";
		echo "\t\t\t<th class=\"small-caption\">" . lang_get( 'log_page_event' ) . "</th>\n";
		echo "\t\t</tr>\n";
		echo "\t</thead>\n";
		echo "\t<tbody>\n";

		for( $i = 0; $i < $t_total_event_count; $i++ ) {
			if( $g_log_events[$i][1] == LOG_DATABASE ) {
				if( !in_array( $g_log_events[$i][2][0], $t_unique_queries ) ) {
					$t_unique_queries_count++;
					$g_log_events[$i][2][2] = false;
					array_push( $t_unique_queries, $g_log_events[$i][2][0] );
				} else {
					$g_log_events[$i][2][2] = true;
				}
				$t_total_query_execution_time += $g_log_events[$i][2][1];
			}
		}

		$t_count = array();
		foreach( $g_log_events as $t_log_event ) {
			$t_level = $g_log_levels[$t_log_event[1]];
			if( isset( $t_count[$t_log_event[1]] ) ) {
				$t_count[$t_log_event[1]]++;
			} else {
				$t_count[$t_log_event[1]] = 1;
			}
			switch( $t_log_event[1] ) {
				case LOG_DATABASE:
					$t_total_queries_count++;
					$t_query_duplicate_class = '';
					if( $t_log_event[2][2] ) {
						$t_query_duplicate_class = ' class="duplicate-query"';
					}
					echo "\t\t<tr " . $t_query_duplicate_class . '><td class="small">' . $t_level . '-' . $t_count[$t_log_event[1]] . '</td><td class="small">' . $t_log_event[2][1] . '</td><td class="small">' . string_html_specialchars( $t_log_event[3] ) . '</td><td class="small">' . string_html_specialchars( $t_log_event[2][0] ) . "</td></tr>\n";
					break;
				default:
					echo "\t\t<tr><td class=\"small\">" . $t_level . '-' . $t_count[$t_log_event[1]] . '</td><td class="small">' . $t_log_event[2][1] . '</td><td class="small">' . string_html_specialchars( $t_log_event[3] ) . '</td><td class="small">' . string_html_specialchars( $t_log_event[2][0] ) . "</td></tr>\n";
			}
		}

		# output any summary data
		if( $t_unique_queries_count != 0 ) {
			$t_unique_queries_executed = sprintf( lang_get( 'unique_queries_executed' ), $t_unique_queries_count );
			echo "\t\t<tr><td class=\"small\">" . $g_log_levels[LOG_DATABASE] . '</td><td colspan="3" class="small">' . $t_unique_queries_executed . "</td></tr>\n";
		}
		if( $t_total_queries_count != 0 ) {
			$t_total_queries_executed = sprintf( lang_get( 'total_queries_executed' ), $t_total_queries_count );
			echo "\t\t<tr><td class=\"small\">" . $g_log_levels[LOG_DATABASE] . '</td><td colspan="3" class="small">' . $t_total_queries_executed . "</td></tr>\n";
		}
		if( $t_total_query_execution_time != 0 ) {
			$t_total_query_time = sprintf( lang_get( 'total_query_execution_time' ), $t_total_query_execution_time );
			echo "\t\t<tr><td class=\"small\">" . $g_log_levels[LOG_DATABASE] . '</td><td colspan="3" class="small">' . $t_total_query_time . "</td></tr>\n";
		}
		echo "\t</tbody>\n\t</table>\n";
		echo "</div></div></div></div></div></div>\n";

		echo "<!--END Mantis Debug Log Output-->\n\n";
	}
}
