|  | @@ -12,6 +12,7 @@ import (
 | 
	
		
			
				|  |  |  	"os"
 | 
	
		
			
				|  |  |  	"path"
 | 
	
		
			
				|  |  |  	"path/filepath"
 | 
	
		
			
				|  |  | +	"regexp"
 | 
	
		
			
				|  |  |  	"strings"
 | 
	
		
			
				|  |  |  	"sync"
 | 
	
		
			
				|  |  |  	"time"
 | 
	
	
		
			
				|  | @@ -26,68 +27,26 @@ import (
 | 
	
		
			
				|  |  |  	"github.com/gogits/gogs/modules/log"
 | 
	
		
			
				|  |  |  )
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -// Repository represents a git repository.
 | 
	
		
			
				|  |  | -type Repository struct {
 | 
	
		
			
				|  |  | -	Id          int64
 | 
	
		
			
				|  |  | -	OwnerId     int64 `xorm:"unique(s)"`
 | 
	
		
			
				|  |  | -	ForkId      int64
 | 
	
		
			
				|  |  | -	LowerName   string `xorm:"unique(s) index not null"`
 | 
	
		
			
				|  |  | -	Name        string `xorm:"index not null"`
 | 
	
		
			
				|  |  | -	Description string
 | 
	
		
			
				|  |  | -	Website     string
 | 
	
		
			
				|  |  | -	Private     bool
 | 
	
		
			
				|  |  | -	NumWatchs   int
 | 
	
		
			
				|  |  | -	NumStars    int
 | 
	
		
			
				|  |  | -	NumForks    int
 | 
	
		
			
				|  |  | -	Created     time.Time `xorm:"created"`
 | 
	
		
			
				|  |  | -	Updated     time.Time `xorm:"updated"`
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -// Watch is connection request for receiving repository notifycation.
 | 
	
		
			
				|  |  | -type Watch struct {
 | 
	
		
			
				|  |  | -	Id     int64
 | 
	
		
			
				|  |  | -	RepoId int64 `xorm:"UNIQUE(watch)"`
 | 
	
		
			
				|  |  | -	UserId int64 `xorm:"UNIQUE(watch)"`
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -// Watch or unwatch repository.
 | 
	
		
			
				|  |  | -func WatchRepo(userId, repoId int64, watch bool) (err error) {
 | 
	
		
			
				|  |  | -	if watch {
 | 
	
		
			
				|  |  | -		_, err = orm.Insert(&Watch{RepoId: repoId, UserId: userId})
 | 
	
		
			
				|  |  | -	} else {
 | 
	
		
			
				|  |  | -		_, err = orm.Delete(&Watch{0, repoId, userId})
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -	return err
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -// GetWatches returns all watches of given repository.
 | 
	
		
			
				|  |  | -func GetWatches(repoId int64) ([]Watch, error) {
 | 
	
		
			
				|  |  | -	watches := make([]Watch, 0, 10)
 | 
	
		
			
				|  |  | -	err := orm.Find(&watches, &Watch{RepoId: repoId})
 | 
	
		
			
				|  |  | -	return watches, err
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -// IsWatching checks if user has watched given repository.
 | 
	
		
			
				|  |  | -func IsWatching(userId, repoId int64) bool {
 | 
	
		
			
				|  |  | -	has, _ := orm.Get(&Watch{0, repoId, userId})
 | 
	
		
			
				|  |  | -	return has
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  var (
 | 
	
		
			
				|  |  | -	gitInitLocker          = sync.Mutex{}
 | 
	
		
			
				|  |  | -	LanguageIgns, Licenses []string
 | 
	
		
			
				|  |  | +	ErrRepoAlreadyExist  = errors.New("Repository already exist")
 | 
	
		
			
				|  |  | +	ErrRepoNotExist      = errors.New("Repository does not exist")
 | 
	
		
			
				|  |  | +	ErrRepoFileNotExist  = errors.New("Target Repo file does not exist")
 | 
	
		
			
				|  |  | +	ErrRepoNameIllegal   = errors.New("Repository name contains illegal characters")
 | 
	
		
			
				|  |  | +	ErrRepoFileNotLoaded = fmt.Errorf("repo file not loaded")
 | 
	
		
			
				|  |  |  )
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +var gitInitLocker = sync.Mutex{}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  var (
 | 
	
		
			
				|  |  | -	ErrRepoAlreadyExist = errors.New("Repository already exist")
 | 
	
		
			
				|  |  | -	ErrRepoNotExist     = errors.New("Repository does not exist")
 | 
	
		
			
				|  |  | -	ErrRepoFileNotExist = errors.New("Target Repo file does not exist")
 | 
	
		
			
				|  |  | +	LanguageIgns, Licenses []string
 | 
	
		
			
				|  |  |  )
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -func init() {
 | 
	
		
			
				|  |  | +func LoadRepoConfig() {
 | 
	
		
			
				|  |  |  	LanguageIgns = strings.Split(base.Cfg.MustValue("repository", "LANG_IGNS"), "|")
 | 
	
		
			
				|  |  |  	Licenses = strings.Split(base.Cfg.MustValue("repository", "LICENSES"), "|")
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +func NewRepoContext() {
 | 
	
		
			
				|  |  |  	zip.Verbose = false
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	// Check if server has basic git setting.
 | 
	
	
		
			
				|  | @@ -104,6 +63,32 @@ func init() {
 | 
	
		
			
				|  |  |  			os.Exit(2)
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	// Initialize illegal patterns.
 | 
	
		
			
				|  |  | +	for i := range illegalPatterns[1:] {
 | 
	
		
			
				|  |  | +		pattern := ""
 | 
	
		
			
				|  |  | +		for j := range illegalPatterns[i+1] {
 | 
	
		
			
				|  |  | +			pattern += "[" + string(illegalPatterns[i+1][j]-32) + string(illegalPatterns[i+1][j]) + "]"
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +		illegalPatterns[i+1] = pattern
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// Repository represents a git repository.
 | 
	
		
			
				|  |  | +type Repository struct {
 | 
	
		
			
				|  |  | +	Id          int64
 | 
	
		
			
				|  |  | +	OwnerId     int64 `xorm:"unique(s)"`
 | 
	
		
			
				|  |  | +	ForkId      int64
 | 
	
		
			
				|  |  | +	LowerName   string `xorm:"unique(s) index not null"`
 | 
	
		
			
				|  |  | +	Name        string `xorm:"index not null"`
 | 
	
		
			
				|  |  | +	Description string
 | 
	
		
			
				|  |  | +	Website     string
 | 
	
		
			
				|  |  | +	Private     bool
 | 
	
		
			
				|  |  | +	NumWatches  int
 | 
	
		
			
				|  |  | +	NumStars    int
 | 
	
		
			
				|  |  | +	NumForks    int
 | 
	
		
			
				|  |  | +	Created     time.Time `xorm:"created"`
 | 
	
		
			
				|  |  | +	Updated     time.Time `xorm:"updated"`
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  // IsRepositoryExist returns true if the repository with given name under user has already existed.
 | 
	
	
		
			
				|  | @@ -120,8 +105,28 @@ func IsRepositoryExist(user *User, repoName string) (bool, error) {
 | 
	
		
			
				|  |  |  	return s.IsDir(), nil
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +var (
 | 
	
		
			
				|  |  | +	// Define as all lower case!!
 | 
	
		
			
				|  |  | +	illegalPatterns = []string{"[.][Gg][Ii][Tt]", "user", "help", "stars", "issues", "pulls", "commits", "admin", "repo", "template", "admin"}
 | 
	
		
			
				|  |  | +)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// IsLegalName returns false if name contains illegal characters.
 | 
	
		
			
				|  |  | +func IsLegalName(repoName string) bool {
 | 
	
		
			
				|  |  | +	for _, pattern := range illegalPatterns {
 | 
	
		
			
				|  |  | +		has, _ := regexp.MatchString(pattern, repoName)
 | 
	
		
			
				|  |  | +		if has {
 | 
	
		
			
				|  |  | +			return false
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	return true
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  // CreateRepository creates a repository for given user or orgnaziation.
 | 
	
		
			
				|  |  |  func CreateRepository(user *User, repoName, desc, repoLang, license string, private bool, initReadme bool) (*Repository, error) {
 | 
	
		
			
				|  |  | +	if !IsLegalName(repoName) {
 | 
	
		
			
				|  |  | +		return nil, ErrRepoNameIllegal
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	isExist, err := IsRepositoryExist(user, repoName)
 | 
	
		
			
				|  |  |  	if err != nil {
 | 
	
		
			
				|  |  |  		return nil, err
 | 
	
	
		
			
				|  | @@ -331,6 +336,82 @@ func initRepository(f string, user *User, repo *Repository, initReadme bool, rep
 | 
	
		
			
				|  |  |  	return nil
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +// UserRepo reporesents a repository with user name.
 | 
	
		
			
				|  |  | +type UserRepo struct {
 | 
	
		
			
				|  |  | +	*Repository
 | 
	
		
			
				|  |  | +	UserName string
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// GetRepos returns given number of repository objects with offset.
 | 
	
		
			
				|  |  | +func GetRepos(num, offset int) ([]UserRepo, error) {
 | 
	
		
			
				|  |  | +	repos := make([]Repository, 0, num)
 | 
	
		
			
				|  |  | +	if err := orm.Limit(num, offset).Asc("id").Find(&repos); err != nil {
 | 
	
		
			
				|  |  | +		return nil, err
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	urepos := make([]UserRepo, len(repos))
 | 
	
		
			
				|  |  | +	for i := range repos {
 | 
	
		
			
				|  |  | +		urepos[i].Repository = &repos[i]
 | 
	
		
			
				|  |  | +		u := new(User)
 | 
	
		
			
				|  |  | +		has, err := orm.Id(urepos[i].Repository.OwnerId).Get(u)
 | 
	
		
			
				|  |  | +		if err != nil {
 | 
	
		
			
				|  |  | +			return nil, err
 | 
	
		
			
				|  |  | +		} else if !has {
 | 
	
		
			
				|  |  | +			return nil, ErrUserNotExist
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +		urepos[i].UserName = u.Name
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	return urepos, nil
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +func RepoPath(userName, repoName string) string {
 | 
	
		
			
				|  |  | +	return filepath.Join(UserPath(userName), repoName+".git")
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// DeleteRepository deletes a repository for a user or orgnaztion.
 | 
	
		
			
				|  |  | +func DeleteRepository(userId, repoId int64, userName string) (err error) {
 | 
	
		
			
				|  |  | +	repo := &Repository{Id: repoId, OwnerId: userId}
 | 
	
		
			
				|  |  | +	has, err := orm.Get(repo)
 | 
	
		
			
				|  |  | +	if err != nil {
 | 
	
		
			
				|  |  | +		return err
 | 
	
		
			
				|  |  | +	} else if !has {
 | 
	
		
			
				|  |  | +		return ErrRepoNotExist
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	session := orm.NewSession()
 | 
	
		
			
				|  |  | +	if err = session.Begin(); err != nil {
 | 
	
		
			
				|  |  | +		return err
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	if _, err = session.Delete(&Repository{Id: repoId}); err != nil {
 | 
	
		
			
				|  |  | +		session.Rollback()
 | 
	
		
			
				|  |  | +		return err
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	if _, err := session.Delete(&Access{UserName: userName, RepoName: repo.Name}); err != nil {
 | 
	
		
			
				|  |  | +		session.Rollback()
 | 
	
		
			
				|  |  | +		return err
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	rawSql := "UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?"
 | 
	
		
			
				|  |  | +	if _, err = session.Exec(rawSql, userId); err != nil {
 | 
	
		
			
				|  |  | +		session.Rollback()
 | 
	
		
			
				|  |  | +		return err
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	if _, err = session.Delete(&Watch{RepoId: repoId}); err != nil {
 | 
	
		
			
				|  |  | +		session.Rollback()
 | 
	
		
			
				|  |  | +		return err
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	if err = session.Commit(); err != nil {
 | 
	
		
			
				|  |  | +		session.Rollback()
 | 
	
		
			
				|  |  | +		return err
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	if err = os.RemoveAll(RepoPath(userName, repo.Name)); err != nil {
 | 
	
		
			
				|  |  | +		// TODO: log and delete manully
 | 
	
		
			
				|  |  | +		log.Error("delete repo %s/%s failed: %v", userName, repo.Name, err)
 | 
	
		
			
				|  |  | +		return err
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	return nil
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  // GetRepositoryByName returns the repository by given name under user if exists.
 | 
	
		
			
				|  |  |  func GetRepositoryByName(user *User, repoName string) (*Repository, error) {
 | 
	
		
			
				|  |  |  	repo := &Repository{
 | 
	
	
		
			
				|  | @@ -368,6 +449,45 @@ func GetRepositoryCount(user *User) (int64, error) {
 | 
	
		
			
				|  |  |  	return orm.Count(&Repository{OwnerId: user.Id})
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +// Watch is connection request for receiving repository notifycation.
 | 
	
		
			
				|  |  | +type Watch struct {
 | 
	
		
			
				|  |  | +	Id     int64
 | 
	
		
			
				|  |  | +	RepoId int64 `xorm:"UNIQUE(watch)"`
 | 
	
		
			
				|  |  | +	UserId int64 `xorm:"UNIQUE(watch)"`
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// Watch or unwatch repository.
 | 
	
		
			
				|  |  | +func WatchRepo(userId, repoId int64, watch bool) (err error) {
 | 
	
		
			
				|  |  | +	if watch {
 | 
	
		
			
				|  |  | +		if _, err = orm.Insert(&Watch{RepoId: repoId, UserId: userId}); err != nil {
 | 
	
		
			
				|  |  | +			return err
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		rawSql := "UPDATE `repository` SET num_watches = num_watches + 1 WHERE id = ?"
 | 
	
		
			
				|  |  | +		_, err = orm.Exec(rawSql, repoId)
 | 
	
		
			
				|  |  | +	} else {
 | 
	
		
			
				|  |  | +		if _, err = orm.Delete(&Watch{0, repoId, userId}); err != nil {
 | 
	
		
			
				|  |  | +			return err
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +		rawSql := "UPDATE `repository` SET num_watches = num_watches - 1 WHERE id = ?"
 | 
	
		
			
				|  |  | +		_, err = orm.Exec(rawSql, repoId)
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	return err
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// GetWatches returns all watches of given repository.
 | 
	
		
			
				|  |  | +func GetWatches(repoId int64) ([]Watch, error) {
 | 
	
		
			
				|  |  | +	watches := make([]Watch, 0, 10)
 | 
	
		
			
				|  |  | +	err := orm.Find(&watches, &Watch{RepoId: repoId})
 | 
	
		
			
				|  |  | +	return watches, err
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// IsWatching checks if user has watched given repository.
 | 
	
		
			
				|  |  | +func IsWatching(userId, repoId int64) bool {
 | 
	
		
			
				|  |  | +	has, _ := orm.Get(&Watch{0, repoId, userId})
 | 
	
		
			
				|  |  | +	return has
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  func StarReposiory(user *User, repoName string) error {
 | 
	
		
			
				|  |  |  	return nil
 | 
	
		
			
				|  |  |  }
 | 
	
	
		
			
				|  | @@ -388,56 +508,6 @@ func ForkRepository(reposName string, userId int64) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -func RepoPath(userName, repoName string) string {
 | 
	
		
			
				|  |  | -	return filepath.Join(UserPath(userName), repoName+".git")
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -// DeleteRepository deletes a repository for a user or orgnaztion.
 | 
	
		
			
				|  |  | -func DeleteRepository(userId, repoId int64, userName string) (err error) {
 | 
	
		
			
				|  |  | -	repo := &Repository{Id: repoId, OwnerId: userId}
 | 
	
		
			
				|  |  | -	has, err := orm.Get(repo)
 | 
	
		
			
				|  |  | -	if err != nil {
 | 
	
		
			
				|  |  | -		return err
 | 
	
		
			
				|  |  | -	} else if !has {
 | 
	
		
			
				|  |  | -		return ErrRepoNotExist
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -	session := orm.NewSession()
 | 
	
		
			
				|  |  | -	if err = session.Begin(); err != nil {
 | 
	
		
			
				|  |  | -		return err
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -	if _, err = session.Delete(&Repository{Id: repoId}); err != nil {
 | 
	
		
			
				|  |  | -		session.Rollback()
 | 
	
		
			
				|  |  | -		return err
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -	if _, err := session.Delete(&Access{UserName: userName, RepoName: repo.Name}); err != nil {
 | 
	
		
			
				|  |  | -		session.Rollback()
 | 
	
		
			
				|  |  | -		return err
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -	rawSql := "UPDATE user SET num_repos = num_repos - 1 WHERE id = ?"
 | 
	
		
			
				|  |  | -	if base.Cfg.MustValue("database", "DB_TYPE") == "postgres" {
 | 
	
		
			
				|  |  | -		rawSql = "UPDATE \"user\" SET num_repos = num_repos - 1 WHERE id = ?"
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -	if _, err = session.Exec(rawSql, userId); err != nil {
 | 
	
		
			
				|  |  | -		session.Rollback()
 | 
	
		
			
				|  |  | -		return err
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -	if err = session.Commit(); err != nil {
 | 
	
		
			
				|  |  | -		session.Rollback()
 | 
	
		
			
				|  |  | -		return err
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -	if err = os.RemoveAll(RepoPath(userName, repo.Name)); err != nil {
 | 
	
		
			
				|  |  | -		// TODO: log and delete manully
 | 
	
		
			
				|  |  | -		log.Error("delete repo %s/%s failed: %v", userName, repo.Name, err)
 | 
	
		
			
				|  |  | -		return err
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -	return nil
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -var (
 | 
	
		
			
				|  |  | -	ErrRepoFileNotLoaded = fmt.Errorf("repo file not loaded")
 | 
	
		
			
				|  |  | -)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  // RepoFile represents a file object in git repository.
 | 
	
		
			
				|  |  |  type RepoFile struct {
 | 
	
		
			
				|  |  |  	*git.TreeEntry
 |