mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09:00 
			
		
		
		
	mistakes
This commit is contained in:
		| @@ -1,62 +0,0 @@ | ||||
| // Copyright 2014 The Gogs Authors. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| package models | ||||
|  | ||||
| import ( | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/lunny/xorm" | ||||
| ) | ||||
|  | ||||
| // Access types. | ||||
| const ( | ||||
| 	AU_READABLE = iota + 1 | ||||
| 	AU_WRITABLE | ||||
| ) | ||||
|  | ||||
| // Access represents the accessibility of user to repository. | ||||
| type Access struct { | ||||
| 	Id       int64 | ||||
| 	UserName string    `xorm:"unique(s)"` | ||||
| 	RepoName string    `xorm:"unique(s)"` | ||||
| 	Mode     int       `xorm:"unique(s)"` | ||||
| 	Created  time.Time `xorm:"created"` | ||||
| } | ||||
|  | ||||
| // AddAccess adds new access record. | ||||
| func AddAccess(access *Access) error { | ||||
| 	access.UserName = strings.ToLower(access.UserName) | ||||
| 	access.RepoName = strings.ToLower(access.RepoName) | ||||
| 	_, err := orm.Insert(access) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // UpdateAccess updates access information. | ||||
| func UpdateAccess(access *Access) error { | ||||
| 	access.UserName = strings.ToLower(access.UserName) | ||||
| 	access.RepoName = strings.ToLower(access.RepoName) | ||||
| 	_, err := orm.Id(access.Id).Update(access) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // UpdateAccess updates access information with session for rolling back. | ||||
| func UpdateAccessWithSession(sess *xorm.Session, access *Access) error { | ||||
| 	if _, err := sess.Id(access.Id).Update(access); err != nil { | ||||
| 		sess.Rollback() | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // HasAccess returns true if someone can read or write to given repository. | ||||
| func HasAccess(userName, repoName string, mode int) (bool, error) { | ||||
| 	return orm.Get(&Access{ | ||||
| 		Id:       0, | ||||
| 		UserName: strings.ToLower(userName), | ||||
| 		RepoName: strings.ToLower(repoName), | ||||
| 		Mode:     mode, | ||||
| 	}) | ||||
| } | ||||
							
								
								
									
										135
									
								
								models/action.go
									
									
									
									
									
								
							
							
						
						
									
										135
									
								
								models/action.go
									
									
									
									
									
								
							| @@ -1,135 +0,0 @@ | ||||
| // Copyright 2014 The Gogs Authors. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| package models | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/gogits/gogs/modules/base" | ||||
| 	"github.com/gogits/gogs/modules/log" | ||||
| ) | ||||
|  | ||||
| // Operation types of user action. | ||||
| const ( | ||||
| 	OP_CREATE_REPO = iota + 1 | ||||
| 	OP_DELETE_REPO | ||||
| 	OP_STAR_REPO | ||||
| 	OP_FOLLOW_REPO | ||||
| 	OP_COMMIT_REPO | ||||
| 	OP_CREATE_ISSUE | ||||
| 	OP_PULL_REQUEST | ||||
| 	OP_TRANSFER_REPO | ||||
| ) | ||||
|  | ||||
| // Action represents user operation type and other information to repository., | ||||
| // it implemented interface base.Actioner so that can be used in template render. | ||||
| type Action struct { | ||||
| 	Id          int64 | ||||
| 	UserId      int64  // Receiver user id. | ||||
| 	OpType      int    // Operations: CREATE DELETE STAR ... | ||||
| 	ActUserId   int64  // Action user id. | ||||
| 	ActUserName string // Action user name. | ||||
| 	ActEmail    string | ||||
| 	RepoId      int64 | ||||
| 	RepoName    string | ||||
| 	RefName     string | ||||
| 	Content     string    `xorm:"TEXT"` | ||||
| 	Created     time.Time `xorm:"created"` | ||||
| } | ||||
|  | ||||
| func (a Action) GetOpType() int { | ||||
| 	return a.OpType | ||||
| } | ||||
|  | ||||
| func (a Action) GetActUserName() string { | ||||
| 	return a.ActUserName | ||||
| } | ||||
|  | ||||
| func (a Action) GetActEmail() string { | ||||
| 	return a.ActEmail | ||||
| } | ||||
|  | ||||
| func (a Action) GetRepoName() string { | ||||
| 	return a.RepoName | ||||
| } | ||||
|  | ||||
| func (a Action) GetBranch() string { | ||||
| 	return a.RefName | ||||
| } | ||||
|  | ||||
| func (a Action) GetContent() string { | ||||
| 	return a.Content | ||||
| } | ||||
|  | ||||
| // CommitRepoAction adds new action for committing repository. | ||||
| func CommitRepoAction(userId int64, userName, actEmail string, | ||||
| 	repoId int64, repoName string, refName string, commit *base.PushCommits) error { | ||||
| 	log.Trace("action.CommitRepoAction(start): %d/%s", userId, repoName) | ||||
|  | ||||
| 	bs, err := json.Marshal(commit) | ||||
| 	if err != nil { | ||||
| 		log.Error("action.CommitRepoAction(json): %d/%s", userId, repoName) | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if err = NotifyWatchers(&Action{ActUserId: userId, ActUserName: userName, ActEmail: actEmail, | ||||
| 		OpType: OP_COMMIT_REPO, Content: string(bs), RepoId: repoId, RepoName: repoName, RefName: refName}); err != nil { | ||||
| 		log.Error("action.CommitRepoAction(notify watchers): %d/%s", userId, repoName) | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// Change repository bare status and update last updated time. | ||||
| 	repo, err := GetRepositoryByName(userId, repoName) | ||||
| 	if err != nil { | ||||
| 		log.Error("action.CommitRepoAction(GetRepositoryByName): %d/%s", userId, repoName) | ||||
| 		return err | ||||
| 	} | ||||
| 	repo.IsBare = false | ||||
| 	if err = UpdateRepository(repo); err != nil { | ||||
| 		log.Error("action.CommitRepoAction(UpdateRepository): %d/%s", userId, repoName) | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	log.Trace("action.CommitRepoAction(end): %d/%s", userId, repoName) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // NewRepoAction adds new action for creating repository. | ||||
| func NewRepoAction(user *User, repo *Repository) (err error) { | ||||
| 	if err = NotifyWatchers(&Action{ActUserId: user.Id, ActUserName: user.Name, ActEmail: user.Email, | ||||
| 		OpType: OP_CREATE_REPO, RepoId: repo.Id, RepoName: repo.Name}); err != nil { | ||||
| 		log.Error("action.NewRepoAction(notify watchers): %d/%s", user.Id, repo.Name) | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	log.Trace("action.NewRepoAction: %s/%s", user.LowerName, repo.LowerName) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // TransferRepoAction adds new action for transfering repository. | ||||
| func TransferRepoAction(user, newUser *User, repo *Repository) (err error) { | ||||
| 	if err = NotifyWatchers(&Action{ActUserId: user.Id, ActUserName: user.Name, ActEmail: user.Email, | ||||
| 		OpType: OP_TRANSFER_REPO, RepoId: repo.Id, RepoName: repo.Name, Content: newUser.Name}); err != nil { | ||||
| 		log.Error("action.TransferRepoAction(notify watchers): %d/%s", user.Id, repo.Name) | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	log.Trace("action.TransferRepoAction: %s/%s", user.LowerName, repo.LowerName) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // GetFeeds returns action list of given user in given context. | ||||
| func GetFeeds(userid, offset int64, isProfile bool) ([]Action, error) { | ||||
| 	actions := make([]Action, 0, 20) | ||||
| 	sess := orm.Limit(20, int(offset)).Desc("id").Where("user_id=?", userid) | ||||
| 	if isProfile { | ||||
| 		sess.And("act_user_id=?", userid) | ||||
| 	} else { | ||||
| 		sess.And("act_user_id!=?", userid) | ||||
| 	} | ||||
| 	err := sess.Find(&actions) | ||||
| 	return actions, err | ||||
| } | ||||
							
								
								
									
										411
									
								
								models/git.go
									
									
									
									
									
								
							
							
						
						
									
										411
									
								
								models/git.go
									
									
									
									
									
								
							| @@ -1,411 +0,0 @@ | ||||
| // Copyright 2014 The Gogs Authors. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| package models | ||||
|  | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"container/list" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"os/exec" | ||||
| 	"path" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/gogits/git" | ||||
|  | ||||
| 	"github.com/gogits/gogs/modules/base" | ||||
| ) | ||||
|  | ||||
| // RepoFile represents a file object in git repository. | ||||
| type RepoFile struct { | ||||
| 	*git.TreeEntry | ||||
| 	Path   string | ||||
| 	Size   int64 | ||||
| 	Repo   *git.Repository | ||||
| 	Commit *git.Commit | ||||
| } | ||||
|  | ||||
| // LookupBlob returns the content of an object. | ||||
| func (file *RepoFile) LookupBlob() (*git.Blob, error) { | ||||
| 	if file.Repo == nil { | ||||
| 		return nil, ErrRepoFileNotLoaded | ||||
| 	} | ||||
|  | ||||
| 	return file.Repo.LookupBlob(file.Id) | ||||
| } | ||||
|  | ||||
| // GetBranches returns all branches of given repository. | ||||
| func GetBranches(userName, repoName string) ([]string, error) { | ||||
| 	repo, err := git.OpenRepository(RepoPath(userName, repoName)) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	refs, err := repo.AllReferences() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	brs := make([]string, len(refs)) | ||||
| 	for i, ref := range refs { | ||||
| 		brs[i] = ref.BranchName() | ||||
| 	} | ||||
| 	return brs, nil | ||||
| } | ||||
|  | ||||
| // GetTags returns all tags of given repository. | ||||
| func GetTags(userName, repoName string) ([]string, error) { | ||||
| 	repo, err := git.OpenRepository(RepoPath(userName, repoName)) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	refs, err := repo.AllTags() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	tags := make([]string, len(refs)) | ||||
| 	for i, ref := range refs { | ||||
| 		tags[i] = ref.Name | ||||
| 	} | ||||
| 	return tags, nil | ||||
| } | ||||
|  | ||||
| func IsBranchExist(userName, repoName, branchName string) bool { | ||||
| 	repo, err := git.OpenRepository(RepoPath(userName, repoName)) | ||||
| 	if err != nil { | ||||
| 		return false | ||||
| 	} | ||||
| 	return repo.IsBranchExist(branchName) | ||||
| } | ||||
|  | ||||
| func GetTargetFile(userName, repoName, branchName, commitId, rpath string) (*RepoFile, error) { | ||||
| 	repo, err := git.OpenRepository(RepoPath(userName, repoName)) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	commit, err := repo.GetCommitOfBranch(branchName) | ||||
| 	if err != nil { | ||||
| 		commit, err = repo.GetCommit(commitId) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	parts := strings.Split(path.Clean(rpath), "/") | ||||
|  | ||||
| 	var entry *git.TreeEntry | ||||
| 	tree := commit.Tree | ||||
| 	for i, part := range parts { | ||||
| 		if i == len(parts)-1 { | ||||
| 			entry = tree.EntryByName(part) | ||||
| 			if entry == nil { | ||||
| 				return nil, ErrRepoFileNotExist | ||||
| 			} | ||||
| 		} else { | ||||
| 			tree, err = repo.SubTree(tree, part) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	size, err := repo.ObjectSize(entry.Id) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	repoFile := &RepoFile{ | ||||
| 		entry, | ||||
| 		rpath, | ||||
| 		size, | ||||
| 		repo, | ||||
| 		commit, | ||||
| 	} | ||||
|  | ||||
| 	return repoFile, nil | ||||
| } | ||||
|  | ||||
| // GetReposFiles returns a list of file object in given directory of repository. | ||||
| // func GetReposFilesOfBranch(userName, repoName, branchName, rpath string) ([]*RepoFile, error) { | ||||
| // 	return getReposFiles(userName, repoName, commitId, rpath) | ||||
| // } | ||||
|  | ||||
| // GetReposFiles returns a list of file object in given directory of repository. | ||||
| func GetReposFiles(userName, repoName, commitId, rpath string) ([]*RepoFile, error) { | ||||
| 	return getReposFiles(userName, repoName, commitId, rpath) | ||||
| } | ||||
|  | ||||
| func getReposFiles(userName, repoName, commitId string, rpath string) ([]*RepoFile, error) { | ||||
| 	repopath := RepoPath(userName, repoName) | ||||
| 	repo, err := git.OpenRepository(repopath) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	commit, err := repo.GetCommit(commitId) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	var repodirs []*RepoFile | ||||
| 	var repofiles []*RepoFile | ||||
| 	commit.Tree.Walk(func(dirname string, entry *git.TreeEntry) int { | ||||
| 		if dirname == rpath { | ||||
| 			// TODO: size get method shoule be improved | ||||
| 			size, err := repo.ObjectSize(entry.Id) | ||||
| 			if err != nil { | ||||
| 				return 0 | ||||
| 			} | ||||
|  | ||||
| 			cmd := exec.Command("git", "log", "-1", "--pretty=format:%H", commitId, "--", path.Join(dirname, entry.Name)) | ||||
| 			cmd.Dir = repopath | ||||
| 			out, err := cmd.Output() | ||||
| 			if err != nil { | ||||
| 				return 0 | ||||
| 			} | ||||
| 			filecm, err := repo.GetCommit(string(out)) | ||||
| 			if err != nil { | ||||
| 				return 0 | ||||
| 			} | ||||
|  | ||||
| 			rp := &RepoFile{ | ||||
| 				entry, | ||||
| 				path.Join(dirname, entry.Name), | ||||
| 				size, | ||||
| 				repo, | ||||
| 				filecm, | ||||
| 			} | ||||
|  | ||||
| 			if entry.IsFile() { | ||||
| 				repofiles = append(repofiles, rp) | ||||
| 			} else if entry.IsDir() { | ||||
| 				repodirs = append(repodirs, rp) | ||||
| 			} | ||||
| 		} | ||||
| 		return 0 | ||||
| 	}) | ||||
|  | ||||
| 	return append(repodirs, repofiles...), nil | ||||
| } | ||||
|  | ||||
| func GetCommit(userName, repoName, commitId string) (*git.Commit, error) { | ||||
| 	repo, err := git.OpenRepository(RepoPath(userName, repoName)) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return repo.GetCommit(commitId) | ||||
| } | ||||
|  | ||||
| // GetCommitsByBranch returns all commits of given branch of repository. | ||||
| func GetCommitsByBranch(userName, repoName, branchName string) (*list.List, error) { | ||||
| 	repo, err := git.OpenRepository(RepoPath(userName, repoName)) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	r, err := repo.LookupReference(fmt.Sprintf("refs/heads/%s", branchName)) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return r.AllCommits() | ||||
| } | ||||
|  | ||||
| // GetCommitsByCommitId returns all commits of given commitId of repository. | ||||
| func GetCommitsByCommitId(userName, repoName, commitId string) (*list.List, error) { | ||||
| 	repo, err := git.OpenRepository(RepoPath(userName, repoName)) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	oid, err := git.NewOidFromString(commitId) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return repo.CommitsBefore(oid) | ||||
| } | ||||
|  | ||||
| // Diff line types. | ||||
| const ( | ||||
| 	DIFF_LINE_PLAIN = iota + 1 | ||||
| 	DIFF_LINE_ADD | ||||
| 	DIFF_LINE_DEL | ||||
| 	DIFF_LINE_SECTION | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	DIFF_FILE_ADD = iota + 1 | ||||
| 	DIFF_FILE_CHANGE | ||||
| 	DIFF_FILE_DEL | ||||
| ) | ||||
|  | ||||
| type DiffLine struct { | ||||
| 	LeftIdx  int | ||||
| 	RightIdx int | ||||
| 	Type     int | ||||
| 	Content  string | ||||
| } | ||||
|  | ||||
| func (d DiffLine) GetType() int { | ||||
| 	return d.Type | ||||
| } | ||||
|  | ||||
| type DiffSection struct { | ||||
| 	Name  string | ||||
| 	Lines []*DiffLine | ||||
| } | ||||
|  | ||||
| type DiffFile struct { | ||||
| 	Name               string | ||||
| 	Addition, Deletion int | ||||
| 	Type               int | ||||
| 	Sections           []*DiffSection | ||||
| } | ||||
|  | ||||
| type Diff struct { | ||||
| 	TotalAddition, TotalDeletion int | ||||
| 	Files                        []*DiffFile | ||||
| } | ||||
|  | ||||
| func (diff *Diff) NumFiles() int { | ||||
| 	return len(diff.Files) | ||||
| } | ||||
|  | ||||
| const DIFF_HEAD = "diff --git " | ||||
|  | ||||
| func ParsePatch(reader io.Reader) (*Diff, error) { | ||||
| 	scanner := bufio.NewScanner(reader) | ||||
| 	var ( | ||||
| 		curFile    *DiffFile | ||||
| 		curSection = &DiffSection{ | ||||
| 			Lines: make([]*DiffLine, 0, 10), | ||||
| 		} | ||||
|  | ||||
| 		leftLine, rightLine int | ||||
| 	) | ||||
|  | ||||
| 	diff := &Diff{Files: make([]*DiffFile, 0)} | ||||
| 	var i int | ||||
| 	for scanner.Scan() { | ||||
| 		line := scanner.Text() | ||||
| 		// fmt.Println(i, line) | ||||
| 		if strings.HasPrefix(line, "+++ ") || strings.HasPrefix(line, "--- ") { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		i = i + 1 | ||||
| 		if line == "" { | ||||
| 			continue | ||||
| 		} | ||||
| 		if line[0] == ' ' { | ||||
| 			diffLine := &DiffLine{Type: DIFF_LINE_PLAIN, Content: line, LeftIdx: leftLine, RightIdx: rightLine} | ||||
| 			leftLine++ | ||||
| 			rightLine++ | ||||
| 			curSection.Lines = append(curSection.Lines, diffLine) | ||||
| 			continue | ||||
| 		} else if line[0] == '@' { | ||||
| 			curSection = &DiffSection{} | ||||
| 			curFile.Sections = append(curFile.Sections, curSection) | ||||
| 			ss := strings.Split(line, "@@") | ||||
| 			diffLine := &DiffLine{Type: DIFF_LINE_SECTION, Content: line} | ||||
| 			curSection.Lines = append(curSection.Lines, diffLine) | ||||
|  | ||||
| 			// Parse line number. | ||||
| 			ranges := strings.Split(ss[len(ss)-2][1:], " ") | ||||
| 			leftLine, _ = base.StrTo(strings.Split(ranges[0], ",")[0][1:]).Int() | ||||
| 			rightLine, _ = base.StrTo(strings.Split(ranges[1], ",")[0]).Int() | ||||
| 			continue | ||||
| 		} else if line[0] == '+' { | ||||
| 			curFile.Addition++ | ||||
| 			diff.TotalAddition++ | ||||
| 			diffLine := &DiffLine{Type: DIFF_LINE_ADD, Content: line, RightIdx: rightLine} | ||||
| 			rightLine++ | ||||
| 			curSection.Lines = append(curSection.Lines, diffLine) | ||||
| 			continue | ||||
| 		} else if line[0] == '-' { | ||||
| 			curFile.Deletion++ | ||||
| 			diff.TotalDeletion++ | ||||
| 			diffLine := &DiffLine{Type: DIFF_LINE_DEL, Content: line, LeftIdx: leftLine} | ||||
| 			if leftLine > 0 { | ||||
| 				leftLine++ | ||||
| 			} | ||||
| 			curSection.Lines = append(curSection.Lines, diffLine) | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		// Get new file. | ||||
| 		if strings.HasPrefix(line, DIFF_HEAD) { | ||||
| 			fs := strings.Split(line[len(DIFF_HEAD):], " ") | ||||
| 			a := fs[0] | ||||
|  | ||||
| 			curFile = &DiffFile{ | ||||
| 				Name:     a[strings.Index(a, "/")+1:], | ||||
| 				Type:     DIFF_FILE_CHANGE, | ||||
| 				Sections: make([]*DiffSection, 0, 10), | ||||
| 			} | ||||
| 			diff.Files = append(diff.Files, curFile) | ||||
|  | ||||
| 			// Check file diff type. | ||||
| 			for scanner.Scan() { | ||||
| 				switch { | ||||
| 				case strings.HasPrefix(scanner.Text(), "new file"): | ||||
| 					curFile.Type = DIFF_FILE_ADD | ||||
| 				case strings.HasPrefix(scanner.Text(), "deleted"): | ||||
| 					curFile.Type = DIFF_FILE_DEL | ||||
| 				case strings.HasPrefix(scanner.Text(), "index"): | ||||
| 					curFile.Type = DIFF_FILE_CHANGE | ||||
| 				} | ||||
| 				if curFile.Type > 0 { | ||||
| 					break | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return diff, nil | ||||
| } | ||||
|  | ||||
| func GetDiff(repoPath, commitid string) (*Diff, error) { | ||||
| 	repo, err := git.OpenRepository(repoPath) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	commit, err := repo.GetCommit(commitid) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	// First commit of repository. | ||||
| 	if commit.ParentCount() == 0 { | ||||
| 		rd, wr := io.Pipe() | ||||
| 		go func() { | ||||
| 			cmd := exec.Command("git", "show", commitid) | ||||
| 			cmd.Dir = repoPath | ||||
| 			cmd.Stdout = wr | ||||
| 			cmd.Stdin = os.Stdin | ||||
| 			cmd.Stderr = os.Stderr | ||||
| 			cmd.Run() | ||||
| 			wr.Close() | ||||
| 		}() | ||||
| 		defer rd.Close() | ||||
| 		return ParsePatch(rd) | ||||
| 	} | ||||
|  | ||||
| 	rd, wr := io.Pipe() | ||||
| 	go func() { | ||||
| 		cmd := exec.Command("git", "diff", commit.Parent(0).Oid.String(), commitid) | ||||
| 		cmd.Dir = repoPath | ||||
| 		cmd.Stdout = wr | ||||
| 		cmd.Stdin = os.Stdin | ||||
| 		cmd.Stderr = os.Stderr | ||||
| 		cmd.Run() | ||||
| 		wr.Close() | ||||
| 	}() | ||||
| 	defer rd.Close() | ||||
| 	return ParsePatch(rd) | ||||
| } | ||||
							
								
								
									
										234
									
								
								models/issue.go
									
									
									
									
									
								
							
							
						
						
									
										234
									
								
								models/issue.go
									
									
									
									
									
								
							| @@ -1,234 +0,0 @@ | ||||
| // Copyright 2014 The Gogs Authors. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| package models | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/gogits/gogs/modules/base" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	ErrIssueNotExist = errors.New("Issue does not exist") | ||||
| ) | ||||
|  | ||||
| // Issue represents an issue or pull request of repository. | ||||
| type Issue struct { | ||||
| 	Id              int64 | ||||
| 	Index           int64 // Index in one repository. | ||||
| 	Name            string | ||||
| 	RepoId          int64       `xorm:"index"` | ||||
| 	Repo            *Repository `xorm:"-"` | ||||
| 	PosterId        int64 | ||||
| 	Poster          *User `xorm:"-"` | ||||
| 	MilestoneId     int64 | ||||
| 	AssigneeId      int64 | ||||
| 	IsPull          bool // Indicates whether is a pull request or not. | ||||
| 	IsClosed        bool | ||||
| 	Labels          string `xorm:"TEXT"` | ||||
| 	Mentions        string `xorm:"TEXT"` | ||||
| 	Content         string `xorm:"TEXT"` | ||||
| 	RenderedContent string `xorm:"-"` | ||||
| 	NumComments     int | ||||
| 	Created         time.Time `xorm:"created"` | ||||
| 	Updated         time.Time `xorm:"updated"` | ||||
| } | ||||
|  | ||||
| // CreateIssue creates new issue for repository. | ||||
| func CreateIssue(userId, repoId, milestoneId, assigneeId int64, issueCount int, name, labels, content string, isPull bool) (issue *Issue, err error) { | ||||
| 	// TODO: find out mentions | ||||
| 	mentions := "" | ||||
|  | ||||
| 	sess := orm.NewSession() | ||||
| 	defer sess.Close() | ||||
| 	sess.Begin() | ||||
|  | ||||
| 	issue = &Issue{ | ||||
| 		Index:       int64(issueCount) + 1, | ||||
| 		Name:        name, | ||||
| 		RepoId:      repoId, | ||||
| 		PosterId:    userId, | ||||
| 		MilestoneId: milestoneId, | ||||
| 		AssigneeId:  assigneeId, | ||||
| 		IsPull:      isPull, | ||||
| 		Labels:      labels, | ||||
| 		Mentions:    mentions, | ||||
| 		Content:     content, | ||||
| 	} | ||||
| 	if _, err = sess.Insert(issue); err != nil { | ||||
| 		sess.Rollback() | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	rawSql := "UPDATE `repository` SET num_issues = num_issues + 1 WHERE id = ?" | ||||
| 	if _, err = sess.Exec(rawSql, repoId); err != nil { | ||||
| 		sess.Rollback() | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	if err = sess.Commit(); err != nil { | ||||
| 		sess.Rollback() | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return issue, nil | ||||
| } | ||||
|  | ||||
| // GetIssueById returns issue object by given id. | ||||
| func GetIssueByIndex(repoId, index int64) (*Issue, error) { | ||||
| 	issue := &Issue{RepoId: repoId, Index: index} | ||||
| 	has, err := orm.Get(issue) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} else if !has { | ||||
| 		return nil, ErrIssueNotExist | ||||
| 	} | ||||
| 	return issue, nil | ||||
| } | ||||
|  | ||||
| // GetIssues returns a list of issues by given conditions. | ||||
| func GetIssues(userId, repoId, posterId, milestoneId int64, page int, isClosed, isMention bool, labels, sortType string) ([]Issue, error) { | ||||
| 	sess := orm.Limit(20, (page-1)*20) | ||||
|  | ||||
| 	if repoId > 0 { | ||||
| 		sess.Where("repo_id=?", repoId).And("is_closed=?", isClosed) | ||||
| 	} else { | ||||
| 		sess.Where("is_closed=?", isClosed) | ||||
| 	} | ||||
|  | ||||
| 	if userId > 0 { | ||||
| 		sess.And("assignee_id=?", userId) | ||||
| 	} else if posterId > 0 { | ||||
| 		sess.And("poster_id=?", posterId) | ||||
| 	} else if isMention { | ||||
| 		sess.And("mentions like '%$" + base.ToStr(userId) + "|%'") | ||||
| 	} | ||||
|  | ||||
| 	if milestoneId > 0 { | ||||
| 		sess.And("milestone_id=?", milestoneId) | ||||
| 	} | ||||
|  | ||||
| 	if len(labels) > 0 { | ||||
| 		for _, label := range strings.Split(labels, ",") { | ||||
| 			sess.And("mentions like '%$" + label + "|%'") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	switch sortType { | ||||
| 	case "oldest": | ||||
| 		sess.Asc("created") | ||||
| 	case "recentupdate": | ||||
| 		sess.Desc("updated") | ||||
| 	case "leastupdate": | ||||
| 		sess.Asc("updated") | ||||
| 	case "mostcomment": | ||||
| 		sess.Desc("num_comments") | ||||
| 	case "leastcomment": | ||||
| 		sess.Asc("num_comments") | ||||
| 	default: | ||||
| 		sess.Desc("created") | ||||
| 	} | ||||
|  | ||||
| 	var issues []Issue | ||||
| 	err := sess.Find(&issues) | ||||
| 	return issues, err | ||||
| } | ||||
|  | ||||
| // GetUserIssueCount returns the number of issues that were created by given user in repository. | ||||
| func GetUserIssueCount(userId, repoId int64) int64 { | ||||
| 	count, _ := orm.Where("poster_id=?", userId).And("repo_id=?", repoId).Count(new(Issue)) | ||||
| 	return count | ||||
| } | ||||
|  | ||||
| // UpdateIssue updates information of issue. | ||||
| func UpdateIssue(issue *Issue) error { | ||||
| 	_, err := orm.Id(issue.Id).AllCols().Update(issue) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // Label represents a list of labels of repository for issues. | ||||
| type Label struct { | ||||
| 	Id     int64 | ||||
| 	RepoId int64 `xorm:"index"` | ||||
| 	Names  string | ||||
| 	Colors string | ||||
| } | ||||
|  | ||||
| // Milestone represents a milestone of repository. | ||||
| type Milestone struct { | ||||
| 	Id        int64 | ||||
| 	Name      string | ||||
| 	RepoId    int64 `xorm:"index"` | ||||
| 	IsClosed  bool | ||||
| 	Content   string | ||||
| 	NumIssues int | ||||
| 	DueDate   time.Time | ||||
| 	Created   time.Time `xorm:"created"` | ||||
| } | ||||
|  | ||||
| // Issue types. | ||||
| const ( | ||||
| 	IT_PLAIN  = iota // Pure comment. | ||||
| 	IT_REOPEN        // Issue reopen status change prompt. | ||||
| 	IT_CLOSE         // Issue close status change prompt. | ||||
| ) | ||||
|  | ||||
| // Comment represents a comment in commit and issue page. | ||||
| type Comment struct { | ||||
| 	Id       int64 | ||||
| 	Type     int | ||||
| 	PosterId int64 | ||||
| 	Poster   *User `xorm:"-"` | ||||
| 	IssueId  int64 | ||||
| 	CommitId int64 | ||||
| 	Line     int64 | ||||
| 	Content  string | ||||
| 	Created  time.Time `xorm:"created"` | ||||
| } | ||||
|  | ||||
| // CreateComment creates comment of issue or commit. | ||||
| func CreateComment(userId, repoId, issueId, commitId, line int64, cmtType int, content string) error { | ||||
| 	sess := orm.NewSession() | ||||
| 	defer sess.Close() | ||||
| 	sess.Begin() | ||||
|  | ||||
| 	if _, err := orm.Insert(&Comment{PosterId: userId, Type: cmtType, IssueId: issueId, | ||||
| 		CommitId: commitId, Line: line, Content: content}); err != nil { | ||||
| 		sess.Rollback() | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// Check comment type. | ||||
| 	switch cmtType { | ||||
| 	case IT_PLAIN: | ||||
| 		rawSql := "UPDATE `issue` SET num_comments = num_comments + 1 WHERE id = ?" | ||||
| 		if _, err := sess.Exec(rawSql, issueId); err != nil { | ||||
| 			sess.Rollback() | ||||
| 			return err | ||||
| 		} | ||||
| 	case IT_REOPEN: | ||||
| 		rawSql := "UPDATE `repository` SET num_closed_issues = num_closed_issues - 1 WHERE id = ?" | ||||
| 		if _, err := sess.Exec(rawSql, repoId); err != nil { | ||||
| 			sess.Rollback() | ||||
| 			return err | ||||
| 		} | ||||
| 	case IT_CLOSE: | ||||
| 		rawSql := "UPDATE `repository` SET num_closed_issues = num_closed_issues + 1 WHERE id = ?" | ||||
| 		if _, err := sess.Exec(rawSql, repoId); err != nil { | ||||
| 			sess.Rollback() | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return sess.Commit() | ||||
| } | ||||
|  | ||||
| // GetIssueComments returns list of comment by given issue id. | ||||
| func GetIssueComments(issueId int64) ([]Comment, error) { | ||||
| 	comments := make([]Comment, 0, 10) | ||||
| 	err := orm.Asc("created").Find(&comments, &Comment{IssueId: issueId}) | ||||
| 	return comments, err | ||||
| } | ||||
							
								
								
									
										131
									
								
								models/models.go
									
									
									
									
									
								
							
							
						
						
									
										131
									
								
								models/models.go
									
									
									
									
									
								
							| @@ -1,131 +0,0 @@ | ||||
| // Copyright 2014 The Gogs Authors. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| package models | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"path" | ||||
|  | ||||
| 	_ "github.com/go-sql-driver/mysql" | ||||
| 	_ "github.com/lib/pq" | ||||
| 	"github.com/lunny/xorm" | ||||
| 	// _ "github.com/mattn/go-sqlite3" | ||||
|  | ||||
| 	"github.com/gogits/gogs/modules/base" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	orm    *xorm.Engine | ||||
| 	tables []interface{} | ||||
|  | ||||
| 	HasEngine bool | ||||
|  | ||||
| 	DbCfg struct { | ||||
| 		Type, Host, Name, User, Pwd, Path, SslMode string | ||||
| 	} | ||||
|  | ||||
| 	UseSQLite3 bool | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	tables = append(tables, new(User), new(PublicKey), new(Repository), new(Watch), | ||||
| 		new(Action), new(Access), new(Issue), new(Comment), new(Oauth2)) | ||||
| } | ||||
|  | ||||
| func LoadModelsConfig() { | ||||
| 	DbCfg.Type = base.Cfg.MustValue("database", "DB_TYPE") | ||||
| 	if DbCfg.Type == "sqlite3" { | ||||
| 		UseSQLite3 = true | ||||
| 	} | ||||
| 	DbCfg.Host = base.Cfg.MustValue("database", "HOST") | ||||
| 	DbCfg.Name = base.Cfg.MustValue("database", "NAME") | ||||
| 	DbCfg.User = base.Cfg.MustValue("database", "USER") | ||||
| 	DbCfg.Pwd = base.Cfg.MustValue("database", "PASSWD") | ||||
| 	DbCfg.SslMode = base.Cfg.MustValue("database", "SSL_MODE") | ||||
| 	DbCfg.Path = base.Cfg.MustValue("database", "PATH", "data/gogs.db") | ||||
| } | ||||
|  | ||||
| func NewTestEngine(x *xorm.Engine) (err error) { | ||||
| 	switch DbCfg.Type { | ||||
| 	case "mysql": | ||||
| 		x, err = xorm.NewEngine("mysql", fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8", | ||||
| 			DbCfg.User, DbCfg.Pwd, DbCfg.Host, DbCfg.Name)) | ||||
| 	case "postgres": | ||||
| 		x, err = xorm.NewEngine("postgres", fmt.Sprintf("user=%s password=%s dbname=%s sslmode=%s", | ||||
| 			DbCfg.User, DbCfg.Pwd, DbCfg.Name, DbCfg.SslMode)) | ||||
| 	// case "sqlite3": | ||||
| 	// 	os.MkdirAll(path.Dir(DbCfg.Path), os.ModePerm) | ||||
| 	// 	x, err = xorm.NewEngine("sqlite3", DbCfg.Path) | ||||
| 	default: | ||||
| 		return fmt.Errorf("Unknown database type: %s", DbCfg.Type) | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("models.init(fail to conntect database): %v", err) | ||||
| 	} | ||||
| 	return x.Sync(tables...) | ||||
| } | ||||
|  | ||||
| func SetEngine() (err error) { | ||||
| 	switch DbCfg.Type { | ||||
| 	case "mysql": | ||||
| 		orm, err = xorm.NewEngine("mysql", fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8", | ||||
| 			DbCfg.User, DbCfg.Pwd, DbCfg.Host, DbCfg.Name)) | ||||
| 	case "postgres": | ||||
| 		orm, err = xorm.NewEngine("postgres", fmt.Sprintf("user=%s password=%s dbname=%s sslmode=%s", | ||||
| 			DbCfg.User, DbCfg.Pwd, DbCfg.Name, DbCfg.SslMode)) | ||||
| 	case "sqlite3": | ||||
| 		os.MkdirAll(path.Dir(DbCfg.Path), os.ModePerm) | ||||
| 		orm, err = xorm.NewEngine("sqlite3", DbCfg.Path) | ||||
| 	default: | ||||
| 		return fmt.Errorf("Unknown database type: %s", DbCfg.Type) | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("models.init(fail to conntect database): %v", err) | ||||
| 	} | ||||
|  | ||||
| 	// WARNNING: for serv command, MUST remove the output to os.stdout, | ||||
| 	// so use log file to instead print to stdout. | ||||
| 	execDir, _ := base.ExecDir() | ||||
| 	logPath := execDir + "/log/xorm.log" | ||||
| 	os.MkdirAll(path.Dir(logPath), os.ModePerm) | ||||
|  | ||||
| 	f, err := os.Create(logPath) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("models.init(fail to create xorm.log): %v", err) | ||||
| 	} | ||||
| 	orm.Logger = f | ||||
|  | ||||
| 	orm.ShowSQL = true | ||||
| 	orm.ShowDebug = true | ||||
| 	orm.ShowErr = true | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func NewEngine() (err error) { | ||||
| 	if err = SetEngine(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if err = orm.Sync(tables...); err != nil { | ||||
| 		return fmt.Errorf("sync database struct error: %v\n", err) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| type Statistic struct { | ||||
| 	Counter struct { | ||||
| 		User, PublicKey, Repo, Watch, Action, Access int64 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func GetStatistic() (stats Statistic) { | ||||
| 	stats.Counter.User, _ = orm.Count(new(User)) | ||||
| 	stats.Counter.PublicKey, _ = orm.Count(new(PublicKey)) | ||||
| 	stats.Counter.Repo, _ = orm.Count(new(Repository)) | ||||
| 	stats.Counter.Watch, _ = orm.Count(new(Watch)) | ||||
| 	stats.Counter.Action, _ = orm.Count(new(Action)) | ||||
| 	stats.Counter.Access, _ = orm.Count(new(Access)) | ||||
| 	return | ||||
| } | ||||
| @@ -1,55 +0,0 @@ | ||||
| // Copyright 2014 The Gogs Authors. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| package models | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/lunny/xorm" | ||||
| 	_ "github.com/mattn/go-sqlite3" | ||||
| 	. "github.com/smartystreets/goconvey/convey" | ||||
|  | ||||
| 	"github.com/gogits/gogs/modules/base" | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	var err error | ||||
| 	orm, err = xorm.NewEngine("sqlite3", "./test.db") | ||||
| 	if err != nil { | ||||
| 		fmt.Println(err) | ||||
| 	} | ||||
|  | ||||
| 	orm.ShowSQL = true | ||||
| 	orm.ShowDebug = true | ||||
|  | ||||
| 	err = orm.Sync(&User{}, &Repository{}) | ||||
| 	if err != nil { | ||||
| 		fmt.Println(err) | ||||
| 	} | ||||
|  | ||||
| 	base.RepoRootPath = "test" | ||||
| } | ||||
|  | ||||
| func TestCreateRepository(t *testing.T) { | ||||
| 	user := User{Id: 1, Name: "foobar", Type: UT_INDIVIDUAL} | ||||
| 	_, err := CreateRepository(&user, "test", "", "", "test repo desc", false, false) | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestDeleteRepository(t *testing.T) { | ||||
| 	err := DeleteRepository(1, 1, "foobar") | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestCommitRepoAction(t *testing.T) { | ||||
| 	Convey("Create a commit repository action", t, func() { | ||||
|  | ||||
| 	}) | ||||
| } | ||||
| @@ -1,49 +0,0 @@ | ||||
| // Copyright 2014 The Gogs Authors. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| package models | ||||
|  | ||||
| import "errors" | ||||
|  | ||||
| // OT: Oauth2 Type | ||||
| const ( | ||||
| 	OT_GITHUB = iota + 1 | ||||
| 	OT_GOOGLE | ||||
| 	OT_TWITTER | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	ErrOauth2RecordNotExists       = errors.New("not exists oauth2 record") | ||||
| 	ErrOauth2NotAssociatedWithUser = errors.New("not associated with user") | ||||
| ) | ||||
|  | ||||
| type Oauth2 struct { | ||||
| 	Id       int64 | ||||
| 	Uid      int64  // userId | ||||
| 	User     *User  `xorm:"-"` | ||||
| 	Type     int    `xorm:"pk unique(oauth)"` // twitter,github,google... | ||||
| 	Identity string `xorm:"pk unique(oauth)"` // id.. | ||||
| 	Token    string `xorm:"VARCHAR(200) not null"` | ||||
| } | ||||
|  | ||||
| func AddOauth2(oa *Oauth2) (err error) { | ||||
| 	if _, err = orm.Insert(oa); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func GetOauth2(identity string) (oa *Oauth2, err error) { | ||||
| 	oa = &Oauth2{Identity: identity} | ||||
| 	isExist, err := orm.Get(oa) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} else if !isExist { | ||||
| 		return nil, ErrOauth2RecordNotExists | ||||
| 	} else if oa.Uid == 0 { | ||||
| 		return oa, ErrOauth2NotAssociatedWithUser | ||||
| 	} | ||||
| 	oa.User, err = GetUserById(oa.Uid) | ||||
| 	return oa, err | ||||
| } | ||||
| @@ -1,228 +0,0 @@ | ||||
| // Copyright 2014 The Gogs Authors. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| package models | ||||
|  | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"os/exec" | ||||
| 	"path" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/Unknwon/com" | ||||
|  | ||||
| 	"github.com/gogits/gogs/modules/log" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	// "### autogenerated by gitgos, DO NOT EDIT\n" | ||||
| 	TPL_PUBLICK_KEY = `command="%s serv key-%d",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty %s` | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	ErrKeyAlreadyExist = errors.New("Public key already exist") | ||||
| ) | ||||
|  | ||||
| var sshOpLocker = sync.Mutex{} | ||||
|  | ||||
| var ( | ||||
| 	sshPath string | ||||
| 	appPath string | ||||
| ) | ||||
|  | ||||
| // exePath returns the executable path. | ||||
| func exePath() (string, error) { | ||||
| 	file, err := exec.LookPath(os.Args[0]) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	return filepath.Abs(file) | ||||
| } | ||||
|  | ||||
| // homeDir returns the home directory of current user. | ||||
| func homeDir() string { | ||||
| 	home, err := com.HomeDir() | ||||
| 	if err != nil { | ||||
| 		return "/" | ||||
| 	} | ||||
| 	return home | ||||
| } | ||||
|  | ||||
| func init() { | ||||
| 	var err error | ||||
|  | ||||
| 	appPath, err = exePath() | ||||
| 	if err != nil { | ||||
| 		fmt.Printf("publickey.init(fail to get app path): %v\n", err) | ||||
| 		os.Exit(2) | ||||
| 	} | ||||
|  | ||||
| 	// Determine and create .ssh path. | ||||
| 	sshPath = filepath.Join(homeDir(), ".ssh") | ||||
| 	if err = os.MkdirAll(sshPath, os.ModePerm); err != nil { | ||||
| 		fmt.Printf("publickey.init(fail to create sshPath(%s)): %v\n", sshPath, err) | ||||
| 		os.Exit(2) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // PublicKey represents a SSH key of user. | ||||
| type PublicKey struct { | ||||
| 	Id          int64 | ||||
| 	OwnerId     int64  `xorm:"unique(s) index not null"` | ||||
| 	Name        string `xorm:"unique(s) not null"` | ||||
| 	Fingerprint string | ||||
| 	Content     string    `xorm:"TEXT not null"` | ||||
| 	Created     time.Time `xorm:"created"` | ||||
| 	Updated     time.Time `xorm:"updated"` | ||||
| } | ||||
|  | ||||
| // GenAuthorizedKey returns formatted public key string. | ||||
| func GenAuthorizedKey(keyId int64, key string) string { | ||||
| 	return fmt.Sprintf(TPL_PUBLICK_KEY+"\n", appPath, keyId, key) | ||||
| } | ||||
|  | ||||
| // AddPublicKey adds new public key to database and SSH key file. | ||||
| func AddPublicKey(key *PublicKey) (err error) { | ||||
| 	// Check if public key name has been used. | ||||
| 	has, err := orm.Get(key) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} else if has { | ||||
| 		return ErrKeyAlreadyExist | ||||
| 	} | ||||
|  | ||||
| 	// Calculate fingerprint. | ||||
| 	tmpPath := strings.Replace(filepath.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond()), | ||||
| 		"id_rsa.pub"), "\\", "/", -1) | ||||
| 	os.MkdirAll(path.Dir(tmpPath), os.ModePerm) | ||||
| 	if err = ioutil.WriteFile(tmpPath, []byte(key.Content), os.ModePerm); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	stdout, _, err := com.ExecCmd("ssh-keygen", "-l", "-f", tmpPath) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} else if len(stdout) < 2 { | ||||
| 		return errors.New("Not enough output for calculating fingerprint") | ||||
| 	} | ||||
| 	key.Fingerprint = strings.Split(stdout, " ")[1] | ||||
|  | ||||
| 	// Save SSH key. | ||||
| 	if _, err = orm.Insert(key); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if err = SaveAuthorizedKeyFile(key); err != nil { | ||||
| 		if _, err2 := orm.Delete(key); err2 != nil { | ||||
| 			return err2 | ||||
| 		} | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func rewriteAuthorizedKeys(key *PublicKey, p, tmpP string) error { | ||||
| 	// Delete SSH key in SSH key file. | ||||
| 	sshOpLocker.Lock() | ||||
| 	defer sshOpLocker.Unlock() | ||||
|  | ||||
| 	fr, err := os.Open(p) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	defer fr.Close() | ||||
|  | ||||
| 	fw, err := os.Create(tmpP) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	defer fw.Close() | ||||
|  | ||||
| 	buf := bufio.NewReader(fr) | ||||
| 	for { | ||||
| 		line, errRead := buf.ReadString('\n') | ||||
| 		line = strings.TrimSpace(line) | ||||
|  | ||||
| 		if errRead != nil { | ||||
| 			if errRead != io.EOF { | ||||
| 				return errRead | ||||
| 			} | ||||
|  | ||||
| 			// Reached end of file, if nothing to read then break, | ||||
| 			// otherwise handle the last line. | ||||
| 			if len(line) == 0 { | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// Found the line and copy rest of file. | ||||
| 		if strings.Contains(line, fmt.Sprintf("key-%d", key.Id)) && strings.Contains(line, key.Content) { | ||||
| 			continue | ||||
| 		} | ||||
| 		// Still finding the line, copy the line that currently read. | ||||
| 		if _, err = fw.WriteString(line + "\n"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		if errRead == io.EOF { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // DeletePublicKey deletes SSH key information both in database and authorized_keys file. | ||||
| func DeletePublicKey(key *PublicKey) (err error) { | ||||
| 	// Delete SSH key in database. | ||||
| 	has, err := orm.Id(key.Id).Get(key) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} else if !has { | ||||
| 		return errors.New("Public key does not exist") | ||||
| 	} | ||||
| 	if _, err = orm.Delete(key); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	p := filepath.Join(sshPath, "authorized_keys") | ||||
| 	tmpP := filepath.Join(sshPath, "authorized_keys.tmp") | ||||
| 	log.Trace("ssh.DeletePublicKey(authorized_keys): %s", p) | ||||
|  | ||||
| 	if err = rewriteAuthorizedKeys(key, p, tmpP); err != nil { | ||||
| 		return err | ||||
| 	} else if err = os.Remove(p); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return os.Rename(tmpP, p) | ||||
| } | ||||
|  | ||||
| // ListPublicKey returns a list of public keys that user has. | ||||
| func ListPublicKey(userId int64) ([]PublicKey, error) { | ||||
| 	keys := make([]PublicKey, 0) | ||||
| 	err := orm.Find(&keys, &PublicKey{OwnerId: userId}) | ||||
| 	return keys, err | ||||
| } | ||||
|  | ||||
| // SaveAuthorizedKeyFile writes SSH key content to SSH key file. | ||||
| func SaveAuthorizedKeyFile(key *PublicKey) error { | ||||
| 	sshOpLocker.Lock() | ||||
| 	defer sshOpLocker.Unlock() | ||||
|  | ||||
| 	p := filepath.Join(sshPath, "authorized_keys") | ||||
| 	f, err := os.OpenFile(p, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	defer f.Close() | ||||
|  | ||||
| 	_, err = f.WriteString(GenAuthorizedKey(key.Id, key.Content)) | ||||
| 	return err | ||||
| } | ||||
							
								
								
									
										647
									
								
								models/repo.go
									
									
									
									
									
								
							
							
						
						
									
										647
									
								
								models/repo.go
									
									
									
									
									
								
							| @@ -1,647 +0,0 @@ | ||||
| // Copyright 2014 The Gogs Authors. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| package models | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"os/exec" | ||||
| 	"path" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 	"unicode/utf8" | ||||
|  | ||||
| 	"github.com/Unknwon/cae/zip" | ||||
| 	"github.com/Unknwon/com" | ||||
|  | ||||
| 	"github.com/gogits/git" | ||||
|  | ||||
| 	"github.com/gogits/gogs/modules/base" | ||||
| 	"github.com/gogits/gogs/modules/log" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	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 ( | ||||
| 	LanguageIgns, Licenses []string | ||||
| ) | ||||
|  | ||||
| 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. | ||||
| 	stdout, _, err := com.ExecCmd("git", "config", "--get", "user.name") | ||||
| 	if err != nil { | ||||
| 		fmt.Printf("repo.init(fail to get git user.name): %v", err) | ||||
| 		os.Exit(2) | ||||
| 	} else if len(stdout) == 0 { | ||||
| 		if _, _, err = com.ExecCmd("git", "config", "--global", "user.email", "gogitservice@gmail.com"); err != nil { | ||||
| 			fmt.Printf("repo.init(fail to set git user.email): %v", err) | ||||
| 			os.Exit(2) | ||||
| 		} else if _, _, err = com.ExecCmd("git", "config", "--global", "user.name", "Gogs"); err != nil { | ||||
| 			fmt.Printf("repo.init(fail to set git user.name): %v", err) | ||||
| 			os.Exit(2) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // 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 | ||||
| 	NumWatches      int | ||||
| 	NumStars        int | ||||
| 	NumForks        int | ||||
| 	NumIssues       int | ||||
| 	NumReleases     int `xorm:"NOT NULL"` | ||||
| 	NumClosedIssues int | ||||
| 	NumOpenIssues   int `xorm:"-"` | ||||
| 	IsPrivate       bool | ||||
| 	IsBare          bool | ||||
| 	IsGoget         bool | ||||
| 	Created         time.Time `xorm:"created"` | ||||
| 	Updated         time.Time `xorm:"updated"` | ||||
| } | ||||
|  | ||||
| // IsRepositoryExist returns true if the repository with given name under user has already existed. | ||||
| func IsRepositoryExist(user *User, repoName string) (bool, error) { | ||||
| 	repo := Repository{OwnerId: user.Id} | ||||
| 	has, err := orm.Where("lower_name = ?", strings.ToLower(repoName)).Get(&repo) | ||||
| 	if err != nil { | ||||
| 		return has, err | ||||
| 	} else if !has { | ||||
| 		return false, nil | ||||
| 	} | ||||
|  | ||||
| 	return com.IsDir(RepoPath(user.Name, repoName)), nil | ||||
| } | ||||
|  | ||||
| var ( | ||||
| 	illegalEquals  = []string{"raw", "install", "api", "avatar", "user", "help", "stars", "issues", "pulls", "commits", "repo", "template", "admin"} | ||||
| 	illegalSuffixs = []string{".git"} | ||||
| ) | ||||
|  | ||||
| // IsLegalName returns false if name contains illegal characters. | ||||
| func IsLegalName(repoName string) bool { | ||||
| 	repoName = strings.ToLower(repoName) | ||||
| 	for _, char := range illegalEquals { | ||||
| 		if repoName == char { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	for _, char := range illegalSuffixs { | ||||
| 		if strings.HasSuffix(repoName, char) { | ||||
| 			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 | ||||
| 	} else if isExist { | ||||
| 		return nil, ErrRepoAlreadyExist | ||||
| 	} | ||||
|  | ||||
| 	repo := &Repository{ | ||||
| 		OwnerId:     user.Id, | ||||
| 		Name:        repoName, | ||||
| 		LowerName:   strings.ToLower(repoName), | ||||
| 		Description: desc, | ||||
| 		IsPrivate:   private, | ||||
| 		IsBare:      repoLang == "" && license == "" && !initReadme, | ||||
| 	} | ||||
| 	repoPath := RepoPath(user.Name, repoName) | ||||
|  | ||||
| 	sess := orm.NewSession() | ||||
| 	defer sess.Close() | ||||
| 	sess.Begin() | ||||
|  | ||||
| 	if _, err = sess.Insert(repo); err != nil { | ||||
| 		if err2 := os.RemoveAll(repoPath); err2 != nil { | ||||
| 			log.Error("repo.CreateRepository(repo): %v", err) | ||||
| 			return nil, errors.New(fmt.Sprintf( | ||||
| 				"delete repo directory %s/%s failed(1): %v", user.Name, repoName, err2)) | ||||
| 		} | ||||
| 		sess.Rollback() | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	access := Access{ | ||||
| 		UserName: user.LowerName, | ||||
| 		RepoName: strings.ToLower(path.Join(user.Name, repo.Name)), | ||||
| 		Mode:     AU_WRITABLE, | ||||
| 	} | ||||
| 	if _, err = sess.Insert(&access); err != nil { | ||||
| 		sess.Rollback() | ||||
| 		if err2 := os.RemoveAll(repoPath); err2 != nil { | ||||
| 			log.Error("repo.CreateRepository(access): %v", err) | ||||
| 			return nil, errors.New(fmt.Sprintf( | ||||
| 				"delete repo directory %s/%s failed(2): %v", user.Name, repoName, err2)) | ||||
| 		} | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	rawSql := "UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?" | ||||
| 	if _, err = sess.Exec(rawSql, user.Id); err != nil { | ||||
| 		sess.Rollback() | ||||
| 		if err2 := os.RemoveAll(repoPath); err2 != nil { | ||||
| 			log.Error("repo.CreateRepository(repo count): %v", err) | ||||
| 			return nil, errors.New(fmt.Sprintf( | ||||
| 				"delete repo directory %s/%s failed(3): %v", user.Name, repoName, err2)) | ||||
| 		} | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	if err = sess.Commit(); err != nil { | ||||
| 		sess.Rollback() | ||||
| 		if err2 := os.RemoveAll(repoPath); err2 != nil { | ||||
| 			log.Error("repo.CreateRepository(commit): %v", err) | ||||
| 			return nil, errors.New(fmt.Sprintf( | ||||
| 				"delete repo directory %s/%s failed(3): %v", user.Name, repoName, err2)) | ||||
| 		} | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	c := exec.Command("git", "update-server-info") | ||||
| 	c.Dir = repoPath | ||||
| 	if err = c.Run(); err != nil { | ||||
| 		log.Error("repo.CreateRepository(exec update-server-info): %v", err) | ||||
| 	} | ||||
|  | ||||
| 	if err = NewRepoAction(user, repo); err != nil { | ||||
| 		log.Error("repo.CreateRepository(NewRepoAction): %v", err) | ||||
| 	} | ||||
|  | ||||
| 	if err = WatchRepo(user.Id, repo.Id, true); err != nil { | ||||
| 		log.Error("repo.CreateRepository(WatchRepo): %v", err) | ||||
| 	} | ||||
|  | ||||
| 	if err = initRepository(repoPath, user, repo, initReadme, repoLang, license); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return repo, nil | ||||
| } | ||||
|  | ||||
| // extractGitBareZip extracts git-bare.zip to repository path. | ||||
| func extractGitBareZip(repoPath string) error { | ||||
| 	z, err := zip.Open("conf/content/git-bare.zip") | ||||
| 	if err != nil { | ||||
| 		fmt.Println("shi?") | ||||
| 		return err | ||||
| 	} | ||||
| 	defer z.Close() | ||||
|  | ||||
| 	return z.ExtractTo(repoPath) | ||||
| } | ||||
|  | ||||
| // initRepoCommit temporarily changes with work directory. | ||||
| func initRepoCommit(tmpPath string, sig *git.Signature) (err error) { | ||||
| 	var stderr string | ||||
| 	if _, stderr, err = com.ExecCmdDir(tmpPath, "git", "add", "--all"); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if len(stderr) > 0 { | ||||
| 		log.Trace("stderr(1): %s", stderr) | ||||
| 	} | ||||
|  | ||||
| 	if _, stderr, err = com.ExecCmdDir(tmpPath, "git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), | ||||
| 		"-m", "Init commit"); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if len(stderr) > 0 { | ||||
| 		log.Trace("stderr(2): %s", stderr) | ||||
| 	} | ||||
|  | ||||
| 	if _, stderr, err = com.ExecCmdDir(tmpPath, "git", "push", "origin", "master"); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if len(stderr) > 0 { | ||||
| 		log.Trace("stderr(3): %s", stderr) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func createHookUpdate(hookPath, content string) error { | ||||
| 	pu, err := os.OpenFile(hookPath, os.O_CREATE|os.O_WRONLY, 0777) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	defer pu.Close() | ||||
|  | ||||
| 	_, err = pu.WriteString(content) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // SetRepoEnvs sets environment variables for command update. | ||||
| func SetRepoEnvs(userId int64, userName, repoName string) { | ||||
| 	os.Setenv("userId", base.ToStr(userId)) | ||||
| 	os.Setenv("userName", userName) | ||||
| 	os.Setenv("repoName", repoName) | ||||
| } | ||||
|  | ||||
| // InitRepository initializes README and .gitignore if needed. | ||||
| func initRepository(f string, user *User, repo *Repository, initReadme bool, repoLang, license string) error { | ||||
| 	repoPath := RepoPath(user.Name, repo.Name) | ||||
|  | ||||
| 	// Create bare new repository. | ||||
| 	if err := extractGitBareZip(repoPath); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// hook/post-update | ||||
| 	if err := createHookUpdate(filepath.Join(repoPath, "hooks", "update"), | ||||
| 		fmt.Sprintf("#!/usr/bin/env bash\n%s update $1 $2 $3\n", | ||||
| 			strings.Replace(appPath, "\\", "/", -1))); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// Initialize repository according to user's choice. | ||||
| 	fileName := map[string]string{} | ||||
| 	if initReadme { | ||||
| 		fileName["readme"] = "README.md" | ||||
| 	} | ||||
| 	if repoLang != "" { | ||||
| 		fileName["gitign"] = ".gitignore" | ||||
| 	} | ||||
| 	if license != "" { | ||||
| 		fileName["license"] = "LICENSE" | ||||
| 	} | ||||
|  | ||||
| 	// Clone to temprory path and do the init commit. | ||||
| 	tmpDir := filepath.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond())) | ||||
| 	os.MkdirAll(tmpDir, os.ModePerm) | ||||
|  | ||||
| 	if _, _, err := com.ExecCmd("git", "clone", repoPath, tmpDir); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// README | ||||
| 	if initReadme { | ||||
| 		defaultReadme := repo.Name + "\n" + strings.Repeat("=", | ||||
| 			utf8.RuneCountInString(repo.Name)) + "\n\n" + repo.Description | ||||
| 		if err := ioutil.WriteFile(filepath.Join(tmpDir, fileName["readme"]), | ||||
| 			[]byte(defaultReadme), 0644); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// .gitignore | ||||
| 	if repoLang != "" { | ||||
| 		filePath := "conf/gitignore/" + repoLang | ||||
| 		if com.IsFile(filePath) { | ||||
| 			if _, err := com.Copy(filePath, | ||||
| 				filepath.Join(tmpDir, fileName["gitign"])); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// LICENSE | ||||
| 	if license != "" { | ||||
| 		filePath := "conf/license/" + license | ||||
| 		if com.IsFile(filePath) { | ||||
| 			if _, err := com.Copy(filePath, | ||||
| 				filepath.Join(tmpDir, fileName["license"])); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if len(fileName) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	SetRepoEnvs(user.Id, user.Name, repo.Name) | ||||
|  | ||||
| 	// Apply changes and commit. | ||||
| 	return initRepoCommit(tmpDir, user.NewGitSig()) | ||||
| } | ||||
|  | ||||
| // 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), strings.ToLower(repoName)+".git") | ||||
| } | ||||
|  | ||||
| // TransferOwnership transfers all corresponding setting from old user to new one. | ||||
| func TransferOwnership(user *User, newOwner string, repo *Repository) (err error) { | ||||
| 	newUser, err := GetUserByName(newOwner) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// Update accesses. | ||||
| 	accesses := make([]Access, 0, 10) | ||||
| 	if err = orm.Find(&accesses, &Access{RepoName: user.LowerName + "/" + repo.LowerName}); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	sess := orm.NewSession() | ||||
| 	defer sess.Close() | ||||
| 	if err = sess.Begin(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	for i := range accesses { | ||||
| 		accesses[i].RepoName = newUser.LowerName + "/" + repo.LowerName | ||||
| 		if accesses[i].UserName == user.LowerName { | ||||
| 			accesses[i].UserName = newUser.LowerName | ||||
| 		} | ||||
| 		if err = UpdateAccessWithSession(sess, &accesses[i]); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Update repository. | ||||
| 	repo.OwnerId = newUser.Id | ||||
| 	if _, err := sess.Id(repo.Id).Update(repo); err != nil { | ||||
| 		sess.Rollback() | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// Update user repository number. | ||||
| 	rawSql := "UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?" | ||||
| 	if _, err = sess.Exec(rawSql, newUser.Id); err != nil { | ||||
| 		sess.Rollback() | ||||
| 		return err | ||||
| 	} | ||||
| 	rawSql = "UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?" | ||||
| 	if _, err = sess.Exec(rawSql, user.Id); err != nil { | ||||
| 		sess.Rollback() | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// Add watch of new owner to repository. | ||||
| 	if !IsWatching(newUser.Id, repo.Id) { | ||||
| 		if err = WatchRepo(newUser.Id, repo.Id, true); err != nil { | ||||
| 			sess.Rollback() | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if err = TransferRepoAction(user, newUser, repo); err != nil { | ||||
| 		sess.Rollback() | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// Change repository directory name. | ||||
| 	if err = os.Rename(RepoPath(user.Name, repo.Name), RepoPath(newUser.Name, repo.Name)); err != nil { | ||||
| 		sess.Rollback() | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return sess.Commit() | ||||
| } | ||||
|  | ||||
| // ChangeRepositoryName changes all corresponding setting from old repository name to new one. | ||||
| func ChangeRepositoryName(userName, oldRepoName, newRepoName string) (err error) { | ||||
| 	// Update accesses. | ||||
| 	accesses := make([]Access, 0, 10) | ||||
| 	if err = orm.Find(&accesses, &Access{RepoName: strings.ToLower(userName + "/" + oldRepoName)}); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	sess := orm.NewSession() | ||||
| 	defer sess.Close() | ||||
| 	if err = sess.Begin(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	for i := range accesses { | ||||
| 		accesses[i].RepoName = userName + "/" + newRepoName | ||||
| 		if err = UpdateAccessWithSession(sess, &accesses[i]); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Change repository directory name. | ||||
| 	if err = os.Rename(RepoPath(userName, oldRepoName), RepoPath(userName, newRepoName)); err != nil { | ||||
| 		sess.Rollback() | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return sess.Commit() | ||||
| } | ||||
|  | ||||
| func UpdateRepository(repo *Repository) error { | ||||
| 	repo.LowerName = strings.ToLower(repo.Name) | ||||
|  | ||||
| 	if len(repo.Description) > 255 { | ||||
| 		repo.Description = repo.Description[:255] | ||||
| 	} | ||||
| 	if len(repo.Website) > 255 { | ||||
| 		repo.Website = repo.Website[:255] | ||||
| 	} | ||||
| 	_, err := orm.Id(repo.Id).AllCols().Update(repo) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // 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 | ||||
| 	} | ||||
|  | ||||
| 	sess := orm.NewSession() | ||||
| 	defer sess.Close() | ||||
| 	if err = sess.Begin(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if _, err = sess.Delete(&Repository{Id: repoId}); err != nil { | ||||
| 		sess.Rollback() | ||||
| 		return err | ||||
| 	} | ||||
| 	if _, err := sess.Delete(&Access{RepoName: strings.ToLower(path.Join(userName, repo.Name))}); err != nil { | ||||
| 		sess.Rollback() | ||||
| 		return err | ||||
| 	} | ||||
| 	rawSql := "UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?" | ||||
| 	if _, err = sess.Exec(rawSql, userId); err != nil { | ||||
| 		sess.Rollback() | ||||
| 		return err | ||||
| 	} | ||||
| 	if _, err = sess.Delete(&Watch{RepoId: repoId}); err != nil { | ||||
| 		sess.Rollback() | ||||
| 		return err | ||||
| 	} | ||||
| 	if err = sess.Commit(); err != nil { | ||||
| 		sess.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(userId int64, repoName string) (*Repository, error) { | ||||
| 	repo := &Repository{ | ||||
| 		OwnerId:   userId, | ||||
| 		LowerName: strings.ToLower(repoName), | ||||
| 	} | ||||
| 	has, err := orm.Get(repo) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} else if !has { | ||||
| 		return nil, ErrRepoNotExist | ||||
| 	} | ||||
| 	return repo, err | ||||
| } | ||||
|  | ||||
| // GetRepositoryById returns the repository by given id if exists. | ||||
| func GetRepositoryById(id int64) (*Repository, error) { | ||||
| 	repo := &Repository{} | ||||
| 	has, err := orm.Id(id).Get(repo) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} else if !has { | ||||
| 		return nil, ErrRepoNotExist | ||||
| 	} | ||||
| 	return repo, err | ||||
| } | ||||
|  | ||||
| // GetRepositories returns the list of repositories of given user. | ||||
| func GetRepositories(user *User) ([]Repository, error) { | ||||
| 	repos := make([]Repository, 0, 10) | ||||
| 	err := orm.Desc("updated").Find(&repos, &Repository{OwnerId: user.Id}) | ||||
| 	return repos, err | ||||
| } | ||||
|  | ||||
| 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 | ||||
| } | ||||
|  | ||||
| // NotifyWatchers creates batch of actions for every watcher. | ||||
| func NotifyWatchers(act *Action) error { | ||||
| 	// Add feeds for user self and all watchers. | ||||
| 	watches, err := GetWatches(act.RepoId) | ||||
| 	if err != nil { | ||||
| 		return errors.New("repo.NotifyWatchers(get watches): " + err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	// Add feed for actioner. | ||||
| 	act.UserId = act.ActUserId | ||||
| 	if _, err = orm.InsertOne(act); err != nil { | ||||
| 		return errors.New("repo.NotifyWatchers(create action): " + err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	for i := range watches { | ||||
| 		if act.ActUserId == watches[i].UserId { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		act.Id = 0 | ||||
| 		act.UserId = watches[i].UserId | ||||
| 		if _, err = orm.InsertOne(act); err != nil { | ||||
| 			return errors.New("repo.NotifyWatchers(create action): " + err.Error()) | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // IsWatching checks if user has watched given repository. | ||||
| func IsWatching(userId, repoId int64) bool { | ||||
| 	has, _ := orm.Get(&Watch{0, repoId, userId}) | ||||
| 	return has | ||||
| } | ||||
|  | ||||
| func ForkRepository(reposName string, userId int64) { | ||||
|  | ||||
| } | ||||
							
								
								
									
										479
									
								
								models/user.go
									
									
									
									
									
								
							
							
						
						
									
										479
									
								
								models/user.go
									
									
									
									
									
								
							| @@ -1,479 +0,0 @@ | ||||
| // Copyright 2014 The Gogs Authors. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| package models | ||||
|  | ||||
| import ( | ||||
| 	"crypto/sha256" | ||||
| 	"encoding/hex" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/gogits/git" | ||||
|  | ||||
| 	"github.com/gogits/gogs/modules/base" | ||||
| 	"github.com/gogits/gogs/modules/log" | ||||
| ) | ||||
|  | ||||
| // User types. | ||||
| const ( | ||||
| 	UT_INDIVIDUAL = iota + 1 | ||||
| 	UT_ORGANIZATION | ||||
| ) | ||||
|  | ||||
| // Login types. | ||||
| const ( | ||||
| 	LT_PLAIN = iota + 1 | ||||
| 	LT_LDAP | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	ErrUserOwnRepos     = errors.New("User still have ownership of repositories") | ||||
| 	ErrUserAlreadyExist = errors.New("User already exist") | ||||
| 	ErrUserNotExist     = errors.New("User does not exist") | ||||
| 	ErrEmailAlreadyUsed = errors.New("E-mail already used") | ||||
| 	ErrUserNameIllegal  = errors.New("User name contains illegal characters") | ||||
| 	ErrKeyNotExist      = errors.New("Public key does not exist") | ||||
| ) | ||||
|  | ||||
| // User represents the object of individual and member of organization. | ||||
| type User struct { | ||||
| 	Id            int64 | ||||
| 	LowerName     string `xorm:"unique not null"` | ||||
| 	Name          string `xorm:"unique not null"` | ||||
| 	Email         string `xorm:"unique not null"` | ||||
| 	Passwd        string `xorm:"not null"` | ||||
| 	LoginType     int | ||||
| 	Type          int | ||||
| 	NumFollowers  int | ||||
| 	NumFollowings int | ||||
| 	NumStars      int | ||||
| 	NumRepos      int | ||||
| 	Avatar        string `xorm:"varchar(2048) not null"` | ||||
| 	AvatarEmail   string `xorm:"not null"` | ||||
| 	Location      string | ||||
| 	Website       string | ||||
| 	IsActive      bool | ||||
| 	IsAdmin       bool | ||||
| 	Rands         string    `xorm:"VARCHAR(10)"` | ||||
| 	Salt          string    `xorm:"VARCHAR(10)"` | ||||
| 	Created       time.Time `xorm:"created"` | ||||
| 	Updated       time.Time `xorm:"updated"` | ||||
| } | ||||
|  | ||||
| // HomeLink returns the user home page link. | ||||
| func (user *User) HomeLink() string { | ||||
| 	return "/user/" + user.Name | ||||
| } | ||||
|  | ||||
| // AvatarLink returns the user gravatar link. | ||||
| func (user *User) AvatarLink() string { | ||||
| 	if base.Service.EnableCacheAvatar { | ||||
| 		return "/avatar/" + user.Avatar | ||||
| 	} | ||||
| 	return "http://1.gravatar.com/avatar/" + user.Avatar | ||||
| } | ||||
|  | ||||
| // NewGitSig generates and returns the signature of given user. | ||||
| func (user *User) NewGitSig() *git.Signature { | ||||
| 	return &git.Signature{ | ||||
| 		Name:  user.Name, | ||||
| 		Email: user.Email, | ||||
| 		When:  time.Now(), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // EncodePasswd encodes password to safe format. | ||||
| func (user *User) EncodePasswd() { | ||||
| 	newPasswd := base.PBKDF2([]byte(user.Passwd), []byte(user.Salt), 10000, 50, sha256.New) | ||||
| 	user.Passwd = fmt.Sprintf("%x", newPasswd) | ||||
| } | ||||
|  | ||||
| // Member represents user is member of organization. | ||||
| type Member struct { | ||||
| 	Id     int64 | ||||
| 	OrgId  int64 `xorm:"unique(member) index"` | ||||
| 	UserId int64 `xorm:"unique(member)"` | ||||
| } | ||||
|  | ||||
| // IsUserExist checks if given user name exist, | ||||
| // the user name should be noncased unique. | ||||
| func IsUserExist(name string) (bool, error) { | ||||
| 	if len(name) == 0 { | ||||
| 		return false, nil | ||||
| 	} | ||||
| 	return orm.Get(&User{LowerName: strings.ToLower(name)}) | ||||
| } | ||||
|  | ||||
| // IsEmailUsed returns true if the e-mail has been used. | ||||
| func IsEmailUsed(email string) (bool, error) { | ||||
| 	if len(email) == 0 { | ||||
| 		return false, nil | ||||
| 	} | ||||
| 	return orm.Get(&User{Email: email}) | ||||
| } | ||||
|  | ||||
| // return a user salt token | ||||
| func GetUserSalt() string { | ||||
| 	return base.GetRandomString(10) | ||||
| } | ||||
|  | ||||
| // RegisterUser creates record of a new user. | ||||
| func RegisterUser(user *User) (*User, error) { | ||||
| 	if !IsLegalName(user.Name) { | ||||
| 		return nil, ErrUserNameIllegal | ||||
| 	} | ||||
|  | ||||
| 	isExist, err := IsUserExist(user.Name) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} else if isExist { | ||||
| 		return nil, ErrUserAlreadyExist | ||||
| 	} | ||||
|  | ||||
| 	isExist, err = IsEmailUsed(user.Email) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} else if isExist { | ||||
| 		return nil, ErrEmailAlreadyUsed | ||||
| 	} | ||||
|  | ||||
| 	user.LowerName = strings.ToLower(user.Name) | ||||
| 	user.Avatar = base.EncodeMd5(user.Email) | ||||
| 	user.AvatarEmail = user.Email | ||||
| 	user.Rands = GetUserSalt() | ||||
| 	user.Salt = GetUserSalt() | ||||
| 	user.EncodePasswd() | ||||
| 	if _, err = orm.Insert(user); err != nil { | ||||
| 		return nil, err | ||||
| 	} else if err = os.MkdirAll(UserPath(user.Name), os.ModePerm); err != nil { | ||||
| 		if _, err := orm.Id(user.Id).Delete(&User{}); err != nil { | ||||
| 			return nil, errors.New(fmt.Sprintf( | ||||
| 				"both create userpath %s and delete table record faild: %v", user.Name, err)) | ||||
| 		} | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	if user.Id == 1 { | ||||
| 		user.IsAdmin = true | ||||
| 		user.IsActive = true | ||||
| 		_, err = orm.Id(user.Id).UseBool().Update(user) | ||||
| 	} | ||||
| 	return user, err | ||||
| } | ||||
|  | ||||
| // GetUsers returns given number of user objects with offset. | ||||
| func GetUsers(num, offset int) ([]User, error) { | ||||
| 	users := make([]User, 0, num) | ||||
| 	err := orm.Limit(num, offset).Asc("id").Find(&users) | ||||
| 	return users, err | ||||
| } | ||||
|  | ||||
| // get user by erify code | ||||
| func getVerifyUser(code string) (user *User) { | ||||
| 	if len(code) <= base.TimeLimitCodeLength { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	// use tail hex username query user | ||||
| 	hexStr := code[base.TimeLimitCodeLength:] | ||||
| 	if b, err := hex.DecodeString(hexStr); err == nil { | ||||
| 		if user, err = GetUserByName(string(b)); user != nil { | ||||
| 			return user | ||||
| 		} | ||||
| 		log.Error("user.getVerifyUser: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // verify active code when active account | ||||
| func VerifyUserActiveCode(code string) (user *User) { | ||||
| 	minutes := base.Service.ActiveCodeLives | ||||
|  | ||||
| 	if user = getVerifyUser(code); user != nil { | ||||
| 		// time limit code | ||||
| 		prefix := code[:base.TimeLimitCodeLength] | ||||
| 		data := base.ToStr(user.Id) + user.Email + user.LowerName + user.Passwd + user.Rands | ||||
|  | ||||
| 		if base.VerifyTimeLimitCode(data, minutes, prefix) { | ||||
| 			return user | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // ChangeUserName changes all corresponding setting from old user name to new one. | ||||
| func ChangeUserName(user *User, newUserName string) (err error) { | ||||
| 	newUserName = strings.ToLower(newUserName) | ||||
|  | ||||
| 	// Update accesses of user. | ||||
| 	accesses := make([]Access, 0, 10) | ||||
| 	if err = orm.Find(&accesses, &Access{UserName: user.LowerName}); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	sess := orm.NewSession() | ||||
| 	defer sess.Close() | ||||
| 	if err = sess.Begin(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	for i := range accesses { | ||||
| 		accesses[i].UserName = newUserName | ||||
| 		if strings.HasPrefix(accesses[i].RepoName, user.LowerName+"/") { | ||||
| 			accesses[i].RepoName = strings.Replace(accesses[i].RepoName, user.LowerName, newUserName, 1) | ||||
| 			if err = UpdateAccessWithSession(sess, &accesses[i]); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	repos, err := GetRepositories(user) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	for i := range repos { | ||||
| 		accesses = make([]Access, 0, 10) | ||||
| 		// Update accesses of user repository. | ||||
| 		if err = orm.Find(&accesses, &Access{RepoName: user.LowerName + "/" + repos[i].LowerName}); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		for j := range accesses { | ||||
| 			accesses[j].RepoName = newUserName + "/" + repos[i].LowerName | ||||
| 			if err = UpdateAccessWithSession(sess, &accesses[j]); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Change user directory name. | ||||
| 	if err = os.Rename(UserPath(user.LowerName), UserPath(newUserName)); err != nil { | ||||
| 		sess.Rollback() | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return sess.Commit() | ||||
| } | ||||
|  | ||||
| // UpdateUser updates user's information. | ||||
| func UpdateUser(user *User) (err error) { | ||||
| 	user.LowerName = strings.ToLower(user.Name) | ||||
|  | ||||
| 	if len(user.Location) > 255 { | ||||
| 		user.Location = user.Location[:255] | ||||
| 	} | ||||
| 	if len(user.Website) > 255 { | ||||
| 		user.Website = user.Website[:255] | ||||
| 	} | ||||
|  | ||||
| 	_, err = orm.Id(user.Id).AllCols().Update(user) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // DeleteUser completely deletes everything of the user. | ||||
| func DeleteUser(user *User) error { | ||||
| 	// Check ownership of repository. | ||||
| 	count, err := GetRepositoryCount(user) | ||||
| 	if err != nil { | ||||
| 		return errors.New("modesl.GetRepositories: " + err.Error()) | ||||
| 	} else if count > 0 { | ||||
| 		return ErrUserOwnRepos | ||||
| 	} | ||||
|  | ||||
| 	// TODO: check issues, other repos' commits | ||||
|  | ||||
| 	// Delete all followers. | ||||
| 	if _, err = orm.Delete(&Follow{FollowId: user.Id}); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// Delete all feeds. | ||||
| 	if _, err = orm.Delete(&Action{UserId: user.Id}); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// Delete all watches. | ||||
| 	if _, err = orm.Delete(&Watch{UserId: user.Id}); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// Delete all accesses. | ||||
| 	if _, err = orm.Delete(&Access{UserName: user.LowerName}); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// Delete all SSH keys. | ||||
| 	keys := make([]PublicKey, 0, 10) | ||||
| 	if err = orm.Find(&keys, &PublicKey{OwnerId: user.Id}); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	for _, key := range keys { | ||||
| 		if err = DeletePublicKey(&key); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Delete user directory. | ||||
| 	if err = os.RemoveAll(UserPath(user.Name)); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	_, err = orm.Delete(user) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // UserPath returns the path absolute path of user repositories. | ||||
| func UserPath(userName string) string { | ||||
| 	return filepath.Join(base.RepoRootPath, strings.ToLower(userName)) | ||||
| } | ||||
|  | ||||
| func GetUserByKeyId(keyId int64) (*User, error) { | ||||
| 	user := new(User) | ||||
| 	rawSql := "SELECT a.* FROM `user` AS a, public_key AS b WHERE a.id = b.owner_id AND b.id=?" | ||||
| 	has, err := orm.Sql(rawSql, keyId).Get(user) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} else if !has { | ||||
| 		err = errors.New("not exist key owner") | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return user, nil | ||||
| } | ||||
|  | ||||
| // GetUserById returns the user object by given id if exists. | ||||
| func GetUserById(id int64) (*User, error) { | ||||
| 	user := new(User) | ||||
| 	has, err := orm.Id(id).Get(user) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if !has { | ||||
| 		return nil, ErrUserNotExist | ||||
| 	} | ||||
| 	return user, nil | ||||
| } | ||||
|  | ||||
| // GetUserByName returns the user object by given name if exists. | ||||
| func GetUserByName(name string) (*User, error) { | ||||
| 	if len(name) == 0 { | ||||
| 		return nil, ErrUserNotExist | ||||
| 	} | ||||
| 	user := &User{LowerName: strings.ToLower(name)} | ||||
| 	has, err := orm.Get(user) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} else if !has { | ||||
| 		return nil, ErrUserNotExist | ||||
| 	} | ||||
| 	return user, nil | ||||
| } | ||||
|  | ||||
| // GetUserEmailsByNames returns a slice of e-mails corresponds to names. | ||||
| func GetUserEmailsByNames(names []string) []string { | ||||
| 	mails := make([]string, 0, len(names)) | ||||
| 	for _, name := range names { | ||||
| 		u, err := GetUserByName(name) | ||||
| 		if err != nil { | ||||
| 			continue | ||||
| 		} | ||||
| 		mails = append(mails, u.Email) | ||||
| 	} | ||||
| 	return mails | ||||
| } | ||||
|  | ||||
| // GetUserByEmail returns the user object by given e-mail if exists. | ||||
| func GetUserByEmail(email string) (*User, error) { | ||||
| 	if len(email) == 0 { | ||||
| 		return nil, ErrUserNotExist | ||||
| 	} | ||||
| 	user := &User{Email: strings.ToLower(email)} | ||||
| 	has, err := orm.Get(user) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} else if !has { | ||||
| 		return nil, ErrUserNotExist | ||||
| 	} | ||||
| 	return user, nil | ||||
| } | ||||
|  | ||||
| // LoginUserPlain validates user by raw user name and password. | ||||
| func LoginUserPlain(name, passwd string) (*User, error) { | ||||
| 	user := User{LowerName: strings.ToLower(name)} | ||||
| 	has, err := orm.Get(&user) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} else if !has { | ||||
| 		return nil, ErrUserNotExist | ||||
| 	} | ||||
|  | ||||
| 	newUser := &User{Passwd: passwd, Salt: user.Salt} | ||||
| 	newUser.EncodePasswd() | ||||
| 	if user.Passwd != newUser.Passwd { | ||||
| 		return nil, ErrUserNotExist | ||||
| 	} | ||||
| 	return &user, nil | ||||
| } | ||||
|  | ||||
| // Follow is connection request for receiving user notifycation. | ||||
| type Follow struct { | ||||
| 	Id       int64 | ||||
| 	UserId   int64 `xorm:"unique(follow)"` | ||||
| 	FollowId int64 `xorm:"unique(follow)"` | ||||
| } | ||||
|  | ||||
| // FollowUser marks someone be another's follower. | ||||
| func FollowUser(userId int64, followId int64) (err error) { | ||||
| 	session := orm.NewSession() | ||||
| 	defer session.Close() | ||||
| 	session.Begin() | ||||
|  | ||||
| 	if _, err = session.Insert(&Follow{UserId: userId, FollowId: followId}); err != nil { | ||||
| 		session.Rollback() | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	rawSql := "UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?" | ||||
| 	if _, err = session.Exec(rawSql, followId); err != nil { | ||||
| 		session.Rollback() | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	rawSql = "UPDATE `user` SET num_followings = num_followings + 1 WHERE id = ?" | ||||
| 	if _, err = session.Exec(rawSql, userId); err != nil { | ||||
| 		session.Rollback() | ||||
| 		return err | ||||
| 	} | ||||
| 	return session.Commit() | ||||
| } | ||||
|  | ||||
| // UnFollowUser unmarks someone be another's follower. | ||||
| func UnFollowUser(userId int64, unFollowId int64) (err error) { | ||||
| 	session := orm.NewSession() | ||||
| 	defer session.Close() | ||||
| 	session.Begin() | ||||
|  | ||||
| 	if _, err = session.Delete(&Follow{UserId: userId, FollowId: unFollowId}); err != nil { | ||||
| 		session.Rollback() | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	rawSql := "UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?" | ||||
| 	if _, err = session.Exec(rawSql, unFollowId); err != nil { | ||||
| 		session.Rollback() | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	rawSql = "UPDATE `user` SET num_followings = num_followings - 1 WHERE id = ?" | ||||
| 	if _, err = session.Exec(rawSql, userId); err != nil { | ||||
| 		session.Rollback() | ||||
| 		return err | ||||
| 	} | ||||
| 	return session.Commit() | ||||
| } | ||||
		Reference in New Issue
	
	Block a user