From c09f76fec824ffdf8fe3a24beb6a498f0f4215d8 Mon Sep 17 00:00:00 2001 From: Florian Forster Date: Sun, 16 Jan 2011 14:31:25 +0100 Subject: [PATCH] Initial commit. --- README | 58 +++++++++++++++ plugin.php | 234 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 292 insertions(+) create mode 100644 README create mode 100644 plugin.php diff --git a/README b/README new file mode 100644 index 0000000..ddd41cf --- /dev/null +++ b/README @@ -0,0 +1,58 @@ +Gitweb plugin for YOURLS +======================== + + +This plugin can be used to automatically redirect to a Gitweb installation, if +the provided keywors is not found in the database. + +For example, imagine a user opening this URL: + + http://sho.rt/a06c1a52 + +And further imagine that “a06c1a52” is not a short URL stored in the database. +This plugin will intercept the “redirect_keyword_not_found” action and check if +the keyword is a hexadecimal number with at least six (hexadecimal) digits. + +If so, the plugin will iterate over all Git repositories in a base directory +and check if there's an object in one of them which matches the given id. If +such an object is found, the user will be redirected to the appropriate Gitweb +page. Otherwise, YOURLS will proceed as usual. + +Installation +------------ +First, you need at least YOURLS, version 1.5. Versions before that do not +provide a plugin infrastructure. + + * Copy the plugin to “$yourls_dir/user/plugins/”. + * Open the administrative interface in the web-browser of your choice. + * Open the “Plugins” page and search for the “Gitweb” plugin in the table. + * Click “Activate” in the rightmost “Action” column. + * A new bulletin “Gitweb” should appear below the link to the “Plugins” page. + * Go to the “Gitweb” page. + * Insert the base directory and the Gitweb URL. The base directory should be + the same directory you specified using “$projectroot” in your Gitweb + configuration. You can pass any valid Gitweb URL into the second box – + superfluous arguments are automatically stripped. + * Click “Update” to store your changes. + +Caveats +------- +The plugin executes the “git” command line utility to check for objects in the +configured Git repositories. Great care has been taken that every argument +passed to the shell has been sanitized, but you may experience problems if +PHP's “safe mode” is activated. The commands used are: + + * git rev-parse + Check existence of an object and determine it's complete object id. + * git cat-file + Determine an object's type. + +License +------- +The “Gitweb” plugin is distributed under the terms of the MIT license. The +complete licensing terms can be found at the beginning of the file +“plugin.php”. + +Author +------ +Florian “octo” Forster diff --git a/plugin.php b/plugin.php new file mode 100644 index 0000000..9759767 --- /dev/null +++ b/plugin.php @@ -0,0 +1,234 @@ + + **/ + +function gitweb_check_repository ($obj, $repo, $dir, $url) /* {{{ */ +{ + $output = array (); + $retval = 0; + + $obj_name = shell_exec ('git --git-dir=' . escapeshellarg ($dir) + . ' rev-parse ' . escapeshellarg ($obj) + . ' 2>/dev/null'); + if (!$obj_type) + return (false); + + $obj_type = shell_exec ('git --git-dir=' . escapeshellarg ($dir) + . ' cat-file -t ' . escapeshellarg ($obj_name) + . ' 2>/dev/null'); + if (!$obj_type) + return (false); + + if ($obj_type == 'commit') + { + $to_url = "$url?p=" . urlencode ($repo) . ';a=commitdiff;h=' . urlencode ($obj_name); + yourls_redirect ($to_url, /* status = */ 301); + return (true); + } + elseif ($obj_type == 'tag') + { + $to_url = "$url?p=" . urlencode ($repo) . ';a=tag;h=' . urlencode ($obj_name); + yourls_redirect ($to_url, /* status = */ 301); + return (true); + + } + elseif ($obj_type == 'tree') + { + $to_url = "$url?p=" . urlencode ($repo) . ";a=tree;h=" . urlencode ($obj_name); + yourls_redirect ($to_url, /* status = */ 301); + return (true); + } + elseif ($obj_type == 'blob') + { + $to_url = "$url?p=" . urlencode ($repo) . ";a=blob;h=" . urlencode ($obj_name); + yourls_redirect ($to_url, /* status = */ 301); + return (true); + } + else + { + error_log ("Gitweb plugin: Object \"$obj_name\" in repository \"$repo\" has unknown type \"$obj_type\"."); + return (false); + } +} /* }}} function gitweb_check_repository */ + +/* This callback function is called when the given keyword was not found in the + * database. I'll see if this looks like an object identifier in a Git + * repository and, if so, try to locate the object using the local + * repositories. */ +function gitweb_redirect_keyword_not_found ($args) /* {{{ */ +{ + $keyword = $args[0]; + + if (!preg_match ('/^[0-9a-fA-F]{6,40}$/', $keyword)) + return; + + $base_directory = yourls_get_option ('gitweb_base_directory'); + if (!$base_directory) + return; + + $base_url = yourls_get_option ('gitweb_base_url'); + if (!$base_url) + return; + + $dh = opendir ($base_directory); + if (!$dh) + return; + + while (($subdir = readdir ($dh)) !== false) + { + /* Ignore all files and directories starting with a dot, including the + * special directories "." and "..". */ + if (substr ($subdir, 0, 1) == '.') + continue; + + $absdir = "$base_directory/$subdir"; + if (!is_dir ($absdir)) + continue; + + /* Ignore repositories which are private (i.e. not exported by the + * git-daemon(1). We might leak information if we don't. */ + if (!file_exists ("$absdir/git-daemon-export-ok")) + continue; + + if (gitweb_check_repository ($keyword, $subdir, $absdir, $base_url)) + break; + } + + closedir ($dh); +} /* }}} function gitweb_redirect_keyword_not_found */ + +function gitweb_set_base_directory ($dir) /* {{{ */ +{ + /* Remove trailing slashes. */ + $dir = preg_replace ('/\/+$/', '', $dir); + + if (!preg_match ('/^\//', $dir)) + { + print ("

Not an absolute path: " + . htmlspecialchars ($dir) + . "

\n"); + return (false); + } + + if (!is_dir ($dir)) + { + print ("

Not a directory: " + . htmlspecialchars ($dir) + . "

\n"); + return (false); + } + + /* Open the directory only to check its permissions. */ + $dh = opendir ($dir); + if (!$dh) + { + print ("

Unable to open directory.

\n"); + return (false); + } + closedir ($dh); + + yourls_update_option ('gitweb_base_directory', $dir); + return (true); +} /* }}} function gitweb_set_base_directory */ + +function gitweb_set_base_url ($url) /* {{{ */ +{ + if (!preg_match ('/https?:\/\//i', $url)) + { + print ("

This does not look like a valid URL: " + . htmlspecialchars ($url) + . "

\n"); + return (false); + } + + $url = preg_replace ('/\?.*/', '', $url); + + yourls_update_option ('gitweb_base_url', $url); + return (true); +} /* }}} function gitweb_set_base_directory */ + +function gitweb_show_plugin_page () /* {{{ */ +{ + echo <<Gitweb Plugin Administration Page +

This plugin redirects to a Gitweb installation if a keyword wasn't + found in the database, looks like a Git object ID and is found in a + local Git repository.

+HTML; + + if (isset ($_POST['base_directory'])) + gitweb_set_base_directory ($_POST['base_directory']); + + if (isset ($_POST['base_url'])) + gitweb_set_base_url ($_POST['base_url']); + + $base_directory = yourls_get_option ('gitweb_base_directory'); + if ($base_directory) + $base_directory = htmlspecialchars ($base_directory); + + $base_url = yourls_get_option ('gitweb_base_url'); + if ($base_url) + $base_url = htmlspecialchars ($base_url); + + echo << + + + + + + + + + + + + +
+ +HTML; +} /* }}} function gitweb_show_plugin_page */ + +function gitweb_register_plugin_page () /* {{{ */ +{ + yourls_register_plugin_page ('gitweb_page', 'Gitweb', + 'gitweb_show_plugin_page'); +} /* }}} function gitweb_register_plugin_page */ + +yourls_add_action ('plugins_loaded', 'gitweb_register_plugin_page'); +yourls_add_action ('redirect_keyword_not_found', 'gitweb_redirect_keyword_not_found'); + +/* vim: set sw=2 sts=2 et fdm=marker : */ +?> -- 2.11.0