Initial commit.
[yourls-gitweb.git] / plugin.php
1 <?php\r
2 /*\r
3 Plugin Name: Gitweb\r
4 Plugin URI: http://octo.it/yourls-gitweb/\r
5 Description: Automatically redirect to a Gitweb installation if an appropriate Git object exists.\r
6 Version: 1.0\r
7 Author: Florian "octo" Forster\r
8 Author URI: http://octo.it/\r
9 */\r
10 \r
11 /**\r
12  * yourls -- Gitweb plugin\r
13  * Copyright (C) 2011  Florian Forster\r
14  *\r
15  * Permission is hereby granted, free of charge, to any person obtaining a\r
16  * copy of this software and associated documentation files (the "Software"),\r
17  * to deal in the Software without restriction, including without limitation\r
18  * the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
19  * and/or sell copies of the Software, and to permit persons to whom the\r
20  * Software is furnished to do so, subject to the following conditions:\r
21  *\r
22  * The above copyright notice and this permission notice shall be included in\r
23  * all copies or substantial portions of the Software.\r
24  *\r
25  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
26  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
27  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
28  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
29  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\r
30  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\r
31  * DEALINGS IN THE SOFTWARE.\r
32  *\r
33  * Authors:\r
34  *   Florian Forster <ff at octo.it>\r
35  **/\r
36 \r
37 function gitweb_check_repository ($obj, $repo, $dir, $url) /* {{{ */\r
38 {\r
39   $output = array ();\r
40   $retval = 0;\r
41 \r
42   $obj_name = shell_exec ('git --git-dir=' . escapeshellarg ($dir)\r
43     . ' rev-parse ' . escapeshellarg ($obj)\r
44     . ' 2>/dev/null');\r
45   if (!$obj_type)\r
46     return (false);\r
47 \r
48   $obj_type = shell_exec ('git --git-dir=' . escapeshellarg ($dir)\r
49     . ' cat-file -t ' . escapeshellarg ($obj_name)\r
50     . ' 2>/dev/null');\r
51   if (!$obj_type)\r
52     return (false);\r
53 \r
54   if ($obj_type == 'commit')\r
55   {\r
56     $to_url = "$url?p=" . urlencode ($repo) . ';a=commitdiff;h=' . urlencode ($obj_name);\r
57     yourls_redirect ($to_url, /* status = */ 301);\r
58     return (true);\r
59   }\r
60   elseif ($obj_type == 'tag')\r
61   {\r
62     $to_url = "$url?p=" . urlencode ($repo) . ';a=tag;h=' . urlencode ($obj_name);\r
63     yourls_redirect ($to_url, /* status = */ 301);\r
64     return (true);\r
65 \r
66   }\r
67   elseif ($obj_type == 'tree')\r
68   {\r
69     $to_url = "$url?p=" . urlencode ($repo) . ";a=tree;h=" . urlencode ($obj_name);\r
70     yourls_redirect ($to_url, /* status = */ 301);\r
71     return (true);\r
72   }\r
73   elseif ($obj_type == 'blob')\r
74   {\r
75     $to_url = "$url?p=" . urlencode ($repo) . ";a=blob;h=" . urlencode ($obj_name);\r
76     yourls_redirect ($to_url, /* status = */ 301);\r
77     return (true);\r
78   }\r
79   else\r
80   {\r
81     error_log ("Gitweb plugin: Object \"$obj_name\" in repository \"$repo\" has unknown type \"$obj_type\".");\r
82     return (false);\r
83   }\r
84 } /* }}} function gitweb_check_repository */\r
85 \r
86 /* This callback function is called when the given keyword was not found in the \r
87   * database. I'll see if this looks like an object identifier in a Git \r
88   * repository and, if so, try to locate the object using the local \r
89   * repositories. */\r
90 function gitweb_redirect_keyword_not_found ($args) /* {{{ */\r
91 {\r
92   $keyword = $args[0];\r
93 \r
94   if (!preg_match ('/^[0-9a-fA-F]{6,40}$/', $keyword))\r
95     return;\r
96 \r
97   $base_directory = yourls_get_option ('gitweb_base_directory');\r
98   if (!$base_directory)\r
99     return;\r
100 \r
101   $base_url = yourls_get_option ('gitweb_base_url');\r
102   if (!$base_url)\r
103     return;\r
104 \r
105   $dh = opendir ($base_directory);\r
106   if (!$dh)\r
107     return;\r
108 \r
109   while (($subdir = readdir ($dh)) !== false)\r
110   {\r
111     /* Ignore all files and directories starting with a dot, including the \r
112      * special directories "." and "..". */\r
113     if (substr ($subdir, 0, 1) == '.')\r
114       continue;\r
115 \r
116     $absdir = "$base_directory/$subdir";\r
117     if (!is_dir ($absdir))\r
118       continue;\r
119 \r
120     /* Ignore repositories which are private (i.e. not exported by the \r
121      * git-daemon(1). We might leak information if we don't. */\r
122     if (!file_exists ("$absdir/git-daemon-export-ok"))\r
123       continue;\r
124 \r
125     if (gitweb_check_repository ($keyword, $subdir, $absdir, $base_url))\r
126       break;\r
127   }\r
128 \r
129   closedir ($dh);\r
130 } /* }}} function gitweb_redirect_keyword_not_found */\r
131 \r
132 function gitweb_set_base_directory ($dir) /* {{{ */\r
133 {\r
134   /* Remove trailing slashes. */\r
135   $dir = preg_replace ('/\/+$/', '', $dir);\r
136 \r
137   if (!preg_match ('/^\//', $dir))\r
138   {\r
139     print ("<p class=\"error\">Not an absolute path: "\r
140       . htmlspecialchars ($dir)\r
141       . "</p>\n");\r
142     return (false);\r
143   }\r
144 \r
145   if (!is_dir ($dir))\r
146   {\r
147     print ("<p class=\"error\">Not a directory: "\r
148       . htmlspecialchars ($dir)\r
149       . "</p>\n");\r
150     return (false);\r
151   }\r
152 \r
153   /* Open the directory only to check its permissions. */\r
154   $dh = opendir ($dir);\r
155   if (!$dh)\r
156   {\r
157     print ("<p class=\"error\">Unable to open directory.</p>\n");\r
158     return (false);\r
159   }\r
160   closedir ($dh);\r
161 \r
162   yourls_update_option ('gitweb_base_directory', $dir);\r
163   return (true);\r
164 } /* }}} function gitweb_set_base_directory */\r
165 \r
166 function gitweb_set_base_url ($url) /* {{{ */\r
167 {\r
168   if (!preg_match ('/https?:\/\//i', $url))\r
169   {\r
170     print ("<p class=\"error\">This does not look like a valid URL: "\r
171       . htmlspecialchars ($url)\r
172       . "</p>\n");\r
173     return (false);\r
174   }\r
175 \r
176   $url = preg_replace ('/\?.*/', '', $url);\r
177 \r
178   yourls_update_option ('gitweb_base_url', $url);\r
179   return (true);\r
180 } /* }}} function gitweb_set_base_directory */\r
181 \r
182 function gitweb_show_plugin_page () /* {{{ */\r
183 {\r
184   echo <<<HTML\r
185     <h2>Gitweb Plugin Administration Page</h2>\r
186     <p>This plugin redirects to a Gitweb installation if a keyword wasn't\r
187       found in the database, looks like a Git object ID and is found in a\r
188       local Git repository.</p>\r
189 HTML;\r
190 \r
191   if (isset ($_POST['base_directory']))\r
192     gitweb_set_base_directory ($_POST['base_directory']);\r
193 \r
194   if (isset ($_POST['base_url']))\r
195     gitweb_set_base_url ($_POST['base_url']);\r
196 \r
197   $base_directory = yourls_get_option ('gitweb_base_directory');\r
198   if ($base_directory)\r
199     $base_directory = htmlspecialchars ($base_directory);\r
200 \r
201   $base_url = yourls_get_option ('gitweb_base_url');\r
202   if ($base_url)\r
203     $base_url = htmlspecialchars ($base_url);\r
204 \r
205   echo <<<HTML\r
206     <form method="post">\r
207       <table style="background-color: #cdcdcd; border-spacing: 1px;">\r
208         <tr>\r
209           <th style="border: 1px solid white; background: #C7E7FF; padding: 4px;"><label for="base_directory">Base directory</label></th>\r
210           <td style="background-color: white; padding: 4px;"><input type="text" id="base_directory" name="base_directory" value="$base_directory" /></td>\r
211         </tr>\r
212         <tr>\r
213           <th style="border: 1px solid white; background: #C7E7FF; padding: 4px;"><label for="base_url">Gitweb URL</label></th>\r
214           <td style="background-color: white; padding: 4px;"><input type="text" id="base_url" name="base_url" value="$base_url" /></td>\r
215         </tr>\r
216         <tr>\r
217           <td colspan="2" style="border: 1px solid white; background-color: #E3F3FF; text-align: right; padding: 4px;"><input type="submit" value="Update" class="button primary" /></td>\r
218         </tr>\r
219       </table>\r
220     </form>\r
221 HTML;\r
222 } /* }}} function gitweb_show_plugin_page */\r
223 \r
224 function gitweb_register_plugin_page () /* {{{ */\r
225 {\r
226   yourls_register_plugin_page ('gitweb_page', 'Gitweb',\r
227     'gitweb_show_plugin_page');\r
228 } /* }}} function gitweb_register_plugin_page */\r
229 \r
230 yourls_add_action ('plugins_loaded', 'gitweb_register_plugin_page');\r
231 yourls_add_action ('redirect_keyword_not_found', 'gitweb_redirect_keyword_not_found');\r
232 \r
233 /* vim: set sw=2 sts=2 et fdm=marker : */\r
234 ?>\r