3 * FusionForge Git plugin
5 * Copyright 2009, Roland Mas
6 * Copyright 2009, Mehdi Dogguy <mehdi@debian.org>
7 * Copyright 2012-2014,2016, Franck Villaume - TrivialDev
9 * Thorsten Glaser <t.glaser@tarent.de>
10 * http://fusionforge.org
12 * This file is part of FusionForge.
14 * FusionForge is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published
16 * by the Free Software Foundation; either version 2 of the License,
17 * or (at your option) any later version.
19 * FusionForge is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * General Public License for more details.
24 * You should have received a copy of the GNU General Public License along
25 * with this program; if not, write to the Free Software Foundation, Inc.,
26 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29 require_once $gfcommon.'include/plugins_utils.php';
31 forge_define_config_item('default_server', 'scmgit', forge_get_config('scm_host'));
32 forge_define_config_item('repos_path', 'scmgit', forge_get_config('chroot').'/scmrepos/git');
33 forge_define_config_item('use_ssh', 'scmgit', false);
34 forge_set_config_item_bool('use_ssh', 'scmgit');
35 forge_define_config_item('use_dav', 'scmgit', true);
36 forge_set_config_item_bool('use_dav', 'scmgit');
37 forge_define_config_item('use_ssl', 'scmgit', true);
38 forge_set_config_item_bool('use_ssl', 'scmgit');
40 class GitPlugin extends SCMPlugin {
41 function __construct() {
43 $this->name = 'scmgit';
44 $this->text = _('Git');
46 _("This plugin contains the Git subsystem of FusionForge. It allows each
47 FusionForge project to have its own Git repository, and gives some
48 control over it to the project's administrator.");
49 $this->_addHook('scm_browser_page');
50 $this->_addHook('scm_update_repolist');
51 $this->_addHook('scm_generate_snapshots');
52 $this->_addHook('scm_gather_stats');
53 $this->_addHook('scm_admin_form');
54 $this->_addHook('scm_add_repo');
55 $this->_addHook('scm_delete_repo');
56 $this->_addHook('widget_instance', 'myPageBox', false);
57 $this->_addHook('widgets', 'widgets', false);
58 $this->_addHook('activity');
59 $this->_addHook('weekly');
63 function getDefaultServer() {
64 return forge_get_config('default_server', 'scmgit');
67 function printShortStats($params) {
68 $project = $this->checkParams($params);
73 if ($project->usesPlugin($this->name) && forge_check_perm('scm', $project->getID(), 'read')) {
74 $result = db_query_params('SELECT sum(updates) AS updates, sum(adds) AS adds FROM stats_cvs_group WHERE group_id=$1',
75 array($project->getID()));
76 $commit_num = db_result($result,0,'updates');
77 $add_num = db_result($result, 0, 'adds');
84 $params['result'] .= ' (Git: '.sprintf(_('<strong>%1$s</strong> updates, <strong>%2$s</strong> adds'), number_format($commit_num, 0), number_format($add_num, 0)).')';
89 return html_e('p', array(), sprintf(_('Documentation for %1$s is available at <a href="%2$s">%2$s</a>.'),
91 'http://git-scm.com/'));
94 function getInstructionsForAnon($project) {
95 $repo_list = array($project->getUnixName());
96 $protocol = forge_get_config('use_ssl', 'scmgit')? 'https' : 'http';
97 $result = db_query_params('SELECT repo_name FROM scm_secondary_repos WHERE group_id=$1 AND next_action = $2 AND plugin_id=$3 ORDER BY repo_name',
98 array($project->getID(),
99 SCM_EXTRA_REPO_ACTION_UPDATE,
101 $rows = db_numrows($result);
102 for ($i=0; $i<$rows; $i++) {
103 $repo_list[] = db_result($result,$i,'repo_name');
105 $clone_commands = array();
106 foreach ($repo_list as $repo_name) {
107 if (forge_get_config('use_smarthttp', 'scmgit')) {
108 $clone_commands[] = 'git clone '.$protocol.'://'.forge_get_config('scm_host').'/anonscm/git/'.$project->getUnixName().'/'.$repo_name.'.git';
112 $b = html_e('h2', array(), _('Anonymous Access'));
114 $b .= html_e('p', array(),
115 ngettext("This project's Git repository can be checked out through anonymous access with the following command.",
116 "This project's Git repositories can be checked out through anonymous access with the following commands.",
120 foreach ($clone_commands as $cmd) {
121 $htmlRepo .= html_e('tt', array(), $cmd).html_e('br');;
123 $b .= html_e('p', array(), $htmlRepo);
125 $result = db_query_params('SELECT u.user_id, u.user_name, u.realname FROM scm_personal_repos p, users u WHERE p.group_id=$1 AND u.user_id=p.user_id AND u.unix_status=$2 AND plugin_id=$3',
126 array($project->getID(),
129 $rows = db_numrows($result);
132 $b .= html_e('h3', array(),
133 ngettext("Member repository",
134 "Members repositories",
136 $b .= html_e('p', array(),
137 ngettext("One of this project's members also has a personal Git repository that can be checked out anonymously.",
138 "Some of this project's members also have personal Git repositories that can be checked out anonymously.",
141 for ($i=0; $i<$rows; $i++) {
142 $user_id = db_result($result, $i, 'user_id');
143 $user_name = db_result($result, $i, 'user_name');
144 $real_name = db_result($result, $i, 'realname');
145 $htmlRepo .= html_e('tt', array(), 'git clone '.$protocol.'://'.forge_get_config('scm_host').'/anonscm/git/'.$project->getUnixName().'/users/'.$user_name.'.git')
146 . ' ('.util_make_link_u($user_name, $user_id, $real_name).')'
149 $b .= html_e('p', array(), $htmlRepo);
155 function getInstructionsForRW($project) {
157 $repo_list = array($project->getUnixName());
159 $result = db_query_params('SELECT repo_name FROM scm_secondary_repos WHERE group_id=$1 AND next_action = $2 AND plugin_id=$3 ORDER BY repo_name',
160 array($project->getID(),
161 SCM_EXTRA_REPO_ACTION_UPDATE,
163 $rows = db_numrows($result);
164 for ($i=0; $i<$rows; $i++) {
165 $repo_list[] = db_result($result, $i, 'repo_name');
169 $b .= html_e('h2', array(), _('Developer Access'));
170 if (session_loggedin()) {
171 $u = user_get_object(user_getid());
172 $d = $u->getUnixName();
173 if (forge_get_config('use_ssh', 'scmgit')) {
174 $b .= html_e('h3', array(), _('via SSH'));
175 $b .= html_e('p', array(),
176 ngettext('Only project developers can access the Git repository via this method.',
177 'Only project developers can access the Git repositories via this method.',
179 ' '. _('SSH must be installed on your client machine.'));
181 foreach ($repo_list as $repo_name) {
182 $htmlRepo .= html_e('tt', array(), 'git clone git+ssh://'.$d.'@' . forge_get_config('scm_host') . forge_get_config('repos_path', 'scmgit') .'/'. $project->getUnixName() .'/'. $repo_name .'.git').html_e('br');
184 $b .= html_e('p', array(), $htmlRepo);
186 if (forge_get_config('use_smarthttp', 'scmgit')) {
187 $b .= html_e('h3', array(), _('via "smart HTTP"'));
188 $b .= html_e('p', array(),
189 ngettext('Only project developers can access the Git repository via this method.',
190 'Only project developers can access the Git repositories via this method.',
192 ' '. _('Enter your site password when prompted.'));
195 $protocol = forge_get_config('use_ssl', 'scmgit') ? 'https' : 'http';
196 foreach ($repo_list as $repo_name) {
197 $htmlRepo .= '<tt>git clone '.$protocol.'://'.$d.'@' . forge_get_config('scm_host').'/authscm/'.$d.'/git/'.$project->getUnixName() .'/'. $repo_name .'.git</tt><br />';
199 $b .= html_e('p', array(), $htmlRepo);
202 if (forge_get_config('use_ssh', 'scmgit')) {
203 $b .= html_e('h3', array(), _('via SSH'));
204 $b .= html_e('p', array(),
205 ngettext('Only project developers can access the Git repository via this method.',
206 'Only project developers can access the Git repositories via this method.',
208 ' '. _('SSH must be installed on your client machine.').
209 ' '. _('Substitute <em>developername</em> with the proper value.'));
211 foreach ($repo_list as $repo_name) {
212 $htmlRepo .= html_e('tt', array(), 'git clone git+ssh://'.html_e('i', array(), _('developername'), true, false).'@' . forge_get_config('scm_host') . forge_get_config('repos_path', 'scmgit') .'/'. $project->getUnixName() .'/'. $repo_name .'.git').html_e('br');
214 $b .= html_e('p', array(), $htmlRepo);
216 if (forge_get_config('use_smarthttp', 'scmgit')) {
217 $protocol = forge_get_config('use_ssl', 'scmgit')? 'https' : 'http';
218 $b .= html_e('h3', array(), _('via "smart HTTP"'));
219 $b .= html_e('p', array(),
220 ngettext('Only project developers can access the Git repository via this method.',
221 'Only project developers can access the Git repositories via this method.',
223 ' '. _('Enter your site password when prompted.'));
225 foreach ($repo_list as $repo_name) {
226 $b .= '<tt>git clone '.$protocol.'://<i>'._('developername').'</i>@' . forge_get_config('scm_host').'/authscm/<i>'._('developername').'</i>/git/'.$project->getUnixName() .'/'. $repo_name .'.git</tt><br />';
228 $b .= html_e('p', array(), $htmlRepo);
232 $b .= $HTML->error_msg(_('Error')._(': ')._('No access protocol has been allowed for the Git plugin in scmgit.ini: use_ssh and use_smarthttp are disabled'));
235 if (session_loggedin()) {
236 $u = user_get_object(user_getid());
237 if ($u->getUnixStatus() == 'A') {
238 $result = db_query_params('SELECT * FROM scm_personal_repos p WHERE p.group_id=$1 AND p.user_id=$2 AND plugin_id=$3',
239 array($project->getID(),
242 if ($result && db_numrows($result) > 0) {
243 $b .= html_e('h3', array(), _('Access to your personal repository'));
244 $b .= html_e('p', array(), _('You have a personal repository for this project, accessible through the following methods. Enter your site password when prompted.'));
245 if (forge_get_config('use_ssh', 'scmgit')) {
246 $b .= html_e('tt', array(), 'git clone git+ssh://'.$u->getUnixName().'@' . forge_get_config('scm_host') . forge_get_config('repos_path', 'scmgit') .'/'. $project->getUnixName() .'/users/'. $u->getUnixName() .'.git').html_e('br');
248 if (forge_get_config('use_smarthttp', 'scmgit')) {
249 $b .= html_e('tt', array(), 'git clone '.$protocol.'://'.$u->getUnixName().'@' . forge_get_config('scm_host').'/authscm/'.$u->getUnixName().'/git/'.$project->getUnixName() .'/users/'. $u->getUnixName() .'.git').html_e('br');
252 $glist = $u->getGroups();
253 foreach ($glist as $g) {
254 if ($g->getID() == $project->getID()) {
255 $b .= html_e('h3', array(), _('Request a personal repository'));
256 $b .= html_e('p', array(), _("You can clone the project repository into a personal one into which you alone will be able to write. Other members of the project will only have read access. Access for non-members will follow the same rules as for the project's main repository. Note that the personal repository may take some time before it is created (less than an hour in most situations)."));
257 $b .= html_e('p', array(), util_make_link('/plugins/scmgit/index.php?func=request-personal-repo&group_id='.$project->getID(), _('Request a personal repository')));
266 function getSnapshotPara($project) {
268 $filename = $project->getUnixName().'-scm-latest.tar'.util_get_compressed_file_extension();
269 if (file_exists(forge_get_config('scm_snapshots_path').'/'.$filename)) {
270 $b .= html_e('p', array(), '['.util_make_link('/snapshots.php?group_id='.$project->getID(), _('Download the nightly snapshot')).']');
275 function printBrowserPage($params) {
278 $project = $this->checkParams($params);
283 if ($this->browserDisplayable($project)) {
284 if ($params['user_id']) {
285 $repo_user = user_get_object($params['user_id']);
286 $repo = $project->getUnixName().'/users/'.$repo_user->getUnixName().'.git';
287 } else if ($params['extra']) {
288 $repo = $project->getUnixName().'/'.$params['extra'].'.git';
290 $repo = $project->getUnixName().'/'.$project->getUnixName().'.git';
293 if ($project->enableAnonSCM()) {
294 $protocol = forge_get_config('use_ssl', 'scmgit')? 'https' : 'http';
295 $box = forge_get_config('scm_host');
296 $iframesrc = "$protocol://$box/anonscm/gitweb/?p=$repo";
297 } elseif (session_loggedin()) {
298 $logged_user = user_get_object(user_getid())->getUnixName();
299 $protocol = forge_get_config('use_ssl', 'scmgit')? 'https' : 'http';
300 $box = forge_get_config('scm_host');
301 $iframesrc = "$protocol://$box/authscm/$logged_user/gitweb/?p=$repo";
303 if ($params['commit']) {
304 $iframesrc .= ';a=log;h='.$params['commit'];
306 htmlIframeResizer($iframesrc, array('id'=>'scmgit_iframe', 'absolute'=>true));
310 function getBrowserLinkBlock($project) {
311 $b = html_e('h2', array(), _('Git Repository Browser'));
312 $b .= html_e('p', array(), _("Browsing the Git tree gives you a view into the current status"
313 . " of this project's code. You may also view the complete"
314 . " history of any file in the repository."));
315 $b .= html_e('p', array(), '['.util_make_link('/scm/browser.php?group_id='.$project->getID(),
316 _('Browse main git repository')).']');
319 $result = db_query_params('SELECT repo_name FROM scm_secondary_repos WHERE group_id=$1 AND next_action = $2 AND plugin_id=$3 ORDER BY repo_name',
320 array($project->getID(),
321 SCM_EXTRA_REPO_ACTION_UPDATE,
323 $rows = db_numrows($result);
324 $repo_list = array();
325 for ($i=0; $i<$rows; $i++) {
326 $repo_list[] = db_result($result,$i,'repo_name');
328 foreach ($repo_list as $repo_name) {
329 if (forge_get_config('use_smarthttp', 'scmgit')) {
330 $protocol = forge_get_config('use_ssl', 'scmgit')? 'https' : 'http';
331 $b .= '['.util_make_link('/scm/browser.php?group_id='.$project->getID().'&extra='.$repo_name, _('Browse extra git repository')._(': ').$repo_name).']'.html_e('br');
335 $result = db_query_params('SELECT u.user_id, u.user_name FROM scm_personal_repos p, users u WHERE p.group_id=$1 AND u.user_id=p.user_id AND u.unix_status=$2 AND plugin_id=$3',
336 array($project->getID(),
339 $rows = db_numrows($result);
340 for ($i=0; $i<$rows; $i++) {
341 $user_id = db_result($result,$i,'user_id');
342 $user_name = db_result($result,$i,'user_name');
343 $b .= '['.util_make_link('/scm/browser.php?group_id='.$project->getID().'&user_id='.$user_id, _('Browse personal git repository')._(': ').$user_name).']'.html_e('br');
349 function getStatsBlock($project) {
353 $result = db_query_params('SELECT u.realname, u.user_name, u.user_id, sum(updates) as updates, sum(adds) as adds, sum(adds+updates) as combined FROM stats_cvs_user s, users u WHERE group_id=$1 AND s.user_id=u.user_id AND (updates>0 OR adds >0) GROUP BY u.user_id, realname, user_name, u.user_id ORDER BY combined DESC, realname',
354 array($project->getID()));
356 if (db_numrows($result) > 0) {
357 $tableHeaders = array(
362 $b .= $HTML->listTableTop($tableHeaders, false, '', 'repo-history');
365 $total = array('adds' => 0, 'updates' => 0);
367 while ($data = db_fetch_array($result)) {
369 $cells[] = array(util_make_link_u($data['user_name'], $data['user_id'], $data['realname']), 'class' => 'halfwidth');
370 $cells[] = array($data['adds'], 'class' => 'onequarterwidth align-right');
371 $cells[] = array($data['updates'], 'class' => 'onequarterwidth align-right');
372 $b .= $HTML->multiTableRow(array('class' => $HTML->boxGetAltRowStyle($i, true)), $cells);
373 $total['adds'] += $data['adds'];
374 $total['updates'] += $data['updates'];
378 $cells[] = array(html_e('strong', array(), _('Total')._(':')), 'class' => 'halfwidth');
379 $cells[] = array($total['adds'], 'class' => 'onequarterwidth align-right');
380 $cells[] = array($total['updates'], 'class' => 'onequarterwidth align-right');
381 $b .= $HTML->multiTableRow(array('class' => $HTML->boxGetAltRowStyle($i, true)), $cells);
382 $b .= $HTML->listTableBottom();
384 $b .= $HTML->information(_('No history yet'));
391 * Create user repository under non-privileged uid
393 static function createUserRepo($params) {
394 $project = $params['project'];
395 $project_name = $project->getUnixName();
396 $user_name = $params['user_name'];
397 $main_repo = $params['main_repo'];
398 $root = $params['root'];
400 // dir was already created by root
401 $repodir = $root . '/users/' . $user_name . '.git';
402 chmod($repodir, 00755);
403 if (!is_file("$repodir/HEAD") && !is_dir("$repodir/objects") && !is_dir("$repodir/refs")) {
404 // 'cd $root' because git will abort if e.g. we're in a 0700 /root after setuid
405 system("cd $root; LC_ALL=C git clone --bare --quiet --no-hardlinks $main_repo $repodir 2>&1 >/dev/null | grep -v 'warning: You appear to have cloned an empty repository.' >&2");
406 system("GIT_DIR=\"$repodir\" git update-server-info");
407 system("GIT_DIR=\"$repodir\" git config http.receivepack true");
408 if (is_file("$repodir/hooks/post-update.sample")) {
409 rename("$repodir/hooks/post-update.sample",
410 "$repodir/hooks/post-update");
412 if (!is_file("$repodir/hooks/post-update")) {
413 $f = fopen("$repodir/hooks/post-update","x+");
414 fwrite($f, "exec git-update-server-info\n");
417 if (is_file("$repodir/hooks/post-update")) {
418 system("chmod +x $repodir/hooks/post-update");
420 system("echo \"Git repository for user $user_name in project $project_name\" > $repodir/description");
421 system("chmod -R go=rX $repodir");
425 function createOrUpdateRepo($params) {
428 $project = $this->checkParams($params);
429 if (!$project) return false;
430 if (!$project->isActive()) return false;
431 if (!$project->usesPlugin($this->name)) return false;
433 $project_name = $project->getUnixName();
434 $unix_group_ro = $project_name . '_scmro';
435 $unix_group_rw = $project_name . '_scmrw';
437 $root = forge_get_config('repos_path', 'scmgit') . '/' . $project_name;
438 if (!is_dir($root)) {
439 system("mkdir -p $root");
440 system("chgrp $unix_group_ro $root");
442 if ($project->enableAnonSCM()) {
443 system("chmod 2755 $root");
445 system("chmod 2750 $root");
448 // Create main repository
449 $main_repo = $root . '/' . $project_name . '.git';
450 if (!is_dir($main_repo) || (!is_file("$main_repo/HEAD") &&
451 !is_dir("$main_repo/objects") && !is_dir("$main_repo/refs"))) {
452 $tmp_repo = util_mkdtemp('.git', $project_name);
453 if ($tmp_repo == false) {
457 exec("GIT_DIR=\"$tmp_repo\" git init --bare --shared=group", $result);
458 $output .= join("<br />", $result);
460 exec("GIT_DIR=\"$tmp_repo\" git update-server-info", $result);
461 exec("GIT_DIR=\"$tmp_repo\" git config http.receivepack true", $result);
462 $output .= join("<br />", $result);
463 if (is_file("$tmp_repo/hooks/post-update.sample")) {
464 rename("$tmp_repo/hooks/post-update.sample",
465 "$tmp_repo/hooks/post-update");
467 if (!is_file("$tmp_repo/hooks/post-update")) {
468 $f = fopen("$tmp_repo/hooks/post-update", 'w');
469 fwrite($f, "exec git-update-server-info\n");
472 if (is_file("$tmp_repo/hooks/post-update")) {
473 system("chmod +x $tmp_repo/hooks/post-update");
475 system("echo \"Git repository for $project_name\" > $tmp_repo/description");
476 system("find $tmp_repo -type d | xargs chmod g+s");
477 system("chgrp -R $unix_group_rw $tmp_repo");
478 system("chmod -R g=rwX,o=rX $tmp_repo");
481 * $main_repo can already exist, for example if it’s
482 * not a directory or doesn’t contain a HEAD file or
483 * an objects or refs subdirectory… move it out of
484 * the way in these cases
486 system("if test -e $main_repo || test -h $main_repo; then d=\$(mktemp -d $main_repo.scmgit-moved.XXXXXXXXXX) && mv -f $main_repo \$d/; fi");
487 /* here’s still a TOCTOU but we check $ret below */
488 system("mv $tmp_repo $main_repo", $ret);
492 system("echo \"Git repository for $project_name\" > $main_repo/description");
495 // Create project-wide secondary repositories
496 $result = db_query_params('SELECT repo_name, description, clone_url FROM scm_secondary_repos WHERE group_id=$1 AND next_action = $2 AND plugin_id=$3',
497 array($project->getID(),
498 SCM_EXTRA_REPO_ACTION_UPDATE,
500 $rows = db_numrows($result);
501 for ($i=0; $i<$rows; $i++) {
502 $repo_name = db_result($result, $i, 'repo_name');
503 $description = db_result($result, $i, 'description');
504 $clone_url = db_result($result, $i, 'clone_url');
505 // Clone URLs need to be validated to prevent a potential arbitrary command execution
506 if (!preg_match('|^[-a-zA-Z0-9:./_]+$|', $clone_url)) {
509 $repodir = $root . '/' . $repo_name . '.git';
510 if (!is_file("$repodir/HEAD") && !is_dir("$repodir/objects") && !is_dir("$repodir/refs")) {
511 if ($clone_url != '') {
512 system("cd $root; LC_ALL=C git clone --quiet --bare $clone_url $repodir 2>&1 >/dev/null | grep -v 'warning: You appear to have cloned an empty repository.' >&2");
514 system("GIT_DIR=\"$repodir\" git init --quiet --bare --shared=group");
516 system("GIT_DIR=\"$repodir\" git update-server-info");
517 system("GIT_DIR=\"$repodir\" git config http.receivepack true");
518 if (is_file("$repodir/hooks/post-update.sample")) {
519 rename("$repodir/hooks/post-update.sample",
520 "$repodir/hooks/post-update");
522 if (!is_file("$repodir/hooks/post-update")) {
523 $f = fopen("$repodir/hooks/post-update", 'w');
524 fwrite($f, "exec git-update-server-info\n");
527 if (is_file("$repodir/hooks/post-update")) {
528 system("chmod +x $repodir/hooks/post-update");
530 $f = fopen("$repodir/description", "w");
531 fwrite($f, $description."\n");
533 system("chgrp -R $unix_group_rw $repodir");
534 system("chmod -R g=rwX,o=rX $repodir");
538 // Delete project-wide secondary repositories
539 $result = db_query_params ('SELECT repo_name FROM scm_secondary_repos WHERE group_id=$1 AND next_action = $2 AND plugin_id=$3',
540 array($project->getID(),
541 SCM_EXTRA_REPO_ACTION_DELETE,
543 $rows = db_numrows ($result);
544 for ($i=0; $i<$rows; $i++) {
545 $repo_name = db_result($result, $i, 'repo_name');
546 $repodir = $root . '/' . $repo_name . '.git';
547 if (util_is_valid_repository_name($repo_name)) {
548 system("rm -rf $repodir");
550 db_query_params ('DELETE FROM scm_secondary_repos WHERE group_id=$1 AND repo_name=$2 AND next_action = $3 AND plugin_id=$4',
551 array($project->getID(),
553 SCM_EXTRA_REPO_ACTION_DELETE,
557 // Create users' personal repositories
558 $result = db_query_params ('SELECT u.user_name FROM scm_personal_repos p, users u WHERE p.group_id=$1 AND u.user_id=p.user_id AND u.unix_status=$2 AND plugin_id=$3',
559 array($project->getID(),
562 $rows = db_numrows ($result);
563 if (!is_dir($root.'/users')) {
564 system("mkdir -p $root/users");
565 chgrp("$root/users", 'root'); // make it clear group members don't have write access
566 system("chmod 00755 $root/users");
568 for ($i=0; $i<$rows; $i++) {
569 $user_name = db_result($result, $i, 'user_name');
570 $repodir = $root . '/users/' . $user_name . '.git';
572 if (!is_dir($repodir) && mkdir ($repodir, 0700)) {
573 chown ($repodir, $user_name);
574 chgrp ($repodir, 'root'); // make it clear group members don't have write access
577 $params['project'] = $project;
578 $params['user_name'] = $user_name;
579 $params['root'] = $root;
580 $params['main_repo'] = $main_repo;
582 util_sudo_effective_user($user_name,
583 array("GitPlugin", "createUserRepo"),
587 $params['output'] = $output;
590 function updateRepositoryList($params) {
591 $config_dir = forge_get_config('config_path').'/plugins/scmgit';
592 if (!is_dir($config_dir)) {
593 mkdir($config_dir, 0755, true);
595 $fname = $config_dir . '/gitweb.conf';
596 $f = fopen($fname.'.new', 'w');
597 $rootdir = forge_get_config('repos_path', 'scmgit');
598 fwrite($f, "\$projectroot = '$rootdir';\n");
599 fwrite($f, "\$projects_list = '$config_dir/gitweb.list';\n");
600 fwrite($f, "\$anon_clone_url = '". util_make_url('/anonscm/git') . "';\n");
601 fwrite($f, "\$logo = '". util_make_url('/plugins/scmgit/git-logo.png') . "';\n");
602 fwrite($f, "\$favicon = '". util_make_url('/plugins/scmgit/git-favicon.png')."';\n");
603 fwrite($f, "\$stylesheet = '". util_make_url('/plugins/scmgit/gitweb.css')."';\n");
604 fwrite($f, "\$javascript = '". util_make_url('/plugins/scmgit/gitweb.js')."';\n");
605 fwrite($f, "\$site_html_head_string = '<script type=\"text/javascript\" src=\"". util_make_url('/scripts/iframe-resizer/iframeResizer.contentWindow.min.js'). "\" />';\n");
606 fwrite($f, "\$prevent_xss = 'true';\n");
607 fwrite($f, "\$site_footer = '".forge_get_config('source_path')."/plugins/scmgit/www/gitweb_footer.html';\n");
608 fwrite($f, "\$feature{'actions'}{'default'} = [('project home', '" .
609 util_make_url('/plugins/scmgit/?func=grouppage/%n') .
610 "', 'summary')];\n");
612 fwrite($f, "\$per_request_config = sub {\n");
614 fwrite($f, "push @git_base_url_list, qq,". util_make_url('/anonscm/git') .",;\n");
616 $protocol = forge_get_config('use_ssl', 'scmgit')? 'https' : 'http';
617 if (forge_get_config('use_smarthttp', 'scmgit')) {
618 fwrite($f, "if (defined \$ENV{ITKUID} && \$ENV{ITKUID} ne '".forge_get_config('apache_user')."') { push @git_base_url_list, qq,$protocol://\$ENV{ITKUID}\@".forge_get_config('scm_host')."/authscm/\$ENV{ITKUID}/git,; }\n");
621 if (forge_get_config('use_ssh', 'scmgit')) {
622 fwrite($f, "if (defined \$ENV{ITKUID} && \$ENV{ITKUID} ne '".forge_get_config('apache_user')."') { push @git_base_url_list, qq,git+ssh://\$ENV{ITKUID}\@".forge_get_config('scm_host').forge_get_config('repos_path', 'scmgit').",; }\n");
628 chmod($fname.'.new', 0644);
629 rename($fname.'.new', $fname);
631 # Optimized gitweb.list generation
632 # Useful to list all a project's repos: /gitweb?a=project_list;pf=project_name
633 $fname = $config_dir . '/gitweb.list';
634 $f = fopen($fname.'.new', 'w');
635 $res = db_query_params("SELECT unix_group_name FROM groups
636 JOIN group_plugin ON (groups.group_id=group_plugin.group_id)
637 WHERE groups.status=$1 AND group_plugin.plugin_id=$2
638 ORDER BY unix_group_name", array('A', $this->getID()));
639 while ($arr = db_fetch_array($res)) {
640 fwrite($f, $arr['unix_group_name'].'/'.$arr['unix_group_name'].".git\n");
642 $res = db_query_params("SELECT unix_group_name, repo_name
644 JOIN group_plugin ON (groups.group_id=group_plugin.group_id)
645 JOIN scm_secondary_repos ON (groups.group_id=scm_secondary_repos.group_id)
646 WHERE groups.status=$1 AND group_plugin.plugin_id=$2
647 ORDER BY unix_group_name, repo_name", array('A', $this->getID()));
648 while ($arr = db_fetch_array($res)) {
649 fwrite($f, $arr['unix_group_name'].'/'.$arr['repo_name'].".git\n");
651 $res = db_query_params("SELECT unix_group_name, user_name
653 JOIN group_plugin ON (groups.group_id=group_plugin.group_id)
654 JOIN scm_personal_repos ON (groups.group_id=scm_personal_repos.group_id)
655 JOIN users ON (scm_personal_repos.user_id=users.user_id)
656 WHERE groups.status=$1 AND group_plugin.plugin_id=$2 AND users.status=$3
657 ORDER BY unix_group_name, user_name", array('A', $this->getID(), 'A'));
658 while ($arr = db_fetch_array($res)) {
659 fwrite($f, $arr['unix_group_name'].'/users/'.$arr['user_name'].".git\n");
662 chmod($fname.'.new', 0644);
663 rename($fname.'.new', $fname);
666 function gatherStats($params) {
667 $project = $this->checkParams($params);
672 if (!$project->usesPlugin($this->name)) {
676 if ($params['mode'] == 'day') {
677 $year = $params['year'];
678 $month = $params['month'];
679 $day = $params['day'];
680 $month_string = sprintf("%04d%02d", $year, $month);
681 $start_time = gmmktime(0, 0, 0, $month, $day, $year);
682 $end_time = $start_time + 86400;
685 $usr_updates = array();
686 $usr_deletes = array();
687 $usr_commits = array();
694 $repo = forge_get_config('repos_path', 'scmgit') . '/' . $project->getUnixName() . '/' . $project->getUnixName() . '.git';
695 if (!is_dir($repo) || !is_dir("$repo/refs")) {
696 // echo "No repository $repo\n";
700 # For each commit, get committer full name and e-mail (respecting git .mailmap file),
701 # and a list of files prefixed by their status (A/M/D)
702 $pipe = popen("GIT_DIR=\"$repo\" git log --since=@$start_time --until=@$end_time --all --pretty='format:%n%aN <%aE>' --name-status 2>/dev/null", 'r' );
706 // cleaning stats_cvs_* table for the current day
707 $res = db_query_params('DELETE FROM stats_cvs_group WHERE month=$1 AND day=$2 AND group_id=$3',
712 echo "Error while cleaning stats_cvs_group\n";
717 $res = db_query_params ('DELETE FROM stats_cvs_user WHERE month=$1 AND day=$2 AND group_id=$3',
718 array ($month_string,
720 $project->getID())) ;
722 echo "Error while cleaning stats_cvs_user\n" ;
728 while (!feof($pipe) && $data = fgets($pipe)) {
730 // Replace bad UTF-8 with '?' - it's quite hard to make git output non-UTF-8
731 // (e.g. with i18n.commitEncoding = unknown) - but some users do!
732 // and this makes PostgreSQL choke (SQL> ERROR: invalid byte sequence for encoding "UTF8": 0xf9)
733 $line = mb_convert_encoding($line, 'UTF-8', 'UTF-8');
734 if (strlen($line) > 0) {
735 $result = preg_match("/^(?P<name>.+) <(?P<mail>.+)>/", $line, $matches);
738 $last_user = $matches['name'];
739 $user2email[$last_user] = $matches['mail'];
740 if (!isset($usr_adds[$last_user])) {
741 $usr_adds[$last_user] = 0;
742 $usr_updates[$last_user] = 0;
743 $usr_deletes[$last_user] = 0;
744 $usr_commits[$last_user] = 0;
747 $usr_commits[$last_user]++;
749 // Short-commit stats line
750 $result = preg_match("/^(?P<mode>[AMD])\s+(?P<file>.+)$/", $line, $matches);
753 if ($last_user == "")
755 if (!isset($usr_adds[$last_user]))
756 $usr_adds[$last_user] = 0;
757 if (!isset($usr_updates[$last_user]))
758 $usr_updates[$last_user] = 0;
759 if (!isset($usr_deletes[$last_user]))
760 $usr_deletes[$last_user] = 0;
761 if ($matches['mode'] == 'A') {
762 $usr_adds[$last_user]++;
764 } elseif ($matches['mode'] == 'M') {
765 $usr_updates[$last_user]++;
767 } elseif ($matches['mode'] == 'D') {
768 $usr_deletes[$last_user]++;
775 // inserting group results in stats_cvs_groups
776 if ($updates > 0 || $adds > 0 || $deletes > 0 || $commits > 0) {
777 if (!db_query_params('INSERT INTO stats_cvs_group (month,day,group_id,checkouts,commits,adds,updates,deletes) VALUES ($1,$2,$3,$4,$5,$6,$7,$8)',
786 echo "Error while inserting into stats_cvs_group\n";
792 // building the user list
793 $user_list = array_unique(array_merge(array_keys($usr_adds), array_keys($usr_updates), array_keys($usr_deletes), array_keys($usr_commits)));
795 foreach ($user_list as $user) {
796 // Trying to get user id from user name or email
797 $u = user_get_object_by_name($user);
799 $user_id = $u->getID();
801 $res = db_query_params('SELECT user_id FROM users WHERE lower(realname)=$1 OR lower(email)=$2',
802 array(strtolower($user), strtolower($user2email[$user])));
803 if ($res && db_numrows($res) > 0) {
804 $user_id = db_result($res, 0, 'user_id');
810 $uc = isset($usr_commits[$user]) ? $usr_commits[$user] : 0;
811 $uu = isset($usr_updates[$user]) ? $usr_updates[$user] : 0;
812 $ua = isset($usr_adds[$user]) ? $usr_adds[$user] : 0;
813 $ud = isset($usr_deletes[$user]) ? $usr_deletes[$user] : 0;
814 if ($uu > 0 || $ua > 0 || $uc > 0 || $ud > 0) {
815 if (!db_query_params('INSERT INTO stats_cvs_user (month,day,group_id,user_id,commits,adds,updates,deletes) VALUES ($1,$2,$3,$4,$5,$6,$7,$8)',
824 echo "Error while inserting into stats_cvs_user\n";
834 function generateSnapshots($params) {
835 $us = forge_get_config('use_scm_snapshots') ;
836 $ut = forge_get_config('use_scm_tarballs') ;
841 $project = $this->checkParams($params);
846 $group_name = $project->getUnixName();
848 $snapshot = forge_get_config('scm_snapshots_path').'/'.$group_name.'-scm-latest.tar'.util_get_compressed_file_extension();
849 $tarball = forge_get_config('scm_tarballs_path').'/'.$group_name.'-scmroot.tar'.util_get_compressed_file_extension();
851 if (!$project->usesPlugin($this->name)) {
855 if (!$project->enableAnonSCM()) {
856 if (is_file($snapshot)) {
859 if (is_file($tarball)) {
865 // TODO: ideally we generate one snapshot per git repository
866 $toprepo = forge_get_config('repos_path', 'scmgit');
867 $repo = $toprepo . '/' . $project->getUnixName() . '/' . $project->getUnixName() . '.git';
869 if (!is_dir($repo)) {
870 if (is_file($snapshot)) {
873 if (is_file($tarball)) {
879 // Skip empty repo (no HEAD present in repository)
880 $ref = trim(`GIT_DIR=$repo git symbolic-ref HEAD`);
881 if (!file_exists($repo.'/'.$ref)) {
885 $tmp = trim(`mktemp -d`);
890 $today = date('Y-m-d');
891 system("GIT_DIR=\"$repo\" git archive --format=tar --prefix=$group_name-scm-$today/ HEAD |".forge_get_config('compression_method')." > $tmp/snapshot");
892 chmod("$tmp/snapshot", 0644);
893 copy("$tmp/snapshot", $snapshot);
894 unlink("$tmp/snapshot");
897 system("tar cCf $toprepo - ".$project->getUnixName() ."|".forge_get_config('compression_method')."> $tmp/tarball");
898 chmod("$tmp/tarball", 0644);
899 copy("$tmp/tarball", $tarball);
900 unlink("$tmp/tarball");
901 system("rm -rf $tmp");
906 * widgets - 'widgets' hook handler
908 * @param array $params
911 function widgets($params) {
912 require_once 'common/widget/WidgetLayoutManager.class.php';
913 if ($params['owner_type'] == WidgetLayoutManager::OWNER_TYPE_GROUP) {
914 $params['fusionforge_widgets'][] = 'plugin_scmgit_project_latestcommits';
916 if ($params['owner_type'] == WidgetLayoutManager::OWNER_TYPE_USER) {
917 $params['fusionforge_widgets'][] = 'plugin_scmgit_user_myrepositories';
923 * Process the 'widget_instance' hook to create instances of the widgets
925 * @param array $params
927 function myPageBox($params) {
929 $user = UserManager::instance()->getCurrentUser();
930 require_once 'common/widget/WidgetLayoutManager.class.php';
931 if ($params['widget'] == 'plugin_scmgit_user_myrepositories') {
932 require_once $gfplugins.$this->name.'/common/scmgit_Widget_MyRepositories.class.php';
933 $params['instance'] = new scmgit_Widget_MyRepositories(WidgetLayoutManager::OWNER_TYPE_USER, $user->getId());
937 function weekly(&$params) {
938 $res = db_query_params('SELECT group_id FROM groups WHERE status=$1 AND use_scm=1 ORDER BY group_id DESC',
941 $params['output'] .= 'ScmGit Plugin: Unable to get list of projects using SCM: '.db_error();
945 $params['output'] .= 'ScmGit Plugin: Running "git gc --quiet" on '.db_numrows($res).' repositories.'."\n";
946 while ($row = db_fetch_array($res)) {
947 $project = group_get_object($row['group_id']);
948 if (!$project || !is_object($project)) {
950 } elseif ($project->isError()) {
953 if (!$project->usesPlugin($this->name)) {
957 $project_name = $project->getUnixName();
958 $repo = forge_get_config('repos_path', 'scmgit') . '/' . $project_name . '/' . $project_name . '.git';
961 $params['output'] .= $project_name.': '.`git gc --quiet 2>&1`;
966 function activity($params) {
967 $group_id = $params['group'];
968 $project = group_get_object($group_id);
969 if (!$project->usesPlugin($this->name)) {
972 if (in_array('scmgit', $params['show']) || (count($params['show']) < 1)) {
973 $start_time = $params['begin'];
974 $end_time = $params['end'];
977 $protocol = forge_get_config('use_ssl', 'scmgit') ? 'https://' : 'http://';
978 $u = session_get_user();
979 if ($project->enableAnonSCM())
980 $server_script = '/anonscm/gitlog';
982 $server_script = '/authscm/'.$u->getUnixName().'/gitlog';
983 $script_url = $protocol . forge_get_config('scm_host')
985 .'?unix_group_name='.$project->getUnixName()
987 .'&begin='.$params['begin']
988 .'&end='.$params['end'];
989 $filename = tempnam('/tmp', 'gitlog');
990 $f = fopen($filename, 'w');
992 curl_setopt($ch, CURLOPT_URL, $script_url);
993 curl_setopt($ch, CURLOPT_FILE, $f);
994 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
995 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
996 curl_setopt($ch, CURLOPT_COOKIE, @$_SERVER['HTTP_COOKIE']); // for session validation
997 curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // for session validation
998 curl_setopt($ch, CURLOPT_HTTPHEADER,
999 array('X-Forwarded-For: '.$_SERVER['REMOTE_ADDR'])); // for session validation
1000 $body = curl_exec($ch);
1001 if ($body === false) {
1002 $this->setError(curl_error($ch));
1005 fclose($f); // flush buffer
1006 $f = fopen($filename, 'r');
1009 while (!feof($f) && $data = fgets($f)) {
1010 $line = trim($data);
1011 $splitedLine = explode('||', $line);
1012 if (sizeof($splitedLine) == 4) {
1014 $result['section'] = 'scm';
1015 $result['group_id'] = $group_id;
1016 $result['ref_id'] = 'browser.php?group_id='.$group_id.'&commit='.$splitedLine[3];
1017 $result['description'] = htmlspecialchars($splitedLine[2]).' (commit '.$splitedLine[3].')';
1018 $userObject = user_get_object_by_email($splitedLine[1]);
1019 if (is_a($userObject, 'FFUser')) {
1020 $result['realname'] = util_display_user($userObject->getUnixName(), $userObject->getID(), $userObject->getRealName());
1022 $result['realname'] = '';
1024 $splitedDate = explode(' ', $splitedLine[0]);
1025 $result['activity_date'] = $splitedDate[0];
1026 $result['subref_id'] = '';
1027 $params['results'][] = $result;
1031 $params['ids'][] = $this->name;
1032 $params['texts'][] = _('Git Commits');
1036 function scm_add_repo(&$params) {
1037 $project = $this->checkParams($params);
1041 if (!$project->usesPlugin($this->name)) {
1045 if (!isset($params['repo_name'])) {
1049 if ($params['repo_name'] == $project->getUnixName()) {
1050 $params['error_msg'] = _('Cannot create a secondary repository with the same name as the primary');
1054 if (!util_is_valid_repository_name($params['repo_name'])) {
1055 $params['error_msg'] = _('This repository name is not valid');
1059 $result = db_query_params('SELECT count(*) AS count FROM scm_secondary_repos WHERE group_id=$1 AND repo_name = $2 AND plugin_id=$3',
1060 array($params['group_id'],
1061 $params['repo_name'],
1064 $params['error_msg'] = db_error();
1067 if (db_result($result, 0, 'count')) {
1068 $params['error_msg'] = sprintf(_('A repository %s already exists'), $params['repo_name']);
1074 if (isset($params['clone'])) {
1075 $url = $params['clone'];
1079 } elseif ((preg_match('|^git://|', $url) || preg_match('|^https?://|', $url))
1080 && preg_match('|^[-a-zA-Z0-9:./_]+$|', $url)) {
1081 // External URLs: OK, but they need to be validated to prevent a potential arbitrary command execution
1083 } elseif ($url == $project->getUnixName()) {
1085 } elseif (($result = db_query_params('SELECT count(*) AS count FROM scm_secondary_repos WHERE group_id=$1 AND repo_name = $2 AND plugin_id=$3',
1086 array($project->getID(),
1089 && db_result($result, 0, 'count')) {
1090 // Local repo: try to clone from an existing repo in same project
1094 $params['error_msg'] = _('Invalid URL from which to clone');
1099 if (isset($params['description'])) {
1100 $description = $params['description'];
1102 if ($clone && !$description) {
1103 $description = sprintf(_('Clone of %s'), $params['clone']);
1105 if (!$description) {
1106 $description = "Git repository $params[repo_name] for project ".$project->getUnixName();
1109 $result = db_query_params('INSERT INTO scm_secondary_repos (group_id, repo_name, description, clone_url, plugin_id) VALUES ($1, $2, $3, $4, $5)',
1110 array($params['group_id'],
1111 $params['repo_name'],
1116 $params['error_msg'] = db_error();
1120 plugin_hook ("scm_admin_update", $params);
1124 function scm_admin_form(&$params) {
1126 $project = $this->checkParams($params);
1130 if (!$project->usesPlugin($this->name)) {
1134 session_require_perm('project_admin', $params['group_id']);
1136 $project_name = $project->getUnixName();
1138 $result = db_query_params('SELECT repo_name, description, clone_url FROM scm_secondary_repos WHERE group_id=$1 AND next_action = $2 AND plugin_id=$3 ORDER BY repo_name',
1139 array($params['group_id'],
1140 SCM_EXTRA_REPO_ACTION_UPDATE,
1143 $params['error_msg'] = db_error();
1146 $existing_repos = array();
1147 while($data = db_fetch_array($result)) {
1148 $existing_repos[] = array('repo_name' => $data['repo_name'],
1149 'description' => $data['description'],
1150 'clone_url' => $data['clone_url']);
1152 if (count($existing_repos) == 0) {
1153 echo $HTML->information(_('No extra Git repository for project').' '.$project_name);
1155 echo html_e('h2', array(), sprintf(ngettext('Extra Git repository for project %1$s',
1156 'Extra Git repositories for project %1$s',
1157 count($existing_repos)), $project_name));
1158 $titleArr = array(_('Repository name'), ('Initial repository description'), _('Initial clone URL (if any)'), _('Delete'));
1159 echo $HTML->listTableTop($titleArr);
1160 foreach ($existing_repos as $key => $repo) {
1162 $cells[][] = html_e('tt', array(), $repo['repo_name']);
1163 $cells[][] = $repo['description'];
1164 $cells[][] = $repo['clone_url'];
1165 $deleteForm = $HTML->openForm(array('name' => 'form_delete_repo_'.$repo['repo_name'], 'action' => getStringFromServer('PHP_SELF'), 'method' => 'post'));
1166 $deleteForm .= html_e('input', array('type' => 'hidden', 'name' => 'group_id', 'value' => $params['group_id']));
1167 $deleteForm .= html_e('input', array('type' => 'hidden', 'name' => 'delete_repository', 'value' => 1));
1168 $deleteForm .= html_e('input', array('type' => 'hidden', 'name' => 'repo_name', 'value' => $repo['repo_name']));
1169 $deleteForm .= html_e('input', array('type' => 'hidden', 'name' => 'scm_enable_anonymous', 'value' => ($project->enableAnonSCM()? 1 : 0)));
1170 $deleteForm .= html_e('input', array('type' => 'submit', 'name' => 'submit', 'value' => _('Delete')));
1171 $deleteForm .= $HTML->closeForm();
1172 $cells[][] = $deleteForm;
1173 echo $HTML->multiTableRow(array('class' => $HTML->boxGetAltRowStyle($key, true)), $cells);
1175 echo $HTML->listTableBottom();
1178 echo html_e('h2', array(), _('Create new Git repository for project').' '.$project_name);
1179 echo $HTML->openForm(array('name' => 'form_create_repo', 'action' => getStringFromServer('PHP_SELF'), 'method' => 'post'));
1180 echo html_e('input', array('type' => 'hidden', 'name' => 'group_id', 'value' => $params['group_id']));
1181 echo html_e('input', array('type' => 'hidden', 'name' => 'create_repository', 'value' => 1));
1182 echo html_e('p', array(), html_e('strong', array(), _('Repository name')._(':')).utils_requiredField().html_e('br').
1183 html_e('input', array('type' => 'text', 'required' => 'required', 'size' => 20, 'name' => 'repo_name', 'value' => '')));
1184 echo html_e('p', array(), html_e('strong', array(), _('Description')._(':')).html_e('br').
1185 html_e('input', array('type' => 'text', 'size' => 60, 'name' => 'description', 'value' => '')));
1186 echo html_e('p', array(), html_e('strong', array(), _('Initial clone URL (or name of an existing repository in this project; leave empty to start with an empty repository)')._(':')).html_e('br').
1187 html_e('input', array('type' => 'text', 'size' => 60, 'name' => 'clone', 'value' => $project_name)));
1188 echo html_e('input', array('type' => 'hidden', 'name' => 'scm_enable_anonymous', 'value' => ($project->enableAnonSCM()? 1 : 0)));
1189 echo html_e('input', array('type' => 'submit', 'name' => 'cancel', 'value' => _('Cancel')));
1190 echo html_e('input', array('type' => 'submit', 'name' => 'submit', 'value' => _('Submit')));
1191 echo $HTML->closeForm();
1194 function getCommits($project, $user = null, $nb_commits) {
1196 if ($project->usesPlugin($this->name) && forge_check_perm('scm', $project->getID(), 'read')) {
1197 // Grab&parse commit log
1198 $protocol = forge_get_config('use_ssl', 'scmgit') ? 'https://' : 'http://';
1199 $u = session_get_user();
1200 if ($project->enableAnonSCM())
1201 $server_script = '/anonscm/gitlog';
1203 $server_script = '/authscm/'.$u->getUnixName().'/gitlog';
1205 $email = $user->getEmail();
1206 $realname = $user->getFirstName().' '.$user->getLastName();
1207 $userunixname = $user->getUnixName();
1208 $params = '&mode=latest_user'
1209 .'&email='.urlencode($email)
1210 .'&realname='.urlencode($realname)
1211 .'&user_name='.$userunixname;
1213 $params = '&mode=latest';
1215 $script_url = $protocol . forge_get_config('scm_host')
1217 .'?unix_group_name='.$project->getUnixName()
1219 .'&limit='.$nb_commits;
1220 $filename = tempnam('/tmp', 'gitlog');
1221 $f = fopen($filename, 'w');
1223 curl_setopt($ch, CURLOPT_URL, $script_url);
1224 curl_setopt($ch, CURLOPT_FILE, $f);
1225 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
1226 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
1227 curl_setopt($ch, CURLOPT_COOKIE, $_SERVER['HTTP_COOKIE']); // for session validation
1228 curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // for session validation
1229 curl_setopt($ch, CURLOPT_HTTPHEADER,
1230 array('X-Forwarded-For: '.$_SERVER['REMOTE_ADDR'])); // for session validation
1231 $body = curl_exec($ch);
1232 if ($body === false) {
1233 $this->setError(curl_error($ch));
1236 fclose($f); // flush buffer
1237 $f = fopen($filename, 'r');
1241 while (!feof($f) && $data = fgets($f)) {
1242 $line = trim($data);
1243 $splitedLine = explode('||', $line);
1244 if (sizeof($splitedLine) == 4) {
1245 $commits[$i]['pluginName'] = $this->name;
1246 $commits[$i]['description'] = htmlspecialchars($splitedLine[2]);
1247 $commits[$i]['commit_id'] = $splitedLine[3];
1248 $splitedDate = explode(' ', $splitedLine[0]);
1249 $commits[$i]['date'] = $splitedDate[0];
1260 // c-file-style: "bsd"