<?php
# MantisBT - a php based bugtracking system
# Copyright (C) 2000 - 2002  Kenzaburo Ito - kenito@300baud.org
# Copyright (C) 2002 - 2010  MantisBT Team - mantisbt-dev@lists.sourceforge.net
#
# 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/>.
# See the README and LICENSE files for details
#
#
# GoogleCode - MantisBT interaction on SVN commit
#
# This code can be used to capture the Google Code "Post-Commit
# Web Hooks" and, if the commit log contains a references to an
# issue, automatically add a comment for this issue.
# More info on GC Post-Commit Hooks:
# http://code.google.com/p/support/wiki/PostCommitWebHooks
#
# To make it works, you need to :
# - place this file under the '/scripts/' directory of your Mantis
#   installation,
# - add the account, the regexp and the GC auth. key used to add
#   the comments in the Mantis configuration.
#   ex:
#    $g_source_control_account = 'svn_mantis-robot';  // this account must exist
#    $g_source_control_regexp = '/\bissue [#]{0,1}(\d+)\b/i';
#    $g_source_control_fixed_regexp = '/\bfix [#]{0,1}(\d+)\b/i';
#    $g_source_control_googlecode_secretkey = array(
#             'gc_project_name' => 'dont forget me!',
#             'gc_project_name_2' => 'facultatif',
#             );
#
# - configure your googlecode project (admin / source) and
#   give the URL of this script.
#   For ex: http://mantis.your-domain.net/scripts/checkin_googlecode.php
#
# Note: A big part of this code comes from script/checkin.php
#
# TODO:
# - make it work with Hg too,
# - a request can be sent more than once, duplicating
#   messages. I don't see how to avoid it cleanly.
# - if a key has been badly pasted, it fails (as designed) quite silently.
#   Any idea of improvment on this ?


# include MantisBT core
global $g_bypass_headers;
$g_bypass_headers = 1;
require_once( dirname ( dirname( __FILE__ ) ) . DIRECTORY_SEPARATOR . 'core.php' );


# Check that the username is set and exists
$conf_username = config_get( 'source_control_account' );
if( is_blank( $conf_username ) || ( user_get_id_by_name( $conf_username ) === false ) ) {
	header('HTTP/1.0 500 Inexistent MantisBT account');
	echo "ERROR&nbsp;: Inexistent source control account ('$conf_username').\n";
	exit( 1 );
}

# Login as source control user
if( !auth_attempt_script_login( $conf_username ) ) {
	header('HTTP/1.0 500 Invalid MantisBT user');
	echo "ERROR&nbsp;: Invalid MantisBT user.";
	exit( 1 );
}

# Retrieve the JSON send by GoogleCode
$data_raw = file_get_contents('php://input');
if ( empty($data_raw) or ! ($data = json_decode($data_raw, true)) ) {
	header('HTTP/1.0 500 Invalid or no data transmitted');
	echo "ERROR&nbsp;: Invalid or no data transmitted.";
	exit( 1 );
}

# Retrieve the config items we need
$conf_commit_regexp = config_get( 'source_control_regexp' );
$conf_commit_fixed_regexp = config_get( 'source_control_fixed_regexp' );
$conf_googlecode_secretkey = false;
$t_googlecode_secretkey_config_item = config_get( 'source_control_googlecode_secretkey' );
if ( ! empty($t_googlecode_secretkey_config_item)) {
	# This config item could be an array or a string (in order to simplify mono-mantis project conf.)
	if (is_array($t_googlecode_secretkey_config_item)) {
		if (array_key_exists($data['project_name'], $t_googlecode_secretkey_config_item)) {
			$conf_googlecode_secretkey = $t_googlecode_secretkey_config_item[$data['project_name']];
		}
	}
	else {
		$conf_googlecode_secretkey = $t_googlecode_secretkey_config_item;
	}
}

# Check the signature of the JSON
if ($conf_googlecode_secretkey) {
	if ($_SERVER['HTTP_GOOGLE_CODE_PROJECT_HOSTING_HOOK_HMAC'] != hash_hmac('md5', $data_raw, $conf_googlecode_secretkey)) {
		# We are quite strict here : if the key has been configured and the
		# hash doesn't match, we throw a HTTP-203 (to disable Google retries
		# and be visible in the webserver log) and we just ignore the request
		header('HTTP/1.0 203 Bad signature');
		echo "ERROR : bad signature, message will be ignored.\n";
		exit( 1 );
	}
}

# Loop over each revisions in the json data
foreach($data['revisions'] as $rev) {
	$t_comment = $rev['message'];
	$t_issues = array();
	$t_fixed_issues = array();

	# Run the regexp
	if( preg_match_all( $conf_commit_regexp, $rev['message'], $t_matches ) ) {
		$t_count = count( $t_matches[0] );
		for( $i = 0;$i < $t_count;++$i ) {
			$t_issues[] = $t_matches[1][$i];
		}
	}

	# Run the "fixed" regexp
	if( preg_match_all( $conf_commit_fixed_regexp, $rev['message'], $t_matches ) ) {
		$t_count = count( $t_matches[0] );
		for( $i = 0;$i < $t_count;++$i ) {
			$t_fixed_issues[] = $t_matches[1][$i];
		}
	}

	# Add info about modifications (commit ID and files added, removed, ...)
	$t_comment .= "\n\n";
	$t_comment .= "Commit: " . $rev['revision'] . "\n";
	foreach (array('added', 'modified', 'removed') as $t_action) {
		if ( ! empty($rev[$t_action])) {
			foreach ($rev[$t_action] as $t_file) {
				$t_comment .= ucfirst($t_action) . ' : ' . $t_file . "\n";
			}
		}
	}

	# Add the footer in case no key has been configured
	# TODO : not really clean. And may be a bad idea.
	if ( ! $conf_googlecode_secretkey ) {
		$t_comment .= "[warning : no post-commit auth. key has been configured. These data could be a fake.]\n";
	}

	if ( count($t_issues) > 0 or count($t_fixed_issues) > 0 ) {
		# history parameters are reserved for future use.
		$t_history_old_value = '';
		$t_history_new_value = '';

		# add note to each bug only once
		$t_issues = array_unique( $t_issues );
		$t_fixed_issues = array_unique( $t_fixed_issues );

		# Call the custom function to register the checkin on each issue.
		foreach( $t_issues as $t_issue_id ) {
			if( !in_array( $t_issue_id, $t_fixed_issues ) ) {
				helper_call_custom_function( 'checkin', array( $t_issue_id, $t_comment, $t_history_old_value, $t_history_new_value, false ) );
			}
		}

		foreach( $t_fixed_issues as $t_issue_id ) {
			helper_call_custom_function( 'checkin', array( $t_issue_id, $t_comment, $t_history_old_value, $t_history_new_value, true ) );
		}
	}
}

exit( 0 );

