<?php
/**
 * Gitium provides automatic git version control and deployment for
 * your plugins and themes integrated into wp-admin.
 *
 * Copyright (C) 2014-2025 PRESSINFRA SRL <ping@presslabs.com>
 *
 * Gitium 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 3 of the License, or
 * any later version.
 *
 * Gitium 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 Gitium. If not, see <http://www.gnu.org/licenses/>.
 *
 * @package         Gitium
 */

function gitium_error_log( $message ) {
	if ( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG ) { return; }
	error_log( "gitium_error_log: $message" );
}

function wp_content_is_versioned() {
	return file_exists( WP_CONTENT_DIR . '/.git' );
}

if ( ! function_exists( 'gitium_enable_maintenance_mode' ) ) :
	function gitium_enable_maintenance_mode() {
		$file = ABSPATH . '/.maintenance';

		if ( false === file_put_contents( $file, '<?php $upgrading = ' . time() .';' ) ) {
			return false;
		} else {
			return true;
		}
	}
endif;

if ( ! function_exists( 'gitium_disable_maintenance_mode' ) ) :
	function gitium_disable_maintenance_mode() {
		return unlink( ABSPATH . '/.maintenance' );
	}
endif;

function gitium_get_versions() {
	$versions = get_transient( 'gitium_versions' );
	if ( empty( $versions ) ) {
		$versions = gitium_update_versions();
	}
	return $versions;
}

function _gitium_commit_changes( $message, $dir = '.' ) {
	global $git;

	list( , $git_private_key ) = gitium_get_keypair();
	if (!$git_private_key)
		return false;
	$git->set_key( $git_private_key );

	$git->add( $dir );
	gitium_update_versions();
	$current_user = wp_get_current_user();
	return $git->commit( $message, $current_user->display_name, $current_user->user_email );
}

function _gitium_format_message( $name, $version = false, $prefix = '' ) {
	$commit_message = "`$name`";
	if ( $version ) {
		$commit_message .= " version $version";
	}
	if ( $prefix ) {
		$commit_message = "$prefix $commit_message";
	}
	return $commit_message;
}

/**
 * This function return the basic info about a path.
 *
 * base_path - means the path after wp-content dir (themes/plugins)
 * type      - can be file/theme/plugin
 * name      - the file name of the path, if it is a file, or the theme/plugin name
 * version   - the theme/plugin version, othewise null
 */
/* Some examples:

  with 'wp-content/themes/twentyten/style.css' will return:
  array(
    'base_path' => 'wp-content/themes/twentyten'
    'type'      => 'theme'
    'name'      => 'TwentyTen'
    'version'   => '1.12'
  )

  with 'wp-content/themes/twentyten/img/foo.png' will return:
  array(
    'base_path' => 'wp-content/themes/twentyten'
    'type'      => 'theme'
    'name'      => 'TwentyTen'
    'version'   => '1.12'
  )

  with 'wp-content/plugins/foo.php' will return:
  array(
    'base_path' => 'wp-content/plugins/foo.php'
    'type'      => 'plugin'
    'name'      => 'Foo'
    'varsion'   => '2.0'
  )

  with 'wp-content/plugins/autover/autover.php' will return:
  array(
    'base_path' => 'wp-content/plugins/autover'
    'type'      => 'plugin'
    'name'      => 'autover'
    'version'   => '3.12'
  )

  with 'wp-content/plugins/autover/' will return:
  array(
    'base_path' => 'wp-content/plugins/autover'
    'type'      => 'plugin'
    'name'      => 'autover'
    'version'   => '3.12'
  )
*/
function _gitium_module_by_path( $path ) {
	$versions = gitium_get_versions();

	// default values
	$module   = array(
		'base_path' => $path,
		'type'      => 'file',
		'name'      => basename( $path ),
		'version'   => null,
	);

	// find the base_path
	$split_path = explode( '/', $path );
	if ( 2 < count( $split_path ) ) {
		$module['base_path'] = "{$split_path[0]}/{$split_path[1]}/{$split_path[2]}";
	}

	// find other data for theme
	if ( array_key_exists( 'themes', $versions ) && 0 === strpos( $path, 'wp-content/themes/' ) ) {
		$module['type'] = 'theme';
		foreach ( $versions['themes'] as $theme => $data ) {
			if ( 0 === strpos( $path, "wp-content/themes/$theme" ) ) {
				$module['name']    = $data['name'];
				$module['version'] = $data['version'];
				break;
			}
		}
	}

	// find other data for plugin
	if ( array_key_exists( 'plugins', $versions ) && 0 === strpos( $path, 'wp-content/plugins/' ) ) {
		$module['type'] = 'plugin';
		foreach ( $versions['plugins'] as $plugin => $data ) {
			if ( '.' === dirname( $plugin ) ) { // single file plugin
				if ( "wp-content/plugins/$plugin" === $path ) {
					$module['base_path'] = $path;
					$module['name']      = $data['name'];
					$module['version']   = $data['version'];
					break;
				}
			} else if ( 'wp-content/plugins/' . dirname( $plugin ) === $module['base_path'] ) {
				$module['name']    = $data['name'];
				$module['version'] = $data['version'];
				break;
			}
		}
	}

	return $module;
}

function gitium_group_commit_modified_plugins_and_themes( $msg_append = '' ) {
	global $git;

	$uncommited_changes = $git->get_local_changes();
	$commit_groups = array();
	$commits = array();

	if ( ! empty( $msg_append ) ) {
		$msg_append = "($msg_append)";
	}
	foreach ( $uncommited_changes as $path => $action ) {
		$change = _gitium_module_by_path( $path );
		$change['action'] = $action;
		$commit_groups[ $change['base_path'] ] = $change;
	}

	foreach ( $commit_groups as $base_path => $change ) {
		$commit_message = _gitium_format_message( $change['name'], $change['version'], "{$change['action']} {$change['type']}" );
		$commit = _gitium_commit_changes( "$commit_message $msg_append", $base_path, false );
		if ( $commit ) {
			$commits[] = $commit;
		}
	}

	return $commits;
}

function gitium_commit_and_push_gitignore_file( $path = '' ) {
	global $git;

	$current_user = wp_get_current_user();
	if ( ! empty( $path ) ) { $git->rm_cached( $path ); }
	$git->add( '.gitignore' );
	$commit = $git->commit( 'Update the `.gitignore` file', $current_user->display_name, $current_user->user_email );
	gitium_merge_and_push( $commit );
}

if ( ! function_exists( 'gitium_acquire_merge_lock' ) ) :
	function gitium_acquire_merge_lock() {
		$gitium_lock_path   = apply_filters( 'gitium_lock_path', sys_get_temp_dir().'/.gitium-lock' );
		$gitium_lock_handle = fopen( $gitium_lock_path, 'w+' );

		$lock_timeout    = intval( ini_get( 'max_execution_time' ) ) > 10 ? intval( ini_get( 'max_execution_time' ) ) - 5 : 10;
		$lock_timeout_ms = 10;
		$lock_retries    = 0;
		while ( ! flock( $gitium_lock_handle, LOCK_EX | LOCK_NB ) ) {
			usleep( $lock_timeout_ms * 1000 );
			$lock_retries++;
			if ( $lock_retries * $lock_timeout_ms > $lock_timeout * 1000 ) {
				return false; // timeout
			}
		}
		gitium_error_log( __FUNCTION__ );
		return array( $gitium_lock_path, $gitium_lock_handle );
	}
endif;

if ( ! function_exists( 'gitium_release_merge_lock' ) ) :
	function gitium_release_merge_lock( $lock ) {
		list( $gitium_lock_path, $gitium_lock_handle ) = $lock;
		gitium_error_log( __FUNCTION__ );
		flock( $gitium_lock_handle, LOCK_UN );
		fclose( $gitium_lock_handle );
	}
endif;

// Merges the commits with remote and pushes them back
function gitium_merge_and_push( $commits ) {
	global $git;

	$lock = gitium_acquire_merge_lock()
		or trigger_error( 'Timeout when gitium lock was acquired', E_USER_WARNING );

	if ( ! $git->fetch_ref() ) {
		return false;
	}

	$merge_status = $git->merge_with_accept_mine( $commits );

	gitium_release_merge_lock( $lock );

	return $git->push() && $merge_status;
}

function gitium_check_after_event( $plugin, $event = 'activation' ) {
	global $git;

	if ( 'gitium/gitium.php' == $plugin ) { return; } // do not hook on activation of this plugin

	if ( $git->is_dirty() ) {
		$versions = gitium_update_versions();
		if ( isset( $versions['plugins'][ $plugin ] ) ) {
			$name    = $versions['plugins'][ $plugin ]['name'];
			$version = $versions['plugins'][ $plugin ]['version'];
		} else {
			$name = $plugin;
		}
		gitium_auto_push( _gitium_format_message( $name, $version, "after $event of" ) );
	}
}

function gitium_update_remote_tracking_branch() {
	global $git;
	$remote_branch = $git->get_remote_tracking_branch();
	set_transient( 'gitium_remote_tracking_branch', $remote_branch );

	return $remote_branch;
}

function _gitium_get_remote_tracking_branch( $update_transient = false ) {
	if ( ! $update_transient && ( false !== ( $remote_branch = get_transient( 'gitium_remote_tracking_branch' ) ) ) ) {
		return $remote_branch;
	} else {
		return gitium_update_remote_tracking_branch();
	}
}

function gitium_update_is_status_working() {
	global $git;
	$is_status_working = $git->is_status_working();
	set_transient( 'gitium_is_status_working', $is_status_working );
	return $is_status_working;
}

function _gitium_is_status_working( $update_transient = false ) {
	if ( ! $update_transient && ( false !== ( $is_status_working = get_transient( 'gitium_is_status_working' ) ) ) ) {
		return $is_status_working;
	} else {
		return gitium_update_is_status_working();
	}
}

function _gitium_status( $update_transient = false ) {
	global $git;

	if ( ! $update_transient && ( false !== ( $changes = get_transient( 'gitium_uncommited_changes' ) ) ) ) {
		return $changes;
	}

	$git_version = get_transient( 'gitium_git_version' );
	if ( false === $git_version ) {
		set_transient( 'gitium_git_version', $git->get_version() );
	}

	if ( $git->is_status_working() && $git->get_remote_tracking_branch() ) {
		if ( ! $git->fetch_ref() ) {
			set_transient( 'gitium_remote_disconnected', $git->get_last_error() );
		} else {
			delete_transient( 'gitium_remote_disconnected' );
		}
		$changes = $git->status();
	} else {
		delete_transient( 'gitium_remote_disconnected' );
		$changes = array();
	}

	set_transient( 'gitium_uncommited_changes', $changes, 12 * 60 * 60 ); // cache changes for half-a-day
	return $changes;
}

function _gitium_ssh_encode_buffer( $buffer ) {
	$len = strlen( $buffer );
	if ( ord( $buffer[0] ) & 0x80 ) {
		$len++;
		$buffer = "\x00" . $buffer;
	}
	return pack( 'Na*', $len, $buffer );
}

function _gitium_generate_keypair() {
	$rsa_key = openssl_pkey_new(
		array(
			'private_key_bits' => 2048,
			'private_key_type' => OPENSSL_KEYTYPE_RSA,
		)
	);

	try {
		$private_key = openssl_pkey_get_private( $rsa_key );
		$try = openssl_pkey_export( $private_key, $pem ); //Private Key
		if (!$try)
			return false;
	} catch (Exception $e) {
		return false;
	}

	$key_info   = openssl_pkey_get_details( $rsa_key );
	$buffer     = pack( 'N', 7 ) . 'ssh-rsa' .
					_gitium_ssh_encode_buffer( $key_info['rsa']['e'] ) .
					_gitium_ssh_encode_buffer( $key_info['rsa']['n'] );
	$public_key = 'ssh-rsa ' . base64_encode( $buffer ) . ' gitium@' . parse_url( get_home_url(), PHP_URL_HOST );

	return array( $public_key, $pem );
}

function gitium_get_keypair( $generate_new_keypair = false ) {
	if ( $generate_new_keypair ) {
		$keypair = _gitium_generate_keypair();
		delete_option( 'gitium_keypair' );
		add_option( 'gitium_keypair', $keypair, '', false );
	}
	if ( false === ( $keypair = get_option( 'gitium_keypair', false ) ) ) {
		$keypair = _gitium_generate_keypair();
		add_option( 'gitium_keypair', $keypair, '', false );
	}
	return $keypair;
}

function _gitium_generate_webhook_key() {
	return md5( str_shuffle( 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.()[]{}-_=+!@#%^&*~<>:;' ) );
}

function gitium_get_webhook_key( $generate_new_webhook_key = false ) {
	if ( $generate_new_webhook_key ) {
		$key = _gitium_generate_webhook_key();
		delete_option( 'gitium_webhook_key' );
		add_option( 'gitium_webhook_key', $key, '', false );
		return $key;
	}
	if ( false === ( $key = get_option( 'gitium_webhook_key', false ) ) ) {
		$key = _gitium_generate_webhook_key();
		add_option( 'gitium_webhook_key', $key, '', false );
	}
	return $key;
}

function gitium_get_webhook() {
	if ( defined( 'GIT_WEBHOOK_URL' ) && GIT_WEBHOOK_URL ) { return GIT_WEBHOOK_URL; }
	$key = gitium_get_webhook_key();
	$url = add_query_arg( 'key', $key, plugins_url( 'gitium-webhook.php', __FILE__ ) );
	return apply_filters( 'gitium_webhook_url', $url, $key );
}

function gitium_admin_init() {
	global $git;

	$git_version = get_transient( 'gitium_git_version' );
	if ( false === $git_version ) {
		set_transient( 'gitium_git_version', $git->get_version() );
	}
}
add_action( 'admin_init', 'gitium_admin_init' );

add_action('admin_enqueue_scripts', 'enqueue_script_for_gitium_page');
function enqueue_script_for_gitium_page($hook) {
    // Check if the current page is your plugin's settings page
    if ((isset($_GET['page']) && $_GET['page'] === 'gitium/gitium.php') || (isset($_GET['page']) && $_GET['page'] === 'gitium/gitium-settings.php')) {
        // Enqueue your JavaScript file
        wp_enqueue_script(
            'my-plugin-script',  // Handle for the script
            plugin_dir_url(__FILE__) . 'js/copy-to-clipboard.js',  // URL to the script
            array('jquery'),  // Dependencies
            '1.1',  // Version number 
            true    // Load in footer
        );
    }
}