GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Issues (4873)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/common/backend/BackendSVN.class.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Copyright (c) Xerox Corporation, Codendi Team, 2001-2009. All rights reserved
4
 *
5
 * This file is a part of Codendi.
6
 *
7
 * Codendi is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 2 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * Codendi is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with Codendi. If not, see <http://www.gnu.org/licenses/>.
19
 *
20
 * 
21
 */
22
23
require_once('www/svn/svn_utils.php');
24
25
/**
26
 * Backend class to work on subversion repositories
27
 */
28
class BackendSVN extends Backend {
29
30
    protected $SVNApacheConfNeedUpdate;
31
32
    /**
33
     * For mocking (unit tests)
34
     * 
35
     * @return UGroupDao
36
     */
37
    protected function getUGroupDao() {
38
        return new UGroupDao(CodendiDataAccess::instance());
39
    }
40
     /**
41
     * For mocking (unit tests)
42
     *
43
     * @param array $row a row from the db for a ugroup
44
     * 
45
     * @return ProjectUGroup
46
     */
47
    protected function getUGroupFromRow($row) {
48
        return new ProjectUGroup($row);
49
    }
50
    /**
51
     * For mocking (unit tests)
52
     * 
53
     * @return ServiceDao
54
     */
55
    function _getServiceDao() {
56
        return new ServiceDao(CodendiDataAccess::instance());
57
    }
58
59
    
60
    /**
61
     * Wrapper for Config
62
     * 
63
     * @return ForgeConfig
64
     */
65
    protected function getConfig($var) {
66
        return ForgeConfig::get($var);
67
    }
68
    
69
    /**
70
     * Create project SVN repository
71
     * If the directory already exists, nothing is done.
72
     * 
73
     * @param int $group_id The id of the project to work on
74
     * 
75
     * @return boolean true if repo is successfully created, false otherwise
76
     */
77
    public function createProjectSVN($group_id) {
78
        $project=$this->getProjectManager()->getProject($group_id);
79
        if (!$project) {
80
            return false;
81
        }
82
        $svn_dir = $project->getSVNRootPath();
83
        if (!is_dir($svn_dir)) {
84
            // Let's create a SVN repository for this group
85
            if (!mkdir($svn_dir)) {
86
                $this->log("Can't create project SVN dir: $svn_dir", Backend::LOG_ERROR);
87
                return false;
88
            }
89
            system($GLOBALS['svnadmin_cmd']." create $svn_dir --fs-type fsfs");
90
91
            $unix_group_name=$project->getUnixNameMixedCase(); // May contain upper-case letters
92
            $this->recurseChownChgrp($svn_dir, $this->getHTTPUser(), $unix_group_name);
93
            system("chmod g+rw $svn_dir");
94
        }
95
96
97
        // Put in place the svn post-commit hook for email notification
98
        if (!$this->updateHooks($project)) {
99
            return false;
100
        }
101
102
        if (!$this->updateSVNAccess($group_id)) {
103
            $this->log("Can't update SVN access file", Backend::LOG_ERROR);
104
            return false;
105
        }
106
107
        return true;
108
    }
109
110
    /**
111
     * Check if repository of given project exists
112
     * @param Project
113
     * @return true is repository already exists, false otherwise
114
     */
115
    function repositoryExists(Project $project) {
116
        return is_dir($project->getSVNRootPath());
117
    }
118
119
120
    /**
121
     * Put in place the svn post-commit hook for email notification
122
     * if not present (if the file does not exist it is created)
123
     * 
124
     * @param Project $project The project to work on
125
     * 
126
     * @return boolean true on success or false on failure
127
     */
128
    public function updateHooks(Project $project) {
129
        $unix_group_name=$project->getUnixNameMixedCase(); // May contain upper-case letters
130
        $svn_dir=$project->getSVNRootPath();
131
132
        if ($project->isSVNTracked()) {
133
            $filename = "$svn_dir/hooks/post-commit";
134
            $update_hook=false;
135
            if (! is_file($filename)) {
136
                // File header
137
                $fp = fopen($filename, 'w');
138
                fwrite($fp, "#!/bin/sh\n");
139
                fwrite($fp, "# POST-COMMIT HOOK\n");
140
                fwrite($fp, "#\n");
141
                fwrite($fp, "# The post-commit hook is invoked after a commit.  Subversion runs\n");
142
                fwrite($fp, "# this hook by invoking a program (script, executable, binary, etc.)\n");
143
                fwrite($fp, "# named 'post-commit' (for which this file is a template) with the \n");
144
                fwrite($fp, "# following ordered arguments:\n");
145
                fwrite($fp, "#\n");
146
                fwrite($fp, "#   [1] REPOS-PATH   (the path to this repository)\n");
147
                fwrite($fp, "#   [2] REV          (the number of the revision just committed)\n\n");
148
                fclose($fp);
149
                $update_hook=true;
150
            } else {
151
                $file_array=file($filename);
152
                if (!in_array($this->block_marker_start, $file_array)) {
153
                    $update_hook=true;
154
                }
155
            }
156
            if ($update_hook) {
157
                $command  ='REPOS="$1"'."\n";
158
                $command .='REV="$2"'."\n";
159
                $command .=$GLOBALS['codendi_bin_prefix'].'/commit-email.pl "$REPOS" "$REV" 2>&1 >/dev/null';
160
                $this->addBlock($filename, $command);
161
                $this->chown($filename, $this->getHTTPUser());
162
                $this->chgrp($filename, $unix_group_name);
163
                chmod("$filename", 0775);
164
            }
165
        } else {
166
            // Make sure that the Codendi blocks are removed
167
            $filename = "$svn_dir/hooks/post-commit";
168
            $update_hook=false;
169
            if (is_file($filename)) {
170
                $file_array=file($filename);
171
                if (in_array($this->block_marker_start, $file_array)) {
172
                    $this->removeBlock($filename);
173
                }
174
            }
175
        }
176
177
        // Put in place the Codendi svn pre-commit hook
178
        // if not present (if the file does not exist it is created)
179
        $filename = "$svn_dir/hooks/pre-commit";
180
        $update_hook = false;
181
        if (! is_file($filename)) {
182
            // File header
183
            $fp = fopen($filename, 'w');
184
            fwrite($fp, "#!/bin/sh\n\n");
185
            fwrite($fp, "# PRE-COMMIT HOOK\n");
186
            fwrite($fp, "#\n");
187
            fwrite($fp, "# The pre-commit hook is invoked before a Subversion txn is\n");
188
            fwrite($fp, "# committed.  Subversion runs this hook by invoking a program\n");
189
            fwrite($fp, "# (script, executable, binary, etc.) named 'pre-commit' (for which\n");
190
            fwrite($fp, "# this file is a template), with the following ordered arguments:\n");
191
            fwrite($fp, "#\n");
192
            fwrite($fp, "#   [1] REPOS-PATH   (the path to this repository)\n");
193
            fwrite($fp, "#   [2] TXN-NAME     (the name of the txn about to be committed)\n");
194
            $update_hook=true;
195
        } else {
196
            $file_array=file($filename);
197
            if (!in_array($this->block_marker_start, $file_array)) {
198
                $update_hook=true;
199
            }
200
        }
201
        if ($update_hook) {
202
            $command  = 'REPOS="$1"'."\n";
203
            $command .= 'TXN="$2"'."\n";
204
            $command .= $GLOBALS['codendi_dir'].'/src/utils/php-launcher.sh '.$GLOBALS['codendi_bin_prefix'].'/codendi_svn_pre_commit.php "$REPOS" "$TXN" || exit 1';
205
            $this->addBlock($filename, $command);
206
            $this->chown($filename, $this->getHTTPUser());
207
            $this->chgrp($filename, $unix_group_name);
208
            chmod("$filename", 0775);
209
        }
210
211
        if ($project->canChangeSVNLog()) {
212
            try {
213
                $this->enableCommitMessageUpdate($svn_dir);
214
            } catch (BackendSVNFileForSimlinkAlreadyExistsException $exception) {
215
                throw $exception;
216
            }
217
        } else {
218
            $this->disableCommitMessageUpdate($svn_dir);
219
        }
220
221
        return true;
222
    }
223
224
    /**
225
     * Wrapper for tests
226
     *
227
     * @return SVNAccessFile
228
     */
229
    function _getSVNAccessFile() {
230
        return new SVNAccessFile();
231
    }
232
233
    /**
234
     * Update Subversion DAV access control file if needed
235
     *
236
     * @param int    $group_id        the id of the project
237
     * @param String $ugroup_name     New name of the renamed ugroup (if any)
238
     * @param String $ugroup_old_name Old name of the renamed ugroup (if any)
239
     *
240
     * @return boolean true on success or false on failure
241
     */
242
    public function updateSVNAccess($group_id, $ugroup_name = null, $ugroup_old_name = null) {
243
        $project = $this->getProjectManager()->getProject($group_id);
244
        if (!$project) {
245
            return false;
246
        }
247
        if (! $this->repositoryExists($project)) {
248
            $this->log("Can't update SVN Access file: project SVN repo is missing: ".$project->getSVNRootPath(), Backend::LOG_ERROR);
249
            return false;
250
        }
251
        $svn_dir = $project->getSVNRootPath();
252
        $unix_group_name = $project->getUnixNameMixedCase();
253
254
        $svnaccess_file = $svn_dir."/.SVNAccessFile";
255
        $svnaccess_file_old = $svnaccess_file.".old";
256
        $svnaccess_file_new = $svnaccess_file.".new";
257
        // if you change these block markers also change them in
258
        // src/www/svn/svn_utils.php
259
        $default_block_start="# BEGIN CODENDI DEFAULT SETTINGS - DO NOT REMOVE\n";
260
        $default_block_end="# END CODENDI DEFAULT SETTINGS\n";
261
        $custom_perms='';
262
        $public_svn = 1; // TODO
263
        
264
        $defaultBlock = '';
265
        $defaultBlock .= $this->getSVNAccessGroups($project);
266
        $defaultBlock .= $this->getSVNAccessRootPathDef($project);
267
        
268
        // Retrieve custom permissions, if any
269
        if (is_file("$svnaccess_file")) {
270
            $svnaccess_array = file($svnaccess_file);
271
            $configlines = false;
272
            $contents = '';
273
            while ($line = array_shift($svnaccess_array)) {
274
                if ($configlines) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $configlines of type integer|false is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
275
                    $contents .= $line;
276
                }
277
                if (strcmp($line, $default_block_end) == 0) { 
278
                    $configlines=1;
279
                }
280
            }
281
            $saf = $this->_getSVNAccessFile();
282
            $saf->setRenamedGroup($ugroup_name, $ugroup_old_name);
283
            $saf->setPlatformBlock($defaultBlock);
284
            $custom_perms .= $saf->parseGroupLines($project, $contents);
285
        }
286
287
        $fp = fopen($svnaccess_file_new, 'w');
288
289
        // Codendi specifc
290
        fwrite($fp, "$default_block_start");
291
        fwrite($fp, $defaultBlock);
292
        fwrite($fp, "$default_block_end");
293
294
        // Custom permissions
295
        if ($custom_perms) {
296
            fwrite($fp, $custom_perms);
297
        }
298
        fclose($fp);
299
300
        // Backup existing file and install new one if they are different
301
        $this->installNewFileVersion($svnaccess_file_new, $svnaccess_file, $svnaccess_file_old);
302
303
        // set group ownership, admin user as owner so that
304
        // PHP scripts can write to it directly
305
        $this->chown($svnaccess_file, $this->getHTTPUser());
306
        $this->chgrp($svnaccess_file, $unix_group_name);
307
        chmod("$svnaccess_file", 0775);
308
        
309
        return true;
310
    }
311
312
    /**
313
     * Rewrite the .SVNAccessFile if removed
314
     *
315
     * @return void
316
     */
317
    public function checkSVNAccessPresence($group_id) {
318
        $project = $this->getProjectManager()->getProject($group_id);
319
        if (!$project) {
320
            return false;
321
        }
322
323
        if (! $this->repositoryExists($project)) {
324
            $this->log("Can't update SVN Access file: project SVN repo is missing: ".$project->getSVNRootPath(), Backend::LOG_ERROR);
325
            return false;
326
        }
327
        
328
        $svnaccess_file = $project->getSVNRootPath()."/.SVNAccessFile";
329
        
330
        if (!is_file($svnaccess_file)) {
331
            return $this->updateSVNAccess($group_id);
332
        }
333
        return true;
334
    }
335
336
    /**
337
     * SVNAccessFile groups definitions
338
     *
339
     * @param Project $project
340
     * @return String
341
     */
342
    function getSVNAccessGroups($project) {
343
        $conf = "[groups]\n";
344
        $conf .= $this->getSVNAccessProjectMembers($project);
345
        $conf .= $this->getSVNAccessUserGroupMembers($project);
346
        $conf .= "\n";
347
        return $conf;
348
    }
349
350
    /**
351
     * SVNAccessFile project members group definition
352
     *
353
     * User names must be in lowercase
354
     *
355
     * @param Project $project
356
     *
357
     * @return String
358
     */
359
    function getSVNAccessProjectMembers($project) {
360
        $list  = "";
361
        $first = true;
362
        foreach ($project->getMembersUserNames() as $member) {
363
            if (!$first) {
364
                $list .= ', ';
365
            }
366
            $first = false;
367
            $list .= strtolower($member['user_name']);
368
        }
369
        return "members = ".$list."\n";
370
    }
371
372
    /**
373
     * SVNAccessFile ugroups definitions
374
     *
375
     * @param Project $project
376
     *
377
     * @return String
378
     */
379
    function getSVNAccessUserGroupMembers(Project $project) {
380
        $conf            = "";
381
        $ugroup_dao      = $this->getUGroupDao();
382
        $dar             = $ugroup_dao->searchByGroupId($project->getId());
383
        $project_members = $project->getMembers();
384
        foreach ($dar as $row) {
385
            $ugroup          = $this->getUGroupFromRow($row);
386
            $ugroup_members  = $ugroup->getMembers();
387
            $valid_members   = array();
388
            foreach ($ugroup_members as $ugroup_member) {
389
                if ($project->isPublic() || in_array($ugroup_member, $project_members)) {
390
                    $valid_members[] = $ugroup_member->getUserName();
391
                }
392
            }
393
            // User names must be in lowercase
394
            if ($ugroup->getName() && count($valid_members) > 0) {
395
                $members_list = strtolower(implode(", ", $valid_members));
396
                $conf .= $ugroup->getName()." = ".$members_list."\n";
397
            }
398
        }
399
        $conf .= "\n";
400
        return $conf;
401
    }
402
403
    /**
404
     * SVNAccessFile definition for repository root
405
     *
406
     * @param Project $project
407
     *
408
     * @return String
409
     */
410
    function getSVNAccessRootPathDef($project) {
411
        $conf = "[/]\n";
412
        if (!$project->isPublic() || $project->isSVNPrivate()) {
413
            $conf .= "* = \n";
414
        } else {
415
            $conf .= "* = r\n";
416
        }
417
        $conf .= "@members = rw\n";
418
        return $conf;
419
    }
420
421
    /**
422
     * Update SVN access files into all projects that a given user belongs to
423
     * 
424
     * It includes:
425
     * + projects the user is member of 
426
     * + projects that have user groups that contains the user
427
     * 
428
     * @param PFUser $user
429
     * 
430
     * @return Boolean
431
     */
432
    public function updateSVNAccessForGivenMember($user) {
433
        $projects = $user->getAllProjects(); 
434
        if (isset($projects)) {
435
            foreach ($projects as $groupId) {
436
                $project = $this->getProjectManager()->getProject($groupId);
437
                $this->updateProjectSVNAccessFile($project);
438
            }
439
        }
440
        return true;
441
    }
442
443
    /**
444
     * Update SVNAccessFile of a project
445
     * 
446
     * @param Project $project The project to update
447
     * 
448
     * @return Boolean
449
     */
450
    public function updateProjectSVNAccessFile(Project $project) {
451
        if ($this->repositoryExists($project)) {
452
            return $this->updateSVNAccess($project->getID());
453
        }
454
        return true;
455
    }
456
457
    /**
458
     * Force apache conf update
459
     *
460
     * @return void
461
     */
462
    public function setSVNApacheConfNeedUpdate() {
463
        $this->SVNApacheConfNeedUpdate = true;
464
    }
465
466
    /**
467
     * Say if apache conf need update
468
     * 
469
     * @return boolean
470
     */
471
    public function getSVNApacheConfNeedUpdate() {
472
        return $this->SVNApacheConfNeedUpdate;
473
    }
474
475
476
    /**
477
     * Add Subversion DAV definition for all projects in a dedicated Apache 
478
     * configuration file
479
     * 
480
     * @return boolean true on success or false on failure
481
     */
482
    public function generateSVNApacheConf() {
483
        $svn_root_file = $GLOBALS['svn_root_file'];
484
        $svn_root_file_old = $svn_root_file.".old";
485
        $svn_root_file_new = $svn_root_file.".new";
486
        
487
        $conf = $this->getApacheConf();
488
        if (file_put_contents($svn_root_file_new, $conf) !== strlen($conf)) {
489
            $this->log("Error while writing to $svn_root_file_new", Backend::LOG_ERROR);
490
            return false;
491
        }
492
493
        $this->chown("$svn_root_file_new", $this->getHTTPUser());
494
        $this->chgrp("$svn_root_file_new", $this->getHTTPUser());
495
        chmod("$svn_root_file_new", 0640);
496
497
        // Backup existing file and install new one
498
        return $this->installNewFileVersion($svn_root_file_new, $svn_root_file, $svn_root_file_old, true);
499
    }
500
501
    function getApacheConf() {
502
        $projects = $this->_getServiceDao()->searchActiveUnixGroupByUsedService('svn');
503
        $factory  = $this->getSVNApacheAuthFactory();
504
        $conf = new SVN_Apache_SvnrootConf($factory, $projects);
505
        return $conf->getFullConf();
506
    }
507
    
508
    protected function getSVNApacheAuthFactory() {
509
        return new SVN_Apache_Auth_Factory(
510
            $this->getProjectManager(),
511
            EventManager::instance(),
512
            $this->getSVNTokenManager()
513
        );
514
    }
515
    
516
    /**
517
     * Archive SVN repository: stores a tgz in temp dir, and remove the directory
518
     *
519
     * @param int $group_id The id of the project to work on
520
     * 
521
     * @return boolean true on success or false on failure
522
     */
523
    public function archiveProjectSVN($group_id) {
524
        $project=$this->getProjectManager()->getProject($group_id);
525
        if (!$project) {
526
            return false;
527
        }
528
        $mydir      = $project->getSVNRootPath();
529
        $repopath   = dirname($mydir);
530
        $reponame   = basename($mydir);
531
        $backupfile = ForgeConfig::get('sys_project_backup_path')."/$reponame-svn.tgz";
532
533
        if (is_dir($mydir)) {
534
            system("cd $repopath; tar cfz $backupfile $reponame");
535
            chmod($backupfile, 0600);
536
            $this->recurseDeleteInDir($mydir);
537
            rmdir($mydir);
538
        }
539
        return true;
540
    }
541
    
542
    /**
543
     * Make the cvs repository of the project private or public
544
     * 
545
     * @param Project $project    The project to work on
546
     * @param boolean $is_private true if the repository is private
547
     * 
548
     * @return boolean true if success
549
     */
550
    public function setSVNPrivacy(Project $project, $is_private) {
551
        $perms   = $is_private ? 0770 : 0775;
552
        $svnroot = $project->getSVNRootPath();
553
        return is_dir($svnroot) && $this->chmod($svnroot, $perms);
554
    }
555
556
557
    /** 
558
     * Check ownership/mode/privacy of repository 
559
     * 
560
     * @param Project $project The project to work on
561
     * 
562
     * @return boolean true if success
563
     */
564
    public function checkSVNMode(Project $project) {
565
        $unix_group_name =  $project->getUnixNameMixedCase();
566
        $svnroot = $project->getSVNRootPath();
567
        $is_private = !$project->isPublic() || $project->isSVNPrivate();
568
        if ($is_private) {
569
            $perms = fileperms($svnroot);
570
            // 'others' should have no right on the repository
571
            if (($perms & 0x0004) || ($perms & 0x0002) || ($perms & 0x0001) || ($perms & 0x0200)) {
572
                $this->log("Restoring privacy on SVN dir: $svnroot", Backend::LOG_WARNING);
573
               $this->setSVNPrivacy($project, $is_private);
574
            }
575
        } 
576
        // Sometimes, there might be a bad ownership on file (e.g. chmod failed, maintenance done as root...)
577
        $files_to_check=array('db/current', 'hooks/pre-commit', 'hooks/post-commit', 'db/rep-cache.db');
578
        $need_owner_update = false;
579
        foreach ($files_to_check as $file) {
580
            // Get file stat 
581
            if (file_exists("$svnroot/$file")) {
582
                $stat = stat("$svnroot/$file");
583
                if ( ($stat['uid'] != $this->getHTTPUserUID())
584
                     || ($stat['gid'] != $project->getUnixGID()) ) {
585
                    $need_owner_update = true;
586
                }
587
            }
588
        }
589
        if ($need_owner_update) {
590
            $this->log("Restoring ownership on SVN dir: $svnroot", Backend::LOG_INFO);
591
            $this->recurseChownChgrp($svnroot, $this->getHTTPUser(), $unix_group_name);
592
            $this->chown($svnroot, $this->getHTTPUser());
593
            $this->chgrp($svnroot, $unix_group_name);
594
            system("chmod g+rw $svnroot");
595
        }
596
597
        return true;
598
    }
599
    /**
600
     * Check if given name is not used by a repository or a file or a link
601
     * 
602
     * @param String $name
603
     * 
604
     * @return false if repository or file  or link already exists, true otherwise
605
     */
606
    function isNameAvailable($name) {
607
        $path = $GLOBALS['svn_prefix']."/".$name;
608
        return (!$this->fileExists($path));
609
    }
610
    
611
    /**
612
     * Rename svn repository (following project unix_name change)
613
     * 
614
     * @param Project $project
615
     * @param String  $newName
616
     * 
617
     * @return Boolean
618
     */
619
    public function renameSVNRepository(Project $project, $newName) {
620
        return rename($project->getSVNRootPath(), $GLOBALS['svn_prefix'].'/'.$newName);
621
    }
622
623
    private function enableCommitMessageUpdate($project_svnroot) {
624
        $hook_names = array('pre-revprop-change', 'post-revprop-change');
625
        $hook_error = array();
626
627
        foreach ($hook_names as $hook_name) {
628
            if(! $this->enableHook($project_svnroot, $hook_name, ForgeConfig::get('codendi_bin_prefix').'/'.$hook_name.'.php')) {
629
                $hook_error[] = $this->getHookPath($project_svnroot, $hook_name);
630
            }
631
        }
632
633
        if (! empty($hook_error)) {
634
            $exception_message = $this->buildExceptionMessage($hook_error);
635
            throw new BackendSVNFileForSimlinkAlreadyExistsException($exception_message);
636
        }
637
    }
638
639
    private function buildExceptionMessage(array $hook_error) {
640
        if (count($hook_error) > 1) {
641
            $exception_message = 'Files '. implode(', ', $hook_error) .' already exist';
642
        } else {
643
             $exception_message = 'File ' . implode($hook_error) . ' already exists';
644
        }
645
646
        return $exception_message;
647
    }
648
649
    private function enableHook($project_svnroot, $hook_name, $source_tool) {
650
        $path = $this->getHookPath($project_svnroot, $hook_name);
651
652
        if (file_exists($path) && ! $this->isLinkToTool($source_tool, $path)) {
653
            $message = "file $path already exists";
654
655
            $this->log($message, Backend::LOG_WARNING);
656
            return false;
657
        }
658
659
        if (! is_link($path)) {
660
            symlink($source_tool, $path);
661
        }
662
663
        return true;
664
    }
665
666
    private function isLinkToTool($tool_reference_path, $path) {
667
        return is_link($path) && realpath($tool_reference_path) == realpath(readlink($path));
668
    }
669
670
    private function disableCommitMessageUpdate($project_svnroot) {
671
        $this->deleteHook($project_svnroot, 'pre-revprop-change');
672
        $this->deleteHook($project_svnroot, 'post-revprop-change');
673
    }
674
675
    private function deleteHook($project_svnroot, $hook_name) {
676
        $path = $this->getHookPath($project_svnroot, $hook_name);
677
        if (is_link($path)) {
678
            unlink($path);
679
        }
680
    }
681
682
    private function getHookPath($project_svnroot, $hook_name) {
683
        return $project_svnroot.'/hooks/'.$hook_name;
684
    }
685
686
    /**
687
     * @return SVN_TokenUsageManager
688
     */
689
    protected function getSVNTokenManager() {
690
        return new SVN_TokenUsageManager(new SVN_TokenDao(), $this->getProjectManager());
691
    }
692
}
693