<?PHP
/** @file
 * The main "controller" file of ViewGit.
 *
 * Joomlafied version.
 *
 * All requests come to this file. You can think of it as the controller in the
 * Model-View-Controller pattern. It reads config, processes user input,
 * fetches required data using git commandline, and finally passes the data to
 * templates to be shown to the user.
 *
 */

// no direct access
defined( '_JEXEC' ) or die( 'Restricted access' );
jimport('joomla.plugin.plugin');



class plgContentviewgit extends JPlugin
{


public function onContentPrepare($ctx,&$rw, &$parms, $pg=0 ) {

	if ( !preg_match("#{viewgit}(.*?){/viewgit}#s", $rw->text) ) {
	 	return(true);
	}


	$option=JRequest::getString('option');
	if ($option!='') $option="option=$option"; else $option='';
	$view=JRequest::getString('view');
	if ($view!='') $view="&"."view=$view"; else $view='';
	$layout=JRequest::getString('layout');
	if ($layout!='') $layout="&"."layout=$layout"; else $layout='';
	$id=JRequest::getString('id');
	if ($id!='') $id="&"."id=$id"; else $id='';
	$Itemid=JRequest::getString('Itemid');
	if ($Itemid!='') $Itemid="&"."Itemid=$Itemid"; else $Itemid='';


	global $urlbase;
	$urlbase=JURI::base()."?{$option}{$view}{$layout}{$id}{$Itemid}";

	//print $urlbase;

	$xpr="#{viewgit}(.*?){/viewgit}#s";
	if (preg_match_all($xpr, $rw->text, $matches, PREG_PATTERN_ORDER) > 0) {
	$cnt=0;
	foreach ($matches[0] as $match) {
		$html="";
		$setproject = preg_replace("/{.+?}/", "", $match);
		if (($setproject!='') and (JRequest::getString('a')=='')) {
			JRequest::setVar('p',$setproject);
			JRequest::setVar('a','summary');
		}

//Start Main Viewgit Code
	global $conf;
	global $page;
	require_once('viewgit/inc/config.php');
	require_once('viewgit/inc/functions.php');
	require_once('viewgit/inc/plugins.php');
	$doc =& JFactory::getDocument();
	$doc->addStyleSheet('/plugins/content/viewgit/viewgit/'.$conf['style'].'.css');



		// Include all plugins
		foreach (glob('plugins/*/main.php') as $plugin) {
			require_once($plugin);

			$parts = explode('/', $plugin);
			$name = $parts[1];

			$classname = "${name}plugin";
			$inst = new $classname;
		}

		$old_error_handler = set_error_handler('vg_error_handler');

		// Adjust error_reporting based on config.
		if (!$conf['debug']) {
			error_reporting(E_ALL ^ E_NOTICE);
		}

		if (isset($conf['auth_lib'])){
			require_once("viewgit/inc/auth_{$conf['auth_lib']}.php");
			auth_check();
		}

		if (isset($conf['projects_glob'])) {
			foreach ($conf['projects_glob'] as $glob) {
				foreach (glob($glob) as $path) {
					// Get the last part of the path before .git
					$name = preg_replace(array('#/?\.git$#', '#^.*/#'), array('', ''), $path);

					// Workaround against name collisions; proj, proj1, proj2, ...
					$i = '';
					while (in_array($name . $i, array_keys($conf['projects']))) {
						@$i++;
					}
					$name = $name . $i;
					$conf['projects'][$name] = array('repo' => $path);
				}
			}
		}

		$action = 'index';
		$template = 'index';
		$page['title'] = 'ViewGit';


		if (JRequest::getString('a')) {
			$action = strtolower(JRequest::getString( 'a'));
		}
		$page['action'] = $action;
		/*
		 * index - list of projects
		 */
		if ($action === 'index') {
			$template = 'index';
			$page['title'] = 'List of projects - ViewGit';
			foreach (array_keys($conf['projects']) as $p) {
				$page['projects'][] = get_project_info($p);
			}
		}



		/*
		 * archive - send a tree as an archive to client
		 * @param p project
		 * @param h tree hash
		 * @param t type, "targz" or "zip"
		 * @param n OPTIONAL name suggestion
		 */
		elseif ($action === 'archive') {
			$project = validate_project(JRequest::getString( 'p'));
			$tree = validate_hash(JRequest::getString( 'h'));
			$type = JRequest::getString( 't');

			$basename = "$project-tree-". substr($tree, 0, 7);
			if ((JRequest::getString( 'n'))) {
				$basename = "$project-$_REQUEST[n]-". substr($tree, 0, 6);
			}

			if ($type === 'targz') {
				header("Content-Type: application/x-tar-gz");
				header("Content-Transfer-Encoding: binary");
				header("Content-Disposition: attachment; filename=\"$basename.tar.gz\";");
				run_git_passthru($project, "archive --format=tar $tree |gzip");
			}
			elseif ($type === 'zip') {
				header("Content-Type: application/x-zip");
				header("Content-Transfer-Encoding: binary");
				header("Content-Disposition: attachment; filename=\"$basename.zip\";");
				run_git_passthru($project, "archive --format=zip $tree");
			}
			else {
				die('Invalid archive type requested');
			}

			die();
		}

		/*
		 * blob - send a blob to browser with filename suggestion
		 * @param p project
		 * @param h blob hash
		 * @param n filename
		 */
		elseif ($action === 'blob') {
			$project = validate_project(JRequest::getString( 'p' ));
			$hash = validate_hash(JRequest::getString( 'h'));
			$name = JRequest::getString( 'n');

			header('Content-type: application/octet-stream');
			header("Content-Disposition: attachment; filename=$name"); // FIXME needs quotation

			run_git_passthru($project, "cat-file blob $hash");
			die();
		}

		/*
		 * co - git checkout. These requests come from mod_rewrite, see the .htaccess file.
		 * @param p project
		 * @param r path
		 */
		elseif ($action === 'co') {
			if (!$conf['allow_checkout']) { die('Checkout not allowed'); }

			// For debugging
			debug("Project: $_REQUEST[p] Request: $_REQUEST[r]");

			// eg. info/refs, HEAD
			$p = validate_project(JRequest::getString( 'p')); // project
			$r = JRequest::getString( 'r'); // path

			$gitdir = $conf['projects'][$p]['repo'];
			$filename = $gitdir .'/'. $r;

			// make sure the request is legit (no reading of other files besides those under git projects)
			if ($r === 'HEAD' || $r === 'info/refs' || preg_match('!^objects/info/(packs|http-alternates|alternates)$!', $r) > 0 || preg_match('!^objects/[0-9a-f]{2}/[0-9a-f]{38}$!', $r) > 0) {
				if (file_exists($filename)) {
					debug('OK, sending');
					readfile($filename);
				} else {
					debug('Not found');
					header('HTTP/1.0 404 Not Found');
				}
			} else {
				debug("Denied");
			}

			die();
		}

		/*
		 * commit - view commit information
		 * @param p project
		 * @param h commit hash
		 */
		elseif ($action === 'commit') {
			$template = 'commit';
			$page['project'] = validate_project(JRequest::getString( 'p'));
			$page['title'] = "$page[project] - Commit - ViewGit";
			$page['commit_id'] = validate_hash(JRequest::getString( 'h'));
			$page['subtitle'] = "Commit ". substr($page['commit_id'], 0, 6);

			$info = git_get_commit_info($page['project'], $page['commit_id']);

			$page['author_name'] = $info['author_name'];
			$page['author_mail'] = $info['author_mail'];
			$page['author_datetime'] = gmstrftime($conf['datetime_full'], $info['author_utcstamp']);
			$page['author_datetime_local'] = gmstrftime($conf['datetime_full'], $info['author_stamp']) .' '. $info['author_timezone'];
			$page['committer_name'] = $info['committer_name'];
			$page['committer_mail'] = $info['committer_mail'];
			$page['committer_datetime'] = gmstrftime($conf['datetime_full'], $info['committer_utcstamp']);
			$page['committer_datetime_local'] = gmstrftime($conf['datetime_full'], $info['committer_stamp']) .' '. $info['committer_timezone'];
			$page['tree_id'] = $info['tree'];
			$page['parents'] = $info['parents'];
			$page['message'] = $info['message'];
			$page['message_firstline'] = $info['message_firstline'];
			$page['message_full'] = $info['message_full'];

		}

		/*
		 * commitdiff - view diff of a commit
		 * @param p project
		 * @param h commit hash
		 */
		elseif ($action === 'commitdiff') {
			$template = 'commitdiff';
			$page['project'] = validate_project(JRequest::getString( 'p'));
			$page['title'] = "$page[project] - Commitdiff - ViewGit";
			$hash = validate_hash(JRequest::getString( 'h'));
			$page['commit_id'] = $hash;
			$page['subtitle'] = "Commitdiff ". substr($page['commit_id'], 0, 6);

			$info = git_get_commit_info($page['project'], $hash);

			$page['tree_id'] = $info['tree'];

			$page['message'] = $info['message'];
			$page['message_firstline'] = $info['message_firstline'];
			$page['message_full'] = $info['message_full'];
			$page['author_name'] = $info['author_name'];
			$page['author_mail'] = $info['author_mail'];
			$page['author_datetime'] = gmstrftime($conf['datetime'], $info['author_utcstamp']);

			$text = git_diff($page['project'], "$hash^", $hash);
			list($page['files'], $page['diffdata']) = format_diff($text);
			//$page['diffdata'] = format_diff($text);
		}

		elseif ($action === 'patch') {
			$project = validate_project(JRequest::getString( 'p'));
			$hash = validate_hash(JRequest::getString( 'h'));
			$filename = "$project-". substr($hash, 0, 7) .".patch";

			//header("Content-Type: text/x-diff");
			header("Content-Type: application/octet-stream");
			header("Content-Transfer-Encoding: binary");
			// TODO git-style filename
			header("Content-Disposition: attachment; filename=\"$filename\";");

			run_git_passthru($project, "format-patch --stdout $hash^..$hash");
			die();
		}

		/*
		 * rss-log - RSS feed of project changes
		 * @param p project
		 */
		elseif ($action === 'rss-log') {
			$page['project'] = validate_project(JRequest::getString( 'p'));

			$ext_url = 'http://'. $_SERVER['HTTP_HOST'] . dirname($_SERVER['SCRIPT_NAME']) .'/';

			$page['rss_title'] = "Log for $page[project]";
			$page['rss_link'] = $ext_url . makelink(array('a' => 'summary', 'p' => $page['project']));
			$page['rss_description'] = "Git log for project $page[project], generated by ViewGit.";
			$page['rss_pubDate'] = rss_pubdate(time());
			$page['rss_ttl'] = $conf['rss_ttl'];

			$page['rss_items'] = array();

			$diffstat = strstr($conf['rss_item_description'], '{DIFFSTAT}');

			$revs = git_get_rev_list($page['project'], $conf['rss_max_items']);
			foreach ($revs as $rev) {
				$info = git_get_commit_info($page['project'], $rev);
				$link = $ext_url . makelink(array('a' => 'commit', 'p' => $page['project'], 'h' => $rev));
				if ($diffstat) {
					$info['diffstat'] = git_diffstat($page['project'], $rev);
				}

				$page['rss_items'][] = array(
					'title' => rss_item_format($conf['rss_item_title'], $info),
					'guid' => $link,
					'link' => $link,
					'description' => rss_item_format($conf['rss_item_description'], $info),
					'pubdate' => rss_pubdate($info['author_utcstamp']),
				);
			}

			require('viewgit/templates/rss.php');
			die();
		}

		/*
		 * search - search project history
		 * @param p project
		 * @param st search type: commit,grep,author,committer,pickaxe
		 * @param s string to search for
		 */
		elseif ($action === 'search') {
			$template = 'shortlog';

			$page['project'] = validate_project(JRequest::getString( 'p'));

			$info = git_get_commit_info($page['project']);
			$page['commit_id'] = $info['h'];
			$page['tree_id'] = $info['tree'];

			$type = JRequest::getString( 'st');
			$string = JRequest::getString( 's');

			$page['search_t'] = $type;
			$page['search_s'] = $string;

			$commits = git_search_commits($page['project'], $type, $string);
			$shortlog = array();
			foreach ($commits as $c) {
				$info = git_get_commit_info($page['project'], $c);
				$shortlog[] = array(
					'author' => $info['author_name'],
					'date' => gmstrftime($conf['datetime'], $info['author_utcstamp']),
					'message' => $info['message'],
					'commit_id' => $info['h'],
					'tree' => $info['tree'],
					'refs' => array(),
				);
			}
			$page['shortlog'] = $shortlog;
		}

		/*
		 * shortlog - project shortlog entries
		 * @param p project
		 * @param h OPTIONAL commit id to start showing log from
		 */
		elseif ($action === 'shortlog') {
			$template = 'shortlog';
			$page['project'] = validate_project(JRequest::getString( 'p'));
			$page['title'] = "$page[project] - Shortlog - ViewGit";
			$page['subtitle'] = "Shortlog";
			if ((JRequest::getString( 'h'))) {
				$page['ref'] = validate_hash(JRequest::getString( 'h'));
			} else {
				$page['ref'] = 'HEAD';
			}

			$info = git_get_commit_info($page['project'], $page['ref']);
			$page['commit_id'] = $info['h'];
			$page['tree_id'] = $info['tree'];

			$page['shortlog'] = handle_shortlog($page['project'], $page['ref']);
		}
		elseif ($action === 'summary') {
			$template = 'summary';

			$page['project'] = validate_project(JRequest::getString( 'p'));
			$page['title'] = "$page[project] - Summary - ViewGit";
			$page['subtitle'] = "Summary";

			$info = git_get_commit_info($page['project']);
			$page['commit_id'] = $info['h'];
			$page['tree_id'] = $info['tree'];

			$page['shortlog'] = handle_shortlog($page['project']);

			$page['tags'] = handle_tags($page['project'], $conf['summary_tags']);

			$heads = git_get_heads($page['project']);
			$page['heads'] = array();
			foreach ($heads as $h) {
				$info = git_get_commit_info($page['project'], $h['h']);
				$page['heads'][] = array(
					'date' => gmstrftime($conf['datetime'], $info['author_utcstamp']),
					'h' => $h['h'],
					'fullname' => $h['fullname'],
					'name' => $h['name'],
				);
			}
		}
		elseif ($action === 'tags') {
			$template = 'tags';
			$page['project'] = validate_project(JRequest::getString( 'p'));
			$page['title'] = "$page[project] - Tags - ViewGit";

			$info = git_get_commit_info($page['project']);
			$page['commit_id'] = $info['h'];
			$page['tree_id'] = $info['tree'];

			$page['tags'] = handle_tags($page['project']);
		}
		/*
		 * Shows a tree, with list of directories/files, links to them and download
		 * links to archives.
		 *
		 * @param p project
		 * @param h tree hash
		 * @param hb OPTIONAL base commit (trees can be part of multiple commits, this
		 * one denotes which commit the user navigated from)
		 * @param f OPTIONAL path the user has followed to view this tree
		 */
		elseif ($action === 'tree') {
			$template = 'tree';
			$page['project'] = validate_project(JRequest::getString( 'p'));
			if ((JRequest::getString( 'h'))) {
				$page['tree_id'] = validate_hash(JRequest::getString( 'h'));
			}
			/*
			else {
				// TODO walk the tree
				$page['tree_id'] = 'HEAD';
			}
			*/
			$page['title'] = "$page[project] - Tree - ViewGit";

			// 'hb' optionally contains the commit_id this tree is related to
			if ((JRequest::getString( 'hb'))) {
				$page['commit_id'] = validate_hash(JRequest::getString( 'hb'));
			}
			else {
				// for the header
				$info = git_get_commit_info($page['project']);
				$page['commit_id'] = $info['h'];
			}

			$page['path'] = '';
			if ((JRequest::getString( 'f'))) {
				$page['path'] = JRequest::getString( 'f'); // TODO validate?
			}

			// get path info for the header
			$page['pathinfo'] = git_get_path_info($page['project'], $page['commit_id'], $page['path']);
			if (!isset($page['tree_id'])) {
				// Take the last hash from the tree
				if (count($page['pathinfo']) > 0) {
					$page['tree_id'] = $page['pathinfo'][count($page['pathinfo']) - 1]['hash'];
				} else {
					$page['tree_id'] = 'HEAD';
				}
			}

			$page['subtitle'] = "Tree ". substr($page['tree_id'], 0, 6);
			$page['entries'] = git_ls_tree($page['project'], $page['tree_id']);
		}
		/*
		 * View a blob as inline, embedded on the page.
		 * @param p project
		 * @param h blob hash
		 * @param hb OPTIONAL base commit
		 */
		elseif ($action === 'viewblob') {
			$template = 'blob';
			$page['project'] = validate_project(JRequest::getString( 'p'));
			$page['hash'] = validate_hash(JRequest::getString( 'h'));
			$page['title'] = "$page[project] - Blob - ViewGit";
			if ((JRequest::getString( 'hb'))) {
				$page['commit_id'] = validate_hash(JRequest::getString( 'hb'));
			}
			else {
				$page['commit_id'] = 'HEAD';
			}
			$page['subtitle'] = "Blob ". substr($page['hash'], 0, 6);

			$page['path'] = '';
			if ((JRequest::getString( 'f'))) {
				$page['path'] = JRequest::getString( 'f'); // TODO validate?
			}

			// For the header's pagenav
			$info = git_get_commit_info($page['project'], $page['commit_id']);
			$page['commit_id'] = $info['h'];
			$page['tree_id'] = $info['tree'];

			$page['pathinfo'] = git_get_path_info($page['project'], $page['commit_id'], $page['path']);

			$page['data'] = join("\n", run_git($page['project'], "cat-file blob $page[hash]"));

			// GeSHi support
			if ($conf['geshi'] && strpos($page['path'], '.')) {
				$old_mask = error_reporting(E_ALL ^ E_NOTICE);
				require_once($conf['geshi_path']);
				$ext = array_pop(explode('.', $page['path']));
				$lang = Geshi::get_language_name_from_extension($ext);
				if (strlen($lang) > 0) {
					$geshi =& new Geshi($page['data'], $lang);
					if (is_int($conf['geshi_line_numbers'])) {
						if ($conf['geshi_line_numbers'] == 0) {
							$geshi->enable_line_numbers(GESHI_NORMAL_LINE_NUMBERS);
						}
						else {
							$geshi->enable_line_numbers(GESHI_FANCY_LINE_NUMBERS, $conf['geshi_line_numbers']);
						}
					}
					$page['html_data'] = $geshi->parse_code();
				}
				error_reporting($old_mask);
			}
		}
		elseif (in_array($action, array_keys(VGPlugin::$plugin_actions))) {
			VGPlugin::$plugin_actions[$action]->action($action);
			die();
		}
		else {
			die('Invalid action');
		}
//End Viewgit Code
	ob_start();
	require 'viewgit/templates/header.php';
	require "viewgit/templates/".$template.".php";
	require 'viewgit/templates/footer.php';

	if (isset($page['project'])) {
		$attribs = array('type' => 'application/rss+xml', 'title' => 'RSS 2.0');
		$href="/plugins/content/viewgit/viewgit/index.php?a=rss-log&p=".$page['project'];
		$doc->addHeadLink( $href, 'alternate', 'rel', $attribs );
	}




	 //VGPlugin::call_hooks('header');


$html = ob_get_contents();
ob_end_clean();
		$rw->text=preg_replace($xpr,$html,$rw->text,1);
	}
	return true;
	}

	}
}

?>