Merge branch 'dev' of https://github.com/gogits/Gogs into issue/281
Conflicts: modules/base/tool.go
							
								
								
									
										17
									
								
								.bra.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,17 @@ | ||||||
|  | [run] | ||||||
|  | init_cmds = [["./gogs", "web"]] | ||||||
|  | watch_all = true | ||||||
|  | watch_dirs = [ | ||||||
|  | 	"$WORKDIR/conf/locale", | ||||||
|  | 	"$WORKDIR/cmd", | ||||||
|  | 	"$WORKDIR/models", | ||||||
|  | 	"$WORKDIR/modules", | ||||||
|  | 	"$WORKDIR/routers" | ||||||
|  | ] | ||||||
|  | watch_exts = [".go", ".ini"] | ||||||
|  | build_delay = 1500 | ||||||
|  | cmds = [ | ||||||
|  | 	["go", "install"], | ||||||
|  | 	["go", "build"], | ||||||
|  | 	["./gogs", "web"] | ||||||
|  | ] | ||||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						|  | @ -36,3 +36,4 @@ gogs | ||||||
| __pycache__ | __pycache__ | ||||||
| *.pem | *.pem | ||||||
| output* | output* | ||||||
|  | config.codekit | ||||||
|  |  | ||||||
|  | @ -1,11 +1,12 @@ | ||||||
| filesets: | filesets: | ||||||
|     includes: |     includes: | ||||||
|         - templates |         - conf | ||||||
|  |         - etc | ||||||
|         - public |         - public | ||||||
|  |         - scripts | ||||||
|  |         - templates | ||||||
|         - LICENSE |         - LICENSE | ||||||
|         - README.md |         - README.md | ||||||
|         - README_ZH.md |         - README_ZH.md | ||||||
|         - start.bat |  | ||||||
|         - start.sh |  | ||||||
|     excludes: |     excludes: | ||||||
|         - \.git |         - \.git | ||||||
|  |  | ||||||
							
								
								
									
										37
									
								
								.gopmfile
									
									
									
									
									
								
							
							
						
						|  | @ -2,24 +2,25 @@ | ||||||
| path = github.com/gogits/gogs | path = github.com/gogits/gogs | ||||||
| 
 | 
 | ||||||
| [deps] | [deps] | ||||||
| github.com/Unknwon/cae = `commit:a1fa53b` | github.com/Unknwon/cae =  | ||||||
| github.com/Unknwon/com = `commit:019c36f` | github.com/Unknwon/com =  | ||||||
| github.com/Unknwon/goconfig = `commit:c4e325f` | github.com/Unknwon/goconfig =  | ||||||
| github.com/codegangsta/cli = `commit:bb91895` | github.com/Unknwon/i18n =  | ||||||
| github.com/go-martini/martini = `commit:49411a5` | github.com/Unknwon/macaron =  | ||||||
| github.com/go-sql-driver/mysql = `commit:b44cac6` | github.com/codegangsta/cli =  | ||||||
| github.com/go-xorm/core = `commit:267e375` | github.com/go-sql-driver/mysql =  | ||||||
| github.com/go-xorm/xorm = `commit:bd1487b` | github.com/go-xorm/core =  | ||||||
| github.com/gogits/cache = `commit:f9bb61f` | github.com/go-xorm/xorm =  | ||||||
| github.com/gogits/gfm = `commit:40f747a` | github.com/gogits/cache =  | ||||||
| github.com/gogits/git = `commit:3d9e771` | github.com/gogits/gfm =  | ||||||
| github.com/gogits/logs = `commit:0a97a46` | github.com/gogits/git =  | ||||||
| github.com/gogits/oauth2 = `commit:99cbec8` | github.com/gogits/oauth2 =  | ||||||
| github.com/gogits/session = `commit:7ab78d4` | github.com/juju2013/goldap =  | ||||||
| github.com/juju2013/goldap = `commit:f4a7f67` | github.com/lib/pq =  | ||||||
| github.com/lib/pq = `commit:529edd9` | github.com/macaron-contrib/i18n =  | ||||||
| github.com/nfnt/resize = `commit:8aee0d9` | github.com/macaron-contrib/session =  | ||||||
|  | github.com/nfnt/resize =  | ||||||
| 
 | 
 | ||||||
| [res] | [res] | ||||||
| include = templates|public | include = conf|etc|public|scripts|templates | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,11 +1,11 @@ | ||||||
| Gogs - Go Git Service [](https://app.wercker.com/project/bykey/ad0bdb0bc450ac6f09bc56b9640a50aa) [](https://drone.io/github.com/gogits/gogs/latest) | Gogs - Go Git Service [](https://app.wercker.com/project/bykey/ad0bdb0bc450ac6f09bc56b9640a50aa) [](https://drone.io/github.com/gogits/gogs/latest) | ||||||
| ===================== | ===================== | ||||||
| 
 | 
 | ||||||
| Gogs(Go Git Service) is a Self Hosted Git Service in the Go Programming Language. | Gogs(Go Git Service) is a painless self-hosted Git Service written in Go. | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
| 
 | 
 | ||||||
| ##### Current version: 0.4.5 Alpha | ##### Current version: 0.4.7 Alpha | ||||||
| 
 | 
 | ||||||
| ### NOTICES | ### NOTICES | ||||||
| 
 | 
 | ||||||
|  | @ -18,9 +18,7 @@ Gogs(Go Git Service) is a Self Hosted Git Service in the Go Programming Language | ||||||
| 
 | 
 | ||||||
| ## Purpose | ## Purpose | ||||||
| 
 | 
 | ||||||
| Since we choose to use pure Go implementation of Git manipulation, Gogs certainly supports **ALL platforms**  that Go supports, including Linux, Mac OS X, and Windows with **ZERO** dependency.  | The goal of this project is to make the easiest, fastest and most painless way to set up a self-hosted Git service. With Go, this can be done in independent binary distribution across **ALL platforms** that Go supports, including Linux, Mac OS X, and Windows. | ||||||
| 
 |  | ||||||
| More importantly, Gogs only needs one binary to setup your own project hosting on the fly! |  | ||||||
| 
 | 
 | ||||||
| ## Overview | ## Overview | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,17 +1,15 @@ | ||||||
| Gogs - Go Git Service [](https://app.wercker.com/project/bykey/ad0bdb0bc450ac6f09bc56b9640a50aa) [](https://drone.io/github.com/gogits/gogs/latest) | Gogs - Go Git Service [](https://app.wercker.com/project/bykey/ad0bdb0bc450ac6f09bc56b9640a50aa) [](https://drone.io/github.com/gogits/gogs/latest) | ||||||
| ===================== | ===================== | ||||||
| 
 | 
 | ||||||
| Gogs(Go Git Service) 是一个由 Go 语言编写的自助 Git 托管服务。 | Gogs(Go Git Service) 是一个基于 Go 语言的自助 Git 服务。 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
| 
 | 
 | ||||||
| ##### 当前版本:0.4.5 Alpha | ##### 当前版本:0.4.7 Alpha | ||||||
| 
 | 
 | ||||||
| ## 开发目的 | ## 开发目的 | ||||||
| 
 | 
 | ||||||
| Gogs 完全使用 Go 语言来实现对 Git 数据的操作,实现 **零** 依赖,并且支持 Go 语言所支持的 **所有平台**,包括 Linux、Mac OS X 以及 Windows。 | Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自助 Git 服务。使用 Go 语言开发使得 Gogs 能够通过独立的二进制分发,并且支持 Go 语言支持的 **所有平台**,包括 Linux、Mac OS X 以及 Windows。 | ||||||
| 
 |  | ||||||
| 更重要的是,您只需要一个可执行文件就能借助 Gogs 快速搭建属于您自己的代码托管服务! |  | ||||||
| 
 | 
 | ||||||
| ## 项目概览 | ## 项目概览 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										23
									
								
								bee.json
									
									
									
									
									
								
							
							
						
						|  | @ -1,23 +0,0 @@ | ||||||
| { |  | ||||||
| 	"version": 0, |  | ||||||
| 	"gopm": { |  | ||||||
| 		"enable": false, |  | ||||||
| 		"install": false |  | ||||||
| 	}, |  | ||||||
| 	"go_install": true, |  | ||||||
| 	"watch_ext": [], |  | ||||||
| 	"dir_structure": { |  | ||||||
| 		"watch_all": true, |  | ||||||
| 		"controllers": "routers", |  | ||||||
| 		"models": "", |  | ||||||
| 		"others": [ |  | ||||||
| 			"modules", |  | ||||||
| 			"$GOPATH/src/github.com/gogits/logs", |  | ||||||
| 			"$GOPATH/src/github.com/gogits/git" |  | ||||||
| 		] |  | ||||||
| 	}, |  | ||||||
| 	"cmd_args": [ |  | ||||||
| 		"web" |  | ||||||
| 	], |  | ||||||
| 	"envs": [] |  | ||||||
| } |  | ||||||
							
								
								
									
										47
									
								
								cmd/serve.go
									
									
									
									
									
								
							
							
						
						|  | @ -10,11 +10,12 @@ import ( | ||||||
| 	"os/exec" | 	"os/exec" | ||||||
| 	"path" | 	"path" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| 	"strconv" |  | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"github.com/codegangsta/cli" | 	"github.com/codegangsta/cli" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/Unknwon/com" | ||||||
|  | 
 | ||||||
| 	"github.com/gogits/gogs/models" | 	"github.com/gogits/gogs/models" | ||||||
| 	"github.com/gogits/gogs/modules/log" | 	"github.com/gogits/gogs/modules/log" | ||||||
| 	"github.com/gogits/gogs/modules/setting" | 	"github.com/gogits/gogs/modules/setting" | ||||||
|  | @ -81,22 +82,22 @@ func runServ(k *cli.Context) { | ||||||
| 	keys := strings.Split(os.Args[2], "-") | 	keys := strings.Split(os.Args[2], "-") | ||||||
| 	if len(keys) != 2 { | 	if len(keys) != 2 { | ||||||
| 		println("Gogs: auth file format error") | 		println("Gogs: auth file format error") | ||||||
| 		log.GitLogger.Fatal("Invalid auth file format: %s", os.Args[2]) | 		log.GitLogger.Fatal(2, "Invalid auth file format: %s", os.Args[2]) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	keyId, err := strconv.ParseInt(keys[1], 10, 64) | 	keyId, err := com.StrTo(keys[1]).Int64() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		println("Gogs: auth file format error") | 		println("Gogs: auth file format error") | ||||||
| 		log.GitLogger.Fatal("Invalid auth file format: %v", err) | 		log.GitLogger.Fatal(2, "Invalid auth file format: %v", err) | ||||||
| 	} | 	} | ||||||
| 	user, err := models.GetUserByKeyId(keyId) | 	user, err := models.GetUserByKeyId(keyId) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if err == models.ErrUserNotKeyOwner { | 		if err == models.ErrUserNotKeyOwner { | ||||||
| 			println("Gogs: you are not the owner of SSH key") | 			println("Gogs: you are not the owner of SSH key") | ||||||
| 			log.GitLogger.Fatal("Invalid owner of SSH key: %d", keyId) | 			log.GitLogger.Fatal(2, "Invalid owner of SSH key: %d", keyId) | ||||||
| 		} | 		} | ||||||
| 		println("Gogs: internal error:", err) | 		println("Gogs: internal error:", err) | ||||||
| 		log.GitLogger.Fatal("Fail to get user by key ID(%d): %v", keyId, err) | 		log.GitLogger.Fatal(2, "Fail to get user by key ID(%d): %v", keyId, err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	cmd := os.Getenv("SSH_ORIGINAL_COMMAND") | 	cmd := os.Getenv("SSH_ORIGINAL_COMMAND") | ||||||
|  | @ -110,7 +111,7 @@ func runServ(k *cli.Context) { | ||||||
| 	rr := strings.SplitN(repoPath, "/", 2) | 	rr := strings.SplitN(repoPath, "/", 2) | ||||||
| 	if len(rr) != 2 { | 	if len(rr) != 2 { | ||||||
| 		println("Gogs: unavailable repository", args) | 		println("Gogs: unavailable repository", args) | ||||||
| 		log.GitLogger.Fatal("Unavailable repository: %v", args) | 		log.GitLogger.Fatal(2, "Unavailable repository: %v", args) | ||||||
| 	} | 	} | ||||||
| 	repoUserName := rr[0] | 	repoUserName := rr[0] | ||||||
| 	repoName := strings.TrimSuffix(rr[1], ".git") | 	repoName := strings.TrimSuffix(rr[1], ".git") | ||||||
|  | @ -122,10 +123,10 @@ func runServ(k *cli.Context) { | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if err == models.ErrUserNotExist { | 		if err == models.ErrUserNotExist { | ||||||
| 			println("Gogs: given repository owner are not registered") | 			println("Gogs: given repository owner are not registered") | ||||||
| 			log.GitLogger.Fatal("Unregistered owner: %s", repoUserName) | 			log.GitLogger.Fatal(2, "Unregistered owner: %s", repoUserName) | ||||||
| 		} | 		} | ||||||
| 		println("Gogs: internal error:", err) | 		println("Gogs: internal error:", err) | ||||||
| 		log.GitLogger.Fatal("Fail to get repository owner(%s): %v", repoUserName, err) | 		log.GitLogger.Fatal(2, "Fail to get repository owner(%s): %v", repoUserName, err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Access check.
 | 	// Access check.
 | ||||||
|  | @ -134,20 +135,20 @@ func runServ(k *cli.Context) { | ||||||
| 		has, err := models.HasAccess(user.Name, path.Join(repoUserName, repoName), models.WRITABLE) | 		has, err := models.HasAccess(user.Name, path.Join(repoUserName, repoName), models.WRITABLE) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			println("Gogs: internal error:", err) | 			println("Gogs: internal error:", err) | ||||||
| 			log.GitLogger.Fatal("Fail to check write access:", err) | 			log.GitLogger.Fatal(2, "Fail to check write access:", err) | ||||||
| 		} else if !has { | 		} else if !has { | ||||||
| 			println("You have no right to write this repository") | 			println("You have no right to write this repository") | ||||||
| 			log.GitLogger.Fatal("User %s has no right to write repository %s", user.Name, repoPath) | 			log.GitLogger.Fatal(2, "User %s has no right to write repository %s", user.Name, repoPath) | ||||||
| 		} | 		} | ||||||
| 	case isRead: | 	case isRead: | ||||||
| 		repo, err := models.GetRepositoryByName(repoUser.Id, repoName) | 		repo, err := models.GetRepositoryByName(repoUser.Id, repoName) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			if err == models.ErrRepoNotExist { | 			if err == models.ErrRepoNotExist { | ||||||
| 				println("Gogs: given repository does not exist") | 				println("Gogs: given repository does not exist") | ||||||
| 				log.GitLogger.Fatal("Repository does not exist: %s/%s", repoUser.Name, repoName) | 				log.GitLogger.Fatal(2, "Repository does not exist: %s/%s", repoUser.Name, repoName) | ||||||
| 			} | 			} | ||||||
| 			println("Gogs: internal error:", err) | 			println("Gogs: internal error:", err) | ||||||
| 			log.GitLogger.Fatal("Fail to get repository: %v", err) | 			log.GitLogger.Fatal(2, "Fail to get repository: %v", err) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if !repo.IsPrivate { | 		if !repo.IsPrivate { | ||||||
|  | @ -157,10 +158,10 @@ func runServ(k *cli.Context) { | ||||||
| 		has, err := models.HasAccess(user.Name, path.Join(repoUserName, repoName), models.READABLE) | 		has, err := models.HasAccess(user.Name, path.Join(repoUserName, repoName), models.READABLE) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			println("Gogs: internal error:", err) | 			println("Gogs: internal error:", err) | ||||||
| 			log.GitLogger.Fatal("Fail to check read access:", err) | 			log.GitLogger.Fatal(2, "Fail to check read access:", err) | ||||||
| 		} else if !has { | 		} else if !has { | ||||||
| 			println("You have no right to access this repository") | 			println("You have no right to access this repository") | ||||||
| 			log.GitLogger.Fatal("User %s has no right to read repository %s", user.Name, repoPath) | 			log.GitLogger.Fatal(2, "User %s has no right to read repository %s", user.Name, repoPath) | ||||||
| 		} | 		} | ||||||
| 	default: | 	default: | ||||||
| 		println("Unknown command") | 		println("Unknown command") | ||||||
|  | @ -175,29 +176,27 @@ func runServ(k *cli.Context) { | ||||||
| 	gitcmd.Stdout = os.Stdout | 	gitcmd.Stdout = os.Stdout | ||||||
| 	gitcmd.Stdin = os.Stdin | 	gitcmd.Stdin = os.Stdin | ||||||
| 	gitcmd.Stderr = os.Stderr | 	gitcmd.Stderr = os.Stderr | ||||||
| 	err = gitcmd.Run() | 	if err = gitcmd.Run(); err != nil { | ||||||
| 	if err != nil { | 		println("Gogs: internal error:", err.Error()) | ||||||
| 		println("Gogs: internal error:", err) | 		log.GitLogger.Fatal(2, "Fail to execute git command: %v", err) | ||||||
| 		log.GitLogger.Fatal("Fail to execute git command: %v", err) |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if isWrite { | 	if isWrite { | ||||||
| 		tasks, err := models.GetUpdateTasksByUuid(uuid) | 		tasks, err := models.GetUpdateTasksByUuid(uuid) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.GitLogger.Fatal("Fail to get update task: %v", err) | 			log.GitLogger.Fatal(2, "Fail to get update task: %v", err) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		for _, task := range tasks { | 		for _, task := range tasks { | ||||||
| 			err = models.Update(task.RefName, task.OldCommitId, task.NewCommitId, | 			err = models.Update(task.RefName, task.OldCommitId, task.NewCommitId, | ||||||
| 				user.Name, repoUserName, repoName, user.Id) | 				user.Name, repoUserName, repoName, user.Id) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				log.GitLogger.Fatal("Fail to update: %v", err) | 				log.GitLogger.Fatal(2, "Fail to update: %v", err) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		err = models.DelUpdateTasksByUuid(uuid) | 		if err = models.DelUpdateTasksByUuid(uuid); err != nil { | ||||||
| 		if err != nil { | 			log.GitLogger.Fatal(2, "Fail to del update task: %v", err) | ||||||
| 			log.GitLogger.Fatal("Fail to del update task: %v", err) |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ import ( | ||||||
| 	"os" | 	"os" | ||||||
| 
 | 
 | ||||||
| 	"github.com/codegangsta/cli" | 	"github.com/codegangsta/cli" | ||||||
|  | 
 | ||||||
| 	"github.com/gogits/gogs/models" | 	"github.com/gogits/gogs/models" | ||||||
| 	"github.com/gogits/gogs/modules/log" | 	"github.com/gogits/gogs/modules/log" | ||||||
| ) | ) | ||||||
|  | @ -30,9 +31,9 @@ func runUpdate(c *cli.Context) { | ||||||
| 
 | 
 | ||||||
| 	args := c.Args() | 	args := c.Args() | ||||||
| 	if len(args) != 3 { | 	if len(args) != 3 { | ||||||
| 		log.GitLogger.Fatal("received less 3 parameters") | 		log.GitLogger.Fatal(2, "received less 3 parameters") | ||||||
| 	} else if args[0] == "" { | 	} else if args[0] == "" { | ||||||
| 		log.GitLogger.Fatal("refName is empty, shouldn't use") | 		log.GitLogger.Fatal(2, "refName is empty, shouldn't use") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	uuid := os.Getenv("uuid") | 	uuid := os.Getenv("uuid") | ||||||
|  | @ -45,6 +46,6 @@ func runUpdate(c *cli.Context) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err := models.AddUpdateTask(&task); err != nil { | 	if err := models.AddUpdateTask(&task); err != nil { | ||||||
| 		log.GitLogger.Fatal(err.Error()) | 		log.GitLogger.Fatal(2, err.Error()) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										163
									
								
								cmd/web.go
									
									
									
									
									
								
							
							
						
						|  | @ -12,13 +12,16 @@ import ( | ||||||
| 	"os" | 	"os" | ||||||
| 	"path" | 	"path" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/Unknwon/macaron" | ||||||
| 	"github.com/codegangsta/cli" | 	"github.com/codegangsta/cli" | ||||||
| 	"github.com/go-martini/martini" | 	"github.com/macaron-contrib/i18n" | ||||||
|  | 	"github.com/macaron-contrib/session" | ||||||
| 
 | 
 | ||||||
| 	"github.com/gogits/gogs/modules/auth" | 	"github.com/gogits/gogs/modules/auth" | ||||||
| 	"github.com/gogits/gogs/modules/auth/apiv1" | 	"github.com/gogits/gogs/modules/auth/apiv1" | ||||||
| 	"github.com/gogits/gogs/modules/avatar" | 	"github.com/gogits/gogs/modules/avatar" | ||||||
| 	"github.com/gogits/gogs/modules/base" | 	"github.com/gogits/gogs/modules/base" | ||||||
|  | 	"github.com/gogits/gogs/modules/captcha" | ||||||
| 	"github.com/gogits/gogs/modules/log" | 	"github.com/gogits/gogs/modules/log" | ||||||
| 	"github.com/gogits/gogs/modules/middleware" | 	"github.com/gogits/gogs/modules/middleware" | ||||||
| 	"github.com/gogits/gogs/modules/middleware/binding" | 	"github.com/gogits/gogs/modules/middleware/binding" | ||||||
|  | @ -43,45 +46,55 @@ and it takes care of all the other things for you`, | ||||||
| 
 | 
 | ||||||
| // checkVersion checks if binary matches the version of temolate files.
 | // checkVersion checks if binary matches the version of temolate files.
 | ||||||
| func checkVersion() { | func checkVersion() { | ||||||
| 	data, err := ioutil.ReadFile(path.Join(setting.StaticRootPath, "templates/VERSION")) | 	data, err := ioutil.ReadFile(path.Join(setting.StaticRootPath, "templates/.VERSION")) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatal("Fail to read 'templates/VERSION': %v", err) | 		log.Fatal(4, "Fail to read 'templates/.VERSION': %v", err) | ||||||
| 	} | 	} | ||||||
| 	if string(data) != setting.AppVer { | 	if string(data) != setting.AppVer { | ||||||
| 		log.Fatal("Binary and template file version does not match, did you forget to recompile?") | 		log.Fatal(4, "Binary and template file version does not match, did you forget to recompile?") | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func newMartini() *martini.ClassicMartini { | // newMacaron initializes Macaron instance.
 | ||||||
| 	r := martini.NewRouter() | func newMacaron() *macaron.Macaron { | ||||||
| 	m := martini.New() | 	m := macaron.New() | ||||||
| 	m.Use(middleware.Logger()) | 	m.Use(macaron.Logger()) | ||||||
| 	m.Use(martini.Recovery()) | 	m.Use(macaron.Recovery()) | ||||||
| 	m.Use(middleware.Static("public", | 	if setting.EnableGzip { | ||||||
| 		middleware.StaticOptions{SkipLogging: !setting.DisableRouterLog})) | 		m.Use(macaron.Gzip()) | ||||||
| 	m.MapTo(r, (*martini.Routes)(nil)) | 	} | ||||||
| 	m.Action(r.Handle) | 	m.Use(macaron.Static("public", | ||||||
| 	return &martini.ClassicMartini{m, r} | 		macaron.StaticOptions{ | ||||||
|  | 			SkipLogging: !setting.DisableRouterLog, | ||||||
|  | 		}, | ||||||
|  | 	)) | ||||||
|  | 	m.Use(macaron.Renderer(macaron.RenderOptions{ | ||||||
|  | 		Directory:  path.Join(setting.StaticRootPath, "templates"), | ||||||
|  | 		Funcs:      []template.FuncMap{base.TemplateFuncs}, | ||||||
|  | 		IndentJSON: macaron.Env != macaron.PROD, | ||||||
|  | 	})) | ||||||
|  | 	m.Use(i18n.I18n(i18n.LocaleOptions{ | ||||||
|  | 		Langs:    setting.Langs, | ||||||
|  | 		Names:    setting.Names, | ||||||
|  | 		Redirect: true, | ||||||
|  | 	})) | ||||||
|  | 	m.Use(session.Sessioner(session.Options{ | ||||||
|  | 		Provider: setting.SessionProvider, | ||||||
|  | 		Config:   *setting.SessionConfig, | ||||||
|  | 	})) | ||||||
|  | 	m.Use(middleware.Contexter()) | ||||||
|  | 	return m | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func runWeb(*cli.Context) { | func runWeb(*cli.Context) { | ||||||
| 	routers.GlobalInit() | 	routers.GlobalInit() | ||||||
| 	checkVersion() | 	checkVersion() | ||||||
| 
 | 
 | ||||||
| 	m := newMartini() | 	m := newMacaron() | ||||||
| 
 |  | ||||||
| 	// Middlewares.
 |  | ||||||
| 	m.Use(middleware.Renderer(middleware.RenderOptions{ |  | ||||||
| 		Directory:  path.Join(setting.StaticRootPath, "templates"), |  | ||||||
| 		Funcs:      []template.FuncMap{base.TemplateFuncs}, |  | ||||||
| 		IndentJSON: true, |  | ||||||
| 	})) |  | ||||||
| 	m.Use(middleware.InitContext()) |  | ||||||
| 
 | 
 | ||||||
| 	reqSignIn := middleware.Toggle(&middleware.ToggleOptions{SignInRequire: true}) | 	reqSignIn := middleware.Toggle(&middleware.ToggleOptions{SignInRequire: true}) | ||||||
| 	ignSignIn := middleware.Toggle(&middleware.ToggleOptions{SignInRequire: setting.Service.RequireSignInView}) | 	ignSignIn := middleware.Toggle(&middleware.ToggleOptions{SignInRequire: setting.Service.RequireSignInView}) | ||||||
| 	ignSignInAndCsrf := middleware.Toggle(&middleware.ToggleOptions{DisableCsrf: true}) | 	ignSignInAndCsrf := middleware.Toggle(&middleware.ToggleOptions{DisableCsrf: true}) | ||||||
| 
 |  | ||||||
| 	reqSignOut := middleware.Toggle(&middleware.ToggleOptions{SignOutRequire: true}) | 	reqSignOut := middleware.Toggle(&middleware.ToggleOptions{SignOutRequire: true}) | ||||||
| 
 | 
 | ||||||
| 	bindIgnErr := binding.BindIgnErr | 	bindIgnErr := binding.BindIgnErr | ||||||
|  | @ -90,14 +103,15 @@ func runWeb(*cli.Context) { | ||||||
| 	m.Get("/", ignSignIn, routers.Home) | 	m.Get("/", ignSignIn, routers.Home) | ||||||
| 	m.Get("/install", bindIgnErr(auth.InstallForm{}), routers.Install) | 	m.Get("/install", bindIgnErr(auth.InstallForm{}), routers.Install) | ||||||
| 	m.Post("/install", bindIgnErr(auth.InstallForm{}), routers.InstallPost) | 	m.Post("/install", bindIgnErr(auth.InstallForm{}), routers.InstallPost) | ||||||
| 	m.Group("", func(r martini.Router) { | 	m.Group("", func(r *macaron.Router) { | ||||||
| 		r.Get("/issues", user.Issues) | 		r.Get("/issues", user.Issues) | ||||||
| 		r.Get("/pulls", user.Pulls) | 		r.Get("/pulls", user.Pulls) | ||||||
| 		r.Get("/stars", user.Stars) | 		r.Get("/stars", user.Stars) | ||||||
| 	}, reqSignIn) | 	}, reqSignIn) | ||||||
| 
 | 
 | ||||||
| 	m.Group("/api", func(_ martini.Router) { | 	// API routers.
 | ||||||
| 		m.Group("/v1", func(r martini.Router) { | 	m.Group("/api", func(_ *macaron.Router) { | ||||||
|  | 		m.Group("/v1", func(r *macaron.Router) { | ||||||
| 			// Miscellaneous.
 | 			// Miscellaneous.
 | ||||||
| 			r.Post("/markdown", bindIgnErr(apiv1.MarkdownForm{}), v1.Markdown) | 			r.Post("/markdown", bindIgnErr(apiv1.MarkdownForm{}), v1.Markdown) | ||||||
| 			r.Post("/markdown/raw", v1.MarkdownRaw) | 			r.Post("/markdown/raw", v1.MarkdownRaw) | ||||||
|  | @ -118,41 +132,46 @@ func runWeb(*cli.Context) { | ||||||
| 	os.MkdirAll("public/img/avatar/", os.ModePerm) | 	os.MkdirAll("public/img/avatar/", os.ModePerm) | ||||||
| 	m.Get("/avatar/:hash", avt.ServeHTTP) | 	m.Get("/avatar/:hash", avt.ServeHTTP) | ||||||
| 
 | 
 | ||||||
| 	m.Group("/user", func(r martini.Router) { | 	// User routers.
 | ||||||
|  | 	m.Group("/user", func(r *macaron.Router) { | ||||||
| 		r.Get("/login", user.SignIn) | 		r.Get("/login", user.SignIn) | ||||||
| 		r.Post("/login", bindIgnErr(auth.LogInForm{}), user.SignInPost) | 		r.Post("/login", bindIgnErr(auth.SignInForm{}), user.SignInPost) | ||||||
| 		r.Get("/login/:name", user.SocialSignIn) | 		r.Get("/login/:name", user.SocialSignIn) | ||||||
| 		r.Get("/sign_up", user.SignUp) | 		r.Get("/sign_up", user.SignUp) | ||||||
| 		r.Post("/sign_up", bindIgnErr(auth.RegisterForm{}), user.SignUpPost) | 		r.Post("/sign_up", bindIgnErr(auth.RegisterForm{}), user.SignUpPost) | ||||||
| 		r.Get("/reset_password", user.ResetPasswd) | 		r.Get("/reset_password", user.ResetPasswd) | ||||||
| 		r.Post("/reset_password", user.ResetPasswdPost) | 		r.Post("/reset_password", user.ResetPasswdPost) | ||||||
| 	}, reqSignOut) | 	}, reqSignOut) | ||||||
| 	m.Group("/user", func(r martini.Router) { | 	m.Group("/user", func(r *macaron.Router) { | ||||||
| 		r.Get("/delete", user.Delete) | 		r.Get("/settings", user.Settings) | ||||||
| 		r.Post("/delete", user.DeletePost) | 		r.Post("/settings", bindIgnErr(auth.UpdateProfileForm{}), user.SettingsPost) | ||||||
| 		r.Get("/settings", user.Setting) | 		m.Group("/settings", func(r *macaron.Router) { | ||||||
| 		r.Post("/settings", bindIgnErr(auth.UpdateProfileForm{}), user.SettingPost) | 			r.Get("/password", user.SettingsPassword) | ||||||
|  | 			r.Post("/password", bindIgnErr(auth.ChangePasswordForm{}), user.SettingsPasswordPost) | ||||||
|  | 			r.Get("/ssh", user.SettingsSSHKeys) | ||||||
|  | 			r.Post("/ssh", bindIgnErr(auth.AddSSHKeyForm{}), user.SettingsSSHKeysPost) | ||||||
|  | 			r.Get("/social", user.SettingsSocial) | ||||||
|  | 			r.Get("/orgs", user.SettingsOrgs) | ||||||
|  | 			r.Route("/delete", "GET,POST", user.SettingsDelete) | ||||||
|  | 		}) | ||||||
| 	}, reqSignIn) | 	}, reqSignIn) | ||||||
| 	m.Group("/user", func(r martini.Router) { | 	m.Group("/user", func(r *macaron.Router) { | ||||||
| 		r.Get("/feeds", binding.Bind(auth.FeedsForm{}), user.Feeds) | 		// r.Get("/feeds", binding.Bind(auth.FeedsForm{}), user.Feeds)
 | ||||||
| 		r.Any("/activate", user.Activate) | 		r.Any("/activate", user.Activate) | ||||||
| 		r.Get("/email2user", user.Email2User) | 		r.Get("/email2user", user.Email2User) | ||||||
| 		r.Get("/forget_password", user.ForgotPasswd) | 		r.Get("/forget_password", user.ForgotPasswd) | ||||||
| 		r.Post("/forget_password", user.ForgotPasswdPost) | 		r.Post("/forget_password", user.ForgotPasswdPost) | ||||||
| 		r.Get("/logout", user.SignOut) | 		r.Get("/logout", user.SignOut) | ||||||
| 	}) | 	}) | ||||||
| 	m.Group("/user/settings", func(r martini.Router) { |  | ||||||
| 		r.Get("/social", user.SettingSocial) |  | ||||||
| 		r.Get("/password", user.SettingPassword) |  | ||||||
| 		r.Post("/password", bindIgnErr(auth.UpdatePasswdForm{}), user.SettingPasswordPost) |  | ||||||
| 		r.Any("/ssh", bindIgnErr(auth.AddSSHKeyForm{}), user.SettingSSHKeys) |  | ||||||
| 		r.Get("/notification", user.SettingNotification) |  | ||||||
| 		r.Get("/security", user.SettingSecurity) |  | ||||||
| 	}, reqSignIn) |  | ||||||
| 
 | 
 | ||||||
| 	m.Get("/user/:username", ignSignIn, user.Profile) | 	m.Get("/user/:username", ignSignIn, user.Profile) // TODO: Legacy
 | ||||||
| 
 | 
 | ||||||
| 	m.Group("/repo", func(r martini.Router) { | 	// Captcha service.
 | ||||||
|  | 	cpt := captcha.NewCaptcha("/captcha/", setting.Cache) | ||||||
|  | 	m.Map(cpt) | ||||||
|  | 	m.Get("/captcha/*", cpt.Handler) | ||||||
|  | 
 | ||||||
|  | 	m.Group("/repo", func(r *macaron.Router) { | ||||||
| 		r.Get("/create", repo.Create) | 		r.Get("/create", repo.Create) | ||||||
| 		r.Post("/create", bindIgnErr(auth.CreateRepoForm{}), repo.CreatePost) | 		r.Post("/create", bindIgnErr(auth.CreateRepoForm{}), repo.CreatePost) | ||||||
| 		r.Get("/migrate", repo.Migrate) | 		r.Get("/migrate", repo.Migrate) | ||||||
|  | @ -162,14 +181,14 @@ func runWeb(*cli.Context) { | ||||||
| 	adminReq := middleware.Toggle(&middleware.ToggleOptions{SignInRequire: true, AdminRequire: true}) | 	adminReq := middleware.Toggle(&middleware.ToggleOptions{SignInRequire: true, AdminRequire: true}) | ||||||
| 
 | 
 | ||||||
| 	m.Get("/admin", adminReq, admin.Dashboard) | 	m.Get("/admin", adminReq, admin.Dashboard) | ||||||
| 	m.Group("/admin", func(r martini.Router) { | 	m.Group("/admin", func(r *macaron.Router) { | ||||||
| 		r.Get("/users", admin.Users) | 		r.Get("/users", admin.Users) | ||||||
| 		r.Get("/repos", admin.Repositories) | 		r.Get("/repos", admin.Repositories) | ||||||
| 		r.Get("/auths", admin.Auths) | 		r.Get("/auths", admin.Auths) | ||||||
| 		r.Get("/config", admin.Config) | 		r.Get("/config", admin.Config) | ||||||
| 		r.Get("/monitor", admin.Monitor) | 		r.Get("/monitor", admin.Monitor) | ||||||
| 	}, adminReq) | 	}, adminReq) | ||||||
| 	m.Group("/admin/users", func(r martini.Router) { | 	m.Group("/admin/users", func(r *macaron.Router) { | ||||||
| 		r.Get("/new", admin.NewUser) | 		r.Get("/new", admin.NewUser) | ||||||
| 		r.Post("/new", bindIgnErr(auth.RegisterForm{}), admin.NewUserPost) | 		r.Post("/new", bindIgnErr(auth.RegisterForm{}), admin.NewUserPost) | ||||||
| 		r.Get("/:userid", admin.EditUser) | 		r.Get("/:userid", admin.EditUser) | ||||||
|  | @ -177,7 +196,7 @@ func runWeb(*cli.Context) { | ||||||
| 		r.Get("/:userid/delete", admin.DeleteUser) | 		r.Get("/:userid/delete", admin.DeleteUser) | ||||||
| 	}, adminReq) | 	}, adminReq) | ||||||
| 
 | 
 | ||||||
| 	m.Group("/admin/auths", func(r martini.Router) { | 	m.Group("/admin/auths", func(r *macaron.Router) { | ||||||
| 		r.Get("/new", admin.NewAuthSource) | 		r.Get("/new", admin.NewAuthSource) | ||||||
| 		r.Post("/new", bindIgnErr(auth.AuthenticationForm{}), admin.NewAuthSourcePost) | 		r.Post("/new", bindIgnErr(auth.AuthenticationForm{}), admin.NewAuthSourcePost) | ||||||
| 		r.Get("/:authid", admin.EditAuthSource) | 		r.Get("/:authid", admin.EditAuthSource) | ||||||
|  | @ -187,14 +206,15 @@ func runWeb(*cli.Context) { | ||||||
| 
 | 
 | ||||||
| 	m.Get("/:username", ignSignIn, user.Profile) | 	m.Get("/:username", ignSignIn, user.Profile) | ||||||
| 
 | 
 | ||||||
| 	if martini.Env == martini.Dev { | 	if macaron.Env == macaron.DEV { | ||||||
| 		m.Get("/template/**", dev.TemplatePreview) | 		m.Get("/template/**", dev.TemplatePreview) | ||||||
| 		dev.RegisterDebugRoutes(m) | 		dev.RegisterDebugRoutes(m) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	reqTrueOwner := middleware.RequireTrueOwner() | 	reqTrueOwner := middleware.RequireTrueOwner() | ||||||
| 
 | 
 | ||||||
| 	m.Group("/org", func(r martini.Router) { | 	// Organization routers.
 | ||||||
|  | 	m.Group("/org", func(r *macaron.Router) { | ||||||
| 		r.Get("/create", org.New) | 		r.Get("/create", org.New) | ||||||
| 		r.Post("/create", bindIgnErr(auth.CreateOrgForm{}), org.NewPost) | 		r.Post("/create", bindIgnErr(auth.CreateOrgForm{}), org.NewPost) | ||||||
| 		r.Get("/:org", org.Home) | 		r.Get("/:org", org.Home) | ||||||
|  | @ -213,11 +233,11 @@ func runWeb(*cli.Context) { | ||||||
| 		r.Post("/:org/settings/delete", org.DeletePost) | 		r.Post("/:org/settings/delete", org.DeletePost) | ||||||
| 	}, reqSignIn) | 	}, reqSignIn) | ||||||
| 
 | 
 | ||||||
| 	m.Group("/:username/:reponame", func(r martini.Router) { | 	m.Group("/:username/:reponame", func(r *macaron.Router) { | ||||||
| 		r.Get("/settings", repo.Setting) | 		r.Get("/settings", repo.Setting) | ||||||
| 		r.Post("/settings", bindIgnErr(auth.RepoSettingForm{}), repo.SettingPost) | 		r.Post("/settings", bindIgnErr(auth.RepoSettingForm{}), repo.SettingPost) | ||||||
| 
 | 
 | ||||||
| 		m.Group("/settings", func(r martini.Router) { | 		m.Group("/settings", func(r *macaron.Router) { | ||||||
| 			r.Get("/collaboration", repo.Collaboration) | 			r.Get("/collaboration", repo.Collaboration) | ||||||
| 			r.Post("/collaboration", repo.CollaborationPost) | 			r.Post("/collaboration", repo.CollaborationPost) | ||||||
| 			r.Get("/hooks", repo.WebHooks) | 			r.Get("/hooks", repo.WebHooks) | ||||||
|  | @ -228,10 +248,10 @@ func runWeb(*cli.Context) { | ||||||
| 		}) | 		}) | ||||||
| 	}, reqSignIn, middleware.RepoAssignment(true), reqTrueOwner) | 	}, reqSignIn, middleware.RepoAssignment(true), reqTrueOwner) | ||||||
| 
 | 
 | ||||||
| 	m.Group("/:username/:reponame", func(r martini.Router) { | 	m.Group("/:username/:reponame", func(r *macaron.Router) { | ||||||
| 		r.Get("/action/:action", repo.Action) | 		// r.Get("/action/:action", repo.Action)
 | ||||||
| 
 | 
 | ||||||
| 		m.Group("/issues", func(r martini.Router) { | 		m.Group("/issues", func(r *macaron.Router) { | ||||||
| 			r.Get("/new", repo.CreateIssue) | 			r.Get("/new", repo.CreateIssue) | ||||||
| 			r.Post("/new", bindIgnErr(auth.CreateIssueForm{}), repo.CreateIssuePost) | 			r.Post("/new", bindIgnErr(auth.CreateIssueForm{}), repo.CreateIssuePost) | ||||||
| 			r.Post("/:index", bindIgnErr(auth.CreateIssueForm{}), repo.UpdateIssue) | 			r.Post("/:index", bindIgnErr(auth.CreateIssueForm{}), repo.UpdateIssue) | ||||||
|  | @ -255,35 +275,36 @@ func runWeb(*cli.Context) { | ||||||
| 		r.Get("/releases/edit/:tagname", repo.EditRelease) | 		r.Get("/releases/edit/:tagname", repo.EditRelease) | ||||||
| 	}, reqSignIn, middleware.RepoAssignment(true)) | 	}, reqSignIn, middleware.RepoAssignment(true)) | ||||||
| 
 | 
 | ||||||
| 	m.Group("/:username/:reponame", func(r martini.Router) { | 	m.Group("/:username/:reponame", func(r *macaron.Router) { | ||||||
| 		r.Post("/releases/new", bindIgnErr(auth.NewReleaseForm{}), repo.NewReleasePost) | 		r.Post("/releases/new", bindIgnErr(auth.NewReleaseForm{}), repo.NewReleasePost) | ||||||
| 		r.Post("/releases/edit/:tagname", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost) | 		r.Post("/releases/edit/:tagname", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost) | ||||||
| 	}, reqSignIn, middleware.RepoAssignment(true, true)) | 	}, reqSignIn, middleware.RepoAssignment(true, true)) | ||||||
| 
 | 
 | ||||||
| 	m.Group("/:username/:reponame", func(r martini.Router) { | 	m.Group("/:username/:reponame", func(r *macaron.Router) { | ||||||
| 		r.Get("/issues", repo.Issues) | 		r.Get("/issues", repo.Issues) | ||||||
| 		r.Get("/issues/:index", repo.ViewIssue) | 		r.Get("/issues/:index", repo.ViewIssue) | ||||||
| 		r.Get("/pulls", repo.Pulls) | 		r.Get("/pulls", repo.Pulls) | ||||||
| 		r.Get("/branches", repo.Branches) | 		r.Get("/branches", repo.Branches) | ||||||
| 	}, ignSignIn, middleware.RepoAssignment(true)) | 	}, ignSignIn, middleware.RepoAssignment(true)) | ||||||
| 
 | 
 | ||||||
| 	m.Group("/:username/:reponame", func(r martini.Router) { | 	m.Group("/:username/:reponame", func(r *macaron.Router) { | ||||||
| 		r.Get("/src/:branchname", repo.Single) | 		r.Get("/src/:branchname", repo.Home) | ||||||
| 		r.Get("/src/:branchname/**", repo.Single) | 		r.Get("/src/:branchname/*", repo.Home) | ||||||
| 		r.Get("/raw/:branchname/**", repo.SingleDownload) | 		r.Get("/raw/:branchname/*", repo.SingleDownload) | ||||||
| 		r.Get("/commits/:branchname", repo.Commits) | 		r.Get("/commits/:branchname", repo.Commits) | ||||||
| 		r.Get("/commits/:branchname/search", repo.SearchCommits) | 		r.Get("/commits/:branchname/search", repo.SearchCommits) | ||||||
| 		r.Get("/commits/:branchname/**", repo.FileHistory) | 		r.Get("/commits/:branchname/*", repo.FileHistory) | ||||||
| 		r.Get("/commit/:branchname", repo.Diff) | 		r.Get("/commit/:branchname", repo.Diff) | ||||||
| 		r.Get("/commit/:branchname/**", repo.Diff) | 		r.Get("/commit/:branchname/*", repo.Diff) | ||||||
| 		r.Get("/releases", repo.Releases) | 		r.Get("/releases", repo.Releases) | ||||||
| 		r.Get("/archive/:branchname/:reponame.zip", repo.ZipDownload) | 		r.Get("/archive/*.*", repo.Download) | ||||||
| 		r.Get("/archive/:branchname/:reponame.tar.gz", repo.TarGzDownload) |  | ||||||
| 	}, ignSignIn, middleware.RepoAssignment(true, true)) | 	}, ignSignIn, middleware.RepoAssignment(true, true)) | ||||||
| 
 | 
 | ||||||
| 	m.Group("/:username", func(r martini.Router) { | 	m.Group("/:username", func(r *macaron.Router) { | ||||||
| 		r.Get("/:reponame", middleware.RepoAssignment(true, true, true), repo.Single) | 		r.Get("/:reponame", middleware.RepoAssignment(true, true, true), repo.Home) | ||||||
| 		r.Any("/:reponame/**", repo.Http) | 		m.Group("/:reponame", func(r *macaron.Router) { | ||||||
|  | 			r.Any("/*", repo.Http) | ||||||
|  | 		}) | ||||||
| 	}, ignSignInAndCsrf) | 	}, ignSignInAndCsrf) | ||||||
| 
 | 
 | ||||||
| 	// Not found handler.
 | 	// Not found handler.
 | ||||||
|  | @ -298,10 +319,10 @@ func runWeb(*cli.Context) { | ||||||
| 	case setting.HTTPS: | 	case setting.HTTPS: | ||||||
| 		err = http.ListenAndServeTLS(listenAddr, setting.CertFile, setting.KeyFile, m) | 		err = http.ListenAndServeTLS(listenAddr, setting.CertFile, setting.KeyFile, m) | ||||||
| 	default: | 	default: | ||||||
| 		log.Fatal("Invalid protocol: %s", setting.Protocol) | 		log.Fatal(4, "Invalid protocol: %s", setting.Protocol) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatal("Fail to start server: %v", err) | 		log.Fatal(4, "Fail to start server: %v", err) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,9 +0,0 @@ | ||||||
| ## NOTICE |  | ||||||
| 
 |  | ||||||
| This directory only used for development, and us [go-bindata](https://github.com/jteeuwen/go-bindata) to store in memory for releases. |  | ||||||
| 
 |  | ||||||
| To apply any change in this directory, install [go-bindata](https://github.com/jteeuwen/go-bindata), and then execute following command in root of source directory: |  | ||||||
| 
 |  | ||||||
| ``` |  | ||||||
| $ go-bindata -ignore="\\.DS_Store|README.md" -o modules/bin/conf.go -pkg="bin" conf/... |  | ||||||
| ``` |  | ||||||
							
								
								
									
										37
									
								
								conf/app.ini
									
									
									
									
									
								
							
							
						
						|  | @ -28,6 +28,8 @@ KEY_FILE = custom/https/key.pem | ||||||
| ; Upper level of template and static file path | ; Upper level of template and static file path | ||||||
| ; default is the path where Gogs is executed | ; default is the path where Gogs is executed | ||||||
| STATIC_ROOT_PATH =  | STATIC_ROOT_PATH =  | ||||||
|  | ; Application level GZIP support | ||||||
|  | ENABLE_GZIP = false | ||||||
| 
 | 
 | ||||||
| [database] | [database] | ||||||
| ; Either "mysql", "postgres" or "sqlite3", it's your choice | ; Either "mysql", "postgres" or "sqlite3", it's your choice | ||||||
|  | @ -125,14 +127,6 @@ SCOPES = all | ||||||
| AUTH_URL = https://open.t.qq.com/cgi-bin/oauth2/authorize | AUTH_URL = https://open.t.qq.com/cgi-bin/oauth2/authorize | ||||||
| TOKEN_URL = https://open.t.qq.com/cgi-bin/oauth2/access_token | TOKEN_URL = https://open.t.qq.com/cgi-bin/oauth2/access_token | ||||||
| 
 | 
 | ||||||
| [oauth.twitter] |  | ||||||
| ENABLED = false |  | ||||||
| CLIENT_ID =  |  | ||||||
| CLIENT_SECRET =  |  | ||||||
| SCOPES = all |  | ||||||
| AUTH_URL = https://api.twitter.com/oauth/authorize |  | ||||||
| TOKEN_URL = https://api.twitter.com/oauth/access_token |  | ||||||
| 
 |  | ||||||
| [oauth.weibo] | [oauth.weibo] | ||||||
| ENABLED = false | ENABLED = false | ||||||
| CLIENT_ID =  | CLIENT_ID =  | ||||||
|  | @ -147,8 +141,8 @@ ADAPTER = memory | ||||||
| ; For "memory" only, GC interval in seconds, default is 60 | ; For "memory" only, GC interval in seconds, default is 60 | ||||||
| INTERVAL = 60 | INTERVAL = 60 | ||||||
| ; For "redis" and "memcache", connection host address | ; For "redis" and "memcache", connection host address | ||||||
| ; redis: ":6039" | ; redis: `:6039` | ||||||
| ; memcache: "127.0.0.1:11211" | ; memcache: `127.0.0.1:11211` | ||||||
| HOST = | HOST = | ||||||
| 
 | 
 | ||||||
| [session] | [session] | ||||||
|  | @ -156,9 +150,9 @@ HOST = | ||||||
| PROVIDER = file | PROVIDER = file | ||||||
| ; Provider config options | ; Provider config options | ||||||
| ; memory: not have any config yet | ; memory: not have any config yet | ||||||
| ; file: session file path, e.g. "data/sessions" | ; file: session file path, e.g. `data/sessions` | ||||||
| ; redis: config like redis server addr, poolSize, password, e.g. "127.0.0.1:6379,100,astaxie" | ; redis: config like redis server addr, poolSize, password, e.g. `127.0.0.1:6379,100,gogs` | ||||||
| ; mysql: go-sql-driver/mysql dsn config string, e.g. "root:password@/session_table" | ; mysql: go-sql-driver/mysql dsn config string, e.g. `root:password@/session_table` | ||||||
| PROVIDER_CONFIG = data/sessions | PROVIDER_CONFIG = data/sessions | ||||||
| ; Session cookie name | ; Session cookie name | ||||||
| COOKIE_NAME = i_like_gogits | COOKIE_NAME = i_like_gogits | ||||||
|  | @ -182,15 +176,15 @@ DISABLE_GRAVATAR = false | ||||||
| 
 | 
 | ||||||
| [attachment] | [attachment] | ||||||
| ; Whether attachments are enabled. Defaults to `true` | ; Whether attachments are enabled. Defaults to `true` | ||||||
| ENABLE = | ENABLE = true | ||||||
| ; Path for attachments. Defaults to files/attachments | ; Path for attachments. Defaults to `data/attachments` | ||||||
| PATH =  | PATH = data/attachments | ||||||
| ; One or more allowed types, e.g. image/jpeg|image/png | ; One or more allowed types, e.g. image/jpeg|image/png | ||||||
| ALLOWED_TYPES =  | ALLOWED_TYPES = image/jpeg|image/png | ||||||
| ; Max size of each file. Defaults to 32MB | ; Max size of each file. Defaults to 32MB | ||||||
| MAX_SIZE | MAX_SIZE = 32 | ||||||
| ; Max number of files per upload. Defaults to 10 | ; Max number of files per upload. Defaults to 10 | ||||||
| MAX_FILES = | MAX_FILES = 10 | ||||||
| 
 | 
 | ||||||
| [time] | [time] | ||||||
| ; Specifies the format for fully outputed dates. Defaults to RFC1123 | ; Specifies the format for fully outputed dates. Defaults to RFC1123 | ||||||
|  | @ -215,7 +209,6 @@ LEVEL = | ||||||
| ; For "file" mode only | ; For "file" mode only | ||||||
| [log.file] | [log.file] | ||||||
| LEVEL =  | LEVEL =  | ||||||
| FILE_NAME = log/gogs.log |  | ||||||
| ; This enables automated log rotate(switch of following options), default is true | ; This enables automated log rotate(switch of following options), default is true | ||||||
| LOG_ROTATE = true | LOG_ROTATE = true | ||||||
| ; Max line number of single file, default is 1000000 | ; Max line number of single file, default is 1000000 | ||||||
|  | @ -259,3 +252,7 @@ LEVEL = | ||||||
| DRIVER =  | DRIVER =  | ||||||
| ; Based on xorm, e.g.: root:root@localhost/gogs?charset=utf8 | ; Based on xorm, e.g.: root:root@localhost/gogs?charset=utf8 | ||||||
| CONN =  | CONN =  | ||||||
|  | 
 | ||||||
|  | [i18n] | ||||||
|  | LANGS = en-US,zh-CN | ||||||
|  | NAMES = English,简体中文 | ||||||
|  |  | ||||||
							
								
								
									
										23
									
								
								conf/license/BSD license
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,23 @@ | ||||||
|  | Copyright (c) 2014 | ||||||
|  | All rights reserved. | ||||||
|  | 
 | ||||||
|  | Redistribution and use in source and binary forms, with or without | ||||||
|  | modification, are permitted provided that the following conditions are met: | ||||||
|  | 
 | ||||||
|  | * Redistributions of source code must retain the above copyright notice, this | ||||||
|  |   list of conditions and the following disclaimer. | ||||||
|  | 
 | ||||||
|  | * Redistributions in binary form must reproduce the above copyright notice, | ||||||
|  |   this list of conditions and the following disclaimer in the documentation | ||||||
|  |   and/or other materials provided with the distribution. | ||||||
|  | 
 | ||||||
|  | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||||
|  | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||||
|  | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||||
|  | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||||
|  | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||||||
|  | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||||
|  | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
							
								
								
									
										116
									
								
								conf/license/CC0 1.0 Universal
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,116 @@ | ||||||
|  | CC0 1.0 Universal | ||||||
|  | 
 | ||||||
|  | Statement of Purpose | ||||||
|  | 
 | ||||||
|  | The laws of most jurisdictions throughout the world automatically confer | ||||||
|  | exclusive Copyright and Related Rights (defined below) upon the creator and | ||||||
|  | subsequent owner(s) (each and all, an "owner") of an original work of | ||||||
|  | authorship and/or a database (each, a "Work"). | ||||||
|  | 
 | ||||||
|  | Certain owners wish to permanently relinquish those rights to a Work for the | ||||||
|  | purpose of contributing to a commons of creative, cultural and scientific | ||||||
|  | works ("Commons") that the public can reliably and without fear of later | ||||||
|  | claims of infringement build upon, modify, incorporate in other works, reuse | ||||||
|  | and redistribute as freely as possible in any form whatsoever and for any | ||||||
|  | purposes, including without limitation commercial purposes. These owners may | ||||||
|  | contribute to the Commons to promote the ideal of a free culture and the | ||||||
|  | further production of creative, cultural and scientific works, or to gain | ||||||
|  | reputation or greater distribution for their Work in part through the use and | ||||||
|  | efforts of others. | ||||||
|  | 
 | ||||||
|  | For these and/or other purposes and motivations, and without any expectation | ||||||
|  | of additional consideration or compensation, the person associating CC0 with a | ||||||
|  | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright | ||||||
|  | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work | ||||||
|  | and publicly distribute the Work under its terms, with knowledge of his or her | ||||||
|  | Copyright and Related Rights in the Work and the meaning and intended legal | ||||||
|  | effect of CC0 on those rights. | ||||||
|  | 
 | ||||||
|  | 1. Copyright and Related Rights. A Work made available under CC0 may be | ||||||
|  | protected by copyright and related or neighboring rights ("Copyright and | ||||||
|  | Related Rights"). Copyright and Related Rights include, but are not limited | ||||||
|  | to, the following: | ||||||
|  | 
 | ||||||
|  |   i. the right to reproduce, adapt, distribute, perform, display, communicate, | ||||||
|  |   and translate a Work; | ||||||
|  | 
 | ||||||
|  |   ii. moral rights retained by the original author(s) and/or performer(s); | ||||||
|  | 
 | ||||||
|  |   iii. publicity and privacy rights pertaining to a person's image or likeness | ||||||
|  |   depicted in a Work; | ||||||
|  | 
 | ||||||
|  |   iv. rights protecting against unfair competition in regards to a Work, | ||||||
|  |   subject to the limitations in paragraph 4(a), below; | ||||||
|  | 
 | ||||||
|  |   v. rights protecting the extraction, dissemination, use and reuse of data in | ||||||
|  |   a Work; | ||||||
|  | 
 | ||||||
|  |   vi. database rights (such as those arising under Directive 96/9/EC of the | ||||||
|  |   European Parliament and of the Council of 11 March 1996 on the legal | ||||||
|  |   protection of databases, and under any national implementation thereof, | ||||||
|  |   including any amended or successor version of such directive); and | ||||||
|  | 
 | ||||||
|  |   vii. other similar, equivalent or corresponding rights throughout the world | ||||||
|  |   based on applicable law or treaty, and any national implementations thereof. | ||||||
|  | 
 | ||||||
|  | 2. Waiver. To the greatest extent permitted by, but not in contravention of, | ||||||
|  | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and | ||||||
|  | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright | ||||||
|  | and Related Rights and associated claims and causes of action, whether now | ||||||
|  | known or unknown (including existing as well as future claims and causes of | ||||||
|  | action), in the Work (i) in all territories worldwide, (ii) for the maximum | ||||||
|  | duration provided by applicable law or treaty (including future time | ||||||
|  | extensions), (iii) in any current or future medium and for any number of | ||||||
|  | copies, and (iv) for any purpose whatsoever, including without limitation | ||||||
|  | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes | ||||||
|  | the Waiver for the benefit of each member of the public at large and to the | ||||||
|  | detriment of Affirmer's heirs and successors, fully intending that such Waiver | ||||||
|  | shall not be subject to revocation, rescission, cancellation, termination, or | ||||||
|  | any other legal or equitable action to disrupt the quiet enjoyment of the Work | ||||||
|  | by the public as contemplated by Affirmer's express Statement of Purpose. | ||||||
|  | 
 | ||||||
|  | 3. Public License Fallback. Should any part of the Waiver for any reason be | ||||||
|  | judged legally invalid or ineffective under applicable law, then the Waiver | ||||||
|  | shall be preserved to the maximum extent permitted taking into account | ||||||
|  | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver | ||||||
|  | is so judged Affirmer hereby grants to each affected person a royalty-free, | ||||||
|  | non transferable, non sublicensable, non exclusive, irrevocable and | ||||||
|  | unconditional license to exercise Affirmer's Copyright and Related Rights in | ||||||
|  | the Work (i) in all territories worldwide, (ii) for the maximum duration | ||||||
|  | provided by applicable law or treaty (including future time extensions), (iii) | ||||||
|  | in any current or future medium and for any number of copies, and (iv) for any | ||||||
|  | purpose whatsoever, including without limitation commercial, advertising or | ||||||
|  | promotional purposes (the "License"). The License shall be deemed effective as | ||||||
|  | of the date CC0 was applied by Affirmer to the Work. Should any part of the | ||||||
|  | License for any reason be judged legally invalid or ineffective under | ||||||
|  | applicable law, such partial invalidity or ineffectiveness shall not | ||||||
|  | invalidate the remainder of the License, and in such case Affirmer hereby | ||||||
|  | affirms that he or she will not (i) exercise any of his or her remaining | ||||||
|  | Copyright and Related Rights in the Work or (ii) assert any associated claims | ||||||
|  | and causes of action with respect to the Work, in either case contrary to | ||||||
|  | Affirmer's express Statement of Purpose. | ||||||
|  | 
 | ||||||
|  | 4. Limitations and Disclaimers. | ||||||
|  | 
 | ||||||
|  |   a. No trademark or patent rights held by Affirmer are waived, abandoned, | ||||||
|  |   surrendered, licensed or otherwise affected by this document. | ||||||
|  | 
 | ||||||
|  |   b. Affirmer offers the Work as-is and makes no representations or warranties | ||||||
|  |   of any kind concerning the Work, express, implied, statutory or otherwise, | ||||||
|  |   including without limitation warranties of title, merchantability, fitness | ||||||
|  |   for a particular purpose, non infringement, or the absence of latent or | ||||||
|  |   other defects, accuracy, or the present or absence of errors, whether or not | ||||||
|  |   discoverable, all to the greatest extent permissible under applicable law. | ||||||
|  | 
 | ||||||
|  |   c. Affirmer disclaims responsibility for clearing rights of other persons | ||||||
|  |   that may apply to the Work or any use thereof, including without limitation | ||||||
|  |   any person's Copyright and Related Rights in the Work. Further, Affirmer | ||||||
|  |   disclaims responsibility for obtaining any necessary consents, permissions | ||||||
|  |   or other rights required for any use of the Work. | ||||||
|  | 
 | ||||||
|  |   d. Affirmer understands and acknowledges that Creative Commons is not a | ||||||
|  |   party to this document and has no duty or obligation with respect to this | ||||||
|  |   CC0 or use of the Work. | ||||||
|  | 
 | ||||||
|  | For more information, please see | ||||||
|  | <http://creativecommons.org/publicdomain/zero/1.0/> | ||||||
							
								
								
									
										203
									
								
								conf/license/Eclipse Public License v1.0
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,203 @@ | ||||||
|  | Eclipse Public License - v 1.0 | ||||||
|  | 
 | ||||||
|  | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC | ||||||
|  | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM | ||||||
|  | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. | ||||||
|  | 
 | ||||||
|  | 1. DEFINITIONS | ||||||
|  | 
 | ||||||
|  | "Contribution" means: | ||||||
|  | 
 | ||||||
|  | a) in the case of the initial Contributor, the initial code and documentation | ||||||
|  |    distributed under this Agreement, and | ||||||
|  | b) in the case of each subsequent Contributor: | ||||||
|  |     i) changes to the Program, and | ||||||
|  |    ii) additions to the Program; | ||||||
|  | 
 | ||||||
|  |    where such changes and/or additions to the Program originate from and are | ||||||
|  |    distributed by that particular Contributor. A Contribution 'originates' | ||||||
|  |    from a Contributor if it was added to the Program by such Contributor | ||||||
|  |    itself or anyone acting on such Contributor's behalf. Contributions do not | ||||||
|  |    include additions to the Program which: (i) are separate modules of | ||||||
|  |    software distributed in conjunction with the Program under their own | ||||||
|  |    license agreement, and (ii) are not derivative works of the Program. | ||||||
|  | 
 | ||||||
|  | "Contributor" means any person or entity that distributes the Program. | ||||||
|  | 
 | ||||||
|  | "Licensed Patents" mean patent claims licensable by a Contributor which are | ||||||
|  | necessarily infringed by the use or sale of its Contribution alone or when | ||||||
|  | combined with the Program. | ||||||
|  | 
 | ||||||
|  | "Program" means the Contributions distributed in accordance with this | ||||||
|  | Agreement. | ||||||
|  | 
 | ||||||
|  | "Recipient" means anyone who receives the Program under this Agreement, | ||||||
|  | including all Contributors. | ||||||
|  | 
 | ||||||
|  | 2. GRANT OF RIGHTS | ||||||
|  |   a) Subject to the terms of this Agreement, each Contributor hereby grants | ||||||
|  |      Recipient a non-exclusive, worldwide, royalty-free copyright license to | ||||||
|  |      reproduce, prepare derivative works of, publicly display, publicly | ||||||
|  |      perform, distribute and sublicense the Contribution of such Contributor, | ||||||
|  |      if any, and such derivative works, in source code and object code form. | ||||||
|  |   b) Subject to the terms of this Agreement, each Contributor hereby grants | ||||||
|  |      Recipient a non-exclusive, worldwide, royalty-free patent license under | ||||||
|  |      Licensed Patents to make, use, sell, offer to sell, import and otherwise | ||||||
|  |      transfer the Contribution of such Contributor, if any, in source code and | ||||||
|  |      object code form. This patent license shall apply to the combination of | ||||||
|  |      the Contribution and the Program if, at the time the Contribution is | ||||||
|  |      added by the Contributor, such addition of the Contribution causes such | ||||||
|  |      combination to be covered by the Licensed Patents. The patent license | ||||||
|  |      shall not apply to any other combinations which include the Contribution. | ||||||
|  |      No hardware per se is licensed hereunder. | ||||||
|  |   c) Recipient understands that although each Contributor grants the licenses | ||||||
|  |      to its Contributions set forth herein, no assurances are provided by any | ||||||
|  |      Contributor that the Program does not infringe the patent or other | ||||||
|  |      intellectual property rights of any other entity. Each Contributor | ||||||
|  |      disclaims any liability to Recipient for claims brought by any other | ||||||
|  |      entity based on infringement of intellectual property rights or | ||||||
|  |      otherwise. As a condition to exercising the rights and licenses granted | ||||||
|  |      hereunder, each Recipient hereby assumes sole responsibility to secure | ||||||
|  |      any other intellectual property rights needed, if any. For example, if a | ||||||
|  |      third party patent license is required to allow Recipient to distribute | ||||||
|  |      the Program, it is Recipient's responsibility to acquire that license | ||||||
|  |      before distributing the Program. | ||||||
|  |   d) Each Contributor represents that to its knowledge it has sufficient | ||||||
|  |      copyright rights in its Contribution, if any, to grant the copyright | ||||||
|  |      license set forth in this Agreement. | ||||||
|  | 
 | ||||||
|  | 3. REQUIREMENTS | ||||||
|  | 
 | ||||||
|  | A Contributor may choose to distribute the Program in object code form under | ||||||
|  | its own license agreement, provided that: | ||||||
|  | 
 | ||||||
|  |   a) it complies with the terms and conditions of this Agreement; and | ||||||
|  |   b) its license agreement: | ||||||
|  |       i) effectively disclaims on behalf of all Contributors all warranties | ||||||
|  |          and conditions, express and implied, including warranties or | ||||||
|  |          conditions of title and non-infringement, and implied warranties or | ||||||
|  |          conditions of merchantability and fitness for a particular purpose; | ||||||
|  |      ii) effectively excludes on behalf of all Contributors all liability for | ||||||
|  |          damages, including direct, indirect, special, incidental and | ||||||
|  |          consequential damages, such as lost profits; | ||||||
|  |     iii) states that any provisions which differ from this Agreement are | ||||||
|  |          offered by that Contributor alone and not by any other party; and | ||||||
|  |      iv) states that source code for the Program is available from such | ||||||
|  |          Contributor, and informs licensees how to obtain it in a reasonable | ||||||
|  |          manner on or through a medium customarily used for software exchange. | ||||||
|  | 
 | ||||||
|  | When the Program is made available in source code form: | ||||||
|  | 
 | ||||||
|  |   a) it must be made available under this Agreement; and | ||||||
|  |   b) a copy of this Agreement must be included with each copy of the Program. | ||||||
|  |      Contributors may not remove or alter any copyright notices contained | ||||||
|  |      within the Program. | ||||||
|  | 
 | ||||||
|  | Each Contributor must identify itself as the originator of its Contribution, | ||||||
|  | if | ||||||
|  | any, in a manner that reasonably allows subsequent Recipients to identify the | ||||||
|  | originator of the Contribution. | ||||||
|  | 
 | ||||||
|  | 4. COMMERCIAL DISTRIBUTION | ||||||
|  | 
 | ||||||
|  | Commercial distributors of software may accept certain responsibilities with | ||||||
|  | respect to end users, business partners and the like. While this license is | ||||||
|  | intended to facilitate the commercial use of the Program, the Contributor who | ||||||
|  | includes the Program in a commercial product offering should do so in a manner | ||||||
|  | which does not create potential liability for other Contributors. Therefore, | ||||||
|  | if a Contributor includes the Program in a commercial product offering, such | ||||||
|  | Contributor ("Commercial Contributor") hereby agrees to defend and indemnify | ||||||
|  | every other Contributor ("Indemnified Contributor") against any losses, | ||||||
|  | damages and costs (collectively "Losses") arising from claims, lawsuits and | ||||||
|  | other legal actions brought by a third party against the Indemnified | ||||||
|  | Contributor to the extent caused by the acts or omissions of such Commercial | ||||||
|  | Contributor in connection with its distribution of the Program in a commercial | ||||||
|  | product offering. The obligations in this section do not apply to any claims | ||||||
|  | or Losses relating to any actual or alleged intellectual property | ||||||
|  | infringement. In order to qualify, an Indemnified Contributor must: | ||||||
|  | a) promptly notify the Commercial Contributor in writing of such claim, and | ||||||
|  | b) allow the Commercial Contributor to control, and cooperate with the | ||||||
|  | Commercial Contributor in, the defense and any related settlement | ||||||
|  | negotiations. The Indemnified Contributor may participate in any such claim at | ||||||
|  | its own expense. | ||||||
|  | 
 | ||||||
|  | For example, a Contributor might include the Program in a commercial product | ||||||
|  | offering, Product X. That Contributor is then a Commercial Contributor. If | ||||||
|  | that Commercial Contributor then makes performance claims, or offers | ||||||
|  | warranties related to Product X, those performance claims and warranties are | ||||||
|  | such Commercial Contributor's responsibility alone. Under this section, the | ||||||
|  | Commercial Contributor would have to defend claims against the other | ||||||
|  | Contributors related to those performance claims and warranties, and if a | ||||||
|  | court requires any other Contributor to pay any damages as a result, the | ||||||
|  | Commercial Contributor must pay those damages. | ||||||
|  | 
 | ||||||
|  | 5. NO WARRANTY | ||||||
|  | 
 | ||||||
|  | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN | ||||||
|  | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR | ||||||
|  | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, | ||||||
|  | NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each | ||||||
|  | Recipient is solely responsible for determining the appropriateness of using | ||||||
|  | and distributing the Program and assumes all risks associated with its | ||||||
|  | exercise of rights under this Agreement , including but not limited to the | ||||||
|  | risks and costs of program errors, compliance with applicable laws, damage to | ||||||
|  | or loss of data, programs or equipment, and unavailability or interruption of | ||||||
|  | operations. | ||||||
|  | 
 | ||||||
|  | 6. DISCLAIMER OF LIABILITY | ||||||
|  | 
 | ||||||
|  | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY | ||||||
|  | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||||
|  | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION | ||||||
|  | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||||||
|  | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||||||
|  | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE | ||||||
|  | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY | ||||||
|  | OF SUCH DAMAGES. | ||||||
|  | 
 | ||||||
|  | 7. GENERAL | ||||||
|  | 
 | ||||||
|  | If any provision of this Agreement is invalid or unenforceable under | ||||||
|  | applicable law, it shall not affect the validity or enforceability of the | ||||||
|  | remainder of the terms of this Agreement, and without further action by the | ||||||
|  | parties hereto, such provision shall be reformed to the minimum extent | ||||||
|  | necessary to make such provision valid and enforceable. | ||||||
|  | 
 | ||||||
|  | If Recipient institutes patent litigation against any entity (including a | ||||||
|  | cross-claim or counterclaim in a lawsuit) alleging that the Program itself | ||||||
|  | (excluding combinations of the Program with other software or hardware) | ||||||
|  | infringes such Recipient's patent(s), then such Recipient's rights granted | ||||||
|  | under Section 2(b) shall terminate as of the date such litigation is filed. | ||||||
|  | 
 | ||||||
|  | All Recipient's rights under this Agreement shall terminate if it fails to | ||||||
|  | comply with any of the material terms or conditions of this Agreement and does | ||||||
|  | not cure such failure in a reasonable period of time after becoming aware of | ||||||
|  | such noncompliance. If all Recipient's rights under this Agreement terminate, | ||||||
|  | Recipient agrees to cease use and distribution of the Program as soon as | ||||||
|  | reasonably practicable. However, Recipient's obligations under this Agreement | ||||||
|  | and any licenses granted by Recipient relating to the Program shall continue | ||||||
|  | and survive. | ||||||
|  | 
 | ||||||
|  | Everyone is permitted to copy and distribute copies of this Agreement, but in | ||||||
|  | order to avoid inconsistency the Agreement is copyrighted and may only be | ||||||
|  | modified in the following manner. The Agreement Steward reserves the right to | ||||||
|  | publish new versions (including revisions) of this Agreement from time to | ||||||
|  | time. No one other than the Agreement Steward has the right to modify this | ||||||
|  | Agreement. The Eclipse Foundation is the initial Agreement Steward. The | ||||||
|  | Eclipse Foundation may assign the responsibility to serve as the Agreement | ||||||
|  | Steward to a suitable separate entity. Each new version of the Agreement will | ||||||
|  | be given a distinguishing version number. The Program (including | ||||||
|  | Contributions) may always be distributed subject to the version of the | ||||||
|  | Agreement under which it was received. In addition, after a new version of the | ||||||
|  | Agreement is published, Contributor may elect to distribute the Program | ||||||
|  | (including its Contributions) under the new version. Except as expressly | ||||||
|  | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or | ||||||
|  | licenses to the intellectual property of any Contributor under this Agreement, | ||||||
|  | whether expressly, by implication, estoppel or otherwise. All rights in the | ||||||
|  | Program not expressly granted under this Agreement are reserved. | ||||||
|  | 
 | ||||||
|  | This Agreement is governed by the laws of the State of New York and the | ||||||
|  | intellectual property laws of the United States of America. No party to this | ||||||
|  | Agreement will bring a legal action under this Agreement more than one year | ||||||
|  | after the cause of action arose. Each party waives its rights to a jury trial in | ||||||
|  | any resulting litigation. | ||||||
							
								
								
									
										674
									
								
								conf/license/GPL v3
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,674 @@ | ||||||
|  | GNU GENERAL PUBLIC LICENSE | ||||||
|  |                        Version 3, 29 June 2007 | ||||||
|  | 
 | ||||||
|  |  Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> | ||||||
|  |  Everyone is permitted to copy and distribute verbatim copies | ||||||
|  |  of this license document, but changing it is not allowed. | ||||||
|  | 
 | ||||||
|  |                             Preamble | ||||||
|  | 
 | ||||||
|  |   The GNU General Public License is a free, copyleft license for | ||||||
|  | software and other kinds of works. | ||||||
|  | 
 | ||||||
|  |   The licenses for most software and other practical works are designed | ||||||
|  | to take away your freedom to share and change the works.  By contrast, | ||||||
|  | the GNU General Public License is intended to guarantee your freedom to | ||||||
|  | share and change all versions of a program--to make sure it remains free | ||||||
|  | software for all its users.  We, the Free Software Foundation, use the | ||||||
|  | GNU General Public License for most of our software; it applies also to | ||||||
|  | any other work released this way by its authors.  You can apply it to | ||||||
|  | your programs, too. | ||||||
|  | 
 | ||||||
|  |   When we speak of free software, we are referring to freedom, not | ||||||
|  | price.  Our General Public Licenses are designed to make sure that you | ||||||
|  | have the freedom to distribute copies of free software (and charge for | ||||||
|  | them if you wish), that you receive source code or can get it if you | ||||||
|  | want it, that you can change the software or use pieces of it in new | ||||||
|  | free programs, and that you know you can do these things. | ||||||
|  | 
 | ||||||
|  |   To protect your rights, we need to prevent others from denying you | ||||||
|  | these rights or asking you to surrender the rights.  Therefore, you have | ||||||
|  | certain responsibilities if you distribute copies of the software, or if | ||||||
|  | you modify it: responsibilities to respect the freedom of others. | ||||||
|  | 
 | ||||||
|  |   For example, if you distribute copies of such a program, whether | ||||||
|  | gratis or for a fee, you must pass on to the recipients the same | ||||||
|  | freedoms that you received.  You must make sure that they, too, receive | ||||||
|  | or can get the source code.  And you must show them these terms so they | ||||||
|  | know their rights. | ||||||
|  | 
 | ||||||
|  |   Developers that use the GNU GPL protect your rights with two steps: | ||||||
|  | (1) assert copyright on the software, and (2) offer you this License | ||||||
|  | giving you legal permission to copy, distribute and/or modify it. | ||||||
|  | 
 | ||||||
|  |   For the developers' and authors' protection, the GPL clearly explains | ||||||
|  | that there is no warranty for this free software.  For both users' and | ||||||
|  | authors' sake, the GPL requires that modified versions be marked as | ||||||
|  | changed, so that their problems will not be attributed erroneously to | ||||||
|  | authors of previous versions. | ||||||
|  | 
 | ||||||
|  |   Some devices are designed to deny users access to install or run | ||||||
|  | modified versions of the software inside them, although the manufacturer | ||||||
|  | can do so.  This is fundamentally incompatible with the aim of | ||||||
|  | protecting users' freedom to change the software.  The systematic | ||||||
|  | pattern of such abuse occurs in the area of products for individuals to | ||||||
|  | use, which is precisely where it is most unacceptable.  Therefore, we | ||||||
|  | have designed this version of the GPL to prohibit the practice for those | ||||||
|  | products.  If such problems arise substantially in other domains, we | ||||||
|  | stand ready to extend this provision to those domains in future versions | ||||||
|  | of the GPL, as needed to protect the freedom of users. | ||||||
|  | 
 | ||||||
|  |   Finally, every program is threatened constantly by software patents. | ||||||
|  | States should not allow patents to restrict development and use of | ||||||
|  | software on general-purpose computers, but in those that do, we wish to | ||||||
|  | avoid the special danger that patents applied to a free program could | ||||||
|  | make it effectively proprietary.  To prevent this, the GPL assures that | ||||||
|  | patents cannot be used to render the program non-free. | ||||||
|  | 
 | ||||||
|  |   The precise terms and conditions for copying, distribution and | ||||||
|  | modification follow. | ||||||
|  | 
 | ||||||
|  |                        TERMS AND CONDITIONS | ||||||
|  | 
 | ||||||
|  |   0. Definitions. | ||||||
|  | 
 | ||||||
|  |   "This License" refers to version 3 of the GNU General Public License. | ||||||
|  | 
 | ||||||
|  |   "Copyright" also means copyright-like laws that apply to other kinds of | ||||||
|  | works, such as semiconductor masks. | ||||||
|  | 
 | ||||||
|  |   "The Program" refers to any copyrightable work licensed under this | ||||||
|  | License.  Each licensee is addressed as "you".  "Licensees" and | ||||||
|  | "recipients" may be individuals or organizations. | ||||||
|  | 
 | ||||||
|  |   To "modify" a work means to copy from or adapt all or part of the work | ||||||
|  | in a fashion requiring copyright permission, other than the making of an | ||||||
|  | exact copy.  The resulting work is called a "modified version" of the | ||||||
|  | earlier work or a work "based on" the earlier work. | ||||||
|  | 
 | ||||||
|  |   A "covered work" means either the unmodified Program or a work based | ||||||
|  | on the Program. | ||||||
|  | 
 | ||||||
|  |   To "propagate" a work means to do anything with it that, without | ||||||
|  | permission, would make you directly or secondarily liable for | ||||||
|  | infringement under applicable copyright law, except executing it on a | ||||||
|  | computer or modifying a private copy.  Propagation includes copying, | ||||||
|  | distribution (with or without modification), making available to the | ||||||
|  | public, and in some countries other activities as well. | ||||||
|  | 
 | ||||||
|  |   To "convey" a work means any kind of propagation that enables other | ||||||
|  | parties to make or receive copies.  Mere interaction with a user through | ||||||
|  | a computer network, with no transfer of a copy, is not conveying. | ||||||
|  | 
 | ||||||
|  |   An interactive user interface displays "Appropriate Legal Notices" | ||||||
|  | to the extent that it includes a convenient and prominently visible | ||||||
|  | feature that (1) displays an appropriate copyright notice, and (2) | ||||||
|  | tells the user that there is no warranty for the work (except to the | ||||||
|  | extent that warranties are provided), that licensees may convey the | ||||||
|  | work under this License, and how to view a copy of this License.  If | ||||||
|  | the interface presents a list of user commands or options, such as a | ||||||
|  | menu, a prominent item in the list meets this criterion. | ||||||
|  | 
 | ||||||
|  |   1. Source Code. | ||||||
|  | 
 | ||||||
|  |   The "source code" for a work means the preferred form of the work | ||||||
|  | for making modifications to it.  "Object code" means any non-source | ||||||
|  | form of a work. | ||||||
|  | 
 | ||||||
|  |   A "Standard Interface" means an interface that either is an official | ||||||
|  | standard defined by a recognized standards body, or, in the case of | ||||||
|  | interfaces specified for a particular programming language, one that | ||||||
|  | is widely used among developers working in that language. | ||||||
|  | 
 | ||||||
|  |   The "System Libraries" of an executable work include anything, other | ||||||
|  | than the work as a whole, that (a) is included in the normal form of | ||||||
|  | packaging a Major Component, but which is not part of that Major | ||||||
|  | Component, and (b) serves only to enable use of the work with that | ||||||
|  | Major Component, or to implement a Standard Interface for which an | ||||||
|  | implementation is available to the public in source code form.  A | ||||||
|  | "Major Component", in this context, means a major essential component | ||||||
|  | (kernel, window system, and so on) of the specific operating system | ||||||
|  | (if any) on which the executable work runs, or a compiler used to | ||||||
|  | produce the work, or an object code interpreter used to run it. | ||||||
|  | 
 | ||||||
|  |   The "Corresponding Source" for a work in object code form means all | ||||||
|  | the source code needed to generate, install, and (for an executable | ||||||
|  | work) run the object code and to modify the work, including scripts to | ||||||
|  | control those activities.  However, it does not include the work's | ||||||
|  | System Libraries, or general-purpose tools or generally available free | ||||||
|  | programs which are used unmodified in performing those activities but | ||||||
|  | which are not part of the work.  For example, Corresponding Source | ||||||
|  | includes interface definition files associated with source files for | ||||||
|  | the work, and the source code for shared libraries and dynamically | ||||||
|  | linked subprograms that the work is specifically designed to require, | ||||||
|  | such as by intimate data communication or control flow between those | ||||||
|  | subprograms and other parts of the work. | ||||||
|  | 
 | ||||||
|  |   The Corresponding Source need not include anything that users | ||||||
|  | can regenerate automatically from other parts of the Corresponding | ||||||
|  | Source. | ||||||
|  | 
 | ||||||
|  |   The Corresponding Source for a work in source code form is that | ||||||
|  | same work. | ||||||
|  | 
 | ||||||
|  |   2. Basic Permissions. | ||||||
|  | 
 | ||||||
|  |   All rights granted under this License are granted for the term of | ||||||
|  | copyright on the Program, and are irrevocable provided the stated | ||||||
|  | conditions are met.  This License explicitly affirms your unlimited | ||||||
|  | permission to run the unmodified Program.  The output from running a | ||||||
|  | covered work is covered by this License only if the output, given its | ||||||
|  | content, constitutes a covered work.  This License acknowledges your | ||||||
|  | rights of fair use or other equivalent, as provided by copyright law. | ||||||
|  | 
 | ||||||
|  |   You may make, run and propagate covered works that you do not | ||||||
|  | convey, without conditions so long as your license otherwise remains | ||||||
|  | in force.  You may convey covered works to others for the sole purpose | ||||||
|  | of having them make modifications exclusively for you, or provide you | ||||||
|  | with facilities for running those works, provided that you comply with | ||||||
|  | the terms of this License in conveying all material for which you do | ||||||
|  | not control copyright.  Those thus making or running the covered works | ||||||
|  | for you must do so exclusively on your behalf, under your direction | ||||||
|  | and control, on terms that prohibit them from making any copies of | ||||||
|  | your copyrighted material outside their relationship with you. | ||||||
|  | 
 | ||||||
|  |   Conveying under any other circumstances is permitted solely under | ||||||
|  | the conditions stated below.  Sublicensing is not allowed; section 10 | ||||||
|  | makes it unnecessary. | ||||||
|  | 
 | ||||||
|  |   3. Protecting Users' Legal Rights From Anti-Circumvention Law. | ||||||
|  | 
 | ||||||
|  |   No covered work shall be deemed part of an effective technological | ||||||
|  | measure under any applicable law fulfilling obligations under article | ||||||
|  | 11 of the WIPO copyright treaty adopted on 20 December 1996, or | ||||||
|  | similar laws prohibiting or restricting circumvention of such | ||||||
|  | measures. | ||||||
|  | 
 | ||||||
|  |   When you convey a covered work, you waive any legal power to forbid | ||||||
|  | circumvention of technological measures to the extent such circumvention | ||||||
|  | is effected by exercising rights under this License with respect to | ||||||
|  | the covered work, and you disclaim any intention to limit operation or | ||||||
|  | modification of the work as a means of enforcing, against the work's | ||||||
|  | users, your or third parties' legal rights to forbid circumvention of | ||||||
|  | technological measures. | ||||||
|  | 
 | ||||||
|  |   4. Conveying Verbatim Copies. | ||||||
|  | 
 | ||||||
|  |   You may convey verbatim copies of the Program's source code as you | ||||||
|  | receive it, in any medium, provided that you conspicuously and | ||||||
|  | appropriately publish on each copy an appropriate copyright notice; | ||||||
|  | keep intact all notices stating that this License and any | ||||||
|  | non-permissive terms added in accord with section 7 apply to the code; | ||||||
|  | keep intact all notices of the absence of any warranty; and give all | ||||||
|  | recipients a copy of this License along with the Program. | ||||||
|  | 
 | ||||||
|  |   You may charge any price or no price for each copy that you convey, | ||||||
|  | and you may offer support or warranty protection for a fee. | ||||||
|  | 
 | ||||||
|  |   5. Conveying Modified Source Versions. | ||||||
|  | 
 | ||||||
|  |   You may convey a work based on the Program, or the modifications to | ||||||
|  | produce it from the Program, in the form of source code under the | ||||||
|  | terms of section 4, provided that you also meet all of these conditions: | ||||||
|  | 
 | ||||||
|  |     a) The work must carry prominent notices stating that you modified | ||||||
|  |     it, and giving a relevant date. | ||||||
|  | 
 | ||||||
|  |     b) The work must carry prominent notices stating that it is | ||||||
|  |     released under this License and any conditions added under section | ||||||
|  |     7.  This requirement modifies the requirement in section 4 to | ||||||
|  |     "keep intact all notices". | ||||||
|  | 
 | ||||||
|  |     c) You must license the entire work, as a whole, under this | ||||||
|  |     License to anyone who comes into possession of a copy.  This | ||||||
|  |     License will therefore apply, along with any applicable section 7 | ||||||
|  |     additional terms, to the whole of the work, and all its parts, | ||||||
|  |     regardless of how they are packaged.  This License gives no | ||||||
|  |     permission to license the work in any other way, but it does not | ||||||
|  |     invalidate such permission if you have separately received it. | ||||||
|  | 
 | ||||||
|  |     d) If the work has interactive user interfaces, each must display | ||||||
|  |     Appropriate Legal Notices; however, if the Program has interactive | ||||||
|  |     interfaces that do not display Appropriate Legal Notices, your | ||||||
|  |     work need not make them do so. | ||||||
|  | 
 | ||||||
|  |   A compilation of a covered work with other separate and independent | ||||||
|  | works, which are not by their nature extensions of the covered work, | ||||||
|  | and which are not combined with it such as to form a larger program, | ||||||
|  | in or on a volume of a storage or distribution medium, is called an | ||||||
|  | "aggregate" if the compilation and its resulting copyright are not | ||||||
|  | used to limit the access or legal rights of the compilation's users | ||||||
|  | beyond what the individual works permit.  Inclusion of a covered work | ||||||
|  | in an aggregate does not cause this License to apply to the other | ||||||
|  | parts of the aggregate. | ||||||
|  | 
 | ||||||
|  |   6. Conveying Non-Source Forms. | ||||||
|  | 
 | ||||||
|  |   You may convey a covered work in object code form under the terms | ||||||
|  | of sections 4 and 5, provided that you also convey the | ||||||
|  | machine-readable Corresponding Source under the terms of this License, | ||||||
|  | in one of these ways: | ||||||
|  | 
 | ||||||
|  |     a) Convey the object code in, or embodied in, a physical product | ||||||
|  |     (including a physical distribution medium), accompanied by the | ||||||
|  |     Corresponding Source fixed on a durable physical medium | ||||||
|  |     customarily used for software interchange. | ||||||
|  | 
 | ||||||
|  |     b) Convey the object code in, or embodied in, a physical product | ||||||
|  |     (including a physical distribution medium), accompanied by a | ||||||
|  |     written offer, valid for at least three years and valid for as | ||||||
|  |     long as you offer spare parts or customer support for that product | ||||||
|  |     model, to give anyone who possesses the object code either (1) a | ||||||
|  |     copy of the Corresponding Source for all the software in the | ||||||
|  |     product that is covered by this License, on a durable physical | ||||||
|  |     medium customarily used for software interchange, for a price no | ||||||
|  |     more than your reasonable cost of physically performing this | ||||||
|  |     conveying of source, or (2) access to copy the | ||||||
|  |     Corresponding Source from a network server at no charge. | ||||||
|  | 
 | ||||||
|  |     c) Convey individual copies of the object code with a copy of the | ||||||
|  |     written offer to provide the Corresponding Source.  This | ||||||
|  |     alternative is allowed only occasionally and noncommercially, and | ||||||
|  |     only if you received the object code with such an offer, in accord | ||||||
|  |     with subsection 6b. | ||||||
|  | 
 | ||||||
|  |     d) Convey the object code by offering access from a designated | ||||||
|  |     place (gratis or for a charge), and offer equivalent access to the | ||||||
|  |     Corresponding Source in the same way through the same place at no | ||||||
|  |     further charge.  You need not require recipients to copy the | ||||||
|  |     Corresponding Source along with the object code.  If the place to | ||||||
|  |     copy the object code is a network server, the Corresponding Source | ||||||
|  |     may be on a different server (operated by you or a third party) | ||||||
|  |     that supports equivalent copying facilities, provided you maintain | ||||||
|  |     clear directions next to the object code saying where to find the | ||||||
|  |     Corresponding Source.  Regardless of what server hosts the | ||||||
|  |     Corresponding Source, you remain obligated to ensure that it is | ||||||
|  |     available for as long as needed to satisfy these requirements. | ||||||
|  | 
 | ||||||
|  |     e) Convey the object code using peer-to-peer transmission, provided | ||||||
|  |     you inform other peers where the object code and Corresponding | ||||||
|  |     Source of the work are being offered to the general public at no | ||||||
|  |     charge under subsection 6d. | ||||||
|  | 
 | ||||||
|  |   A separable portion of the object code, whose source code is excluded | ||||||
|  | from the Corresponding Source as a System Library, need not be | ||||||
|  | included in conveying the object code work. | ||||||
|  | 
 | ||||||
|  |   A "User Product" is either (1) a "consumer product", which means any | ||||||
|  | tangible personal property which is normally used for personal, family, | ||||||
|  | or household purposes, or (2) anything designed or sold for incorporation | ||||||
|  | into a dwelling.  In determining whether a product is a consumer product, | ||||||
|  | doubtful cases shall be resolved in favor of coverage.  For a particular | ||||||
|  | product received by a particular user, "normally used" refers to a | ||||||
|  | typical or common use of that class of product, regardless of the status | ||||||
|  | of the particular user or of the way in which the particular user | ||||||
|  | actually uses, or expects or is expected to use, the product.  A product | ||||||
|  | is a consumer product regardless of whether the product has substantial | ||||||
|  | commercial, industrial or non-consumer uses, unless such uses represent | ||||||
|  | the only significant mode of use of the product. | ||||||
|  | 
 | ||||||
|  |   "Installation Information" for a User Product means any methods, | ||||||
|  | procedures, authorization keys, or other information required to install | ||||||
|  | and execute modified versions of a covered work in that User Product from | ||||||
|  | a modified version of its Corresponding Source.  The information must | ||||||
|  | suffice to ensure that the continued functioning of the modified object | ||||||
|  | code is in no case prevented or interfered with solely because | ||||||
|  | modification has been made. | ||||||
|  | 
 | ||||||
|  |   If you convey an object code work under this section in, or with, or | ||||||
|  | specifically for use in, a User Product, and the conveying occurs as | ||||||
|  | part of a transaction in which the right of possession and use of the | ||||||
|  | User Product is transferred to the recipient in perpetuity or for a | ||||||
|  | fixed term (regardless of how the transaction is characterized), the | ||||||
|  | Corresponding Source conveyed under this section must be accompanied | ||||||
|  | by the Installation Information.  But this requirement does not apply | ||||||
|  | if neither you nor any third party retains the ability to install | ||||||
|  | modified object code on the User Product (for example, the work has | ||||||
|  | been installed in ROM). | ||||||
|  | 
 | ||||||
|  |   The requirement to provide Installation Information does not include a | ||||||
|  | requirement to continue to provide support service, warranty, or updates | ||||||
|  | for a work that has been modified or installed by the recipient, or for | ||||||
|  | the User Product in which it has been modified or installed.  Access to a | ||||||
|  | network may be denied when the modification itself materially and | ||||||
|  | adversely affects the operation of the network or violates the rules and | ||||||
|  | protocols for communication across the network. | ||||||
|  | 
 | ||||||
|  |   Corresponding Source conveyed, and Installation Information provided, | ||||||
|  | in accord with this section must be in a format that is publicly | ||||||
|  | documented (and with an implementation available to the public in | ||||||
|  | source code form), and must require no special password or key for | ||||||
|  | unpacking, reading or copying. | ||||||
|  | 
 | ||||||
|  |   7. Additional Terms. | ||||||
|  | 
 | ||||||
|  |   "Additional permissions" are terms that supplement the terms of this | ||||||
|  | License by making exceptions from one or more of its conditions. | ||||||
|  | Additional permissions that are applicable to the entire Program shall | ||||||
|  | be treated as though they were included in this License, to the extent | ||||||
|  | that they are valid under applicable law.  If additional permissions | ||||||
|  | apply only to part of the Program, that part may be used separately | ||||||
|  | under those permissions, but the entire Program remains governed by | ||||||
|  | this License without regard to the additional permissions. | ||||||
|  | 
 | ||||||
|  |   When you convey a copy of a covered work, you may at your option | ||||||
|  | remove any additional permissions from that copy, or from any part of | ||||||
|  | it.  (Additional permissions may be written to require their own | ||||||
|  | removal in certain cases when you modify the work.)  You may place | ||||||
|  | additional permissions on material, added by you to a covered work, | ||||||
|  | for which you have or can give appropriate copyright permission. | ||||||
|  | 
 | ||||||
|  |   Notwithstanding any other provision of this License, for material you | ||||||
|  | add to a covered work, you may (if authorized by the copyright holders of | ||||||
|  | that material) supplement the terms of this License with terms: | ||||||
|  | 
 | ||||||
|  |     a) Disclaiming warranty or limiting liability differently from the | ||||||
|  |     terms of sections 15 and 16 of this License; or | ||||||
|  | 
 | ||||||
|  |     b) Requiring preservation of specified reasonable legal notices or | ||||||
|  |     author attributions in that material or in the Appropriate Legal | ||||||
|  |     Notices displayed by works containing it; or | ||||||
|  | 
 | ||||||
|  |     c) Prohibiting misrepresentation of the origin of that material, or | ||||||
|  |     requiring that modified versions of such material be marked in | ||||||
|  |     reasonable ways as different from the original version; or | ||||||
|  | 
 | ||||||
|  |     d) Limiting the use for publicity purposes of names of licensors or | ||||||
|  |     authors of the material; or | ||||||
|  | 
 | ||||||
|  |     e) Declining to grant rights under trademark law for use of some | ||||||
|  |     trade names, trademarks, or service marks; or | ||||||
|  | 
 | ||||||
|  |     f) Requiring indemnification of licensors and authors of that | ||||||
|  |     material by anyone who conveys the material (or modified versions of | ||||||
|  |     it) with contractual assumptions of liability to the recipient, for | ||||||
|  |     any liability that these contractual assumptions directly impose on | ||||||
|  |     those licensors and authors. | ||||||
|  | 
 | ||||||
|  |   All other non-permissive additional terms are considered "further | ||||||
|  | restrictions" within the meaning of section 10.  If the Program as you | ||||||
|  | received it, or any part of it, contains a notice stating that it is | ||||||
|  | governed by this License along with a term that is a further | ||||||
|  | restriction, you may remove that term.  If a license document contains | ||||||
|  | a further restriction but permits relicensing or conveying under this | ||||||
|  | License, you may add to a covered work material governed by the terms | ||||||
|  | of that license document, provided that the further restriction does | ||||||
|  | not survive such relicensing or conveying. | ||||||
|  | 
 | ||||||
|  |   If you add terms to a covered work in accord with this section, you | ||||||
|  | must place, in the relevant source files, a statement of the | ||||||
|  | additional terms that apply to those files, or a notice indicating | ||||||
|  | where to find the applicable terms. | ||||||
|  | 
 | ||||||
|  |   Additional terms, permissive or non-permissive, may be stated in the | ||||||
|  | form of a separately written license, or stated as exceptions; | ||||||
|  | the above requirements apply either way. | ||||||
|  | 
 | ||||||
|  |   8. Termination. | ||||||
|  | 
 | ||||||
|  |   You may not propagate or modify a covered work except as expressly | ||||||
|  | provided under this License.  Any attempt otherwise to propagate or | ||||||
|  | modify it is void, and will automatically terminate your rights under | ||||||
|  | this License (including any patent licenses granted under the third | ||||||
|  | paragraph of section 11). | ||||||
|  | 
 | ||||||
|  |   However, if you cease all violation of this License, then your | ||||||
|  | license from a particular copyright holder is reinstated (a) | ||||||
|  | provisionally, unless and until the copyright holder explicitly and | ||||||
|  | finally terminates your license, and (b) permanently, if the copyright | ||||||
|  | holder fails to notify you of the violation by some reasonable means | ||||||
|  | prior to 60 days after the cessation. | ||||||
|  | 
 | ||||||
|  |   Moreover, your license from a particular copyright holder is | ||||||
|  | reinstated permanently if the copyright holder notifies you of the | ||||||
|  | violation by some reasonable means, this is the first time you have | ||||||
|  | received notice of violation of this License (for any work) from that | ||||||
|  | copyright holder, and you cure the violation prior to 30 days after | ||||||
|  | your receipt of the notice. | ||||||
|  | 
 | ||||||
|  |   Termination of your rights under this section does not terminate the | ||||||
|  | licenses of parties who have received copies or rights from you under | ||||||
|  | this License.  If your rights have been terminated and not permanently | ||||||
|  | reinstated, you do not qualify to receive new licenses for the same | ||||||
|  | material under section 10. | ||||||
|  | 
 | ||||||
|  |   9. Acceptance Not Required for Having Copies. | ||||||
|  | 
 | ||||||
|  |   You are not required to accept this License in order to receive or | ||||||
|  | run a copy of the Program.  Ancillary propagation of a covered work | ||||||
|  | occurring solely as a consequence of using peer-to-peer transmission | ||||||
|  | to receive a copy likewise does not require acceptance.  However, | ||||||
|  | nothing other than this License grants you permission to propagate or | ||||||
|  | modify any covered work.  These actions infringe copyright if you do | ||||||
|  | not accept this License.  Therefore, by modifying or propagating a | ||||||
|  | covered work, you indicate your acceptance of this License to do so. | ||||||
|  | 
 | ||||||
|  |   10. Automatic Licensing of Downstream Recipients. | ||||||
|  | 
 | ||||||
|  |   Each time you convey a covered work, the recipient automatically | ||||||
|  | receives a license from the original licensors, to run, modify and | ||||||
|  | propagate that work, subject to this License.  You are not responsible | ||||||
|  | for enforcing compliance by third parties with this License. | ||||||
|  | 
 | ||||||
|  |   An "entity transaction" is a transaction transferring control of an | ||||||
|  | organization, or substantially all assets of one, or subdividing an | ||||||
|  | organization, or merging organizations.  If propagation of a covered | ||||||
|  | work results from an entity transaction, each party to that | ||||||
|  | transaction who receives a copy of the work also receives whatever | ||||||
|  | licenses to the work the party's predecessor in interest had or could | ||||||
|  | give under the previous paragraph, plus a right to possession of the | ||||||
|  | Corresponding Source of the work from the predecessor in interest, if | ||||||
|  | the predecessor has it or can get it with reasonable efforts. | ||||||
|  | 
 | ||||||
|  |   You may not impose any further restrictions on the exercise of the | ||||||
|  | rights granted or affirmed under this License.  For example, you may | ||||||
|  | not impose a license fee, royalty, or other charge for exercise of | ||||||
|  | rights granted under this License, and you may not initiate litigation | ||||||
|  | (including a cross-claim or counterclaim in a lawsuit) alleging that | ||||||
|  | any patent claim is infringed by making, using, selling, offering for | ||||||
|  | sale, or importing the Program or any portion of it. | ||||||
|  | 
 | ||||||
|  |   11. Patents. | ||||||
|  | 
 | ||||||
|  |   A "contributor" is a copyright holder who authorizes use under this | ||||||
|  | License of the Program or a work on which the Program is based.  The | ||||||
|  | work thus licensed is called the contributor's "contributor version". | ||||||
|  | 
 | ||||||
|  |   A contributor's "essential patent claims" are all patent claims | ||||||
|  | owned or controlled by the contributor, whether already acquired or | ||||||
|  | hereafter acquired, that would be infringed by some manner, permitted | ||||||
|  | by this License, of making, using, or selling its contributor version, | ||||||
|  | but do not include claims that would be infringed only as a | ||||||
|  | consequence of further modification of the contributor version.  For | ||||||
|  | purposes of this definition, "control" includes the right to grant | ||||||
|  | patent sublicenses in a manner consistent with the requirements of | ||||||
|  | this License. | ||||||
|  | 
 | ||||||
|  |   Each contributor grants you a non-exclusive, worldwide, royalty-free | ||||||
|  | patent license under the contributor's essential patent claims, to | ||||||
|  | make, use, sell, offer for sale, import and otherwise run, modify and | ||||||
|  | propagate the contents of its contributor version. | ||||||
|  | 
 | ||||||
|  |   In the following three paragraphs, a "patent license" is any express | ||||||
|  | agreement or commitment, however denominated, not to enforce a patent | ||||||
|  | (such as an express permission to practice a patent or covenant not to | ||||||
|  | sue for patent infringement).  To "grant" such a patent license to a | ||||||
|  | party means to make such an agreement or commitment not to enforce a | ||||||
|  | patent against the party. | ||||||
|  | 
 | ||||||
|  |   If you convey a covered work, knowingly relying on a patent license, | ||||||
|  | and the Corresponding Source of the work is not available for anyone | ||||||
|  | to copy, free of charge and under the terms of this License, through a | ||||||
|  | publicly available network server or other readily accessible means, | ||||||
|  | then you must either (1) cause the Corresponding Source to be so | ||||||
|  | available, or (2) arrange to deprive yourself of the benefit of the | ||||||
|  | patent license for this particular work, or (3) arrange, in a manner | ||||||
|  | consistent with the requirements of this License, to extend the patent | ||||||
|  | license to downstream recipients.  "Knowingly relying" means you have | ||||||
|  | actual knowledge that, but for the patent license, your conveying the | ||||||
|  | covered work in a country, or your recipient's use of the covered work | ||||||
|  | in a country, would infringe one or more identifiable patents in that | ||||||
|  | country that you have reason to believe are valid. | ||||||
|  | 
 | ||||||
|  |   If, pursuant to or in connection with a single transaction or | ||||||
|  | arrangement, you convey, or propagate by procuring conveyance of, a | ||||||
|  | covered work, and grant a patent license to some of the parties | ||||||
|  | receiving the covered work authorizing them to use, propagate, modify | ||||||
|  | or convey a specific copy of the covered work, then the patent license | ||||||
|  | you grant is automatically extended to all recipients of the covered | ||||||
|  | work and works based on it. | ||||||
|  | 
 | ||||||
|  |   A patent license is "discriminatory" if it does not include within | ||||||
|  | the scope of its coverage, prohibits the exercise of, or is | ||||||
|  | conditioned on the non-exercise of one or more of the rights that are | ||||||
|  | specifically granted under this License.  You may not convey a covered | ||||||
|  | work if you are a party to an arrangement with a third party that is | ||||||
|  | in the business of distributing software, under which you make payment | ||||||
|  | to the third party based on the extent of your activity of conveying | ||||||
|  | the work, and under which the third party grants, to any of the | ||||||
|  | parties who would receive the covered work from you, a discriminatory | ||||||
|  | patent license (a) in connection with copies of the covered work | ||||||
|  | conveyed by you (or copies made from those copies), or (b) primarily | ||||||
|  | for and in connection with specific products or compilations that | ||||||
|  | contain the covered work, unless you entered into that arrangement, | ||||||
|  | or that patent license was granted, prior to 28 March 2007. | ||||||
|  | 
 | ||||||
|  |   Nothing in this License shall be construed as excluding or limiting | ||||||
|  | any implied license or other defenses to infringement that may | ||||||
|  | otherwise be available to you under applicable patent law. | ||||||
|  | 
 | ||||||
|  |   12. No Surrender of Others' Freedom. | ||||||
|  | 
 | ||||||
|  |   If conditions are imposed on you (whether by court order, agreement or | ||||||
|  | otherwise) that contradict the conditions of this License, they do not | ||||||
|  | excuse you from the conditions of this License.  If you cannot convey a | ||||||
|  | covered work so as to satisfy simultaneously your obligations under this | ||||||
|  | License and any other pertinent obligations, then as a consequence you may | ||||||
|  | not convey it at all.  For example, if you agree to terms that obligate you | ||||||
|  | to collect a royalty for further conveying from those to whom you convey | ||||||
|  | the Program, the only way you could satisfy both those terms and this | ||||||
|  | License would be to refrain entirely from conveying the Program. | ||||||
|  | 
 | ||||||
|  |   13. Use with the GNU Affero General Public License. | ||||||
|  | 
 | ||||||
|  |   Notwithstanding any other provision of this License, you have | ||||||
|  | permission to link or combine any covered work with a work licensed | ||||||
|  | under version 3 of the GNU Affero General Public License into a single | ||||||
|  | combined work, and to convey the resulting work.  The terms of this | ||||||
|  | License will continue to apply to the part which is the covered work, | ||||||
|  | but the special requirements of the GNU Affero General Public License, | ||||||
|  | section 13, concerning interaction through a network will apply to the | ||||||
|  | combination as such. | ||||||
|  | 
 | ||||||
|  |   14. Revised Versions of this License. | ||||||
|  | 
 | ||||||
|  |   The Free Software Foundation may publish revised and/or new versions of | ||||||
|  | the GNU General Public License from time to time.  Such new versions will | ||||||
|  | be similar in spirit to the present version, but may differ in detail to | ||||||
|  | address new problems or concerns. | ||||||
|  | 
 | ||||||
|  |   Each version is given a distinguishing version number.  If the | ||||||
|  | Program specifies that a certain numbered version of the GNU General | ||||||
|  | Public License "or any later version" applies to it, you have the | ||||||
|  | option of following the terms and conditions either of that numbered | ||||||
|  | version or of any later version published by the Free Software | ||||||
|  | Foundation.  If the Program does not specify a version number of the | ||||||
|  | GNU General Public License, you may choose any version ever published | ||||||
|  | by the Free Software Foundation. | ||||||
|  | 
 | ||||||
|  |   If the Program specifies that a proxy can decide which future | ||||||
|  | versions of the GNU General Public License can be used, that proxy's | ||||||
|  | public statement of acceptance of a version permanently authorizes you | ||||||
|  | to choose that version for the Program. | ||||||
|  | 
 | ||||||
|  |   Later license versions may give you additional or different | ||||||
|  | permissions.  However, no additional obligations are imposed on any | ||||||
|  | author or copyright holder as a result of your choosing to follow a | ||||||
|  | later version. | ||||||
|  | 
 | ||||||
|  |   15. Disclaimer of Warranty. | ||||||
|  | 
 | ||||||
|  |   THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY | ||||||
|  | APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT | ||||||
|  | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY | ||||||
|  | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, | ||||||
|  | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||||||
|  | PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM | ||||||
|  | IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF | ||||||
|  | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. | ||||||
|  | 
 | ||||||
|  |   16. Limitation of Liability. | ||||||
|  | 
 | ||||||
|  |   IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING | ||||||
|  | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS | ||||||
|  | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY | ||||||
|  | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE | ||||||
|  | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF | ||||||
|  | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD | ||||||
|  | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), | ||||||
|  | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF | ||||||
|  | SUCH DAMAGES. | ||||||
|  | 
 | ||||||
|  |   17. Interpretation of Sections 15 and 16. | ||||||
|  | 
 | ||||||
|  |   If the disclaimer of warranty and limitation of liability provided | ||||||
|  | above cannot be given local legal effect according to their terms, | ||||||
|  | reviewing courts shall apply local law that most closely approximates | ||||||
|  | an absolute waiver of all civil liability in connection with the | ||||||
|  | Program, unless a warranty or assumption of liability accompanies a | ||||||
|  | copy of the Program in return for a fee. | ||||||
|  | 
 | ||||||
|  |                      END OF TERMS AND CONDITIONS | ||||||
|  | 
 | ||||||
|  |             How to Apply These Terms to Your New Programs | ||||||
|  | 
 | ||||||
|  |   If you develop a new program, and you want it to be of the greatest | ||||||
|  | possible use to the public, the best way to achieve this is to make it | ||||||
|  | free software which everyone can redistribute and change under these terms. | ||||||
|  | 
 | ||||||
|  |   To do so, attach the following notices to the program.  It is safest | ||||||
|  | to attach them to the start of each source file to most effectively | ||||||
|  | state the exclusion of warranty; and each file should have at least | ||||||
|  | the "copyright" line and a pointer to where the full notice is found. | ||||||
|  | 
 | ||||||
|  |     {one line to give the program's name and a brief idea of what it does.} | ||||||
|  |     Copyright (C) {year}  {name of author} | ||||||
|  | 
 | ||||||
|  |     This program is free software: you can redistribute it and/or modify | ||||||
|  |     it under the terms of the GNU General Public License as published by | ||||||
|  |     the Free Software Foundation, either version 3 of the License, or | ||||||
|  |     (at your option) any later version. | ||||||
|  | 
 | ||||||
|  |     This program is distributed in the hope that it will be useful, | ||||||
|  |     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |     GNU General Public License for more details. | ||||||
|  | 
 | ||||||
|  |     You should have received a copy of the GNU General Public License | ||||||
|  |     along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  | 
 | ||||||
|  | Also add information on how to contact you by electronic and paper mail. | ||||||
|  | 
 | ||||||
|  |   If the program does terminal interaction, make it output a short | ||||||
|  | notice like this when it starts in an interactive mode: | ||||||
|  | 
 | ||||||
|  |     {project}  Copyright (C) {year}  {fullname} | ||||||
|  |     This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. | ||||||
|  |     This is free software, and you are welcome to redistribute it | ||||||
|  |     under certain conditions; type `show c' for details. | ||||||
|  | 
 | ||||||
|  | The hypothetical commands `show w' and `show c' should show the appropriate | ||||||
|  | parts of the General Public License.  Of course, your program's commands | ||||||
|  | might be different; for a GUI interface, you would use an "about box". | ||||||
|  | 
 | ||||||
|  |   You should also get your employer (if you work as a programmer) or school, | ||||||
|  | if any, to sign a "copyright disclaimer" for the program, if necessary. | ||||||
|  | For more information on this, and how to apply and follow the GNU GPL, see | ||||||
|  | <http://www.gnu.org/licenses/>. | ||||||
|  | 
 | ||||||
|  |   The GNU General Public License does not permit incorporating your program | ||||||
|  | into proprietary programs.  If your program is a subroutine library, you | ||||||
|  | may consider it more useful to permit linking proprietary applications with | ||||||
|  | the library.  If this is what you want to do, use the GNU Lesser General | ||||||
|  | Public License instead of this License.  But first, please read | ||||||
|  | <http://www.gnu.org/philosophy/why-not-lgpl.html>. | ||||||
							
								
								
									
										13
									
								
								conf/license/ISC license
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,13 @@ | ||||||
|  | Copyright (c) 2014 | ||||||
|  | 
 | ||||||
|  | Permission to use, copy, modify, and/or distribute this software for any | ||||||
|  | purpose with or without fee is hereby granted, provided that the above | ||||||
|  | copyright notice and this permission notice appear in all copies. | ||||||
|  | 
 | ||||||
|  | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||||
|  | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||||
|  | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||||
|  | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||||
|  | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||||
|  | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||||
|  | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||||
							
								
								
									
										504
									
								
								conf/license/LGPL v2.1
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,504 @@ | ||||||
|  | GNU LESSER GENERAL PUBLIC LICENSE | ||||||
|  |                        Version 2.1, February 1999 | ||||||
|  | 
 | ||||||
|  |  Copyright (C) 1991, 1999 Free Software Foundation, Inc. | ||||||
|  |  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA | ||||||
|  |  Everyone is permitted to copy and distribute verbatim copies | ||||||
|  |  of this license document, but changing it is not allowed. | ||||||
|  | 
 | ||||||
|  | (This is the first released version of the Lesser GPL.  It also counts | ||||||
|  |  as the successor of the GNU Library Public License, version 2, hence | ||||||
|  |  the version number 2.1.) | ||||||
|  | 
 | ||||||
|  |                             Preamble | ||||||
|  | 
 | ||||||
|  |   The licenses for most software are designed to take away your | ||||||
|  | freedom to share and change it.  By contrast, the GNU General Public | ||||||
|  | Licenses are intended to guarantee your freedom to share and change | ||||||
|  | free software--to make sure the software is free for all its users. | ||||||
|  | 
 | ||||||
|  |   This license, the Lesser General Public License, applies to some | ||||||
|  | specially designated software packages--typically libraries--of the | ||||||
|  | Free Software Foundation and other authors who decide to use it.  You | ||||||
|  | can use it too, but we suggest you first think carefully about whether | ||||||
|  | this license or the ordinary General Public License is the better | ||||||
|  | strategy to use in any particular case, based on the explanations below. | ||||||
|  | 
 | ||||||
|  |   When we speak of free software, we are referring to freedom of use, | ||||||
|  | not price.  Our General Public Licenses are designed to make sure that | ||||||
|  | you have the freedom to distribute copies of free software (and charge | ||||||
|  | for this service if you wish); that you receive source code or can get | ||||||
|  | it if you want it; that you can change the software and use pieces of | ||||||
|  | it in new free programs; and that you are informed that you can do | ||||||
|  | these things. | ||||||
|  | 
 | ||||||
|  |   To protect your rights, we need to make restrictions that forbid | ||||||
|  | distributors to deny you these rights or to ask you to surrender these | ||||||
|  | rights.  These restrictions translate to certain responsibilities for | ||||||
|  | you if you distribute copies of the library or if you modify it. | ||||||
|  | 
 | ||||||
|  |   For example, if you distribute copies of the library, whether gratis | ||||||
|  | or for a fee, you must give the recipients all the rights that we gave | ||||||
|  | you.  You must make sure that they, too, receive or can get the source | ||||||
|  | code.  If you link other code with the library, you must provide | ||||||
|  | complete object files to the recipients, so that they can relink them | ||||||
|  | with the library after making changes to the library and recompiling | ||||||
|  | it.  And you must show them these terms so they know their rights. | ||||||
|  | 
 | ||||||
|  |   We protect your rights with a two-step method: (1) we copyright the | ||||||
|  | library, and (2) we offer you this license, which gives you legal | ||||||
|  | permission to copy, distribute and/or modify the library. | ||||||
|  | 
 | ||||||
|  |   To protect each distributor, we want to make it very clear that | ||||||
|  | there is no warranty for the free library.  Also, if the library is | ||||||
|  | modified by someone else and passed on, the recipients should know | ||||||
|  | that what they have is not the original version, so that the original | ||||||
|  | author's reputation will not be affected by problems that might be | ||||||
|  | introduced by others. | ||||||
|  | 
 | ||||||
|  |   Finally, software patents pose a constant threat to the existence of | ||||||
|  | any free program.  We wish to make sure that a company cannot | ||||||
|  | effectively restrict the users of a free program by obtaining a | ||||||
|  | restrictive license from a patent holder.  Therefore, we insist that | ||||||
|  | any patent license obtained for a version of the library must be | ||||||
|  | consistent with the full freedom of use specified in this license. | ||||||
|  | 
 | ||||||
|  |   Most GNU software, including some libraries, is covered by the | ||||||
|  | ordinary GNU General Public License.  This license, the GNU Lesser | ||||||
|  | General Public License, applies to certain designated libraries, and | ||||||
|  | is quite different from the ordinary General Public License.  We use | ||||||
|  | this license for certain libraries in order to permit linking those | ||||||
|  | libraries into non-free programs. | ||||||
|  | 
 | ||||||
|  |   When a program is linked with a library, whether statically or using | ||||||
|  | a shared library, the combination of the two is legally speaking a | ||||||
|  | combined work, a derivative of the original library.  The ordinary | ||||||
|  | General Public License therefore permits such linking only if the | ||||||
|  | entire combination fits its criteria of freedom.  The Lesser General | ||||||
|  | Public License permits more lax criteria for linking other code with | ||||||
|  | the library. | ||||||
|  | 
 | ||||||
|  |   We call this license the "Lesser" General Public License because it | ||||||
|  | does Less to protect the user's freedom than the ordinary General | ||||||
|  | Public License.  It also provides other free software developers Less | ||||||
|  | of an advantage over competing non-free programs.  These disadvantages | ||||||
|  | are the reason we use the ordinary General Public License for many | ||||||
|  | libraries.  However, the Lesser license provides advantages in certain | ||||||
|  | special circumstances. | ||||||
|  | 
 | ||||||
|  |   For example, on rare occasions, there may be a special need to | ||||||
|  | encourage the widest possible use of a certain library, so that it becomes | ||||||
|  | a de-facto standard.  To achieve this, non-free programs must be | ||||||
|  | allowed to use the library.  A more frequent case is that a free | ||||||
|  | library does the same job as widely used non-free libraries.  In this | ||||||
|  | case, there is little to gain by limiting the free library to free | ||||||
|  | software only, so we use the Lesser General Public License. | ||||||
|  | 
 | ||||||
|  |   In other cases, permission to use a particular library in non-free | ||||||
|  | programs enables a greater number of people to use a large body of | ||||||
|  | free software.  For example, permission to use the GNU C Library in | ||||||
|  | non-free programs enables many more people to use the whole GNU | ||||||
|  | operating system, as well as its variant, the GNU/Linux operating | ||||||
|  | system. | ||||||
|  | 
 | ||||||
|  |   Although the Lesser General Public License is Less protective of the | ||||||
|  | users' freedom, it does ensure that the user of a program that is | ||||||
|  | linked with the Library has the freedom and the wherewithal to run | ||||||
|  | that program using a modified version of the Library. | ||||||
|  | 
 | ||||||
|  |   The precise terms and conditions for copying, distribution and | ||||||
|  | modification follow.  Pay close attention to the difference between a | ||||||
|  | "work based on the library" and a "work that uses the library".  The | ||||||
|  | former contains code derived from the library, whereas the latter must | ||||||
|  | be combined with the library in order to run. | ||||||
|  | 
 | ||||||
|  |                   GNU LESSER GENERAL PUBLIC LICENSE | ||||||
|  |    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | ||||||
|  | 
 | ||||||
|  |   0. This License Agreement applies to any software library or other | ||||||
|  | program which contains a notice placed by the copyright holder or | ||||||
|  | other authorized party saying it may be distributed under the terms of | ||||||
|  | this Lesser General Public License (also called "this License"). | ||||||
|  | Each licensee is addressed as "you". | ||||||
|  | 
 | ||||||
|  |   A "library" means a collection of software functions and/or data | ||||||
|  | prepared so as to be conveniently linked with application programs | ||||||
|  | (which use some of those functions and data) to form executables. | ||||||
|  | 
 | ||||||
|  |   The "Library", below, refers to any such software library or work | ||||||
|  | which has been distributed under these terms.  A "work based on the | ||||||
|  | Library" means either the Library or any derivative work under | ||||||
|  | copyright law: that is to say, a work containing the Library or a | ||||||
|  | portion of it, either verbatim or with modifications and/or translated | ||||||
|  | straightforwardly into another language.  (Hereinafter, translation is | ||||||
|  | included without limitation in the term "modification".) | ||||||
|  | 
 | ||||||
|  |   "Source code" for a work means the preferred form of the work for | ||||||
|  | making modifications to it.  For a library, complete source code means | ||||||
|  | all the source code for all modules it contains, plus any associated | ||||||
|  | interface definition files, plus the scripts used to control compilation | ||||||
|  | and installation of the library. | ||||||
|  | 
 | ||||||
|  |   Activities other than copying, distribution and modification are not | ||||||
|  | covered by this License; they are outside its scope.  The act of | ||||||
|  | running a program using the Library is not restricted, and output from | ||||||
|  | such a program is covered only if its contents constitute a work based | ||||||
|  | on the Library (independent of the use of the Library in a tool for | ||||||
|  | writing it).  Whether that is true depends on what the Library does | ||||||
|  | and what the program that uses the Library does. | ||||||
|  | 
 | ||||||
|  |   1. You may copy and distribute verbatim copies of the Library's | ||||||
|  | complete source code as you receive it, in any medium, provided that | ||||||
|  | you conspicuously and appropriately publish on each copy an | ||||||
|  | appropriate copyright notice and disclaimer of warranty; keep intact | ||||||
|  | all the notices that refer to this License and to the absence of any | ||||||
|  | warranty; and distribute a copy of this License along with the | ||||||
|  | Library. | ||||||
|  | 
 | ||||||
|  |   You may charge a fee for the physical act of transferring a copy, | ||||||
|  | and you may at your option offer warranty protection in exchange for a | ||||||
|  | fee. | ||||||
|  | 
 | ||||||
|  |   2. You may modify your copy or copies of the Library or any portion | ||||||
|  | of it, thus forming a work based on the Library, and copy and | ||||||
|  | distribute such modifications or work under the terms of Section 1 | ||||||
|  | above, provided that you also meet all of these conditions: | ||||||
|  | 
 | ||||||
|  |     a) The modified work must itself be a software library. | ||||||
|  | 
 | ||||||
|  |     b) You must cause the files modified to carry prominent notices | ||||||
|  |     stating that you changed the files and the date of any change. | ||||||
|  | 
 | ||||||
|  |     c) You must cause the whole of the work to be licensed at no | ||||||
|  |     charge to all third parties under the terms of this License. | ||||||
|  | 
 | ||||||
|  |     d) If a facility in the modified Library refers to a function or a | ||||||
|  |     table of data to be supplied by an application program that uses | ||||||
|  |     the facility, other than as an argument passed when the facility | ||||||
|  |     is invoked, then you must make a good faith effort to ensure that, | ||||||
|  |     in the event an application does not supply such function or | ||||||
|  |     table, the facility still operates, and performs whatever part of | ||||||
|  |     its purpose remains meaningful. | ||||||
|  | 
 | ||||||
|  |     (For example, a function in a library to compute square roots has | ||||||
|  |     a purpose that is entirely well-defined independent of the | ||||||
|  |     application.  Therefore, Subsection 2d requires that any | ||||||
|  |     application-supplied function or table used by this function must | ||||||
|  |     be optional: if the application does not supply it, the square | ||||||
|  |     root function must still compute square roots.) | ||||||
|  | 
 | ||||||
|  | These requirements apply to the modified work as a whole.  If | ||||||
|  | identifiable sections of that work are not derived from the Library, | ||||||
|  | and can be reasonably considered independent and separate works in | ||||||
|  | themselves, then this License, and its terms, do not apply to those | ||||||
|  | sections when you distribute them as separate works.  But when you | ||||||
|  | distribute the same sections as part of a whole which is a work based | ||||||
|  | on the Library, the distribution of the whole must be on the terms of | ||||||
|  | this License, whose permissions for other licensees extend to the | ||||||
|  | entire whole, and thus to each and every part regardless of who wrote | ||||||
|  | it. | ||||||
|  | 
 | ||||||
|  | Thus, it is not the intent of this section to claim rights or contest | ||||||
|  | your rights to work written entirely by you; rather, the intent is to | ||||||
|  | exercise the right to control the distribution of derivative or | ||||||
|  | collective works based on the Library. | ||||||
|  | 
 | ||||||
|  | In addition, mere aggregation of another work not based on the Library | ||||||
|  | with the Library (or with a work based on the Library) on a volume of | ||||||
|  | a storage or distribution medium does not bring the other work under | ||||||
|  | the scope of this License. | ||||||
|  | 
 | ||||||
|  |   3. You may opt to apply the terms of the ordinary GNU General Public | ||||||
|  | License instead of this License to a given copy of the Library.  To do | ||||||
|  | this, you must alter all the notices that refer to this License, so | ||||||
|  | that they refer to the ordinary GNU General Public License, version 2, | ||||||
|  | instead of to this License.  (If a newer version than version 2 of the | ||||||
|  | ordinary GNU General Public License has appeared, then you can specify | ||||||
|  | that version instead if you wish.)  Do not make any other change in | ||||||
|  | these notices. | ||||||
|  | 
 | ||||||
|  |   Once this change is made in a given copy, it is irreversible for | ||||||
|  | that copy, so the ordinary GNU General Public License applies to all | ||||||
|  | subsequent copies and derivative works made from that copy. | ||||||
|  | 
 | ||||||
|  |   This option is useful when you wish to copy part of the code of | ||||||
|  | the Library into a program that is not a library. | ||||||
|  | 
 | ||||||
|  |   4. You may copy and distribute the Library (or a portion or | ||||||
|  | derivative of it, under Section 2) in object code or executable form | ||||||
|  | under the terms of Sections 1 and 2 above provided that you accompany | ||||||
|  | it with the complete corresponding machine-readable source code, which | ||||||
|  | must be distributed under the terms of Sections 1 and 2 above on a | ||||||
|  | medium customarily used for software interchange. | ||||||
|  | 
 | ||||||
|  |   If distribution of object code is made by offering access to copy | ||||||
|  | from a designated place, then offering equivalent access to copy the | ||||||
|  | source code from the same place satisfies the requirement to | ||||||
|  | distribute the source code, even though third parties are not | ||||||
|  | compelled to copy the source along with the object code. | ||||||
|  | 
 | ||||||
|  |   5. A program that contains no derivative of any portion of the | ||||||
|  | Library, but is designed to work with the Library by being compiled or | ||||||
|  | linked with it, is called a "work that uses the Library".  Such a | ||||||
|  | work, in isolation, is not a derivative work of the Library, and | ||||||
|  | therefore falls outside the scope of this License. | ||||||
|  | 
 | ||||||
|  |   However, linking a "work that uses the Library" with the Library | ||||||
|  | creates an executable that is a derivative of the Library (because it | ||||||
|  | contains portions of the Library), rather than a "work that uses the | ||||||
|  | library".  The executable is therefore covered by this License. | ||||||
|  | Section 6 states terms for distribution of such executables. | ||||||
|  | 
 | ||||||
|  |   When a "work that uses the Library" uses material from a header file | ||||||
|  | that is part of the Library, the object code for the work may be a | ||||||
|  | derivative work of the Library even though the source code is not. | ||||||
|  | Whether this is true is especially significant if the work can be | ||||||
|  | linked without the Library, or if the work is itself a library.  The | ||||||
|  | threshold for this to be true is not precisely defined by law. | ||||||
|  | 
 | ||||||
|  |   If such an object file uses only numerical parameters, data | ||||||
|  | structure layouts and accessors, and small macros and small inline | ||||||
|  | functions (ten lines or less in length), then the use of the object | ||||||
|  | file is unrestricted, regardless of whether it is legally a derivative | ||||||
|  | work.  (Executables containing this object code plus portions of the | ||||||
|  | Library will still fall under Section 6.) | ||||||
|  | 
 | ||||||
|  |   Otherwise, if the work is a derivative of the Library, you may | ||||||
|  | distribute the object code for the work under the terms of Section 6. | ||||||
|  | Any executables containing that work also fall under Section 6, | ||||||
|  | whether or not they are linked directly with the Library itself. | ||||||
|  | 
 | ||||||
|  |   6. As an exception to the Sections above, you may also combine or | ||||||
|  | link a "work that uses the Library" with the Library to produce a | ||||||
|  | work containing portions of the Library, and distribute that work | ||||||
|  | under terms of your choice, provided that the terms permit | ||||||
|  | modification of the work for the customer's own use and reverse | ||||||
|  | engineering for debugging such modifications. | ||||||
|  | 
 | ||||||
|  |   You must give prominent notice with each copy of the work that the | ||||||
|  | Library is used in it and that the Library and its use are covered by | ||||||
|  | this License.  You must supply a copy of this License.  If the work | ||||||
|  | during execution displays copyright notices, you must include the | ||||||
|  | copyright notice for the Library among them, as well as a reference | ||||||
|  | directing the user to the copy of this License.  Also, you must do one | ||||||
|  | of these things: | ||||||
|  | 
 | ||||||
|  |     a) Accompany the work with the complete corresponding | ||||||
|  |     machine-readable source code for the Library including whatever | ||||||
|  |     changes were used in the work (which must be distributed under | ||||||
|  |     Sections 1 and 2 above); and, if the work is an executable linked | ||||||
|  |     with the Library, with the complete machine-readable "work that | ||||||
|  |     uses the Library", as object code and/or source code, so that the | ||||||
|  |     user can modify the Library and then relink to produce a modified | ||||||
|  |     executable containing the modified Library.  (It is understood | ||||||
|  |     that the user who changes the contents of definitions files in the | ||||||
|  |     Library will not necessarily be able to recompile the application | ||||||
|  |     to use the modified definitions.) | ||||||
|  | 
 | ||||||
|  |     b) Use a suitable shared library mechanism for linking with the | ||||||
|  |     Library.  A suitable mechanism is one that (1) uses at run time a | ||||||
|  |     copy of the library already present on the user's computer system, | ||||||
|  |     rather than copying library functions into the executable, and (2) | ||||||
|  |     will operate properly with a modified version of the library, if | ||||||
|  |     the user installs one, as long as the modified version is | ||||||
|  |     interface-compatible with the version that the work was made with. | ||||||
|  | 
 | ||||||
|  |     c) Accompany the work with a written offer, valid for at | ||||||
|  |     least three years, to give the same user the materials | ||||||
|  |     specified in Subsection 6a, above, for a charge no more | ||||||
|  |     than the cost of performing this distribution. | ||||||
|  | 
 | ||||||
|  |     d) If distribution of the work is made by offering access to copy | ||||||
|  |     from a designated place, offer equivalent access to copy the above | ||||||
|  |     specified materials from the same place. | ||||||
|  | 
 | ||||||
|  |     e) Verify that the user has already received a copy of these | ||||||
|  |     materials or that you have already sent this user a copy. | ||||||
|  | 
 | ||||||
|  |   For an executable, the required form of the "work that uses the | ||||||
|  | Library" must include any data and utility programs needed for | ||||||
|  | reproducing the executable from it.  However, as a special exception, | ||||||
|  | the materials to be distributed need not include anything that is | ||||||
|  | normally distributed (in either source or binary form) with the major | ||||||
|  | components (compiler, kernel, and so on) of the operating system on | ||||||
|  | which the executable runs, unless that component itself accompanies | ||||||
|  | the executable. | ||||||
|  | 
 | ||||||
|  |   It may happen that this requirement contradicts the license | ||||||
|  | restrictions of other proprietary libraries that do not normally | ||||||
|  | accompany the operating system.  Such a contradiction means you cannot | ||||||
|  | use both them and the Library together in an executable that you | ||||||
|  | distribute. | ||||||
|  | 
 | ||||||
|  |   7. You may place library facilities that are a work based on the | ||||||
|  | Library side-by-side in a single library together with other library | ||||||
|  | facilities not covered by this License, and distribute such a combined | ||||||
|  | library, provided that the separate distribution of the work based on | ||||||
|  | the Library and of the other library facilities is otherwise | ||||||
|  | permitted, and provided that you do these two things: | ||||||
|  | 
 | ||||||
|  |     a) Accompany the combined library with a copy of the same work | ||||||
|  |     based on the Library, uncombined with any other library | ||||||
|  |     facilities.  This must be distributed under the terms of the | ||||||
|  |     Sections above. | ||||||
|  | 
 | ||||||
|  |     b) Give prominent notice with the combined library of the fact | ||||||
|  |     that part of it is a work based on the Library, and explaining | ||||||
|  |     where to find the accompanying uncombined form of the same work. | ||||||
|  | 
 | ||||||
|  |   8. You may not copy, modify, sublicense, link with, or distribute | ||||||
|  | the Library except as expressly provided under this License.  Any | ||||||
|  | attempt otherwise to copy, modify, sublicense, link with, or | ||||||
|  | distribute the Library is void, and will automatically terminate your | ||||||
|  | rights under this License.  However, parties who have received copies, | ||||||
|  | or rights, from you under this License will not have their licenses | ||||||
|  | terminated so long as such parties remain in full compliance. | ||||||
|  | 
 | ||||||
|  |   9. You are not required to accept this License, since you have not | ||||||
|  | signed it.  However, nothing else grants you permission to modify or | ||||||
|  | distribute the Library or its derivative works.  These actions are | ||||||
|  | prohibited by law if you do not accept this License.  Therefore, by | ||||||
|  | modifying or distributing the Library (or any work based on the | ||||||
|  | Library), you indicate your acceptance of this License to do so, and | ||||||
|  | all its terms and conditions for copying, distributing or modifying | ||||||
|  | the Library or works based on it. | ||||||
|  | 
 | ||||||
|  |   10. Each time you redistribute the Library (or any work based on the | ||||||
|  | Library), the recipient automatically receives a license from the | ||||||
|  | original licensor to copy, distribute, link with or modify the Library | ||||||
|  | subject to these terms and conditions.  You may not impose any further | ||||||
|  | restrictions on the recipients' exercise of the rights granted herein. | ||||||
|  | You are not responsible for enforcing compliance by third parties with | ||||||
|  | this License. | ||||||
|  | 
 | ||||||
|  |   11. If, as a consequence of a court judgment or allegation of patent | ||||||
|  | infringement or for any other reason (not limited to patent issues), | ||||||
|  | conditions are imposed on you (whether by court order, agreement or | ||||||
|  | otherwise) that contradict the conditions of this License, they do not | ||||||
|  | excuse you from the conditions of this License.  If you cannot | ||||||
|  | distribute so as to satisfy simultaneously your obligations under this | ||||||
|  | License and any other pertinent obligations, then as a consequence you | ||||||
|  | may not distribute the Library at all.  For example, if a patent | ||||||
|  | license would not permit royalty-free redistribution of the Library by | ||||||
|  | all those who receive copies directly or indirectly through you, then | ||||||
|  | the only way you could satisfy both it and this License would be to | ||||||
|  | refrain entirely from distribution of the Library. | ||||||
|  | 
 | ||||||
|  | If any portion of this section is held invalid or unenforceable under any | ||||||
|  | particular circumstance, the balance of the section is intended to apply, | ||||||
|  | and the section as a whole is intended to apply in other circumstances. | ||||||
|  | 
 | ||||||
|  | It is not the purpose of this section to induce you to infringe any | ||||||
|  | patents or other property right claims or to contest validity of any | ||||||
|  | such claims; this section has the sole purpose of protecting the | ||||||
|  | integrity of the free software distribution system which is | ||||||
|  | implemented by public license practices.  Many people have made | ||||||
|  | generous contributions to the wide range of software distributed | ||||||
|  | through that system in reliance on consistent application of that | ||||||
|  | system; it is up to the author/donor to decide if he or she is willing | ||||||
|  | to distribute software through any other system and a licensee cannot | ||||||
|  | impose that choice. | ||||||
|  | 
 | ||||||
|  | This section is intended to make thoroughly clear what is believed to | ||||||
|  | be a consequence of the rest of this License. | ||||||
|  | 
 | ||||||
|  |   12. If the distribution and/or use of the Library is restricted in | ||||||
|  | certain countries either by patents or by copyrighted interfaces, the | ||||||
|  | original copyright holder who places the Library under this License may add | ||||||
|  | an explicit geographical distribution limitation excluding those countries, | ||||||
|  | so that distribution is permitted only in or among countries not thus | ||||||
|  | excluded.  In such case, this License incorporates the limitation as if | ||||||
|  | written in the body of this License. | ||||||
|  | 
 | ||||||
|  |   13. The Free Software Foundation may publish revised and/or new | ||||||
|  | versions of the Lesser General Public License from time to time. | ||||||
|  | Such new versions will be similar in spirit to the present version, | ||||||
|  | but may differ in detail to address new problems or concerns. | ||||||
|  | 
 | ||||||
|  | Each version is given a distinguishing version number.  If the Library | ||||||
|  | specifies a version number of this License which applies to it and | ||||||
|  | "any later version", you have the option of following the terms and | ||||||
|  | conditions either of that version or of any later version published by | ||||||
|  | the Free Software Foundation.  If the Library does not specify a | ||||||
|  | license version number, you may choose any version ever published by | ||||||
|  | the Free Software Foundation. | ||||||
|  | 
 | ||||||
|  |   14. If you wish to incorporate parts of the Library into other free | ||||||
|  | programs whose distribution conditions are incompatible with these, | ||||||
|  | write to the author to ask for permission.  For software which is | ||||||
|  | copyrighted by the Free Software Foundation, write to the Free | ||||||
|  | Software Foundation; we sometimes make exceptions for this.  Our | ||||||
|  | decision will be guided by the two goals of preserving the free status | ||||||
|  | of all derivatives of our free software and of promoting the sharing | ||||||
|  | and reuse of software generally. | ||||||
|  | 
 | ||||||
|  |                             NO WARRANTY | ||||||
|  | 
 | ||||||
|  |   15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO | ||||||
|  | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. | ||||||
|  | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR | ||||||
|  | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY | ||||||
|  | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||||||
|  | PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE | ||||||
|  | LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME | ||||||
|  | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. | ||||||
|  | 
 | ||||||
|  |   16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN | ||||||
|  | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY | ||||||
|  | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU | ||||||
|  | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR | ||||||
|  | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE | ||||||
|  | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING | ||||||
|  | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A | ||||||
|  | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF | ||||||
|  | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH | ||||||
|  | DAMAGES. | ||||||
|  | 
 | ||||||
|  |                      END OF TERMS AND CONDITIONS | ||||||
|  | 
 | ||||||
|  |            How to Apply These Terms to Your New Libraries | ||||||
|  | 
 | ||||||
|  |   If you develop a new library, and you want it to be of the greatest | ||||||
|  | possible use to the public, we recommend making it free software that | ||||||
|  | everyone can redistribute and change.  You can do so by permitting | ||||||
|  | redistribution under these terms (or, alternatively, under the terms of the | ||||||
|  | ordinary General Public License). | ||||||
|  | 
 | ||||||
|  |   To apply these terms, attach the following notices to the library.  It is | ||||||
|  | safest to attach them to the start of each source file to most effectively | ||||||
|  | convey the exclusion of warranty; and each file should have at least the | ||||||
|  | "copyright" line and a pointer to where the full notice is found. | ||||||
|  | 
 | ||||||
|  |     {description} | ||||||
|  |     Copyright (C) {year} {fullname} | ||||||
|  | 
 | ||||||
|  |     This library is free software; you can redistribute it and/or | ||||||
|  |     modify it under the terms of the GNU Lesser General Public | ||||||
|  |     License as published by the Free Software Foundation; either | ||||||
|  |     version 2.1 of the License, or (at your option) any later version. | ||||||
|  | 
 | ||||||
|  |     This library is distributed in the hope that it will be useful, | ||||||
|  |     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||||
|  |     Lesser General Public License for more details. | ||||||
|  | 
 | ||||||
|  |     You should have received a copy of the GNU Lesser General Public | ||||||
|  |     License along with this library; if not, write to the Free Software | ||||||
|  |     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 | ||||||
|  |     USA | ||||||
|  | 
 | ||||||
|  | Also add information on how to contact you by electronic and paper mail. | ||||||
|  | 
 | ||||||
|  | You should also get your employer (if you work as a programmer) or your | ||||||
|  | school, if any, to sign a "copyright disclaimer" for the library, if | ||||||
|  | necessary.  Here is a sample; alter the names: | ||||||
|  | 
 | ||||||
|  |   Yoyodyne, Inc., hereby disclaims all copyright interest in the | ||||||
|  |   library `Frob' (a library for tweaking knobs) written by James Random | ||||||
|  |   Hacker. | ||||||
|  | 
 | ||||||
|  |   {signature of Ty Coon}, 1 April 1990 | ||||||
|  |   Ty Coon, President of Vice | ||||||
|  | 
 | ||||||
|  | That's all there is to it! | ||||||
							
								
								
									
										165
									
								
								conf/license/LGPL v3
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,165 @@ | ||||||
|  | GNU LESSER GENERAL PUBLIC LICENSE | ||||||
|  |                        Version 3, 29 June 2007 | ||||||
|  | 
 | ||||||
|  |  Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> | ||||||
|  |  Everyone is permitted to copy and distribute verbatim copies | ||||||
|  |  of this license document, but changing it is not allowed. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |   This version of the GNU Lesser General Public License incorporates | ||||||
|  | the terms and conditions of version 3 of the GNU General Public | ||||||
|  | License, supplemented by the additional permissions listed below. | ||||||
|  | 
 | ||||||
|  |   0. Additional Definitions. | ||||||
|  | 
 | ||||||
|  |   As used herein, "this License" refers to version 3 of the GNU Lesser | ||||||
|  | General Public License, and the "GNU GPL" refers to version 3 of the GNU | ||||||
|  | General Public License. | ||||||
|  | 
 | ||||||
|  |   "The Library" refers to a covered work governed by this License, | ||||||
|  | other than an Application or a Combined Work as defined below. | ||||||
|  | 
 | ||||||
|  |   An "Application" is any work that makes use of an interface provided | ||||||
|  | by the Library, but which is not otherwise based on the Library. | ||||||
|  | Defining a subclass of a class defined by the Library is deemed a mode | ||||||
|  | of using an interface provided by the Library. | ||||||
|  | 
 | ||||||
|  |   A "Combined Work" is a work produced by combining or linking an | ||||||
|  | Application with the Library.  The particular version of the Library | ||||||
|  | with which the Combined Work was made is also called the "Linked | ||||||
|  | Version". | ||||||
|  | 
 | ||||||
|  |   The "Minimal Corresponding Source" for a Combined Work means the | ||||||
|  | Corresponding Source for the Combined Work, excluding any source code | ||||||
|  | for portions of the Combined Work that, considered in isolation, are | ||||||
|  | based on the Application, and not on the Linked Version. | ||||||
|  | 
 | ||||||
|  |   The "Corresponding Application Code" for a Combined Work means the | ||||||
|  | object code and/or source code for the Application, including any data | ||||||
|  | and utility programs needed for reproducing the Combined Work from the | ||||||
|  | Application, but excluding the System Libraries of the Combined Work. | ||||||
|  | 
 | ||||||
|  |   1. Exception to Section 3 of the GNU GPL. | ||||||
|  | 
 | ||||||
|  |   You may convey a covered work under sections 3 and 4 of this License | ||||||
|  | without being bound by section 3 of the GNU GPL. | ||||||
|  | 
 | ||||||
|  |   2. Conveying Modified Versions. | ||||||
|  | 
 | ||||||
|  |   If you modify a copy of the Library, and, in your modifications, a | ||||||
|  | facility refers to a function or data to be supplied by an Application | ||||||
|  | that uses the facility (other than as an argument passed when the | ||||||
|  | facility is invoked), then you may convey a copy of the modified | ||||||
|  | version: | ||||||
|  | 
 | ||||||
|  |    a) under this License, provided that you make a good faith effort to | ||||||
|  |    ensure that, in the event an Application does not supply the | ||||||
|  |    function or data, the facility still operates, and performs | ||||||
|  |    whatever part of its purpose remains meaningful, or | ||||||
|  | 
 | ||||||
|  |    b) under the GNU GPL, with none of the additional permissions of | ||||||
|  |    this License applicable to that copy. | ||||||
|  | 
 | ||||||
|  |   3. Object Code Incorporating Material from Library Header Files. | ||||||
|  | 
 | ||||||
|  |   The object code form of an Application may incorporate material from | ||||||
|  | a header file that is part of the Library.  You may convey such object | ||||||
|  | code under terms of your choice, provided that, if the incorporated | ||||||
|  | material is not limited to numerical parameters, data structure | ||||||
|  | layouts and accessors, or small macros, inline functions and templates | ||||||
|  | (ten or fewer lines in length), you do both of the following: | ||||||
|  | 
 | ||||||
|  |    a) Give prominent notice with each copy of the object code that the | ||||||
|  |    Library is used in it and that the Library and its use are | ||||||
|  |    covered by this License. | ||||||
|  | 
 | ||||||
|  |    b) Accompany the object code with a copy of the GNU GPL and this license | ||||||
|  |    document. | ||||||
|  | 
 | ||||||
|  |   4. Combined Works. | ||||||
|  | 
 | ||||||
|  |   You may convey a Combined Work under terms of your choice that, | ||||||
|  | taken together, effectively do not restrict modification of the | ||||||
|  | portions of the Library contained in the Combined Work and reverse | ||||||
|  | engineering for debugging such modifications, if you also do each of | ||||||
|  | the following: | ||||||
|  | 
 | ||||||
|  |    a) Give prominent notice with each copy of the Combined Work that | ||||||
|  |    the Library is used in it and that the Library and its use are | ||||||
|  |    covered by this License. | ||||||
|  | 
 | ||||||
|  |    b) Accompany the Combined Work with a copy of the GNU GPL and this license | ||||||
|  |    document. | ||||||
|  | 
 | ||||||
|  |    c) For a Combined Work that displays copyright notices during | ||||||
|  |    execution, include the copyright notice for the Library among | ||||||
|  |    these notices, as well as a reference directing the user to the | ||||||
|  |    copies of the GNU GPL and this license document. | ||||||
|  | 
 | ||||||
|  |    d) Do one of the following: | ||||||
|  | 
 | ||||||
|  |        0) Convey the Minimal Corresponding Source under the terms of this | ||||||
|  |        License, and the Corresponding Application Code in a form | ||||||
|  |        suitable for, and under terms that permit, the user to | ||||||
|  |        recombine or relink the Application with a modified version of | ||||||
|  |        the Linked Version to produce a modified Combined Work, in the | ||||||
|  |        manner specified by section 6 of the GNU GPL for conveying | ||||||
|  |        Corresponding Source. | ||||||
|  | 
 | ||||||
|  |        1) Use a suitable shared library mechanism for linking with the | ||||||
|  |        Library.  A suitable mechanism is one that (a) uses at run time | ||||||
|  |        a copy of the Library already present on the user's computer | ||||||
|  |        system, and (b) will operate properly with a modified version | ||||||
|  |        of the Library that is interface-compatible with the Linked | ||||||
|  |        Version. | ||||||
|  | 
 | ||||||
|  |    e) Provide Installation Information, but only if you would otherwise | ||||||
|  |    be required to provide such information under section 6 of the | ||||||
|  |    GNU GPL, and only to the extent that such information is | ||||||
|  |    necessary to install and execute a modified version of the | ||||||
|  |    Combined Work produced by recombining or relinking the | ||||||
|  |    Application with a modified version of the Linked Version. (If | ||||||
|  |    you use option 4d0, the Installation Information must accompany | ||||||
|  |    the Minimal Corresponding Source and Corresponding Application | ||||||
|  |    Code. If you use option 4d1, you must provide the Installation | ||||||
|  |    Information in the manner specified by section 6 of the GNU GPL | ||||||
|  |    for conveying Corresponding Source.) | ||||||
|  | 
 | ||||||
|  |   5. Combined Libraries. | ||||||
|  | 
 | ||||||
|  |   You may place library facilities that are a work based on the | ||||||
|  | Library side by side in a single library together with other library | ||||||
|  | facilities that are not Applications and are not covered by this | ||||||
|  | License, and convey such a combined library under terms of your | ||||||
|  | choice, if you do both of the following: | ||||||
|  | 
 | ||||||
|  |    a) Accompany the combined library with a copy of the same work based | ||||||
|  |    on the Library, uncombined with any other library facilities, | ||||||
|  |    conveyed under the terms of this License. | ||||||
|  | 
 | ||||||
|  |    b) Give prominent notice with the combined library that part of it | ||||||
|  |    is a work based on the Library, and explaining where to find the | ||||||
|  |    accompanying uncombined form of the same work. | ||||||
|  | 
 | ||||||
|  |   6. Revised Versions of the GNU Lesser General Public License. | ||||||
|  | 
 | ||||||
|  |   The Free Software Foundation may publish revised and/or new versions | ||||||
|  | of the GNU Lesser General Public License from time to time. Such new | ||||||
|  | versions will be similar in spirit to the present version, but may | ||||||
|  | differ in detail to address new problems or concerns. | ||||||
|  | 
 | ||||||
|  |   Each version is given a distinguishing version number. If the | ||||||
|  | Library as you received it specifies that a certain numbered version | ||||||
|  | of the GNU Lesser General Public License "or any later version" | ||||||
|  | applies to it, you have the option of following the terms and | ||||||
|  | conditions either of that published version or of any later version | ||||||
|  | published by the Free Software Foundation. If the Library as you | ||||||
|  | received it does not specify a version number of the GNU Lesser | ||||||
|  | General Public License, you may choose any version of the GNU Lesser | ||||||
|  | General Public License ever published by the Free Software Foundation. | ||||||
|  | 
 | ||||||
|  |   If the Library as you received it specifies that a proxy can decide | ||||||
|  | whether future versions of the GNU Lesser General Public License shall | ||||||
|  | apply, that proxy's public statement of acceptance of any version is | ||||||
|  | permanent authorization for you to choose that version for the | ||||||
|  | Library. | ||||||
							
								
								
									
										362
									
								
								conf/license/Mozilla Public License Version 2.0
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,362 @@ | ||||||
|  | Mozilla Public License, version 2.0 | ||||||
|  | 
 | ||||||
|  | 1. Definitions | ||||||
|  | 
 | ||||||
|  | 1.1. "Contributor" | ||||||
|  | 
 | ||||||
|  |      means each individual or legal entity that creates, contributes to the | ||||||
|  |      creation of, or owns Covered Software. | ||||||
|  | 
 | ||||||
|  | 1.2. "Contributor Version" | ||||||
|  | 
 | ||||||
|  |      means the combination of the Contributions of others (if any) used by a | ||||||
|  |      Contributor and that particular Contributor's Contribution. | ||||||
|  | 
 | ||||||
|  | 1.3. "Contribution" | ||||||
|  | 
 | ||||||
|  |      means Covered Software of a particular Contributor. | ||||||
|  | 
 | ||||||
|  | 1.4. "Covered Software" | ||||||
|  | 
 | ||||||
|  |      means Source Code Form to which the initial Contributor has attached the | ||||||
|  |      notice in Exhibit A, the Executable Form of such Source Code Form, and | ||||||
|  |      Modifications of such Source Code Form, in each case including portions | ||||||
|  |      thereof. | ||||||
|  | 
 | ||||||
|  | 1.5. "Incompatible With Secondary Licenses" | ||||||
|  |      means | ||||||
|  | 
 | ||||||
|  |      a. that the initial Contributor has attached the notice described in | ||||||
|  |         Exhibit B to the Covered Software; or | ||||||
|  | 
 | ||||||
|  |      b. that the Covered Software was made available under the terms of | ||||||
|  |         version 1.1 or earlier of the License, but not also under the terms of | ||||||
|  |         a Secondary License. | ||||||
|  | 
 | ||||||
|  | 1.6. "Executable Form" | ||||||
|  | 
 | ||||||
|  |      means any form of the work other than Source Code Form. | ||||||
|  | 
 | ||||||
|  | 1.7. "Larger Work" | ||||||
|  | 
 | ||||||
|  |      means a work that combines Covered Software with other material, in a | ||||||
|  |      separate file or files, that is not Covered Software. | ||||||
|  | 
 | ||||||
|  | 1.8. "License" | ||||||
|  | 
 | ||||||
|  |      means this document. | ||||||
|  | 
 | ||||||
|  | 1.9. "Licensable" | ||||||
|  | 
 | ||||||
|  |      means having the right to grant, to the maximum extent possible, whether | ||||||
|  |      at the time of the initial grant or subsequently, any and all of the | ||||||
|  |      rights conveyed by this License. | ||||||
|  | 
 | ||||||
|  | 1.10. "Modifications" | ||||||
|  | 
 | ||||||
|  |      means any of the following: | ||||||
|  | 
 | ||||||
|  |      a. any file in Source Code Form that results from an addition to, | ||||||
|  |         deletion from, or modification of the contents of Covered Software; or | ||||||
|  | 
 | ||||||
|  |      b. any new file in Source Code Form that contains any Covered Software. | ||||||
|  | 
 | ||||||
|  | 1.11. "Patent Claims" of a Contributor | ||||||
|  | 
 | ||||||
|  |       means any patent claim(s), including without limitation, method, | ||||||
|  |       process, and apparatus claims, in any patent Licensable by such | ||||||
|  |       Contributor that would be infringed, but for the grant of the License, | ||||||
|  |       by the making, using, selling, offering for sale, having made, import, | ||||||
|  |       or transfer of either its Contributions or its Contributor Version. | ||||||
|  | 
 | ||||||
|  | 1.12. "Secondary License" | ||||||
|  | 
 | ||||||
|  |       means either the GNU General Public License, Version 2.0, the GNU Lesser | ||||||
|  |       General Public License, Version 2.1, the GNU Affero General Public | ||||||
|  |       License, Version 3.0, or any later versions of those licenses. | ||||||
|  | 
 | ||||||
|  | 1.13. "Source Code Form" | ||||||
|  | 
 | ||||||
|  |       means the form of the work preferred for making modifications. | ||||||
|  | 
 | ||||||
|  | 1.14. "You" (or "Your") | ||||||
|  | 
 | ||||||
|  |       means an individual or a legal entity exercising rights under this | ||||||
|  |       License. For legal entities, "You" includes any entity that controls, is | ||||||
|  |       controlled by, or is under common control with You. For purposes of this | ||||||
|  |       definition, "control" means (a) the power, direct or indirect, to cause | ||||||
|  |       the direction or management of such entity, whether by contract or | ||||||
|  |       otherwise, or (b) ownership of more than fifty percent (50%) of the | ||||||
|  |       outstanding shares or beneficial ownership of such entity. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 2. License Grants and Conditions | ||||||
|  | 
 | ||||||
|  | 2.1. Grants | ||||||
|  | 
 | ||||||
|  |      Each Contributor hereby grants You a world-wide, royalty-free, | ||||||
|  |      non-exclusive license: | ||||||
|  | 
 | ||||||
|  |      a. under intellectual property rights (other than patent or trademark) | ||||||
|  |         Licensable by such Contributor to use, reproduce, make available, | ||||||
|  |         modify, display, perform, distribute, and otherwise exploit its | ||||||
|  |         Contributions, either on an unmodified basis, with Modifications, or | ||||||
|  |         as part of a Larger Work; and | ||||||
|  | 
 | ||||||
|  |      b. under Patent Claims of such Contributor to make, use, sell, offer for | ||||||
|  |         sale, have made, import, and otherwise transfer either its | ||||||
|  |         Contributions or its Contributor Version. | ||||||
|  | 
 | ||||||
|  | 2.2. Effective Date | ||||||
|  | 
 | ||||||
|  |      The licenses granted in Section 2.1 with respect to any Contribution | ||||||
|  |      become effective for each Contribution on the date the Contributor first | ||||||
|  |      distributes such Contribution. | ||||||
|  | 
 | ||||||
|  | 2.3. Limitations on Grant Scope | ||||||
|  | 
 | ||||||
|  |      The licenses granted in this Section 2 are the only rights granted under | ||||||
|  |      this License. No additional rights or licenses will be implied from the | ||||||
|  |      distribution or licensing of Covered Software under this License. | ||||||
|  |      Notwithstanding Section 2.1(b) above, no patent license is granted by a | ||||||
|  |      Contributor: | ||||||
|  | 
 | ||||||
|  |      a. for any code that a Contributor has removed from Covered Software; or | ||||||
|  | 
 | ||||||
|  |      b. for infringements caused by: (i) Your and any other third party's | ||||||
|  |         modifications of Covered Software, or (ii) the combination of its | ||||||
|  |         Contributions with other software (except as part of its Contributor | ||||||
|  |         Version); or | ||||||
|  | 
 | ||||||
|  |      c. under Patent Claims infringed by Covered Software in the absence of | ||||||
|  |         its Contributions. | ||||||
|  | 
 | ||||||
|  |      This License does not grant any rights in the trademarks, service marks, | ||||||
|  |      or logos of any Contributor (except as may be necessary to comply with | ||||||
|  |      the notice requirements in Section 3.4). | ||||||
|  | 
 | ||||||
|  | 2.4. Subsequent Licenses | ||||||
|  | 
 | ||||||
|  |      No Contributor makes additional grants as a result of Your choice to | ||||||
|  |      distribute the Covered Software under a subsequent version of this | ||||||
|  |      License (see Section 10.2) or under the terms of a Secondary License (if | ||||||
|  |      permitted under the terms of Section 3.3). | ||||||
|  | 
 | ||||||
|  | 2.5. Representation | ||||||
|  | 
 | ||||||
|  |      Each Contributor represents that the Contributor believes its | ||||||
|  |      Contributions are its original creation(s) or it has sufficient rights to | ||||||
|  |      grant the rights to its Contributions conveyed by this License. | ||||||
|  | 
 | ||||||
|  | 2.6. Fair Use | ||||||
|  | 
 | ||||||
|  |      This License is not intended to limit any rights You have under | ||||||
|  |      applicable copyright doctrines of fair use, fair dealing, or other | ||||||
|  |      equivalents. | ||||||
|  | 
 | ||||||
|  | 2.7. Conditions | ||||||
|  | 
 | ||||||
|  |      Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in | ||||||
|  |      Section 2.1. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 3. Responsibilities | ||||||
|  | 
 | ||||||
|  | 3.1. Distribution of Source Form | ||||||
|  | 
 | ||||||
|  |      All distribution of Covered Software in Source Code Form, including any | ||||||
|  |      Modifications that You create or to which You contribute, must be under | ||||||
|  |      the terms of this License. You must inform recipients that the Source | ||||||
|  |      Code Form of the Covered Software is governed by the terms of this | ||||||
|  |      License, and how they can obtain a copy of this License. You may not | ||||||
|  |      attempt to alter or restrict the recipients' rights in the Source Code | ||||||
|  |      Form. | ||||||
|  | 
 | ||||||
|  | 3.2. Distribution of Executable Form | ||||||
|  | 
 | ||||||
|  |      If You distribute Covered Software in Executable Form then: | ||||||
|  | 
 | ||||||
|  |      a. such Covered Software must also be made available in Source Code Form, | ||||||
|  |         as described in Section 3.1, and You must inform recipients of the | ||||||
|  |         Executable Form how they can obtain a copy of such Source Code Form by | ||||||
|  |         reasonable means in a timely manner, at a charge no more than the cost | ||||||
|  |         of distribution to the recipient; and | ||||||
|  | 
 | ||||||
|  |      b. You may distribute such Executable Form under the terms of this | ||||||
|  |         License, or sublicense it under different terms, provided that the | ||||||
|  |         license for the Executable Form does not attempt to limit or alter the | ||||||
|  |         recipients' rights in the Source Code Form under this License. | ||||||
|  | 
 | ||||||
|  | 3.3. Distribution of a Larger Work | ||||||
|  | 
 | ||||||
|  |      You may create and distribute a Larger Work under terms of Your choice, | ||||||
|  |      provided that You also comply with the requirements of this License for | ||||||
|  |      the Covered Software. If the Larger Work is a combination of Covered | ||||||
|  |      Software with a work governed by one or more Secondary Licenses, and the | ||||||
|  |      Covered Software is not Incompatible With Secondary Licenses, this | ||||||
|  |      License permits You to additionally distribute such Covered Software | ||||||
|  |      under the terms of such Secondary License(s), so that the recipient of | ||||||
|  |      the Larger Work may, at their option, further distribute the Covered | ||||||
|  |      Software under the terms of either this License or such Secondary | ||||||
|  |      License(s). | ||||||
|  | 
 | ||||||
|  | 3.4. Notices | ||||||
|  | 
 | ||||||
|  |      You may not remove or alter the substance of any license notices | ||||||
|  |      (including copyright notices, patent notices, disclaimers of warranty, or | ||||||
|  |      limitations of liability) contained within the Source Code Form of the | ||||||
|  |      Covered Software, except that You may alter any license notices to the | ||||||
|  |      extent required to remedy known factual inaccuracies. | ||||||
|  | 
 | ||||||
|  | 3.5. Application of Additional Terms | ||||||
|  | 
 | ||||||
|  |      You may choose to offer, and to charge a fee for, warranty, support, | ||||||
|  |      indemnity or liability obligations to one or more recipients of Covered | ||||||
|  |      Software. However, You may do so only on Your own behalf, and not on | ||||||
|  |      behalf of any Contributor. You must make it absolutely clear that any | ||||||
|  |      such warranty, support, indemnity, or liability obligation is offered by | ||||||
|  |      You alone, and You hereby agree to indemnify every Contributor for any | ||||||
|  |      liability incurred by such Contributor as a result of warranty, support, | ||||||
|  |      indemnity or liability terms You offer. You may include additional | ||||||
|  |      disclaimers of warranty and limitations of liability specific to any | ||||||
|  |      jurisdiction. | ||||||
|  | 
 | ||||||
|  | 4. Inability to Comply Due to Statute or Regulation | ||||||
|  | 
 | ||||||
|  |    If it is impossible for You to comply with any of the terms of this License | ||||||
|  |    with respect to some or all of the Covered Software due to statute, | ||||||
|  |    judicial order, or regulation then You must: (a) comply with the terms of | ||||||
|  |    this License to the maximum extent possible; and (b) describe the | ||||||
|  |    limitations and the code they affect. Such description must be placed in a | ||||||
|  |    text file included with all distributions of the Covered Software under | ||||||
|  |    this License. Except to the extent prohibited by statute or regulation, | ||||||
|  |    such description must be sufficiently detailed for a recipient of ordinary | ||||||
|  |    skill to be able to understand it. | ||||||
|  | 
 | ||||||
|  | 5. Termination | ||||||
|  | 
 | ||||||
|  | 5.1. The rights granted under this License will terminate automatically if You | ||||||
|  |      fail to comply with any of its terms. However, if You become compliant, | ||||||
|  |      then the rights granted under this License from a particular Contributor | ||||||
|  |      are reinstated (a) provisionally, unless and until such Contributor | ||||||
|  |      explicitly and finally terminates Your grants, and (b) on an ongoing | ||||||
|  |      basis, if such Contributor fails to notify You of the non-compliance by | ||||||
|  |      some reasonable means prior to 60 days after You have come back into | ||||||
|  |      compliance. Moreover, Your grants from a particular Contributor are | ||||||
|  |      reinstated on an ongoing basis if such Contributor notifies You of the | ||||||
|  |      non-compliance by some reasonable means, this is the first time You have | ||||||
|  |      received notice of non-compliance with this License from such | ||||||
|  |      Contributor, and You become compliant prior to 30 days after Your receipt | ||||||
|  |      of the notice. | ||||||
|  | 
 | ||||||
|  | 5.2. If You initiate litigation against any entity by asserting a patent | ||||||
|  |      infringement claim (excluding declaratory judgment actions, | ||||||
|  |      counter-claims, and cross-claims) alleging that a Contributor Version | ||||||
|  |      directly or indirectly infringes any patent, then the rights granted to | ||||||
|  |      You by any and all Contributors for the Covered Software under Section | ||||||
|  |      2.1 of this License shall terminate. | ||||||
|  | 
 | ||||||
|  | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user | ||||||
|  |      license agreements (excluding distributors and resellers) which have been | ||||||
|  |      validly granted by You or Your distributors under this License prior to | ||||||
|  |      termination shall survive termination. | ||||||
|  | 
 | ||||||
|  | 6. Disclaimer of Warranty | ||||||
|  | 
 | ||||||
|  |    Covered Software is provided under this License on an "as is" basis, | ||||||
|  |    without warranty of any kind, either expressed, implied, or statutory, | ||||||
|  |    including, without limitation, warranties that the Covered Software is free | ||||||
|  |    of defects, merchantable, fit for a particular purpose or non-infringing. | ||||||
|  |    The entire risk as to the quality and performance of the Covered Software | ||||||
|  |    is with You. Should any Covered Software prove defective in any respect, | ||||||
|  |    You (not any Contributor) assume the cost of any necessary servicing, | ||||||
|  |    repair, or correction. This disclaimer of warranty constitutes an essential | ||||||
|  |    part of this License. No use of  any Covered Software is authorized under | ||||||
|  |    this License except under this disclaimer. | ||||||
|  | 
 | ||||||
|  | 7. Limitation of Liability | ||||||
|  | 
 | ||||||
|  |    Under no circumstances and under no legal theory, whether tort (including | ||||||
|  |    negligence), contract, or otherwise, shall any Contributor, or anyone who | ||||||
|  |    distributes Covered Software as permitted above, be liable to You for any | ||||||
|  |    direct, indirect, special, incidental, or consequential damages of any | ||||||
|  |    character including, without limitation, damages for lost profits, loss of | ||||||
|  |    goodwill, work stoppage, computer failure or malfunction, or any and all | ||||||
|  |    other commercial damages or losses, even if such party shall have been | ||||||
|  |    informed of the possibility of such damages. This limitation of liability | ||||||
|  |    shall not apply to liability for death or personal injury resulting from | ||||||
|  |    such party's negligence to the extent applicable law prohibits such | ||||||
|  |    limitation. Some jurisdictions do not allow the exclusion or limitation of | ||||||
|  |    incidental or consequential damages, so this exclusion and limitation may | ||||||
|  |    not apply to You. | ||||||
|  | 
 | ||||||
|  | 8. Litigation | ||||||
|  | 
 | ||||||
|  |    Any litigation relating to this License may be brought only in the courts | ||||||
|  |    of a jurisdiction where the defendant maintains its principal place of | ||||||
|  |    business and such litigation shall be governed by laws of that | ||||||
|  |    jurisdiction, without reference to its conflict-of-law provisions. Nothing | ||||||
|  |    in this Section shall prevent a party's ability to bring cross-claims or | ||||||
|  |    counter-claims. | ||||||
|  | 
 | ||||||
|  | 9. Miscellaneous | ||||||
|  | 
 | ||||||
|  |    This License represents the complete agreement concerning the subject | ||||||
|  |    matter hereof. If any provision of this License is held to be | ||||||
|  |    unenforceable, such provision shall be reformed only to the extent | ||||||
|  |    necessary to make it enforceable. Any law or regulation which provides that | ||||||
|  |    the language of a contract shall be construed against the drafter shall not | ||||||
|  |    be used to construe this License against a Contributor. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 10. Versions of the License | ||||||
|  | 
 | ||||||
|  | 10.1. New Versions | ||||||
|  | 
 | ||||||
|  |       Mozilla Foundation is the license steward. Except as provided in Section | ||||||
|  |       10.3, no one other than the license steward has the right to modify or | ||||||
|  |       publish new versions of this License. Each version will be given a | ||||||
|  |       distinguishing version number. | ||||||
|  | 
 | ||||||
|  | 10.2. Effect of New Versions | ||||||
|  | 
 | ||||||
|  |       You may distribute the Covered Software under the terms of the version | ||||||
|  |       of the License under which You originally received the Covered Software, | ||||||
|  |       or under the terms of any subsequent version published by the license | ||||||
|  |       steward. | ||||||
|  | 
 | ||||||
|  | 10.3. Modified Versions | ||||||
|  | 
 | ||||||
|  |       If you create software not governed by this License, and you want to | ||||||
|  |       create a new license for such software, you may create and use a | ||||||
|  |       modified version of this License if you rename the license and remove | ||||||
|  |       any references to the name of the license steward (except to note that | ||||||
|  |       such modified license differs from this License). | ||||||
|  | 
 | ||||||
|  | 10.4. Distributing Source Code Form that is Incompatible With Secondary | ||||||
|  |       Licenses If You choose to distribute Source Code Form that is | ||||||
|  |       Incompatible With Secondary Licenses under the terms of this version of | ||||||
|  |       the License, the notice described in Exhibit B of this License must be | ||||||
|  |       attached. | ||||||
|  | 
 | ||||||
|  | Exhibit A - Source Code Form License Notice | ||||||
|  | 
 | ||||||
|  |       This Source Code Form is subject to the | ||||||
|  |       terms of the Mozilla Public License, v. | ||||||
|  |       2.0. If a copy of the MPL was not | ||||||
|  |       distributed with this file, You can | ||||||
|  |       obtain one at | ||||||
|  |       http://mozilla.org/MPL/2.0/. | ||||||
|  | 
 | ||||||
|  | If it is not possible or desirable to put the notice in a particular file, | ||||||
|  | then You may include the notice in a location (such as a LICENSE file in a | ||||||
|  | relevant directory) where a recipient would be likely to look for such a | ||||||
|  | notice. | ||||||
|  | 
 | ||||||
|  | You may add additional accurate notices of copyright ownership. | ||||||
|  | 
 | ||||||
|  | Exhibit B - "Incompatible With Secondary Licenses" Notice | ||||||
|  | 
 | ||||||
|  |       This Source Code Form is "Incompatible | ||||||
|  |       With Secondary Licenses", as defined by | ||||||
|  |       the Mozilla Public License, v. 2.0. | ||||||
							
								
								
									
										179
									
								
								conf/locale/locale_en-US.ini
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,179 @@ | ||||||
|  | app_desc = A painless self-hosted Git service written in Go | ||||||
|  | 
 | ||||||
|  | home = Home | ||||||
|  | dashboard = Dashboard | ||||||
|  | explore = Explore | ||||||
|  | help = Help | ||||||
|  | sign_in = Sign In | ||||||
|  | sign_out = Sign Out | ||||||
|  | sign_up = Sign Up | ||||||
|  | register = Register | ||||||
|  | website = Website | ||||||
|  | version = Version | ||||||
|  | page = Page | ||||||
|  | template = Template | ||||||
|  | language = Language | ||||||
|  | 
 | ||||||
|  | username = Username | ||||||
|  | email = E-mail | ||||||
|  | password = Password | ||||||
|  | re_type = Re-Type | ||||||
|  | captcha = Captcha | ||||||
|  | 
 | ||||||
|  | repository = Repository | ||||||
|  | organization = Organization | ||||||
|  | mirror = Mirror | ||||||
|  | new_repo = New Repository | ||||||
|  | new_migrate = New Migration | ||||||
|  | new_org = New Organization | ||||||
|  | manage_org = Manage Organizations | ||||||
|  | admin_panel = Admin Panel | ||||||
|  | account_settings = Account Settings | ||||||
|  | settings = Settings | ||||||
|  | 
 | ||||||
|  | news_feed = News Feed | ||||||
|  | pull_requests = Pull Requests | ||||||
|  | issues = Issues | ||||||
|  | 
 | ||||||
|  | cancel = Cancel | ||||||
|  | 
 | ||||||
|  | [home] | ||||||
|  | uname_holder = Username or E-mail | ||||||
|  | password_holder = Password | ||||||
|  | switch_dashboard_context = Switch Dashboard Context | ||||||
|  | my_repos = My Repositories | ||||||
|  | collaborative_repos = Collaborative Repositories | ||||||
|  | my_orgs = My Organizations | ||||||
|  | my_mirrors = My Mirrors | ||||||
|  | 
 | ||||||
|  | [auth] | ||||||
|  | create_new_account = Create New Account | ||||||
|  | register_hepler_msg = Already have an account? Sign in now! | ||||||
|  | disable_register_prompt = Sorry, registration has been disabled. Please contact the site administrator. | ||||||
|  | remember_me = Remember Me | ||||||
|  | forget_password = Fotget password? | ||||||
|  | sign_up_now = Need an account? Sign up now. | ||||||
|  | 
 | ||||||
|  | [form] | ||||||
|  | UserName = Username | ||||||
|  | Email = E-mail address | ||||||
|  | Password = Password | ||||||
|  | Retype = Re-type password | ||||||
|  | SSHTitle = SSH key name | ||||||
|  | 
 | ||||||
|  | require_error = ` cannot be empty.` | ||||||
|  | alpha_dash_error = ` must be valid alpha or numeric or dash(-_) characters.` | ||||||
|  | alpha_dash_dot_error = ` must be valid alpha or numeric or dash(-_) or dot characters.` | ||||||
|  | min_size_error = ` must contain at least %s characters.` | ||||||
|  | max_size_error = ` must contain at most %s characters.` | ||||||
|  | email_error = ` is not a valid e-mail address.` | ||||||
|  | url_error = ` is not a valid URL.` | ||||||
|  | unknown_error = Unknown error: | ||||||
|  | captcha_incorrect = Captcha didn't match. | ||||||
|  | password_not_match = Password and re-type password are not same. | ||||||
|  | 
 | ||||||
|  | username_been_taken = Username has been already taken. | ||||||
|  | repo_name_been_taken = Repository name has been already taken. | ||||||
|  | email_been_used = E-mail address has been already used. | ||||||
|  | ssh_key_been_used = Public key name has been used. | ||||||
|  | illegal_username = Your username contains illegal characters. | ||||||
|  | illegal_repo_name = Repository name contains illegal characters. | ||||||
|  | username_password_incorrect = Username or password is not correct. | ||||||
|  | 
 | ||||||
|  | invalid_ssh_key = Sorry, we're not able to verify your SSH key: %s | ||||||
|  | 
 | ||||||
|  | still_own_repo = Your account still have ownership of repository, you have to delete or transfer them first. | ||||||
|  | 
 | ||||||
|  | [settings] | ||||||
|  | profile = Profile | ||||||
|  | password = Password | ||||||
|  | ssh_keys = SSH Keys | ||||||
|  | social = Social Accounts | ||||||
|  | orgs = Organizations | ||||||
|  | delete = Delete Accoount | ||||||
|  | 
 | ||||||
|  | public_profile = Public Profile | ||||||
|  | profile_desc = Your Email address is public and will be used for any account related notifications, and any web based operations made via the site. | ||||||
|  | full_name = Full Name | ||||||
|  | website = Website | ||||||
|  | location = Location | ||||||
|  | update_profile = Update Profile | ||||||
|  | update_profile_success = Your profile has been successfully updated. | ||||||
|  | 
 | ||||||
|  | change_password = Change Password | ||||||
|  | old_password = Current Password | ||||||
|  | new_password = New Password | ||||||
|  | password_incorrect = Current password is not correct. | ||||||
|  | change_password_success = Password is changed successfully. You can now sign in via new password. | ||||||
|  | 
 | ||||||
|  | manage_ssh_keys = Manage SSH Keys | ||||||
|  | add_key = Add Key | ||||||
|  | ssh_desc = This is a list of SSH keys associated with your account. Remove any keys that you do not recognize. | ||||||
|  | ssh_helper = <strong>Need help?</strong> Check out our guide to <a href="https://help.github.com/articles/generating-ssh-keys">generating SSH keys</a> or troubleshoot <a href="https://help.github.com/ssh-issues/">common SSH Problems</a>. | ||||||
|  | add_new_key = Add SSH Key | ||||||
|  | key_name = Key Name | ||||||
|  | key_content = Content | ||||||
|  | add_key_success = New SSH Key has been added! | ||||||
|  | delete_key = Delete | ||||||
|  | add_on = Added on | ||||||
|  | last_used = Last used on | ||||||
|  | no_activity = No recent activity | ||||||
|  | 
 | ||||||
|  | manage_orgs = Manage Organizations | ||||||
|  | manage_social = Manage Associated Social Accounts | ||||||
|  | 
 | ||||||
|  | delete_account = Delete Your Account | ||||||
|  | delete_prompt = The operation will delete your account permanently, and <strong>CANNOT</strong> be undo! | ||||||
|  | confirm_delete_account = Confirm Deletion | ||||||
|  | 
 | ||||||
|  | [repo] | ||||||
|  | owner = Owner | ||||||
|  | repo_name = Repository Name | ||||||
|  | repo_name_helper = Great repository names are short, memorable and <strong>unique</strong>. | ||||||
|  | visibility = Visibility | ||||||
|  | visiblity_helper = This repository is <span class="label label-red label-radius">Private</span> | ||||||
|  | repo_desc = Description | ||||||
|  | repo_lang = Language | ||||||
|  | repo_lang_helper = Select a .gitignore file | ||||||
|  | license = License | ||||||
|  | license_helper = Select a license file | ||||||
|  | init_readme = Initialize this repository with a README.md | ||||||
|  | create_repo = Create Repository | ||||||
|  | 
 | ||||||
|  | [action] | ||||||
|  | create_repo = created repository <a href="/%s">%s</a> | ||||||
|  | commit_repo = pushed to <a href="/%s/src/%s">%s</a> at <a href="/%s">%s</a> | ||||||
|  | create_issue = opened issue <a href="/%s/issues/%s">%s#%s</a> | ||||||
|  | comment_issue = commented on issue <a href="/%s/issues/%s">%s#%s</a> | ||||||
|  | 
 | ||||||
|  | [tool] | ||||||
|  | ago = ago | ||||||
|  | from_now = from now | ||||||
|  | now = now | ||||||
|  | 1s = 1 second %s | ||||||
|  | 1m = 1 mintue %s | ||||||
|  | 1h = 1 hour %s | ||||||
|  | 1d = 1 day %s | ||||||
|  | 1w = 1 week %s | ||||||
|  | 1mon = 1 month %s | ||||||
|  | 1y = 1 year %s | ||||||
|  | seconds = %d seconds %s | ||||||
|  | minutes = %d minutes %s | ||||||
|  | hours = %d hours %s | ||||||
|  | days = %d days %s | ||||||
|  | weeks = %d weeks %s | ||||||
|  | months = %d months %s | ||||||
|  | years = %d years %s | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
							
								
								
									
										178
									
								
								conf/locale/locale_zh-CN.ini
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,178 @@ | ||||||
|  | app_desc = 基于 Go 语言的自助 Git 服务 | ||||||
|  | 
 | ||||||
|  | home = 首页 | ||||||
|  | dashboard = 控制面板 | ||||||
|  | explore = 探索 | ||||||
|  | help = 帮助 | ||||||
|  | sign_in = 登录 | ||||||
|  | sign_out = 退出 | ||||||
|  | sign_up = 注册 | ||||||
|  | register = 注册 | ||||||
|  | website = 官方网站 | ||||||
|  | version = 当前版本 | ||||||
|  | page = 页面 | ||||||
|  | template = 模板 | ||||||
|  | language = 语言选项 | ||||||
|  | 
 | ||||||
|  | username = 用户名 | ||||||
|  | email = 邮箱 | ||||||
|  | password = 密码 | ||||||
|  | re_type = 确认密码 | ||||||
|  | captcha = 验证码 | ||||||
|  | 
 | ||||||
|  | repository = 仓库 | ||||||
|  | organization = 组织 | ||||||
|  | mirror = 镜像 | ||||||
|  | new_repo = 创建新的仓库 | ||||||
|  | new_migrate = 迁移外部仓库 | ||||||
|  | new_org = 创建新的组织 | ||||||
|  | manage_org = 管理我的组织 | ||||||
|  | admin_panel = 管理面板 | ||||||
|  | account_settings = 帐户设置 | ||||||
|  | settings = 帐户设置 | ||||||
|  | 
 | ||||||
|  | news_feed = 最新活动 | ||||||
|  | pull_requests = 合并请求 | ||||||
|  | issues = 工单管理 | ||||||
|  | 
 | ||||||
|  | cancel = 取消 | ||||||
|  | 
 | ||||||
|  | [home] | ||||||
|  | uname_holder = 用户名或邮箱 | ||||||
|  | password_holder = 密码 | ||||||
|  | switch_dashboard_context = 切换控制面板用户 | ||||||
|  | my_repos = 我的仓库 | ||||||
|  | collaborative_repos = 参与协作的仓库 | ||||||
|  | my_orgs = 我的组织 | ||||||
|  | my_mirrors = 我的镜像 | ||||||
|  | 
 | ||||||
|  | [auth] | ||||||
|  | create_new_account = 创建帐户 | ||||||
|  | register_hepler_msg = 已经注册?立即登录! | ||||||
|  | disable_register_prompt = 对不起,注册功能已被关闭。请联系网站管理员。 | ||||||
|  | remember_me = 记住登录 | ||||||
|  | forget_password = 忘记密码? | ||||||
|  | sign_up_now = 还没帐户?马上注册。 | ||||||
|  | 
 | ||||||
|  | [form] | ||||||
|  | UserName = 用户名 | ||||||
|  | Email = 邮箱地址 | ||||||
|  | Password = 密码 | ||||||
|  | Retype = 确认密码 | ||||||
|  | SSHTitle = SSH 密钥名称 | ||||||
|  | 
 | ||||||
|  | require_error = 不能为空。 | ||||||
|  | alpha_dash_error = 必须为英文字母、阿拉伯数字或横线(-_)。 | ||||||
|  | alpha_dash_dot_error = 必须为英文字母、阿拉伯数字、横线(-_)或点。 | ||||||
|  | min_size_error = 长度最小为 %s 个字符。 | ||||||
|  | max_size_error = 长度最大为 %s 个字符。 | ||||||
|  | email_error = 不是一个有效的邮箱地址。 | ||||||
|  | url_error = 不是一个有效的 URL。 | ||||||
|  | unknown_error = 未知错误: | ||||||
|  | captcha_incorrect = 验证码未匹配。 | ||||||
|  | password_not_match = 密码与确认密码未匹配。 | ||||||
|  | 
 | ||||||
|  | username_been_taken = 用户名已经被占用。 | ||||||
|  | repo_name_been_taken = 仓库名称已经被占用。 | ||||||
|  | email_been_used = 邮箱地址已经被使用。 | ||||||
|  | ssh_key_been_used = SSH 密钥已经被使用。 | ||||||
|  | illegal_username = 您的用户名包含非法字符。 | ||||||
|  | illegal_repo_name = 仓库名称包含非法字符。 | ||||||
|  | username_password_incorrect = 用户名或密码不正确。 | ||||||
|  | 
 | ||||||
|  | invalid_ssh_key = 很抱歉,我们无法验证您输入的 SSH 密钥:%s | ||||||
|  | 
 | ||||||
|  | still_own_repo = 您的帐户仍然是某些仓库的拥有者,您必须先转移或删除它们才能执行删除帐户操作! | ||||||
|  | 
 | ||||||
|  | [settings] | ||||||
|  | profile = 个人信息 | ||||||
|  | password = 修改密码 | ||||||
|  | ssh_keys = 管理 SSH 密钥 | ||||||
|  | social = 社交帐号绑定 | ||||||
|  | orgs = 管理组织 | ||||||
|  | delete = 删除帐户 | ||||||
|  | 
 | ||||||
|  | public_profile = 公开信息 | ||||||
|  | profile_desc = 您的邮箱地址将会被公开,并被用于接收帐户的所有提醒和通知。 | ||||||
|  | full_name = 自定义名称 | ||||||
|  | website = 个人网站 | ||||||
|  | location = 所在地区 | ||||||
|  | update_profile = 更新信息 | ||||||
|  | update_profile_success = 您的个人信息已经更新成功! | ||||||
|  | 
 | ||||||
|  | change_password = 修改密码 | ||||||
|  | old_password = 当前密码 | ||||||
|  | new_password = 新的密码 | ||||||
|  | password_incorrect = 当前密码不正确! | ||||||
|  | change_password_success = 密码修改成功!您现在可以使用新的密码登录。 | ||||||
|  | 
 | ||||||
|  | manage_ssh_keys = 管理 SSH 密钥 | ||||||
|  | add_key = 增加密钥 | ||||||
|  | ssh_desc = 以下是与您帐户所关联的 SSH 密钥,如果您发现有陌生的密钥,请立即删除它! | ||||||
|  | ssh_helper = <strong>需要帮助?</strong> 请查看有关 <a href="https://help.github.com/articles/generating-ssh-keys">如何生成 SSH 密钥</a> 或 <a href="https://help.github.com/ssh-issues/">常见 SSH 问题</a> 寻找答案。 | ||||||
|  | add_new_key = 增加 SSH 密钥 | ||||||
|  | key_name = 密钥名称 | ||||||
|  | key_content = 密钥内容 | ||||||
|  | add_key_success = 新的 SSH 密钥添加成功! | ||||||
|  | delete_key = 删除 | ||||||
|  | add_on = 增加于 | ||||||
|  | last_used = 上次使用在 | ||||||
|  | no_activity = 没有最近活动 | ||||||
|  | 
 | ||||||
|  | manage_orgs = 管理我的组织 | ||||||
|  | manage_social = 管理关联社交帐户 | ||||||
|  | 
 | ||||||
|  | delete_account = 删除当前帐户 | ||||||
|  | delete_prompt = 删除操作会永久清除您的帐户信息,并且 <strong>不可恢复</strong>! | ||||||
|  | confirm_delete_account = 确认删除帐户 | ||||||
|  | 
 | ||||||
|  | [repo] | ||||||
|  | owner = 拥有者 | ||||||
|  | repo_name = 仓库名称 | ||||||
|  | repo_name_helper = 伟大的仓库名称一般都较短、令人深刻并且 <strong>独一无二</strong> 的。 | ||||||
|  | visibility = 可见性 | ||||||
|  | visiblity_helper = 本仓库将是 <span class="label label-red label-radius">私有的</span> | ||||||
|  | repo_desc = 仓库描述 | ||||||
|  | repo_lang = 仓库语言 | ||||||
|  | repo_lang_helper = 请选择 .gitignore 文件 | ||||||
|  | license = 授权许可 | ||||||
|  | license_helper = 请选择授权许可文件 | ||||||
|  | init_readme = 使用 README.md 文件初始化仓库 | ||||||
|  | create_repo = 创建仓库 | ||||||
|  | 
 | ||||||
|  | [action] | ||||||
|  | create_repo = 创建了仓库 <a href="/%s">%s</a> | ||||||
|  | commit_repo = 推送了 <a href="/%s/src/%s">%s</a> 分支的代码到 <a href="/%s">%s</a> | ||||||
|  | create_issue = 创建了工单 <a href="/%s/issues/%s">%s#%s</a> | ||||||
|  | comment_issue = 评论了工单 <a href="/%s/issues/%s">%s#%s</a> | ||||||
|  | 
 | ||||||
|  | [tool] | ||||||
|  | ago = 之前 | ||||||
|  | from_now = 之后 | ||||||
|  | now = 现在 | ||||||
|  | 1s = 1 秒%s | ||||||
|  | 1m = 1 分钟%s | ||||||
|  | 1h = 1 小时%s | ||||||
|  | 1d = 1 天%s | ||||||
|  | 1w = 1 周%s | ||||||
|  | 1mon = 1 月%s | ||||||
|  | 1y = 1 年%s | ||||||
|  | seconds = %d 秒%s | ||||||
|  | minutes = %d 分钟%s | ||||||
|  | hours = %d 小时%s | ||||||
|  | days = %d 天%s | ||||||
|  | weeks = %d 周%s | ||||||
|  | months = %d 月%s | ||||||
|  | years = %d 年%s | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @ -1,8 +0,0 @@ | ||||||
| [program:gogs] |  | ||||||
| user=git |  | ||||||
| command = /home/git/gogs/start.sh |  | ||||||
| directory = /home/git/gogs |  | ||||||
| autostart = true |  | ||||||
| stdout_logfile = /var/gogs.log |  | ||||||
| stderr_logfile = /var/gogs-error.log |  | ||||||
| environment=HOME="/home/git"   |  | ||||||
|  | @ -1,40 +0,0 @@ | ||||||
| ### Install Gogs With Docker |  | ||||||
| 
 |  | ||||||
| Deploying gogs in [Docker](http://www.docker.io/) is just as easy as eating a pie, what you do is just open the `dockerfiles/build.sh` file, replace the configs: |  | ||||||
| 
 |  | ||||||
| ``` |  | ||||||
| DB_TYPE="YOUR_DB_TYPE"            # type of database, support 'mysql' and 'postgres' |  | ||||||
| MEM_TYPE="YOUR_MEM_TYPE"          # type of memory database, support 'redis' and 'memcache' |  | ||||||
| DB_PASSWORD="YOUR_DB_PASSWORD"    # The database password. |  | ||||||
| DB_RUN_NAME="YOUR_DB_RUN_NAME"    # The --name option value when run the database image. |  | ||||||
| MEM_RUN_NAME="YOUR_MEM_RUN_NAME"  # The --name option value when run the mem database image. |  | ||||||
| HOST_PORT="YOUR_HOST_PORT"        # The port on host, which will be redirected to the port 3000 inside gogs container. |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| And run: |  | ||||||
| ``` |  | ||||||
| cd dockerfiles |  | ||||||
| ./build.sh |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| The build might take some time, just be paient. After it finishes, you will receive the message: |  | ||||||
| 
 |  | ||||||
| ``` |  | ||||||
| Now we have the MySQL image(running) and gogs image, use the follow command to start gogs service( the content might be different, according to your own configs): |  | ||||||
|  docker run -i -t --link YOUR_DB_RUN_NAME:db  --link YOUR_MEM_RUN_NAME:mem  -p YOUR_HOST_PORT:3000 gogits/gogs  |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| Just follow the message, run: |  | ||||||
| 
 |  | ||||||
| ``` |  | ||||||
|  docker run -i -t --link YOUR_DB_RUN_NAME:db  --link YOUR_MEM_RUN_NAME:mem  -p YOUR_HOST_PORT:3000 gogits/gogs  |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| Now we have gogs running! Open the browser and navigate to: |  | ||||||
| 
 |  | ||||||
| ``` |  | ||||||
| http://YOUR_HOST_IP:YOUR_HOST_PORT |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| Let's 'gogs'! |  | ||||||
| Ouya~ |  | ||||||
							
								
								
									
										4
									
								
								gogs.go
									
									
									
									
									
								
							
							
						
						|  | @ -4,7 +4,7 @@ | ||||||
| // Use of this source code is governed by a MIT-style
 | // Use of this source code is governed by a MIT-style
 | ||||||
| // license that can be found in the LICENSE file.
 | // license that can be found in the LICENSE file.
 | ||||||
| 
 | 
 | ||||||
| // Gogs(Go Git Service) is a Self Hosted Git Service in the Go Programming Language.
 | // Gogs(Go Git Service) is a painless self-hosted Git Service written in Go.
 | ||||||
| package main | package main | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | @ -17,7 +17,7 @@ import ( | ||||||
| 	"github.com/gogits/gogs/modules/setting" | 	"github.com/gogits/gogs/modules/setting" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| const APP_VER = "0.4.5.0712 Alpha" | const APP_VER = "0.4.7.0726 Alpha" | ||||||
| 
 | 
 | ||||||
| func init() { | func init() { | ||||||
| 	runtime.GOMAXPROCS(runtime.NumCPU()) | 	runtime.GOMAXPROCS(runtime.NumCPU()) | ||||||
|  |  | ||||||
|  | @ -8,30 +8,31 @@ import ( | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"path" | ||||||
| 	"regexp" | 	"regexp" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
| 	"unicode" | 	"unicode" | ||||||
| 
 | 
 | ||||||
| 	"github.com/gogits/git" |  | ||||||
| 
 |  | ||||||
| 	"github.com/gogits/gogs/modules/base" | 	"github.com/gogits/gogs/modules/base" | ||||||
|  | 	"github.com/gogits/gogs/modules/git" | ||||||
| 	"github.com/gogits/gogs/modules/log" | 	"github.com/gogits/gogs/modules/log" | ||||||
| 	"github.com/gogits/gogs/modules/setting" | 	"github.com/gogits/gogs/modules/setting" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Operation types of user action.
 | type ActionType int | ||||||
|  | 
 | ||||||
| const ( | const ( | ||||||
| 	OP_CREATE_REPO = iota + 1 | 	CREATE_REPO   ActionType = iota + 1 // 1
 | ||||||
| 	OP_DELETE_REPO | 	DELETE_REPO                         // 2
 | ||||||
| 	OP_STAR_REPO | 	STAR_REPO                           // 3
 | ||||||
| 	OP_FOLLOW_REPO | 	FOLLOW_REPO                         // 4
 | ||||||
| 	OP_COMMIT_REPO | 	COMMIT_REPO                         // 5
 | ||||||
| 	OP_CREATE_ISSUE | 	CREATE_ISSUE                        // 6
 | ||||||
| 	OP_PULL_REQUEST | 	PULL_REQUEST                        // 7
 | ||||||
| 	OP_TRANSFER_REPO | 	TRANSFER_REPO                       // 8
 | ||||||
| 	OP_PUSH_TAG | 	PUSH_TAG                            // 9
 | ||||||
| 	OP_COMMENT_ISSUE | 	COMMENT_ISSUE                       // 10
 | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
|  | @ -53,7 +54,7 @@ func init() { | ||||||
| type Action struct { | type Action struct { | ||||||
| 	Id           int64 | 	Id           int64 | ||||||
| 	UserId       int64 // Receiver user id.
 | 	UserId       int64 // Receiver user id.
 | ||||||
| 	OpType       int | 	OpType       ActionType | ||||||
| 	ActUserId    int64  // Action user id.
 | 	ActUserId    int64  // Action user id.
 | ||||||
| 	ActUserName  string // Action user name.
 | 	ActUserName  string // Action user name.
 | ||||||
| 	ActEmail     string | 	ActEmail     string | ||||||
|  | @ -67,7 +68,7 @@ type Action struct { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a Action) GetOpType() int { | func (a Action) GetOpType() int { | ||||||
| 	return a.OpType | 	return int(a.OpType) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a Action) GetActUserName() string { | func (a Action) GetActUserName() string { | ||||||
|  | @ -86,6 +87,10 @@ func (a Action) GetRepoName() string { | ||||||
| 	return a.RepoName | 	return a.RepoName | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (a Action) GetRepoLink() string { | ||||||
|  | 	return path.Join(a.RepoUserName, a.RepoName) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (a Action) GetBranch() string { | func (a Action) GetBranch() string { | ||||||
| 	return a.RefName | 	return a.RefName | ||||||
| } | } | ||||||
|  | @ -94,6 +99,14 @@ func (a Action) GetContent() string { | ||||||
| 	return a.Content | 	return a.Content | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (a Action) GetCreate() time.Time { | ||||||
|  | 	return a.Created | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (a Action) GetIssueInfos() []string { | ||||||
|  | 	return strings.SplitN(a.Content, "|", 2) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func updateIssuesCommit(userId, repoId int64, repoUserName, repoName string, commits []*base.PushCommit) error { | func updateIssuesCommit(userId, repoId int64, repoUserName, repoName string, commits []*base.PushCommit) error { | ||||||
| 	for _, c := range commits { | 	for _, c := range commits { | ||||||
| 		refs := IssueKeywordsPat.FindAllString(c.Message, -1) | 		refs := IssueKeywordsPat.FindAllString(c.Message, -1) | ||||||
|  | @ -160,12 +173,11 @@ func updateIssuesCommit(userId, repoId int64, repoUserName, repoName string, com | ||||||
| // CommitRepoAction adds new action for committing repository.
 | // CommitRepoAction adds new action for committing repository.
 | ||||||
| func CommitRepoAction(userId, repoUserId int64, userName, actEmail string, | func CommitRepoAction(userId, repoUserId int64, userName, actEmail string, | ||||||
| 	repoId int64, repoUserName, repoName string, refFullName string, commit *base.PushCommits) error { | 	repoId int64, repoUserName, repoName string, refFullName string, commit *base.PushCommits) error { | ||||||
| 	// log.Trace("action.CommitRepoAction(start): %d/%s", userId, repoName)
 |  | ||||||
| 
 | 
 | ||||||
| 	opType := OP_COMMIT_REPO | 	opType := COMMIT_REPO | ||||||
| 	// Check it's tag push or branch.
 | 	// Check it's tag push or branch.
 | ||||||
| 	if strings.HasPrefix(refFullName, "refs/tags/") { | 	if strings.HasPrefix(refFullName, "refs/tags/") { | ||||||
| 		opType = OP_PUSH_TAG | 		opType = PUSH_TAG | ||||||
| 		commit = &base.PushCommits{} | 		commit = &base.PushCommits{} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -269,26 +281,26 @@ func CommitRepoAction(userId, repoUserId int64, userName, actEmail string, | ||||||
| // NewRepoAction adds new action for creating repository.
 | // NewRepoAction adds new action for creating repository.
 | ||||||
| func NewRepoAction(u *User, repo *Repository) (err error) { | func NewRepoAction(u *User, repo *Repository) (err error) { | ||||||
| 	if err = NotifyWatchers(&Action{ActUserId: u.Id, ActUserName: u.Name, ActEmail: u.Email, | 	if err = NotifyWatchers(&Action{ActUserId: u.Id, ActUserName: u.Name, ActEmail: u.Email, | ||||||
| 		OpType: OP_CREATE_REPO, RepoId: repo.Id, RepoUserName: repo.Owner.Name, RepoName: repo.Name, | 		OpType: CREATE_REPO, RepoId: repo.Id, RepoUserName: repo.Owner.Name, RepoName: repo.Name, | ||||||
| 		IsPrivate: repo.IsPrivate}); err != nil { | 		IsPrivate: repo.IsPrivate}); err != nil { | ||||||
| 		log.Error("action.NewRepoAction(notify watchers): %d/%s", u.Id, repo.Name) | 		log.Error(4, "NotifyWatchers: %d/%s", u.Id, repo.Name) | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	log.Trace("action.NewRepoAction: %s/%s", u.LowerName, repo.LowerName) | 	log.Trace("action.NewRepoAction: %s/%s", u.Name, repo.Name) | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // TransferRepoAction adds new action for transfering repository.
 | // TransferRepoAction adds new action for transfering repository.
 | ||||||
| func TransferRepoAction(user, newUser *User, repo *Repository) (err error) { | func TransferRepoAction(u, newUser *User, repo *Repository) (err error) { | ||||||
| 	if err = NotifyWatchers(&Action{ActUserId: user.Id, ActUserName: user.Name, ActEmail: user.Email, | 	if err = NotifyWatchers(&Action{ActUserId: u.Id, ActUserName: u.Name, ActEmail: u.Email, | ||||||
| 		OpType: OP_TRANSFER_REPO, RepoId: repo.Id, RepoName: repo.Name, Content: newUser.Name, | 		OpType: TRANSFER_REPO, RepoId: repo.Id, RepoName: repo.Name, Content: newUser.Name, | ||||||
| 		IsPrivate: repo.IsPrivate}); err != nil { | 		IsPrivate: repo.IsPrivate}); err != nil { | ||||||
| 		log.Error("action.TransferRepoAction(notify watchers): %d/%s", user.Id, repo.Name) | 		log.Error(4, "NotifyWatchers: %d/%s", u.Id, repo.Name) | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	log.Trace("action.TransferRepoAction: %s/%s", user.LowerName, repo.LowerName) | 	log.Trace("action.TransferRepoAction: %s/%s", u.Name, repo.Name) | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -13,9 +13,10 @@ import ( | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/Unknwon/com" | ||||||
|  | 
 | ||||||
| 	"github.com/gogits/git" | 	"github.com/gogits/git" | ||||||
| 
 | 
 | ||||||
| 	"github.com/gogits/gogs/modules/base" |  | ||||||
| 	"github.com/gogits/gogs/modules/log" | 	"github.com/gogits/gogs/modules/log" | ||||||
| 	"github.com/gogits/gogs/modules/process" | 	"github.com/gogits/gogs/modules/process" | ||||||
| ) | ) | ||||||
|  | @ -118,8 +119,8 @@ func ParsePatch(pid int64, cmd *exec.Cmd, reader io.Reader) (*Diff, error) { | ||||||
| 
 | 
 | ||||||
| 			// Parse line number.
 | 			// Parse line number.
 | ||||||
| 			ranges := strings.Split(ss[len(ss)-2][1:], " ") | 			ranges := strings.Split(ss[len(ss)-2][1:], " ") | ||||||
| 			leftLine, _ = base.StrTo(strings.Split(ranges[0], ",")[0][1:]).Int() | 			leftLine, _ = com.StrTo(strings.Split(ranges[0], ",")[0][1:]).Int() | ||||||
| 			rightLine, _ = base.StrTo(strings.Split(ranges[1], ",")[0]).Int() | 			rightLine, _ = com.StrTo(strings.Split(ranges[1], ",")[0]).Int() | ||||||
| 			continue | 			continue | ||||||
| 		case line[0] == '+': | 		case line[0] == '+': | ||||||
| 			curFile.Addition++ | 			curFile.Addition++ | ||||||
|  | @ -214,7 +215,7 @@ func GetDiff(repoPath, commitid string) (*Diff, error) { | ||||||
| 		select { | 		select { | ||||||
| 		case <-time.After(5 * time.Minute): | 		case <-time.After(5 * time.Minute): | ||||||
| 			if errKill := process.Kill(pid); errKill != nil { | 			if errKill := process.Kill(pid); errKill != nil { | ||||||
| 				log.Error("git_diff.ParsePatch(Kill): %v", err) | 				log.Error(4, "git_diff.ParsePatch(Kill): %v", err) | ||||||
| 			} | 			} | ||||||
| 			<-done | 			<-done | ||||||
| 			// return "", ErrExecTimeout.Error(), ErrExecTimeout
 | 			// return "", ErrExecTimeout.Error(), ErrExecTimeout
 | ||||||
|  |  | ||||||
|  | @ -13,9 +13,9 @@ import ( | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/Unknwon/com" | ||||||
| 	"github.com/go-xorm/xorm" | 	"github.com/go-xorm/xorm" | ||||||
| 
 | 
 | ||||||
| 	"github.com/gogits/gogs/modules/base" |  | ||||||
| 	"github.com/gogits/gogs/modules/log" | 	"github.com/gogits/gogs/modules/log" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -72,7 +72,7 @@ func (i *Issue) GetLabels() error { | ||||||
| 	strIds := strings.Split(strings.TrimSuffix(i.LabelIds[1:], "|"), "|$") | 	strIds := strings.Split(strings.TrimSuffix(i.LabelIds[1:], "|"), "|$") | ||||||
| 	i.Labels = make([]*Label, 0, len(strIds)) | 	i.Labels = make([]*Label, 0, len(strIds)) | ||||||
| 	for _, strId := range strIds { | 	for _, strId := range strIds { | ||||||
| 		id, _ := base.StrTo(strId).Int64() | 		id, _ := com.StrTo(strId).Int64() | ||||||
| 		if id > 0 { | 		if id > 0 { | ||||||
| 			l, err := GetLabelById(id) | 			l, err := GetLabelById(id) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
|  | @ -337,7 +337,7 @@ func GetIssueUserPairsByRepoIds(rids []int64, isClosed bool, page int) ([]*Issue | ||||||
| 	buf := bytes.NewBufferString("") | 	buf := bytes.NewBufferString("") | ||||||
| 	for _, rid := range rids { | 	for _, rid := range rids { | ||||||
| 		buf.WriteString("repo_id=") | 		buf.WriteString("repo_id=") | ||||||
| 		buf.WriteString(base.ToStr(rid)) | 		buf.WriteString(com.ToStr(rid)) | ||||||
| 		buf.WriteString(" OR ") | 		buf.WriteString(" OR ") | ||||||
| 	} | 	} | ||||||
| 	cond := strings.TrimSuffix(buf.String(), " OR ") | 	cond := strings.TrimSuffix(buf.String(), " OR ") | ||||||
|  | @ -564,7 +564,7 @@ func UpdateLabel(l *Label) error { | ||||||
| 
 | 
 | ||||||
| // DeleteLabel delete a label of given repository.
 | // DeleteLabel delete a label of given repository.
 | ||||||
| func DeleteLabel(repoId int64, strId string) error { | func DeleteLabel(repoId int64, strId string) error { | ||||||
| 	id, _ := base.StrTo(strId).Int64() | 	id, _ := com.StrTo(strId).Int64() | ||||||
| 	l, err := GetLabelById(id) | 	l, err := GetLabelById(id) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if err == ErrLabelNotExist { | 		if err == ErrLabelNotExist { | ||||||
|  | @ -768,7 +768,7 @@ func ChangeMilestoneAssign(oldMid, mid int64, issue *Issue) (err error) { | ||||||
| 			m.Completeness = 0 | 			m.Completeness = 0 | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if _, err = sess.Id(m.Id).Update(m); err != nil { | 		if _, err = sess.Id(m.Id).Cols("num_issues,num_completeness,num_closed_issues").Update(m); err != nil { | ||||||
| 			sess.Rollback() | 			sess.Rollback() | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
|  | @ -796,7 +796,7 @@ func ChangeMilestoneAssign(oldMid, mid int64, issue *Issue) (err error) { | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		m.Completeness = m.NumClosedIssues * 100 / m.NumIssues | 		m.Completeness = m.NumClosedIssues * 100 / m.NumIssues | ||||||
| 		if _, err = sess.Id(m.Id).Update(m); err != nil { | 		if _, err = sess.Id(m.Id).Cols("num_issues,num_completeness,num_closed_issues").Update(m); err != nil { | ||||||
| 			sess.Rollback() | 			sess.Rollback() | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -164,15 +164,14 @@ func UserSignIn(uname, passwd string) (*User, error) { | ||||||
| 	if u.LoginType == NOTYPE { | 	if u.LoginType == NOTYPE { | ||||||
| 		if has { | 		if has { | ||||||
| 			u.LoginType = PLAIN | 			u.LoginType = PLAIN | ||||||
|  | 		} else { | ||||||
|  | 			return nil, ErrUserNotExist | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// for plain login, user must have existed.
 | 	// For plain login, user must exist to reach this line.
 | ||||||
|  | 	// Now verify password.
 | ||||||
| 	if u.LoginType == PLAIN { | 	if u.LoginType == PLAIN { | ||||||
| 		if !has { |  | ||||||
| 			return nil, ErrUserNotExist |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		newUser := &User{Passwd: passwd, Salt: u.Salt} | 		newUser := &User{Passwd: passwd, Salt: u.Salt} | ||||||
| 		newUser.EncodePasswd() | 		newUser.EncodePasswd() | ||||||
| 		if u.Passwd != newUser.Passwd { | 		if u.Passwd != newUser.Passwd { | ||||||
|  | @ -233,18 +232,18 @@ func UserSignIn(uname, passwd string) (*User, error) { | ||||||
| // Query if name/passwd can login against the LDAP direcotry pool
 | // Query if name/passwd can login against the LDAP direcotry pool
 | ||||||
| // Create a local user if success
 | // Create a local user if success
 | ||||||
| // Return the same LoginUserPlain semantic
 | // Return the same LoginUserPlain semantic
 | ||||||
| func LoginUserLdapSource(user *User, name, passwd string, sourceId int64, cfg *LDAPConfig, autoRegister bool) (*User, error) { | func LoginUserLdapSource(u *User, name, passwd string, sourceId int64, cfg *LDAPConfig, autoRegister bool) (*User, error) { | ||||||
| 	mail, logged := cfg.Ldapsource.SearchEntry(name, passwd) | 	mail, logged := cfg.Ldapsource.SearchEntry(name, passwd) | ||||||
| 	if !logged { | 	if !logged { | ||||||
| 		// user not in LDAP, do nothing
 | 		// user not in LDAP, do nothing
 | ||||||
| 		return nil, ErrUserNotExist | 		return nil, ErrUserNotExist | ||||||
| 	} | 	} | ||||||
| 	if !autoRegister { | 	if !autoRegister { | ||||||
| 		return user, nil | 		return u, nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// fake a local user creation
 | 	// fake a local user creation
 | ||||||
| 	user = &User{ | 	u = &User{ | ||||||
| 		LowerName:   strings.ToLower(name), | 		LowerName:   strings.ToLower(name), | ||||||
| 		Name:        strings.ToLower(name), | 		Name:        strings.ToLower(name), | ||||||
| 		LoginType:   LDAP, | 		LoginType:   LDAP, | ||||||
|  | @ -255,7 +254,8 @@ func LoginUserLdapSource(user *User, name, passwd string, sourceId int64, cfg *L | ||||||
| 		Email:       mail, | 		Email:       mail, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return CreateUser(user) | 	err := CreateUser(u) | ||||||
|  | 	return u, err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type loginAuth struct { | type loginAuth struct { | ||||||
|  | @ -322,7 +322,7 @@ func SmtpAuth(host string, port int, a smtp.Auth, useTls bool) error { | ||||||
| // Query if name/passwd can login against the LDAP direcotry pool
 | // Query if name/passwd can login against the LDAP direcotry pool
 | ||||||
| // Create a local user if success
 | // Create a local user if success
 | ||||||
| // Return the same LoginUserPlain semantic
 | // Return the same LoginUserPlain semantic
 | ||||||
| func LoginUserSMTPSource(user *User, name, passwd string, sourceId int64, cfg *SMTPConfig, autoRegister bool) (*User, error) { | func LoginUserSMTPSource(u *User, name, passwd string, sourceId int64, cfg *SMTPConfig, autoRegister bool) (*User, error) { | ||||||
| 	var auth smtp.Auth | 	var auth smtp.Auth | ||||||
| 	if cfg.Auth == SMTP_PLAIN { | 	if cfg.Auth == SMTP_PLAIN { | ||||||
| 		auth = smtp.PlainAuth("", name, passwd, cfg.Host) | 		auth = smtp.PlainAuth("", name, passwd, cfg.Host) | ||||||
|  | @ -340,7 +340,7 @@ func LoginUserSMTPSource(user *User, name, passwd string, sourceId int64, cfg *S | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if !autoRegister { | 	if !autoRegister { | ||||||
| 		return user, nil | 		return u, nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	var loginName = name | 	var loginName = name | ||||||
|  | @ -349,7 +349,7 @@ func LoginUserSMTPSource(user *User, name, passwd string, sourceId int64, cfg *S | ||||||
| 		loginName = name[:idx] | 		loginName = name[:idx] | ||||||
| 	} | 	} | ||||||
| 	// fake a local user creation
 | 	// fake a local user creation
 | ||||||
| 	user = &User{ | 	u = &User{ | ||||||
| 		LowerName:   strings.ToLower(loginName), | 		LowerName:   strings.ToLower(loginName), | ||||||
| 		Name:        strings.ToLower(loginName), | 		Name:        strings.ToLower(loginName), | ||||||
| 		LoginType:   SMTP, | 		LoginType:   SMTP, | ||||||
|  | @ -359,5 +359,6 @@ func LoginUserSMTPSource(user *User, name, passwd string, sourceId int64, cfg *S | ||||||
| 		Passwd:      passwd, | 		Passwd:      passwd, | ||||||
| 		Email:       name, | 		Email:       name, | ||||||
| 	} | 	} | ||||||
| 	return CreateUser(user) | 	err := CreateUser(u) | ||||||
|  | 	return u, err | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -54,7 +54,7 @@ func exePath() (string, error) { | ||||||
| func homeDir() string { | func homeDir() string { | ||||||
| 	home, err := com.HomeDir() | 	home, err := com.HomeDir() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatal("Fail to get home directory: %v", err) | 		log.Fatal(4, "Fail to get home directory: %v", err) | ||||||
| 	} | 	} | ||||||
| 	return home | 	return home | ||||||
| } | } | ||||||
|  | @ -63,25 +63,28 @@ func init() { | ||||||
| 	var err error | 	var err error | ||||||
| 
 | 
 | ||||||
| 	if appPath, err = exePath(); err != nil { | 	if appPath, err = exePath(); err != nil { | ||||||
| 		log.Fatal("publickey.init(fail to get app path): %v\n", err) | 		log.Fatal(4, "fail to get app path: %v\n", err) | ||||||
| 	} | 	} | ||||||
|  | 	appPath = strings.Replace(appPath, "\\", "/", -1) | ||||||
| 
 | 
 | ||||||
| 	// Determine and create .ssh path.
 | 	// Determine and create .ssh path.
 | ||||||
| 	SshPath = filepath.Join(homeDir(), ".ssh") | 	SshPath = filepath.Join(homeDir(), ".ssh") | ||||||
| 	if err = os.MkdirAll(SshPath, os.ModePerm); err != nil { | 	if err = os.MkdirAll(SshPath, os.ModePerm); err != nil { | ||||||
| 		log.Fatal("publickey.init(fail to create SshPath(%s)): %v\n", SshPath, err) | 		log.Fatal(4, "fail to create SshPath(%s): %v\n", SshPath, err) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // PublicKey represents a SSH key.
 | // PublicKey represents a SSH key.
 | ||||||
| type PublicKey struct { | type PublicKey struct { | ||||||
| 	Id          int64 | 	Id                int64 | ||||||
| 	OwnerId     int64  `xorm:"UNIQUE(s) INDEX NOT NULL"` | 	OwnerId           int64  `xorm:"UNIQUE(s) INDEX NOT NULL"` | ||||||
| 	Name        string `xorm:"UNIQUE(s) NOT NULL"` | 	Name              string `xorm:"UNIQUE(s) NOT NULL"` | ||||||
| 	Fingerprint string | 	Fingerprint       string | ||||||
| 	Content     string    `xorm:"TEXT NOT NULL"` | 	Content           string    `xorm:"TEXT NOT NULL"` | ||||||
| 	Created     time.Time `xorm:"CREATED"` | 	Created           time.Time `xorm:"CREATED"` | ||||||
| 	Updated     time.Time `xorm:"UPDATED"` | 	Updated           time.Time | ||||||
|  | 	HasRecentActivity bool `xorm:"-"` | ||||||
|  | 	HasUsed           bool `xorm:"-"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // GetAuthorizedString generates and returns formatted public key string for authorized_keys file.
 | // GetAuthorizedString generates and returns formatted public key string for authorized_keys file.
 | ||||||
|  | @ -89,6 +92,59 @@ func (key *PublicKey) GetAuthorizedString() string { | ||||||
| 	return fmt.Sprintf(_TPL_PUBLICK_KEY, appPath, key.Id, key.Content) | 	return fmt.Sprintf(_TPL_PUBLICK_KEY, appPath, key.Id, key.Content) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | var ( | ||||||
|  | 	MinimumKeySize = map[string]int{ | ||||||
|  | 		"(ED25519)": 256, | ||||||
|  | 		"(ECDSA)":   256, | ||||||
|  | 		"(NTRU)":    1087, | ||||||
|  | 		"(MCE)":     1702, | ||||||
|  | 		"(McE)":     1702, | ||||||
|  | 		"(RSA)":     2048, | ||||||
|  | 	} | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // CheckPublicKeyString checks if the given public key string is recognized by SSH.
 | ||||||
|  | func CheckPublicKeyString(content string) (bool, error) { | ||||||
|  | 	if strings.ContainsAny(content, "\n\r") { | ||||||
|  | 		return false, errors.New("Only a single line with a single key please") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// write the key to a file…
 | ||||||
|  | 	tmpFile, err := ioutil.TempFile(os.TempDir(), "keytest") | ||||||
|  | 	if err != nil { | ||||||
|  | 		return false, err | ||||||
|  | 	} | ||||||
|  | 	tmpPath := tmpFile.Name() | ||||||
|  | 	defer os.Remove(tmpPath) | ||||||
|  | 	tmpFile.WriteString(content) | ||||||
|  | 	tmpFile.Close() | ||||||
|  | 
 | ||||||
|  | 	// … see if ssh-keygen recognizes its contents
 | ||||||
|  | 	stdout, stderr, err := process.Exec("CheckPublicKeyString", "ssh-keygen", "-l", "-f", tmpPath) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return false, errors.New("ssh-keygen -l -f: " + stderr) | ||||||
|  | 	} else if len(stdout) < 2 { | ||||||
|  | 		return false, errors.New("ssh-keygen returned not enough output to evaluate the key") | ||||||
|  | 	} | ||||||
|  | 	sshKeygenOutput := strings.Split(stdout, " ") | ||||||
|  | 	if len(sshKeygenOutput) < 4 { | ||||||
|  | 		return false, errors.New("Not enough fields returned by ssh-keygen -l -f") | ||||||
|  | 	} | ||||||
|  | 	keySize, err := com.StrTo(sshKeygenOutput[0]).Int() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return false, errors.New("Cannot get key size of the given key") | ||||||
|  | 	} | ||||||
|  | 	keyType := strings.TrimSpace(sshKeygenOutput[len(sshKeygenOutput)-1]) | ||||||
|  | 
 | ||||||
|  | 	if minimumKeySize := MinimumKeySize[keyType]; minimumKeySize == 0 { | ||||||
|  | 		return false, errors.New("Sorry, unrecognized public key type") | ||||||
|  | 	} else if keySize < minimumKeySize { | ||||||
|  | 		return false, fmt.Errorf("The minimum accepted size of a public key %s is %d", keyType, minimumKeySize) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return true, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // saveAuthorizedKeyFile writes SSH key content to authorized_keys file.
 | // saveAuthorizedKeyFile writes SSH key content to authorized_keys file.
 | ||||||
| func saveAuthorizedKeyFile(key *PublicKey) error { | func saveAuthorizedKeyFile(key *PublicKey) error { | ||||||
| 	sshOpLocker.Lock() | 	sshOpLocker.Lock() | ||||||
|  | @ -144,10 +200,18 @@ func AddPublicKey(key *PublicKey) (err error) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ListPublicKey returns a list of all public keys that user has.
 | // ListPublicKey returns a list of all public keys that user has.
 | ||||||
| func ListPublicKey(uid int64) ([]PublicKey, error) { | func ListPublicKey(uid int64) ([]*PublicKey, error) { | ||||||
| 	keys := make([]PublicKey, 0, 5) | 	keys := make([]*PublicKey, 0, 5) | ||||||
| 	err := x.Find(&keys, &PublicKey{OwnerId: uid}) | 	err := x.Find(&keys, &PublicKey{OwnerId: uid}) | ||||||
| 	return keys, err | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, key := range keys { | ||||||
|  | 		key.HasUsed = key.Updated.After(key.Created) | ||||||
|  | 		key.HasRecentActivity = key.Updated.Add(7 * 24 * time.Hour).After(time.Now()) | ||||||
|  | 	} | ||||||
|  | 	return keys, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // rewriteAuthorizedKeys finds and deletes corresponding line in authorized_keys file.
 | // rewriteAuthorizedKeys finds and deletes corresponding line in authorized_keys file.
 | ||||||
|  | @ -218,8 +282,6 @@ func DeletePublicKey(key *PublicKey) error { | ||||||
| 
 | 
 | ||||||
| 	fpath := filepath.Join(SshPath, "authorized_keys") | 	fpath := filepath.Join(SshPath, "authorized_keys") | ||||||
| 	tmpPath := filepath.Join(SshPath, "authorized_keys.tmp") | 	tmpPath := filepath.Join(SshPath, "authorized_keys.tmp") | ||||||
| 	log.Trace("publickey.DeletePublicKey(authorized_keys): %s", fpath) |  | ||||||
| 
 |  | ||||||
| 	if err = rewriteAuthorizedKeys(key, fpath, tmpPath); err != nil { | 	if err = rewriteAuthorizedKeys(key, fpath, tmpPath); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} else if err = os.Remove(fpath); err != nil { | 	} else if err = os.Remove(fpath); err != nil { | ||||||
|  |  | ||||||
|  | @ -10,7 +10,7 @@ import ( | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"github.com/gogits/git" | 	"github.com/gogits/gogs/modules/git" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
|  |  | ||||||
							
								
								
									
										155
									
								
								models/repo.go
									
									
									
									
									
								
							
							
						
						|  | @ -14,7 +14,6 @@ import ( | ||||||
| 	"path" | 	"path" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| 	"regexp" | 	"regexp" | ||||||
| 	"runtime" |  | ||||||
| 	"sort" | 	"sort" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
|  | @ -23,10 +22,7 @@ import ( | ||||||
| 	"github.com/Unknwon/cae/zip" | 	"github.com/Unknwon/cae/zip" | ||||||
| 	"github.com/Unknwon/com" | 	"github.com/Unknwon/com" | ||||||
| 
 | 
 | ||||||
| 	"github.com/gogits/git" | 	"github.com/gogits/gogs/modules/git" | ||||||
| 
 |  | ||||||
| 	"github.com/gogits/gogs/modules/base" |  | ||||||
| 	"github.com/gogits/gogs/modules/bin" |  | ||||||
| 	"github.com/gogits/gogs/modules/log" | 	"github.com/gogits/gogs/modules/log" | ||||||
| 	"github.com/gogits/gogs/modules/process" | 	"github.com/gogits/gogs/modules/process" | ||||||
| 	"github.com/gogits/gogs/modules/setting" | 	"github.com/gogits/gogs/modules/setting" | ||||||
|  | @ -47,35 +43,27 @@ var ( | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
| 	LanguageIgns, Licenses []string | 	Gitignores, Licenses []string | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
| 	DescriptionPattern = regexp.MustCompile(`https?://\S+`) | 	DescriptionPattern = regexp.MustCompile(`https?://\S+`) | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // getAssetList returns corresponding asset list in 'conf'.
 |  | ||||||
| func getAssetList(prefix string) []string { |  | ||||||
| 	assets := make([]string, 0, 15) |  | ||||||
| 	for _, name := range bin.AssetNames() { |  | ||||||
| 		if strings.HasPrefix(name, prefix) { |  | ||||||
| 			assets = append(assets, strings.TrimPrefix(name, prefix+"/")) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return assets |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func LoadRepoConfig() { | func LoadRepoConfig() { | ||||||
| 	// Load .gitignore and license files.
 | 	// Load .gitignore and license files.
 | ||||||
| 	types := []string{"gitignore", "license"} | 	types := []string{"gitignore", "license"} | ||||||
| 	typeFiles := make([][]string, 2) | 	typeFiles := make([][]string, 2) | ||||||
| 	for i, t := range types { | 	for i, t := range types { | ||||||
| 		files := getAssetList(path.Join("conf", t)) | 		files, err := com.StatDir(path.Join("conf", t)) | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.Fatal(4, "Fail to get %s files: %v", t, err) | ||||||
|  | 		} | ||||||
| 		customPath := path.Join(setting.CustomPath, "conf", t) | 		customPath := path.Join(setting.CustomPath, "conf", t) | ||||||
| 		if com.IsDir(customPath) { | 		if com.IsDir(customPath) { | ||||||
| 			customFiles, err := com.StatDir(customPath) | 			customFiles, err := com.StatDir(customPath) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				log.Fatal("Fail to get custom %s files: %v", t, err) | 				log.Fatal(4, "Fail to get custom %s files: %v", t, err) | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			for _, f := range customFiles { | 			for _, f := range customFiles { | ||||||
|  | @ -87,34 +75,33 @@ func LoadRepoConfig() { | ||||||
| 		typeFiles[i] = files | 		typeFiles[i] = files | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	LanguageIgns = typeFiles[0] | 	Gitignores = typeFiles[0] | ||||||
| 	Licenses = typeFiles[1] | 	Licenses = typeFiles[1] | ||||||
| 	sort.Strings(LanguageIgns) | 	sort.Strings(Gitignores) | ||||||
| 	sort.Strings(Licenses) | 	sort.Strings(Licenses) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func NewRepoContext() { | func NewRepoContext() { | ||||||
| 	zip.Verbose = false | 	zip.Verbose = false | ||||||
| 
 | 
 | ||||||
|  | 	// Check Git version.
 | ||||||
|  | 	ver, err := git.GetVersion() | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Fatal(4, "Fail to get Git version: %v", err) | ||||||
|  | 	} | ||||||
|  | 	if ver.Major < 2 && ver.Minor < 8 { | ||||||
|  | 		log.Fatal(4, "Gogs requires Git version greater or equal to 1.8.0") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	// Check if server has basic git setting.
 | 	// Check if server has basic git setting.
 | ||||||
| 	stdout, stderr, err := process.Exec("NewRepoContext(get setting)", "git", "config", "--get", "user.name") | 	stdout, stderr, err := process.Exec("NewRepoContext(get setting)", "git", "config", "--get", "user.name") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatal("repo.NewRepoContext(fail to get git user.name): %s", stderr) | 		log.Fatal(4, "Fail to get git user.name: %s", stderr) | ||||||
| 	} else if err != nil || len(strings.TrimSpace(stdout)) == 0 { | 	} else if err != nil || len(strings.TrimSpace(stdout)) == 0 { | ||||||
| 		if _, stderr, err = process.Exec("NewRepoContext(set email)", "git", "config", "--global", "user.email", "gogitservice@gmail.com"); err != nil { | 		if _, stderr, err = process.Exec("NewRepoContext(set email)", "git", "config", "--global", "user.email", "gogitservice@gmail.com"); err != nil { | ||||||
| 			log.Fatal("repo.NewRepoContext(fail to set git user.email): %s", stderr) | 			log.Fatal(4, "Fail to set git user.email: %s", stderr) | ||||||
| 		} else if _, stderr, err = process.Exec("NewRepoContext(set name)", "git", "config", "--global", "user.name", "Gogs"); err != nil { | 		} else if _, stderr, err = process.Exec("NewRepoContext(set name)", "git", "config", "--global", "user.name", "Gogs"); err != nil { | ||||||
| 			log.Fatal("repo.NewRepoContext(fail to set git user.name): %s", stderr) | 			log.Fatal(4, "Fail to set git user.name: %s", stderr) | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	barePath := path.Join(setting.RepoRootPath, "git-bare.zip") |  | ||||||
| 	if !com.IsExist(barePath) { |  | ||||||
| 		data, err := bin.Asset("conf/content/git-bare.zip") |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Fatal("Fail to get asset 'git-bare.zip': %v", err) |  | ||||||
| 		} else if err := ioutil.WriteFile(barePath, data, os.ModePerm); err != nil { |  | ||||||
| 			log.Fatal("Fail to write asset 'git-bare.zip': %v", err) |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | @ -135,12 +122,16 @@ type Repository struct { | ||||||
| 	NumIssues           int | 	NumIssues           int | ||||||
| 	NumClosedIssues     int | 	NumClosedIssues     int | ||||||
| 	NumOpenIssues       int `xorm:"-"` | 	NumOpenIssues       int `xorm:"-"` | ||||||
|  | 	NumPulls            int | ||||||
|  | 	NumClosedPulls      int | ||||||
|  | 	NumOpenPulls        int `xorm:"-"` | ||||||
| 	NumMilestones       int `xorm:"NOT NULL DEFAULT 0"` | 	NumMilestones       int `xorm:"NOT NULL DEFAULT 0"` | ||||||
| 	NumClosedMilestones int `xorm:"NOT NULL DEFAULT 0"` | 	NumClosedMilestones int `xorm:"NOT NULL DEFAULT 0"` | ||||||
| 	NumOpenMilestones   int `xorm:"-"` | 	NumOpenMilestones   int `xorm:"-"` | ||||||
| 	NumTags             int `xorm:"-"` | 	NumTags             int `xorm:"-"` | ||||||
| 	IsPrivate           bool | 	IsPrivate           bool | ||||||
| 	IsMirror            bool | 	IsMirror            bool | ||||||
|  | 	IsFork              bool `xorm:"NOT NULL DEFAULT false"` | ||||||
| 	IsBare              bool | 	IsBare              bool | ||||||
| 	IsGoget             bool | 	IsGoget             bool | ||||||
| 	DefaultBranch       string | 	DefaultBranch       string | ||||||
|  | @ -153,11 +144,11 @@ func (repo *Repository) GetOwner() (err error) { | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // DescriptionHtml does special handles to description and return HTML string.
 | ||||||
| func (repo *Repository) DescriptionHtml() template.HTML { | func (repo *Repository) DescriptionHtml() template.HTML { | ||||||
| 	sanitize := func(s string) string { | 	sanitize := func(s string) string { | ||||||
| 		// TODO(nuss-justin): Improve sanitization. Strip all tags?
 | 		// TODO(nuss-justin): Improve sanitization. Strip all tags?
 | ||||||
| 		ss := html.EscapeString(s) | 		ss := html.EscapeString(s) | ||||||
| 
 |  | ||||||
| 		return fmt.Sprintf(`<a href="%s" target="_blank">%s</a>`, ss, ss) | 		return fmt.Sprintf(`<a href="%s" target="_blank">%s</a>`, ss, ss) | ||||||
| 	} | 	} | ||||||
| 	return template.HTML(DescriptionPattern.ReplaceAllStringFunc(repo.Description, sanitize)) | 	return template.HTML(DescriptionPattern.ReplaceAllStringFunc(repo.Description, sanitize)) | ||||||
|  | @ -225,7 +216,8 @@ func MirrorRepository(repoId int64, userName, repoName, repoPath, url string) er | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return git.UnpackRefs(repoPath) | 	// return git.UnpackRefs(repoPath)
 | ||||||
|  | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func GetMirror(repoId int64) (*Mirror, error) { | func GetMirror(repoId int64) (*Mirror, error) { | ||||||
|  | @ -257,14 +249,14 @@ func MirrorUpdate() { | ||||||
| 			repoPath, fmt.Sprintf("MirrorUpdate: %s", repoPath), | 			repoPath, fmt.Sprintf("MirrorUpdate: %s", repoPath), | ||||||
| 			"git", "remote", "update"); err != nil { | 			"git", "remote", "update"); err != nil { | ||||||
| 			return errors.New("git remote update: " + stderr) | 			return errors.New("git remote update: " + stderr) | ||||||
| 		} else if err = git.UnpackRefs(repoPath); err != nil { | 		} // else if err = git.UnpackRefs(repoPath); err != nil {
 | ||||||
| 			return errors.New("UnpackRefs: " + err.Error()) | 		// 	return errors.New("UnpackRefs: " + err.Error())
 | ||||||
| 		} | 		// }
 | ||||||
| 
 | 
 | ||||||
| 		m.NextUpdate = time.Now().Add(time.Duration(m.Interval) * time.Hour) | 		m.NextUpdate = time.Now().Add(time.Duration(m.Interval) * time.Hour) | ||||||
| 		return UpdateMirror(m) | 		return UpdateMirror(m) | ||||||
| 	}); err != nil { | 	}); err != nil { | ||||||
| 		log.Error("repo.MirrorUpdate: %v", err) | 		log.Error(4, "repo.MirrorUpdate: %v", err) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -317,7 +309,7 @@ func MigrateRepository(u *User, name, desc string, private, mirror bool, url str | ||||||
| 
 | 
 | ||||||
| // extractGitBareZip extracts git-bare.zip to repository path.
 | // extractGitBareZip extracts git-bare.zip to repository path.
 | ||||||
| func extractGitBareZip(repoPath string) error { | func extractGitBareZip(repoPath string) error { | ||||||
| 	z, err := zip.Open(filepath.Join(setting.RepoRootPath, "git-bare.zip")) | 	z, err := zip.Open(path.Join(setting.ConfRootPath, "content/git-bare.zip")) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  | @ -361,34 +353,18 @@ func createHookUpdate(hookPath, content string) error { | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // SetRepoEnvs sets environment variables for command update.
 |  | ||||||
| func SetRepoEnvs(userId int64, userName, repoName, repoUserName string) { |  | ||||||
| 	os.Setenv("userId", base.ToStr(userId)) |  | ||||||
| 	os.Setenv("userName", userName) |  | ||||||
| 	os.Setenv("repoName", repoName) |  | ||||||
| 	os.Setenv("repoUserName", repoUserName) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // InitRepository initializes README and .gitignore if needed.
 | // InitRepository initializes README and .gitignore if needed.
 | ||||||
| func initRepository(f string, user *User, repo *Repository, initReadme bool, repoLang, license string) error { | func initRepository(f string, u *User, repo *Repository, initReadme bool, repoLang, license string) error { | ||||||
| 	repoPath := RepoPath(user.Name, repo.Name) | 	repoPath := RepoPath(u.Name, repo.Name) | ||||||
| 
 | 
 | ||||||
| 	// Create bare new repository.
 | 	// Create bare new repository.
 | ||||||
| 	if err := extractGitBareZip(repoPath); err != nil { | 	if err := extractGitBareZip(repoPath); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if runtime.GOOS == "windows" { |  | ||||||
| 		rp := strings.NewReplacer("\\", "/") |  | ||||||
| 		appPath = "\"" + rp.Replace(appPath) + "\"" |  | ||||||
| 	} else { |  | ||||||
| 		rp := strings.NewReplacer("\\", "/", " ", "\\ ") |  | ||||||
| 		appPath = rp.Replace(appPath) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// hook/post-update
 | 	// hook/post-update
 | ||||||
| 	if err := createHookUpdate(filepath.Join(repoPath, "hooks", "update"), | 	if err := createHookUpdate(filepath.Join(repoPath, "hooks", "update"), | ||||||
| 		fmt.Sprintf(TPL_UPDATE_HOOK, setting.ScriptType, appPath)); err != nil { | 		fmt.Sprintf(TPL_UPDATE_HOOK, setting.ScriptType, "\""+appPath+"\"")); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -405,7 +381,7 @@ func initRepository(f string, user *User, repo *Repository, initReadme bool, rep | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Clone to temprory path and do the init commit.
 | 	// Clone to temprory path and do the init commit.
 | ||||||
| 	tmpDir := filepath.Join(os.TempDir(), base.ToStr(time.Now().Nanosecond())) | 	tmpDir := filepath.Join(os.TempDir(), com.ToStr(time.Now().Nanosecond())) | ||||||
| 	os.MkdirAll(tmpDir, os.ModePerm) | 	os.MkdirAll(tmpDir, os.ModePerm) | ||||||
| 
 | 
 | ||||||
| 	_, stderr, err := process.Exec( | 	_, stderr, err := process.Exec( | ||||||
|  | @ -426,12 +402,11 @@ func initRepository(f string, user *User, repo *Repository, initReadme bool, rep | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// .gitignore
 | 	// .gitignore
 | ||||||
| 	if repoLang != "" { | 	filePath := "conf/gitignore/" + repoLang | ||||||
| 		filePath := "conf/gitignore/" + repoLang | 	if com.IsFile(filePath) { | ||||||
| 		targetPath := path.Join(tmpDir, fileName["gitign"]) | 		targetPath := path.Join(tmpDir, fileName["gitign"]) | ||||||
| 		data, err := bin.Asset(filePath) | 		if com.IsFile(filePath) { | ||||||
| 		if err == nil { | 			if err = com.Copy(filePath, targetPath); err != nil { | ||||||
| 			if err = ioutil.WriteFile(targetPath, data, os.ModePerm); err != nil { |  | ||||||
| 				return err | 				return err | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
|  | @ -443,15 +418,16 @@ func initRepository(f string, user *User, repo *Repository, initReadme bool, rep | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 	} else { | ||||||
|  | 		delete(fileName, "gitign") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// LICENSE
 | 	// LICENSE
 | ||||||
| 	if license != "" { | 	filePath = "conf/license/" + license | ||||||
| 		filePath := "conf/license/" + license | 	if com.IsFile(filePath) { | ||||||
| 		targetPath := path.Join(tmpDir, fileName["license"]) | 		targetPath := path.Join(tmpDir, fileName["license"]) | ||||||
| 		data, err := bin.Asset(filePath) | 		if com.IsFile(filePath) { | ||||||
| 		if err == nil { | 			if err = com.Copy(filePath, targetPath); err != nil { | ||||||
| 			if err = ioutil.WriteFile(targetPath, data, os.ModePerm); err != nil { |  | ||||||
| 				return err | 				return err | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
|  | @ -463,16 +439,16 @@ func initRepository(f string, user *User, repo *Repository, initReadme bool, rep | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 	} else { | ||||||
|  | 		delete(fileName, "license") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if len(fileName) == 0 { | 	if len(fileName) == 0 { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	SetRepoEnvs(user.Id, user.Name, repo.Name, user.Name) |  | ||||||
| 
 |  | ||||||
| 	// Apply changes and commit.
 | 	// Apply changes and commit.
 | ||||||
| 	return initRepoCommit(tmpDir, user.NewGitSig()) | 	return initRepoCommit(tmpDir, u.NewGitSig()) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // CreateRepository creates a repository for given user or organization.
 | // CreateRepository creates a repository for given user or organization.
 | ||||||
|  | @ -549,15 +525,15 @@ func CreateRepository(u *User, name, desc, lang, license string, private, mirror | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	rawSql := "UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?" | 	if _, err = sess.Exec( | ||||||
| 	if _, err = sess.Exec(rawSql, u.Id); err != nil { | 		"UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?", u.Id); err != nil { | ||||||
| 		sess.Rollback() | 		sess.Rollback() | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Update owner team info and count.
 | 	// Update owner team info and count.
 | ||||||
| 	if u.IsOrganization() { | 	if u.IsOrganization() { | ||||||
| 		t.RepoIds += "$" + base.ToStr(repo.Id) + "|" | 		t.RepoIds += "$" + com.ToStr(repo.Id) + "|" | ||||||
| 		t.NumRepos++ | 		t.NumRepos++ | ||||||
| 		if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil { | 		if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil { | ||||||
| 			sess.Rollback() | 			sess.Rollback() | ||||||
|  | @ -572,24 +548,24 @@ func CreateRepository(u *User, name, desc, lang, license string, private, mirror | ||||||
| 	if u.IsOrganization() { | 	if u.IsOrganization() { | ||||||
| 		ous, err := GetOrgUsersByOrgId(u.Id) | 		ous, err := GetOrgUsersByOrgId(u.Id) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Error("repo.CreateRepository(GetOrgUsersByOrgId): %v", err) | 			log.Error(4, "repo.CreateRepository(GetOrgUsersByOrgId): %v", err) | ||||||
| 		} else { | 		} else { | ||||||
| 			for _, ou := range ous { | 			for _, ou := range ous { | ||||||
| 				if err = WatchRepo(ou.Uid, repo.Id, true); err != nil { | 				if err = WatchRepo(ou.Uid, repo.Id, true); err != nil { | ||||||
| 					log.Error("repo.CreateRepository(WatchRepo): %v", err) | 					log.Error(4, "repo.CreateRepository(WatchRepo): %v", err) | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if err = WatchRepo(u.Id, repo.Id, true); err != nil { | 	if err = WatchRepo(u.Id, repo.Id, true); err != nil { | ||||||
| 		log.Error("repo.CreateRepository(WatchRepo2): %v", err) | 		log.Error(4, "WatchRepo2: %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err = NewRepoAction(u, repo); err != nil { | 	if err = NewRepoAction(u, repo); err != nil { | ||||||
| 		log.Error("repo.CreateRepository(NewRepoAction): %v", err) | 		log.Error(4, "NewRepoAction: %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// No need for init for mirror.
 | 	// No need for init mirror.
 | ||||||
| 	if mirror { | 	if mirror { | ||||||
| 		return repo, nil | 		return repo, nil | ||||||
| 	} | 	} | ||||||
|  | @ -597,11 +573,11 @@ func CreateRepository(u *User, name, desc, lang, license string, private, mirror | ||||||
| 	repoPath := RepoPath(u.Name, repo.Name) | 	repoPath := RepoPath(u.Name, repo.Name) | ||||||
| 	if err = initRepository(repoPath, u, repo, initReadme, lang, license); err != nil { | 	if err = initRepository(repoPath, u, repo, initReadme, lang, license); err != nil { | ||||||
| 		if err2 := os.RemoveAll(repoPath); err2 != nil { | 		if err2 := os.RemoveAll(repoPath); err2 != nil { | ||||||
| 			log.Error("repo.CreateRepository(initRepository): %v", err) | 			log.Error(4, "initRepository: %v", err) | ||||||
| 			return nil, errors.New(fmt.Sprintf( | 			return nil, fmt.Errorf( | ||||||
| 				"delete repo directory %s/%s failed(2): %v", u.Name, repo.Name, err2)) | 				"delete repo directory %s/%s failed(2): %v", u.Name, repo.Name, err2) | ||||||
| 		} | 		} | ||||||
| 		return nil, err | 		return nil, fmt.Errorf("initRepository: %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	_, stderr, err := process.ExecDir(-1, | 	_, stderr, err := process.ExecDir(-1, | ||||||
|  | @ -982,15 +958,12 @@ func WatchRepo(uid, rid int64, watch bool) (err error) { | ||||||
| 		if _, err = x.Insert(&Watch{RepoId: rid, UserId: uid}); err != nil { | 		if _, err = x.Insert(&Watch{RepoId: rid, UserId: uid}); err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 
 | 		_, err = x.Exec("UPDATE `repository` SET num_watches = num_watches + 1 WHERE id = ?", rid) | ||||||
| 		rawSql := "UPDATE `repository` SET num_watches = num_watches + 1 WHERE id = ?" |  | ||||||
| 		_, err = x.Exec(rawSql, rid) |  | ||||||
| 	} else { | 	} else { | ||||||
| 		if _, err = x.Delete(&Watch{0, uid, rid}); err != nil { | 		if _, err = x.Delete(&Watch{0, uid, rid}); err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 		rawSql := "UPDATE `repository` SET num_watches = num_watches - 1 WHERE id = ?" | 		_, err = x.Exec("UPDATE `repository` SET num_watches = num_watches - 1 WHERE id = ?", rid) | ||||||
| 		_, err = x.Exec(rawSql, rid) |  | ||||||
| 	} | 	} | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -10,9 +10,8 @@ import ( | ||||||
| 	"os/exec" | 	"os/exec" | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"github.com/gogits/git" |  | ||||||
| 
 |  | ||||||
| 	"github.com/gogits/gogs/modules/base" | 	"github.com/gogits/gogs/modules/base" | ||||||
|  | 	"github.com/gogits/gogs/modules/git" | ||||||
| 	"github.com/gogits/gogs/modules/log" | 	"github.com/gogits/gogs/modules/log" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -47,8 +46,6 @@ func DelUpdateTasksByUuid(uuid string) error { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName string, userId int64) error { | func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName string, userId int64) error { | ||||||
| 	//fmt.Println(refName, oldCommitId, newCommitId)
 |  | ||||||
| 	//fmt.Println(userName, repoUserName, repoName)
 |  | ||||||
| 	isNew := strings.HasPrefix(oldCommitId, "0000000") | 	isNew := strings.HasPrefix(oldCommitId, "0000000") | ||||||
| 	if isNew && | 	if isNew && | ||||||
| 		strings.HasPrefix(newCommitId, "0000000") { | 		strings.HasPrefix(newCommitId, "0000000") { | ||||||
|  | @ -82,12 +79,12 @@ func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName | ||||||
| 		return fmt.Errorf("runUpdate.GetRepositoryByName userId: %v", err) | 		return fmt.Errorf("runUpdate.GetRepositoryByName userId: %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// if tags push
 | 	// Push tags.
 | ||||||
| 	if strings.HasPrefix(refName, "refs/tags/") { | 	if strings.HasPrefix(refName, "refs/tags/") { | ||||||
| 		tagName := git.RefEndName(refName) | 		tagName := git.RefEndName(refName) | ||||||
| 		tag, err := repo.GetTag(tagName) | 		tag, err := repo.GetTag(tagName) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.GitLogger.Fatal("runUpdate.GetTag: %v", err) | 			log.GitLogger.Fatal(4, "runUpdate.GetTag: %v", err) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		var actEmail string | 		var actEmail string | ||||||
|  | @ -96,7 +93,7 @@ func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName | ||||||
| 		} else { | 		} else { | ||||||
| 			cmt, err := tag.Commit() | 			cmt, err := tag.Commit() | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				log.GitLogger.Fatal("runUpdate.GetTag Commit: %v", err) | 				log.GitLogger.Fatal(4, "runUpdate.GetTag Commit: %v", err) | ||||||
| 			} | 			} | ||||||
| 			actEmail = cmt.Committer.Email | 			actEmail = cmt.Committer.Email | ||||||
| 		} | 		} | ||||||
|  | @ -105,7 +102,7 @@ func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName | ||||||
| 
 | 
 | ||||||
| 		if err = CommitRepoAction(userId, ru.Id, userName, actEmail, | 		if err = CommitRepoAction(userId, ru.Id, userName, actEmail, | ||||||
| 			repos.Id, repoUserName, repoName, refName, commit); err != nil { | 			repos.Id, repoUserName, repoName, refName, commit); err != nil { | ||||||
| 			log.GitLogger.Fatal("runUpdate.models.CommitRepoAction: %s/%s:%v", repoUserName, repoName, err) | 			log.GitLogger.Fatal(4, "runUpdate.models.CommitRepoAction: %s/%s:%v", repoUserName, repoName, err) | ||||||
| 		} | 		} | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  | @ -135,7 +132,7 @@ func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName | ||||||
| 
 | 
 | ||||||
| 	// if commits push
 | 	// if commits push
 | ||||||
| 	commits := make([]*base.PushCommit, 0) | 	commits := make([]*base.PushCommit, 0) | ||||||
| 	var maxCommits = 3 | 	var maxCommits = 2 | ||||||
| 	var actEmail string | 	var actEmail string | ||||||
| 	for e := l.Front(); e != nil; e = e.Next() { | 	for e := l.Front(); e != nil; e = e.Next() { | ||||||
| 		commit := e.Value.(*git.Commit) | 		commit := e.Value.(*git.Commit) | ||||||
|  |  | ||||||
							
								
								
									
										113
									
								
								models/user.go
									
									
									
									
									
								
							
							
						
						|  | @ -14,9 +14,10 @@ import ( | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"github.com/gogits/git" | 	"github.com/Unknwon/com" | ||||||
| 
 | 
 | ||||||
| 	"github.com/gogits/gogs/modules/base" | 	"github.com/gogits/gogs/modules/base" | ||||||
|  | 	"github.com/gogits/gogs/modules/git" | ||||||
| 	"github.com/gogits/gogs/modules/log" | 	"github.com/gogits/gogs/modules/log" | ||||||
| 	"github.com/gogits/gogs/modules/setting" | 	"github.com/gogits/gogs/modules/setting" | ||||||
| ) | ) | ||||||
|  | @ -77,6 +78,14 @@ type User struct { | ||||||
| 	Members     []*User `xorm:"-"` | 	Members     []*User `xorm:"-"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // DashboardLink returns the user dashboard page link.
 | ||||||
|  | func (u *User) DashboardLink() string { | ||||||
|  | 	if u.IsOrganization() { | ||||||
|  | 		return "/org/" + u.Name + "/dashboard" | ||||||
|  | 	} | ||||||
|  | 	return "/" | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // HomeLink returns the user home page link.
 | // HomeLink returns the user home page link.
 | ||||||
| func (u *User) HomeLink() string { | func (u *User) HomeLink() string { | ||||||
| 	return "/user/" + u.Name | 	return "/user/" + u.Name | ||||||
|  | @ -157,23 +166,23 @@ func GetUserSalt() string { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // CreateUser creates record of a new user.
 | // CreateUser creates record of a new user.
 | ||||||
| func CreateUser(u *User) (*User, error) { | func CreateUser(u *User) error { | ||||||
| 	if !IsLegalName(u.Name) { | 	if !IsLegalName(u.Name) { | ||||||
| 		return nil, ErrUserNameIllegal | 		return ErrUserNameIllegal | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	isExist, err := IsUserExist(u.Name) | 	isExist, err := IsUserExist(u.Name) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return err | ||||||
| 	} else if isExist { | 	} else if isExist { | ||||||
| 		return nil, ErrUserAlreadyExist | 		return ErrUserAlreadyExist | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	isExist, err = IsEmailUsed(u.Email) | 	isExist, err = IsEmailUsed(u.Email) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return err | ||||||
| 	} else if isExist { | 	} else if isExist { | ||||||
| 		return nil, ErrEmailAlreadyUsed | 		return ErrEmailAlreadyUsed | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	u.LowerName = strings.ToLower(u.Name) | 	u.LowerName = strings.ToLower(u.Name) | ||||||
|  | @ -186,21 +195,17 @@ func CreateUser(u *User) (*User, error) { | ||||||
| 	sess := x.NewSession() | 	sess := x.NewSession() | ||||||
| 	defer sess.Close() | 	defer sess.Close() | ||||||
| 	if err = sess.Begin(); err != nil { | 	if err = sess.Begin(); err != nil { | ||||||
| 		return nil, err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if _, err = sess.Insert(u); err != nil { | 	if _, err = sess.Insert(u); err != nil { | ||||||
| 		sess.Rollback() | 		sess.Rollback() | ||||||
| 		return nil, err | 		return err | ||||||
| 	} | 	} else if err = os.MkdirAll(UserPath(u.Name), os.ModePerm); err != nil { | ||||||
| 
 |  | ||||||
| 	if err = os.MkdirAll(UserPath(u.Name), os.ModePerm); err != nil { |  | ||||||
| 		sess.Rollback() | 		sess.Rollback() | ||||||
| 		return nil, err | 		return err | ||||||
| 	} | 	} else if err = sess.Commit(); err != nil { | ||||||
| 
 | 		return err | ||||||
| 	if err = sess.Commit(); err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Auto-set admin for user whose ID is 1.
 | 	// Auto-set admin for user whose ID is 1.
 | ||||||
|  | @ -209,7 +214,7 @@ func CreateUser(u *User) (*User, error) { | ||||||
| 		u.IsActive = true | 		u.IsActive = true | ||||||
| 		_, err = x.Id(u.Id).UseBool().Update(u) | 		_, err = x.Id(u.Id).UseBool().Update(u) | ||||||
| 	} | 	} | ||||||
| 	return u, err | 	return err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // CountUsers returns number of users.
 | // CountUsers returns number of users.
 | ||||||
|  | @ -237,7 +242,7 @@ func getVerifyUser(code string) (user *User) { | ||||||
| 		if user, err = GetUserByName(string(b)); user != nil { | 		if user, err = GetUserByName(string(b)); user != nil { | ||||||
| 			return user | 			return user | ||||||
| 		} | 		} | ||||||
| 		log.Error("user.getVerifyUser: %v", err) | 		log.Error(4, "user.getVerifyUser: %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return nil | 	return nil | ||||||
|  | @ -250,7 +255,7 @@ func VerifyUserActiveCode(code string) (user *User) { | ||||||
| 	if user = getVerifyUser(code); user != nil { | 	if user = getVerifyUser(code); user != nil { | ||||||
| 		// time limit code
 | 		// time limit code
 | ||||||
| 		prefix := code[:base.TimeLimitCodeLength] | 		prefix := code[:base.TimeLimitCodeLength] | ||||||
| 		data := base.ToStr(user.Id) + user.Email + user.LowerName + user.Passwd + user.Rands | 		data := com.ToStr(user.Id) + user.Email + user.LowerName + user.Passwd + user.Rands | ||||||
| 
 | 
 | ||||||
| 		if base.VerifyTimeLimitCode(data, minutes, prefix) { | 		if base.VerifyTimeLimitCode(data, minutes, prefix) { | ||||||
| 			return user | 			return user | ||||||
|  | @ -260,12 +265,16 @@ func VerifyUserActiveCode(code string) (user *User) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ChangeUserName changes all corresponding setting from old user name to new one.
 | // ChangeUserName changes all corresponding setting from old user name to new one.
 | ||||||
| func ChangeUserName(user *User, newUserName string) (err error) { | func ChangeUserName(u *User, newUserName string) (err error) { | ||||||
|  | 	if !IsLegalName(newUserName) { | ||||||
|  | 		return ErrUserNameIllegal | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	newUserName = strings.ToLower(newUserName) | 	newUserName = strings.ToLower(newUserName) | ||||||
| 
 | 
 | ||||||
| 	// Update accesses of user.
 | 	// Update accesses of user.
 | ||||||
| 	accesses := make([]Access, 0, 10) | 	accesses := make([]Access, 0, 10) | ||||||
| 	if err = x.Find(&accesses, &Access{UserName: user.LowerName}); err != nil { | 	if err = x.Find(&accesses, &Access{UserName: u.LowerName}); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -277,28 +286,28 @@ func ChangeUserName(user *User, newUserName string) (err error) { | ||||||
| 
 | 
 | ||||||
| 	for i := range accesses { | 	for i := range accesses { | ||||||
| 		accesses[i].UserName = newUserName | 		accesses[i].UserName = newUserName | ||||||
| 		if strings.HasPrefix(accesses[i].RepoName, user.LowerName+"/") { | 		if strings.HasPrefix(accesses[i].RepoName, u.LowerName+"/") { | ||||||
| 			accesses[i].RepoName = strings.Replace(accesses[i].RepoName, user.LowerName, newUserName, 1) | 			accesses[i].RepoName = strings.Replace(accesses[i].RepoName, u.LowerName, newUserName, 1) | ||||||
| 		} | 		} | ||||||
| 		if err = UpdateAccessWithSession(sess, &accesses[i]); err != nil { | 		if err = UpdateAccessWithSession(sess, &accesses[i]); err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	repos, err := GetRepositories(user.Id, true) | 	repos, err := GetRepositories(u.Id, true) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	for i := range repos { | 	for i := range repos { | ||||||
| 		accesses = make([]Access, 0, 10) | 		accesses = make([]Access, 0, 10) | ||||||
| 		// Update accesses of user repository.
 | 		// Update accesses of user repository.
 | ||||||
| 		if err = x.Find(&accesses, &Access{RepoName: user.LowerName + "/" + repos[i].LowerName}); err != nil { | 		if err = x.Find(&accesses, &Access{RepoName: u.LowerName + "/" + repos[i].LowerName}); err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		for j := range accesses { | 		for j := range accesses { | ||||||
| 			// if the access is not the user's access (already updated above)
 | 			// if the access is not the user's access (already updated above)
 | ||||||
| 			if accesses[j].UserName != user.LowerName { | 			if accesses[j].UserName != u.LowerName { | ||||||
| 				accesses[j].RepoName = newUserName + "/" + repos[i].LowerName | 				accesses[j].RepoName = newUserName + "/" + repos[i].LowerName | ||||||
| 				if err = UpdateAccessWithSession(sess, &accesses[j]); err != nil { | 				if err = UpdateAccessWithSession(sess, &accesses[j]); err != nil { | ||||||
| 					return err | 					return err | ||||||
|  | @ -308,7 +317,7 @@ func ChangeUserName(user *User, newUserName string) (err error) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Change user directory name.
 | 	// Change user directory name.
 | ||||||
| 	if err = os.Rename(UserPath(user.LowerName), UserPath(newUserName)); err != nil { | 	if err = os.Rename(UserPath(u.LowerName), UserPath(newUserName)); err != nil { | ||||||
| 		sess.Rollback() | 		sess.Rollback() | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  | @ -317,7 +326,7 @@ func ChangeUserName(user *User, newUserName string) (err error) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // UpdateUser updates user's information.
 | // UpdateUser updates user's information.
 | ||||||
| func UpdateUser(u *User) (err error) { | func UpdateUser(u *User) error { | ||||||
| 	u.LowerName = strings.ToLower(u.Name) | 	u.LowerName = strings.ToLower(u.Name) | ||||||
| 
 | 
 | ||||||
| 	if len(u.Location) > 255 { | 	if len(u.Location) > 255 { | ||||||
|  | @ -330,7 +339,7 @@ func UpdateUser(u *User) (err error) { | ||||||
| 		u.Description = u.Description[:255] | 		u.Description = u.Description[:255] | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	_, err = x.Id(u.Id).AllCols().Update(u) | 	_, err := x.Id(u.Id).AllCols().Update(u) | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -340,7 +349,7 @@ func DeleteUser(u *User) error { | ||||||
| 	// Check ownership of repository.
 | 	// Check ownership of repository.
 | ||||||
| 	count, err := GetRepositoryCount(u) | 	count, err := GetRepositoryCount(u) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return errors.New("modesl.GetRepositories(GetRepositoryCount): " + err.Error()) | 		return errors.New("GetRepositoryCount: " + err.Error()) | ||||||
| 	} else if count > 0 { | 	} else if count > 0 { | ||||||
| 		return ErrUserOwnRepos | 		return ErrUserOwnRepos | ||||||
| 	} | 	} | ||||||
|  | @ -562,3 +571,45 @@ func UnFollowUser(userId int64, unFollowId int64) (err error) { | ||||||
| 	} | 	} | ||||||
| 	return session.Commit() | 	return session.Commit() | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func UpdateMentions(userNames []string, issueId int64) error { | ||||||
|  | 	users := make([]*User, 0, len(userNames)) | ||||||
|  | 
 | ||||||
|  | 	if err := x.Where("name IN (?)", strings.Join(userNames, "\",\"")).OrderBy("name ASC").Find(&users); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	ids := make([]int64, 0, len(userNames)) | ||||||
|  | 
 | ||||||
|  | 	for _, user := range users { | ||||||
|  | 		ids = append(ids, user.Id) | ||||||
|  | 
 | ||||||
|  | 		if user.Type == INDIVIDUAL { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if user.NumMembers == 0 { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		tempIds := make([]int64, 0, user.NumMembers) | ||||||
|  | 
 | ||||||
|  | 		orgUsers, err := GetOrgUsersByOrgId(user.Id) | ||||||
|  | 
 | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		for _, orgUser := range orgUsers { | ||||||
|  | 			tempIds = append(tempIds, orgUser.Id) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		ids = append(ids, tempIds...) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err := UpdateIssueUserPairsByMentions(ids, issueId); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -47,7 +47,7 @@ type Webhook struct { | ||||||
| func (w *Webhook) GetEvent() { | func (w *Webhook) GetEvent() { | ||||||
| 	w.HookEvent = &HookEvent{} | 	w.HookEvent = &HookEvent{} | ||||||
| 	if err := json.Unmarshal([]byte(w.Events), w.HookEvent); err != nil { | 	if err := json.Unmarshal([]byte(w.Events), w.HookEvent); err != nil { | ||||||
| 		log.Error("webhook.GetEvent(%d): %v", w.Id, err) | 		log.Error(4, "webhook.GetEvent(%d): %v", w.Id, err) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -193,13 +193,13 @@ func DeliverHooks() { | ||||||
| 			// Only support JSON now.
 | 			// Only support JSON now.
 | ||||||
| 			if _, err := httplib.Post(t.Url).SetTimeout(timeout, timeout). | 			if _, err := httplib.Post(t.Url).SetTimeout(timeout, timeout). | ||||||
| 				Body([]byte(t.PayloadContent)).Response(); err != nil { | 				Body([]byte(t.PayloadContent)).Response(); err != nil { | ||||||
| 				log.Error("webhook.DeliverHooks(Delivery): %v", err) | 				log.Error(4, "webhook.DeliverHooks(Delivery): %v", err) | ||||||
| 				return nil | 				return nil | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			t.IsDeliveried = true | 			t.IsDeliveried = true | ||||||
| 			if err := UpdateHookTask(t); err != nil { | 			if err := UpdateHookTask(t); err != nil { | ||||||
| 				log.Error("webhook.DeliverHooks(UpdateHookTask): %v", err) | 				log.Error(4, "webhook.DeliverHooks(UpdateHookTask): %v", err) | ||||||
| 				return nil | 				return nil | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -5,12 +5,9 @@ | ||||||
| package auth | package auth | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"net/http" | 	"github.com/Unknwon/macaron" | ||||||
| 	"reflect" | 	"github.com/macaron-contrib/i18n" | ||||||
| 
 | 
 | ||||||
| 	"github.com/go-martini/martini" |  | ||||||
| 
 |  | ||||||
| 	"github.com/gogits/gogs/modules/base" |  | ||||||
| 	"github.com/gogits/gogs/modules/middleware/binding" | 	"github.com/gogits/gogs/modules/middleware/binding" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -25,17 +22,6 @@ type AdminEditUserForm struct { | ||||||
| 	LoginType int    `form:"login_type"` | 	LoginType int    `form:"login_type"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (f *AdminEditUserForm) Name(field string) string { | func (f *AdminEditUserForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | ||||||
| 	names := map[string]string{ | 	validate(errs, ctx.Data, f, l) | ||||||
| 		"Email":    "E-mail address", |  | ||||||
| 		"Website":  "Website", |  | ||||||
| 		"Location": "Location", |  | ||||||
| 		"Avatar":   "Gravatar Email", |  | ||||||
| 	} |  | ||||||
| 	return names[field] |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *AdminEditUserForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { |  | ||||||
| 	data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) |  | ||||||
| 	validate(errors, data, f) |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -5,13 +5,12 @@ | ||||||
| package apiv1 | package apiv1 | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"net/http" |  | ||||||
| 	"reflect" | 	"reflect" | ||||||
| 
 | 
 | ||||||
| 	"github.com/go-martini/martini" | 	"github.com/Unknwon/macaron" | ||||||
|  | 	"github.com/macaron-contrib/i18n" | ||||||
| 
 | 
 | ||||||
| 	"github.com/gogits/gogs/modules/auth" | 	"github.com/gogits/gogs/modules/auth" | ||||||
| 	"github.com/gogits/gogs/modules/base" |  | ||||||
| 	"github.com/gogits/gogs/modules/log" | 	"github.com/gogits/gogs/modules/log" | ||||||
| 	"github.com/gogits/gogs/modules/middleware/binding" | 	"github.com/gogits/gogs/modules/middleware/binding" | ||||||
| ) | ) | ||||||
|  | @ -22,17 +21,16 @@ type MarkdownForm struct { | ||||||
| 	Context string `form:"context"` | 	Context string `form:"context"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (f *MarkdownForm) Validate(errs *binding.Errors, req *http.Request, ctx martini.Context) { | func (f *MarkdownForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | ||||||
| 	data := ctx.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) | 	validateApiReq(errs, ctx.Data, f, l) | ||||||
| 	validateApiReq(errs, data, f) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func validateApiReq(errs *binding.Errors, data base.TmplData, f interface{}) { | func validateApiReq(errs *binding.Errors, data map[string]interface{}, f interface{}, l i18n.Locale) { | ||||||
| 	if errs.Count() == 0 { | 	if errs.Count() == 0 { | ||||||
| 		return | 		return | ||||||
| 	} else if len(errs.Overall) > 0 { | 	} else if len(errs.Overall) > 0 { | ||||||
| 		for _, err := range errs.Overall { | 		for _, err := range errs.Overall { | ||||||
| 			log.Error("%s: %v", reflect.TypeOf(f), err) | 			log.Error(4, "%s: %v", reflect.TypeOf(f), err) | ||||||
| 		} | 		} | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -7,49 +7,152 @@ package auth | ||||||
| import ( | import ( | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"reflect" | 	"reflect" | ||||||
|  | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"github.com/go-martini/martini" | 	"github.com/macaron-contrib/i18n" | ||||||
|  | 	"github.com/macaron-contrib/session" | ||||||
| 
 | 
 | ||||||
| 	"github.com/gogits/gogs/modules/base" | 	"github.com/gogits/gogs/models" | ||||||
|  | 	"github.com/gogits/gogs/modules/log" | ||||||
| 	"github.com/gogits/gogs/modules/middleware/binding" | 	"github.com/gogits/gogs/modules/middleware/binding" | ||||||
|  | 	"github.com/gogits/gogs/modules/setting" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type AuthenticationForm struct { | // SignedInId returns the id of signed in user.
 | ||||||
| 	Id                int64  `form:"id"` | func SignedInId(header http.Header, sess session.Store) int64 { | ||||||
| 	Type              int    `form:"type"` | 	if !models.HasEngine { | ||||||
| 	AuthName          string `form:"name" binding:"Required;MaxSize(50)"` | 		return 0 | ||||||
| 	Domain            string `form:"domain"` |  | ||||||
| 	Host              string `form:"host"` |  | ||||||
| 	Port              int    `form:"port"` |  | ||||||
| 	UseSSL            bool   `form:"usessl"` |  | ||||||
| 	BaseDN            string `form:"base_dn"` |  | ||||||
| 	Attributes        string `form:"attributes"` |  | ||||||
| 	Filter            string `form:"filter"` |  | ||||||
| 	MsAdSA            string `form:"ms_ad_sa"` |  | ||||||
| 	IsActived         bool   `form:"is_actived"` |  | ||||||
| 	SmtpAuth          string `form:"smtpauth"` |  | ||||||
| 	SmtpHost          string `form:"smtphost"` |  | ||||||
| 	SmtpPort          int    `form:"smtpport"` |  | ||||||
| 	Tls               bool   `form:"tls"` |  | ||||||
| 	AllowAutoRegister bool   `form:"allowautoregister"` |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *AuthenticationForm) Name(field string) string { |  | ||||||
| 	names := map[string]string{ |  | ||||||
| 		"AuthName":   "Authentication's name", |  | ||||||
| 		"Domain":     "Domain name", |  | ||||||
| 		"Host":       "Host address", |  | ||||||
| 		"Port":       "Port Number", |  | ||||||
| 		"UseSSL":     "Use SSL", |  | ||||||
| 		"BaseDN":     "Base DN", |  | ||||||
| 		"Attributes": "Search attributes", |  | ||||||
| 		"Filter":     "Search filter", |  | ||||||
| 		"MsAdSA":     "Ms Ad SA", |  | ||||||
| 	} | 	} | ||||||
| 	return names[field] | 
 | ||||||
|  | 	if setting.Service.EnableReverseProxyAuth { | ||||||
|  | 		webAuthUser := header.Get(setting.ReverseProxyAuthUser) | ||||||
|  | 		if len(webAuthUser) > 0 { | ||||||
|  | 			u, err := models.GetUserByName(webAuthUser) | ||||||
|  | 			if err != nil { | ||||||
|  | 				if err != models.ErrUserNotExist { | ||||||
|  | 					log.Error(4, "GetUserByName: %v", err) | ||||||
|  | 				} | ||||||
|  | 				return 0 | ||||||
|  | 			} | ||||||
|  | 			return u.Id | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	uid := sess.Get("uid") | ||||||
|  | 	if uid == nil { | ||||||
|  | 		return 0 | ||||||
|  | 	} | ||||||
|  | 	if id, ok := uid.(int64); ok { | ||||||
|  | 		if _, err := models.GetUserById(id); err != nil { | ||||||
|  | 			if err != models.ErrUserNotExist { | ||||||
|  | 				log.Error(4, "GetUserById: %v", err) | ||||||
|  | 			} | ||||||
|  | 			return 0 | ||||||
|  | 		} | ||||||
|  | 		return id | ||||||
|  | 	} | ||||||
|  | 	return 0 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (f *AuthenticationForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { | // SignedInUser returns the user object of signed user.
 | ||||||
| 	data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) | func SignedInUser(header http.Header, sess session.Store) *models.User { | ||||||
| 	validate(errors, data, f) | 	uid := SignedInId(header, sess) | ||||||
|  | 	if uid <= 0 { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	u, err := models.GetUserById(uid) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Error(4, "GetUserById: %v", err) | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	return u | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // AssignForm assign form values back to the template data.
 | ||||||
|  | func AssignForm(form interface{}, data map[string]interface{}) { | ||||||
|  | 	typ := reflect.TypeOf(form) | ||||||
|  | 	val := reflect.ValueOf(form) | ||||||
|  | 
 | ||||||
|  | 	if typ.Kind() == reflect.Ptr { | ||||||
|  | 		typ = typ.Elem() | ||||||
|  | 		val = val.Elem() | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for i := 0; i < typ.NumField(); i++ { | ||||||
|  | 		field := typ.Field(i) | ||||||
|  | 
 | ||||||
|  | 		fieldName := field.Tag.Get("form") | ||||||
|  | 		// Allow ignored fields in the struct
 | ||||||
|  | 		if fieldName == "-" { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		data[fieldName] = val.Field(i).Interface() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func GetMinMaxSize(field reflect.StructField) string { | ||||||
|  | 	for _, rule := range strings.Split(field.Tag.Get("binding"), ";") { | ||||||
|  | 		if strings.HasPrefix(rule, "MinSize(") || strings.HasPrefix(rule, "MaxSize(") { | ||||||
|  | 			return rule[8 : len(rule)-1] | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return "" | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func validate(errs *binding.Errors, data map[string]interface{}, f interface{}, l i18n.Locale) { | ||||||
|  | 	if errs.Count() == 0 { | ||||||
|  | 		return | ||||||
|  | 	} else if len(errs.Overall) > 0 { | ||||||
|  | 		for _, err := range errs.Overall { | ||||||
|  | 			log.Error(4, "%s: %v", reflect.TypeOf(f), err) | ||||||
|  | 		} | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	data["HasError"] = true | ||||||
|  | 	AssignForm(f, data) | ||||||
|  | 
 | ||||||
|  | 	typ := reflect.TypeOf(f) | ||||||
|  | 	val := reflect.ValueOf(f) | ||||||
|  | 
 | ||||||
|  | 	if typ.Kind() == reflect.Ptr { | ||||||
|  | 		typ = typ.Elem() | ||||||
|  | 		val = val.Elem() | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for i := 0; i < typ.NumField(); i++ { | ||||||
|  | 		field := typ.Field(i) | ||||||
|  | 
 | ||||||
|  | 		fieldName := field.Tag.Get("form") | ||||||
|  | 		// Allow ignored fields in the struct
 | ||||||
|  | 		if fieldName == "-" { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if err, ok := errs.Fields[field.Name]; ok { | ||||||
|  | 			data["Err_"+field.Name] = true | ||||||
|  | 			trName := l.Tr("form." + field.Name) | ||||||
|  | 			switch err { | ||||||
|  | 			case binding.BindingRequireError: | ||||||
|  | 				data["ErrorMsg"] = trName + l.Tr("form.require_error") | ||||||
|  | 			case binding.BindingAlphaDashError: | ||||||
|  | 				data["ErrorMsg"] = trName + l.Tr("form.alpha_dash_error") | ||||||
|  | 			case binding.BindingAlphaDashDotError: | ||||||
|  | 				data["ErrorMsg"] = trName + l.Tr("form.alpha_dash_dot_error") | ||||||
|  | 			case binding.BindingMinSizeError: | ||||||
|  | 				data["ErrorMsg"] = trName + l.Tr("form.min_size_error", GetMinMaxSize(field)) | ||||||
|  | 			case binding.BindingMaxSizeError: | ||||||
|  | 				data["ErrorMsg"] = trName + l.Tr("form.max_size_error", GetMinMaxSize(field)) | ||||||
|  | 			case binding.BindingEmailError: | ||||||
|  | 				data["ErrorMsg"] = trName + l.Tr("form.email_error") | ||||||
|  | 			case binding.BindingUrlError: | ||||||
|  | 				data["ErrorMsg"] = trName + l.Tr("form.url_error") | ||||||
|  | 			default: | ||||||
|  | 				data["ErrorMsg"] = l.Tr("form.unknown_error") + " " + err | ||||||
|  | 			} | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										36
									
								
								modules/auth/auth_form.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,36 @@ | ||||||
|  | // 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 auth | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/Unknwon/macaron" | ||||||
|  | 	"github.com/macaron-contrib/i18n" | ||||||
|  | 
 | ||||||
|  | 	"github.com/gogits/gogs/modules/middleware/binding" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type AuthenticationForm struct { | ||||||
|  | 	Id                int64  `form:"id"` | ||||||
|  | 	Type              int    `form:"type"` | ||||||
|  | 	AuthName          string `form:"name" binding:"Required;MaxSize(50)"` | ||||||
|  | 	Domain            string `form:"domain"` | ||||||
|  | 	Host              string `form:"host"` | ||||||
|  | 	Port              int    `form:"port"` | ||||||
|  | 	UseSSL            bool   `form:"usessl"` | ||||||
|  | 	BaseDN            string `form:"base_dn"` | ||||||
|  | 	Attributes        string `form:"attributes"` | ||||||
|  | 	Filter            string `form:"filter"` | ||||||
|  | 	MsAdSA            string `form:"ms_ad_sa"` | ||||||
|  | 	IsActived         bool   `form:"is_actived"` | ||||||
|  | 	SmtpAuth          string `form:"smtpauth"` | ||||||
|  | 	SmtpHost          string `form:"smtphost"` | ||||||
|  | 	SmtpPort          int    `form:"smtpport"` | ||||||
|  | 	Tls               bool   `form:"tls"` | ||||||
|  | 	AllowAutoRegister bool   `form:"allowautoregister"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f *AuthenticationForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | ||||||
|  | 	validate(errs, ctx.Data, f, l) | ||||||
|  | } | ||||||
|  | @ -55,7 +55,7 @@ func LoginUser(name, passwd string) (a string, r bool) { | ||||||
| func (ls Ldapsource) SearchEntry(name, passwd string) (string, bool) { | func (ls Ldapsource) SearchEntry(name, passwd string) (string, bool) { | ||||||
| 	l, err := ldapDial(ls) | 	l, err := ldapDial(ls) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Error("LDAP Connect error, %s:%v", ls.Host, err) | 		log.Error(4, "LDAP Connect error, %s:%v", ls.Host, err) | ||||||
| 		ls.Enabled = false | 		ls.Enabled = false | ||||||
| 		return "", false | 		return "", false | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -5,12 +5,9 @@ | ||||||
| package auth | package auth | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"net/http" | 	"github.com/Unknwon/macaron" | ||||||
| 	"reflect" | 	"github.com/macaron-contrib/i18n" | ||||||
| 
 | 
 | ||||||
| 	"github.com/go-martini/martini" |  | ||||||
| 
 |  | ||||||
| 	"github.com/gogits/gogs/modules/base" |  | ||||||
| 	"github.com/gogits/gogs/modules/middleware/binding" | 	"github.com/gogits/gogs/modules/middleware/binding" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -26,17 +23,8 @@ type CreateOrgForm struct { | ||||||
| 	Email   string `form:"email" binding:"Required;Email;MaxSize(50)"` | 	Email   string `form:"email" binding:"Required;Email;MaxSize(50)"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (f *CreateOrgForm) Name(field string) string { | func (f *CreateOrgForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | ||||||
| 	names := map[string]string{ | 	validate(errs, ctx.Data, f, l) | ||||||
| 		"OrgName": "Organization name", |  | ||||||
| 		"Email":   "E-mail address", |  | ||||||
| 	} |  | ||||||
| 	return names[field] |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *CreateOrgForm) Validate(errs *binding.Errors, req *http.Request, ctx martini.Context) { |  | ||||||
| 	data := ctx.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) |  | ||||||
| 	validate(errs, data, f) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type OrgSettingForm struct { | type OrgSettingForm struct { | ||||||
|  | @ -47,20 +35,8 @@ type OrgSettingForm struct { | ||||||
| 	Location    string `form:"location" binding:"MaxSize(50)"` | 	Location    string `form:"location" binding:"MaxSize(50)"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (f *OrgSettingForm) Name(field string) string { | func (f *OrgSettingForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | ||||||
| 	names := map[string]string{ | 	validate(errs, ctx.Data, f, l) | ||||||
| 		"DisplayName": "Display name", |  | ||||||
| 		"Email":       "E-mail address", |  | ||||||
| 		"Description": "Description", |  | ||||||
| 		"Website":     "Website address", |  | ||||||
| 		"Location":    "Location", |  | ||||||
| 	} |  | ||||||
| 	return names[field] |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *OrgSettingForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { |  | ||||||
| 	data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) |  | ||||||
| 	validate(errors, data, f) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ___________
 | // ___________
 | ||||||
|  | @ -76,15 +52,6 @@ type CreateTeamForm struct { | ||||||
| 	Permission  string `form:"permission"` | 	Permission  string `form:"permission"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (f *CreateTeamForm) Name(field string) string { | func (f *CreateTeamForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | ||||||
| 	names := map[string]string{ | 	validate(errs, ctx.Data, f, l) | ||||||
| 		"TeamName":    "Team name", |  | ||||||
| 		"Description": "Team description", |  | ||||||
| 	} |  | ||||||
| 	return names[field] |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *CreateTeamForm) Validate(errs *binding.Errors, req *http.Request, ctx martini.Context) { |  | ||||||
| 	data := ctx.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) |  | ||||||
| 	validate(errs, data, f) |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,33 +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 auth |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"net/http" |  | ||||||
| 	"reflect" |  | ||||||
| 
 |  | ||||||
| 	"github.com/go-martini/martini" |  | ||||||
| 
 |  | ||||||
| 	"github.com/gogits/gogs/modules/base" |  | ||||||
| 	"github.com/gogits/gogs/modules/middleware/binding" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| type AddSSHKeyForm struct { |  | ||||||
| 	KeyName    string `form:"keyname" binding:"Required"` |  | ||||||
| 	KeyContent string `form:"key_content" binding:"Required"` |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *AddSSHKeyForm) Name(field string) string { |  | ||||||
| 	names := map[string]string{ |  | ||||||
| 		"KeyName":    "SSH key name", |  | ||||||
| 		"KeyContent": "SSH key content", |  | ||||||
| 	} |  | ||||||
| 	return names[field] |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *AddSSHKeyForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { |  | ||||||
| 	data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) |  | ||||||
| 	validate(errors, data, f) |  | ||||||
| } |  | ||||||
							
								
								
									
										21
									
								
								modules/auth/publickey_form.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,21 @@ | ||||||
|  | // 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 auth | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/Unknwon/macaron" | ||||||
|  | 	"github.com/macaron-contrib/i18n" | ||||||
|  | 
 | ||||||
|  | 	"github.com/gogits/gogs/modules/middleware/binding" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type AddSSHKeyForm struct { | ||||||
|  | 	SSHTitle string `form:"title" binding:"Required"` | ||||||
|  | 	Content  string `form:"content" binding:"Required"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f *AddSSHKeyForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | ||||||
|  | 	validate(errs, ctx.Data, f, l) | ||||||
|  | } | ||||||
|  | @ -1,252 +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 auth |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"net/http" |  | ||||||
| 	"reflect" |  | ||||||
| 
 |  | ||||||
| 	"github.com/go-martini/martini" |  | ||||||
| 
 |  | ||||||
| 	"github.com/gogits/gogs/modules/base" |  | ||||||
| 	"github.com/gogits/gogs/modules/middleware/binding" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // __________                           .__  __
 |  | ||||||
| // \______   \ ____ ______   ____  _____|__|/  |_  ___________ ___.__.
 |  | ||||||
| //  |       _// __ \\____ \ /  _ \/  ___/  \   __\/  _ \_  __ <   |  |
 |  | ||||||
| //  |    |   \  ___/|  |_> >  <_> )___ \|  ||  | (  <_> )  | \/\___  |
 |  | ||||||
| //  |____|_  /\___  >   __/ \____/____  >__||__|  \____/|__|   / ____|
 |  | ||||||
| //         \/     \/|__|              \/                       \/
 |  | ||||||
| 
 |  | ||||||
| type CreateRepoForm struct { |  | ||||||
| 	Uid         int64  `form:"uid" binding:"Required"` |  | ||||||
| 	RepoName    string `form:"repo" binding:"Required;AlphaDash;MaxSize(100)"` |  | ||||||
| 	Private     bool   `form:"private"` |  | ||||||
| 	Description string `form:"desc" binding:"MaxSize(255)"` |  | ||||||
| 	Language    string `form:"language"` |  | ||||||
| 	License     string `form:"license"` |  | ||||||
| 	InitReadme  bool   `form:"initReadme"` |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *CreateRepoForm) Name(field string) string { |  | ||||||
| 	names := map[string]string{ |  | ||||||
| 		"RepoName":    "Repository name", |  | ||||||
| 		"Description": "Description", |  | ||||||
| 	} |  | ||||||
| 	return names[field] |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *CreateRepoForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { |  | ||||||
| 	data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) |  | ||||||
| 	validate(errors, data, f) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type MigrateRepoForm struct { |  | ||||||
| 	Url          string `form:"url" binding:"Url"` |  | ||||||
| 	AuthUserName string `form:"auth_username"` |  | ||||||
| 	AuthPasswd   string `form:"auth_password"` |  | ||||||
| 	Uid          int64  `form:"uid" binding:"Required"` |  | ||||||
| 	RepoName     string `form:"repo" binding:"Required;AlphaDash;MaxSize(100)"` |  | ||||||
| 	Mirror       bool   `form:"mirror"` |  | ||||||
| 	Private      bool   `form:"private"` |  | ||||||
| 	Description  string `form:"desc" binding:"MaxSize(255)"` |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *MigrateRepoForm) Name(field string) string { |  | ||||||
| 	names := map[string]string{ |  | ||||||
| 		"Url":         "Migration URL", |  | ||||||
| 		"RepoName":    "Repository name", |  | ||||||
| 		"Description": "Description", |  | ||||||
| 	} |  | ||||||
| 	return names[field] |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *MigrateRepoForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { |  | ||||||
| 	data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) |  | ||||||
| 	validate(errors, data, f) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type RepoSettingForm struct { |  | ||||||
| 	RepoName    string `form:"name" binding:"Required;AlphaDash;MaxSize(100)"` |  | ||||||
| 	Description string `form:"desc" binding:"MaxSize(255)"` |  | ||||||
| 	Website     string `form:"site" binding:"Url;MaxSize(100)"` |  | ||||||
| 	Branch      string `form:"branch"` |  | ||||||
| 	Interval    int    `form:"interval"` |  | ||||||
| 	Private     bool   `form:"private"` |  | ||||||
| 	GoGet       bool   `form:"goget"` |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *RepoSettingForm) Name(field string) string { |  | ||||||
| 	names := map[string]string{ |  | ||||||
| 		"RepoName":    "Repository name", |  | ||||||
| 		"Description": "Description", |  | ||||||
| 		"Website":     "Website address", |  | ||||||
| 	} |  | ||||||
| 	return names[field] |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *RepoSettingForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { |  | ||||||
| 	data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) |  | ||||||
| 	validate(errors, data, f) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| //  __      __      ___.   .__    .__            __
 |  | ||||||
| // /  \    /  \ ____\_ |__ |  |__ |  |__   ____ |  | __
 |  | ||||||
| // \   \/\/   // __ \| __ \|  |  \|  |  \ /  _ \|  |/ /
 |  | ||||||
| //  \        /\  ___/| \_\ \   Y  \   Y  (  <_> )    <
 |  | ||||||
| //   \__/\  /  \___  >___  /___|  /___|  /\____/|__|_ \
 |  | ||||||
| //        \/       \/    \/     \/     \/            \/
 |  | ||||||
| 
 |  | ||||||
| type NewWebhookForm struct { |  | ||||||
| 	Url         string `form:"url" binding:"Required;Url"` |  | ||||||
| 	ContentType string `form:"content_type" binding:"Required"` |  | ||||||
| 	Secret      string `form:"secret""` |  | ||||||
| 	PushOnly    bool   `form:"push_only"` |  | ||||||
| 	Active      bool   `form:"active"` |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *NewWebhookForm) Name(field string) string { |  | ||||||
| 	names := map[string]string{ |  | ||||||
| 		"Url":         "Payload URL", |  | ||||||
| 		"ContentType": "Content type", |  | ||||||
| 	} |  | ||||||
| 	return names[field] |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *NewWebhookForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { |  | ||||||
| 	data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) |  | ||||||
| 	validate(errors, data, f) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // .___
 |  | ||||||
| // |   | ______ ________ __   ____
 |  | ||||||
| // |   |/  ___//  ___/  |  \_/ __ \
 |  | ||||||
| // |   |\___ \ \___ \|  |  /\  ___/
 |  | ||||||
| // |___/____  >____  >____/  \___  >
 |  | ||||||
| //          \/     \/            \/
 |  | ||||||
| 
 |  | ||||||
| type CreateIssueForm struct { |  | ||||||
| 	IssueName   string `form:"title" binding:"Required;MaxSize(50)"` |  | ||||||
| 	MilestoneId int64  `form:"milestoneid"` |  | ||||||
| 	AssigneeId  int64  `form:"assigneeid"` |  | ||||||
| 	Labels      string `form:"labels"` |  | ||||||
| 	Content     string `form:"content"` |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *CreateIssueForm) Name(field string) string { |  | ||||||
| 	names := map[string]string{ |  | ||||||
| 		"IssueName": "Issue name", |  | ||||||
| 	} |  | ||||||
| 	return names[field] |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *CreateIssueForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { |  | ||||||
| 	data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) |  | ||||||
| 	validate(errors, data, f) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| //    _____  .__.__                   __
 |  | ||||||
| //   /     \ |__|  |   ____   _______/  |_  ____   ____   ____
 |  | ||||||
| //  /  \ /  \|  |  | _/ __ \ /  ___/\   __\/  _ \ /    \_/ __ \
 |  | ||||||
| // /    Y    \  |  |_\  ___/ \___ \  |  | (  <_> )   |  \  ___/
 |  | ||||||
| // \____|__  /__|____/\___  >____  > |__|  \____/|___|  /\___  >
 |  | ||||||
| //         \/             \/     \/                   \/     \/
 |  | ||||||
| 
 |  | ||||||
| type CreateMilestoneForm struct { |  | ||||||
| 	Title    string `form:"title" binding:"Required;MaxSize(50)"` |  | ||||||
| 	Content  string `form:"content"` |  | ||||||
| 	Deadline string `form:"due_date"` |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *CreateMilestoneForm) Name(field string) string { |  | ||||||
| 	names := map[string]string{ |  | ||||||
| 		"Title": "Milestone name", |  | ||||||
| 	} |  | ||||||
| 	return names[field] |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *CreateMilestoneForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { |  | ||||||
| 	data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) |  | ||||||
| 	validate(errors, data, f) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // .____          ___.          .__
 |  | ||||||
| // |    |   _____ \_ |__   ____ |  |
 |  | ||||||
| // |    |   \__  \ | __ \_/ __ \|  |
 |  | ||||||
| // |    |___ / __ \| \_\ \  ___/|  |__
 |  | ||||||
| // |_______ (____  /___  /\___  >____/
 |  | ||||||
| //         \/    \/    \/     \/
 |  | ||||||
| 
 |  | ||||||
| type CreateLabelForm struct { |  | ||||||
| 	Title string `form:"title" binding:"Required;MaxSize(50)"` |  | ||||||
| 	Color string `form:"color" binding:"Required;Size(7)"` |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *CreateLabelForm) Name(field string) string { |  | ||||||
| 	names := map[string]string{ |  | ||||||
| 		"Title": "Label name", |  | ||||||
| 		"Color": "Label color", |  | ||||||
| 	} |  | ||||||
| 	return names[field] |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *CreateLabelForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { |  | ||||||
| 	data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) |  | ||||||
| 	validate(errors, data, f) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // __________       .__
 |  | ||||||
| // \______   \ ____ |  |   ____ _____    ______ ____
 |  | ||||||
| //  |       _// __ \|  | _/ __ \\__  \  /  ___// __ \
 |  | ||||||
| //  |    |   \  ___/|  |_\  ___/ / __ \_\___ \\  ___/
 |  | ||||||
| //  |____|_  /\___  >____/\___  >____  /____  >\___  >
 |  | ||||||
| //         \/     \/          \/     \/     \/     \/
 |  | ||||||
| 
 |  | ||||||
| type NewReleaseForm struct { |  | ||||||
| 	TagName    string `form:"tag_name" binding:"Required"` |  | ||||||
| 	Target     string `form:"tag_target" binding:"Required"` |  | ||||||
| 	Title      string `form:"title" binding:"Required"` |  | ||||||
| 	Content    string `form:"content" binding:"Required"` |  | ||||||
| 	Draft      string `form:"draft"` |  | ||||||
| 	Prerelease bool   `form:"prerelease"` |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *NewReleaseForm) Name(field string) string { |  | ||||||
| 	names := map[string]string{ |  | ||||||
| 		"TagName": "Tag name", |  | ||||||
| 		"Target":  "Target", |  | ||||||
| 		"Title":   "Release title", |  | ||||||
| 		"Content": "Release content", |  | ||||||
| 	} |  | ||||||
| 	return names[field] |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *NewReleaseForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { |  | ||||||
| 	data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) |  | ||||||
| 	validate(errors, data, f) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type EditReleaseForm struct { |  | ||||||
| 	Target     string `form:"tag_target" binding:"Required"` |  | ||||||
| 	Title      string `form:"title" binding:"Required"` |  | ||||||
| 	Content    string `form:"content" binding:"Required"` |  | ||||||
| 	Draft      string `form:"draft"` |  | ||||||
| 	Prerelease bool   `form:"prerelease"` |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *EditReleaseForm) Name(field string) string { |  | ||||||
| 	names := map[string]string{ |  | ||||||
| 		"Target":  "Target", |  | ||||||
| 		"Title":   "Release title", |  | ||||||
| 		"Content": "Release content", |  | ||||||
| 	} |  | ||||||
| 	return names[field] |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *EditReleaseForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { |  | ||||||
| 	data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) |  | ||||||
| 	validate(errors, data, f) |  | ||||||
| } |  | ||||||
							
								
								
									
										165
									
								
								modules/auth/repo_form.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,165 @@ | ||||||
|  | // 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 auth | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/Unknwon/macaron" | ||||||
|  | 	"github.com/macaron-contrib/i18n" | ||||||
|  | 
 | ||||||
|  | 	"github.com/gogits/gogs/modules/middleware/binding" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // _______________________________________    _________.______________________ _______________.___.
 | ||||||
|  | // \______   \_   _____/\______   \_____  \  /   _____/|   \__    ___/\_____  \\______   \__  |   |
 | ||||||
|  | //  |       _/|    __)_  |     ___//   |   \ \_____  \ |   | |    |    /   |   \|       _//   |   |
 | ||||||
|  | //  |    |   \|        \ |    |   /    |    \/        \|   | |    |   /    |    \    |   \\____   |
 | ||||||
|  | //  |____|_  /_______  / |____|   \_______  /_______  /|___| |____|   \_______  /____|_  // ______|
 | ||||||
|  | //         \/        \/                   \/        \/                        \/       \/ \/
 | ||||||
|  | 
 | ||||||
|  | type CreateRepoForm struct { | ||||||
|  | 	Uid         int64  `form:"uid" binding:"Required"` | ||||||
|  | 	RepoName    string `form:"repo_name" binding:"Required;AlphaDash;MaxSize(100)"` | ||||||
|  | 	Private     bool   `form:"private"` | ||||||
|  | 	Description string `form:"desc" binding:"MaxSize(255)"` | ||||||
|  | 	Gitignore   string `form:"gitignore"` | ||||||
|  | 	License     string `form:"license"` | ||||||
|  | 	InitReadme  bool   `form:"init_readme"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f *CreateRepoForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | ||||||
|  | 	validate(errs, ctx.Data, f, l) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type MigrateRepoForm struct { | ||||||
|  | 	Url          string `form:"url" binding:"Url"` | ||||||
|  | 	AuthUserName string `form:"auth_username"` | ||||||
|  | 	AuthPasswd   string `form:"auth_password"` | ||||||
|  | 	Uid          int64  `form:"uid" binding:"Required"` | ||||||
|  | 	RepoName     string `form:"repo" binding:"Required;AlphaDash;MaxSize(100)"` | ||||||
|  | 	Mirror       bool   `form:"mirror"` | ||||||
|  | 	Private      bool   `form:"private"` | ||||||
|  | 	Description  string `form:"desc" binding:"MaxSize(255)"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f *MigrateRepoForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | ||||||
|  | 	validate(errs, ctx.Data, f, l) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type RepoSettingForm struct { | ||||||
|  | 	RepoName    string `form:"name" binding:"Required;AlphaDash;MaxSize(100)"` | ||||||
|  | 	Description string `form:"desc" binding:"MaxSize(255)"` | ||||||
|  | 	Website     string `form:"site" binding:"Url;MaxSize(100)"` | ||||||
|  | 	Branch      string `form:"branch"` | ||||||
|  | 	Interval    int    `form:"interval"` | ||||||
|  | 	Private     bool   `form:"private"` | ||||||
|  | 	GoGet       bool   `form:"goget"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f *RepoSettingForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | ||||||
|  | 	validate(errs, ctx.Data, f, l) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //  __      __      ___.   .__    .__            __
 | ||||||
|  | // /  \    /  \ ____\_ |__ |  |__ |  |__   ____ |  | __
 | ||||||
|  | // \   \/\/   // __ \| __ \|  |  \|  |  \ /  _ \|  |/ /
 | ||||||
|  | //  \        /\  ___/| \_\ \   Y  \   Y  (  <_> )    <
 | ||||||
|  | //   \__/\  /  \___  >___  /___|  /___|  /\____/|__|_ \
 | ||||||
|  | //        \/       \/    \/     \/     \/            \/
 | ||||||
|  | 
 | ||||||
|  | type NewWebhookForm struct { | ||||||
|  | 	Url         string `form:"url" binding:"Required;Url"` | ||||||
|  | 	ContentType string `form:"content_type" binding:"Required"` | ||||||
|  | 	Secret      string `form:"secret""` | ||||||
|  | 	PushOnly    bool   `form:"push_only"` | ||||||
|  | 	Active      bool   `form:"active"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f *NewWebhookForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | ||||||
|  | 	validate(errs, ctx.Data, f, l) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // .___
 | ||||||
|  | // |   | ______ ________ __   ____
 | ||||||
|  | // |   |/  ___//  ___/  |  \_/ __ \
 | ||||||
|  | // |   |\___ \ \___ \|  |  /\  ___/
 | ||||||
|  | // |___/____  >____  >____/  \___  >
 | ||||||
|  | //          \/     \/            \/
 | ||||||
|  | 
 | ||||||
|  | type CreateIssueForm struct { | ||||||
|  | 	IssueName   string `form:"title" binding:"Required;MaxSize(50)"` | ||||||
|  | 	MilestoneId int64  `form:"milestoneid"` | ||||||
|  | 	AssigneeId  int64  `form:"assigneeid"` | ||||||
|  | 	Labels      string `form:"labels"` | ||||||
|  | 	Content     string `form:"content"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f *CreateIssueForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | ||||||
|  | 	validate(errs, ctx.Data, f, l) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //    _____  .__.__                   __
 | ||||||
|  | //   /     \ |__|  |   ____   _______/  |_  ____   ____   ____
 | ||||||
|  | //  /  \ /  \|  |  | _/ __ \ /  ___/\   __\/  _ \ /    \_/ __ \
 | ||||||
|  | // /    Y    \  |  |_\  ___/ \___ \  |  | (  <_> )   |  \  ___/
 | ||||||
|  | // \____|__  /__|____/\___  >____  > |__|  \____/|___|  /\___  >
 | ||||||
|  | //         \/             \/     \/                   \/     \/
 | ||||||
|  | 
 | ||||||
|  | type CreateMilestoneForm struct { | ||||||
|  | 	Title    string `form:"title" binding:"Required;MaxSize(50)"` | ||||||
|  | 	Content  string `form:"content"` | ||||||
|  | 	Deadline string `form:"due_date"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f *CreateMilestoneForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | ||||||
|  | 	validate(errs, ctx.Data, f, l) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // .____          ___.          .__
 | ||||||
|  | // |    |   _____ \_ |__   ____ |  |
 | ||||||
|  | // |    |   \__  \ | __ \_/ __ \|  |
 | ||||||
|  | // |    |___ / __ \| \_\ \  ___/|  |__
 | ||||||
|  | // |_______ (____  /___  /\___  >____/
 | ||||||
|  | //         \/    \/    \/     \/
 | ||||||
|  | 
 | ||||||
|  | type CreateLabelForm struct { | ||||||
|  | 	Title string `form:"title" binding:"Required;MaxSize(50)"` | ||||||
|  | 	Color string `form:"color" binding:"Required;Size(7)"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f *CreateLabelForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | ||||||
|  | 	validate(errs, ctx.Data, f, l) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // __________       .__
 | ||||||
|  | // \______   \ ____ |  |   ____ _____    ______ ____
 | ||||||
|  | //  |       _// __ \|  | _/ __ \\__  \  /  ___// __ \
 | ||||||
|  | //  |    |   \  ___/|  |_\  ___/ / __ \_\___ \\  ___/
 | ||||||
|  | //  |____|_  /\___  >____/\___  >____  /____  >\___  >
 | ||||||
|  | //         \/     \/          \/     \/     \/     \/
 | ||||||
|  | 
 | ||||||
|  | type NewReleaseForm struct { | ||||||
|  | 	TagName    string `form:"tag_name" binding:"Required"` | ||||||
|  | 	Target     string `form:"tag_target" binding:"Required"` | ||||||
|  | 	Title      string `form:"title" binding:"Required"` | ||||||
|  | 	Content    string `form:"content" binding:"Required"` | ||||||
|  | 	Draft      string `form:"draft"` | ||||||
|  | 	Prerelease bool   `form:"prerelease"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f *NewReleaseForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | ||||||
|  | 	validate(errs, ctx.Data, f, l) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type EditReleaseForm struct { | ||||||
|  | 	Target     string `form:"tag_target" binding:"Required"` | ||||||
|  | 	Title      string `form:"title" binding:"Required"` | ||||||
|  | 	Content    string `form:"content" binding:"Required"` | ||||||
|  | 	Draft      string `form:"draft"` | ||||||
|  | 	Prerelease bool   `form:"prerelease"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f *EditReleaseForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | ||||||
|  | 	validate(errs, ctx.Data, f, l) | ||||||
|  | } | ||||||
|  | @ -1,299 +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 auth |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"net/http" |  | ||||||
| 	"reflect" |  | ||||||
| 	"strings" |  | ||||||
| 
 |  | ||||||
| 	"github.com/go-martini/martini" |  | ||||||
| 
 |  | ||||||
| 	"github.com/gogits/session" |  | ||||||
| 
 |  | ||||||
| 	"github.com/gogits/gogs/models" |  | ||||||
| 	"github.com/gogits/gogs/modules/base" |  | ||||||
| 	"github.com/gogits/gogs/modules/log" |  | ||||||
| 	"github.com/gogits/gogs/modules/middleware/binding" |  | ||||||
| 	"github.com/gogits/gogs/modules/setting" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // Web form interface.
 |  | ||||||
| type Form interface { |  | ||||||
| 	Name(field string) string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type RegisterForm struct { |  | ||||||
| 	UserName     string `form:"username" binding:"Required;AlphaDashDot;MaxSize(30)"` |  | ||||||
| 	Email        string `form:"email" binding:"Required;Email;MaxSize(50)"` |  | ||||||
| 	Password     string `form:"passwd" binding:"Required;MinSize(6);MaxSize(30)"` |  | ||||||
| 	RetypePasswd string `form:"retypepasswd"` |  | ||||||
| 	LoginType    string `form:"logintype"` |  | ||||||
| 	LoginName    string `form:"loginname"` |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *RegisterForm) Name(field string) string { |  | ||||||
| 	names := map[string]string{ |  | ||||||
| 		"UserName":     "Username", |  | ||||||
| 		"Email":        "E-mail address", |  | ||||||
| 		"Password":     "Password", |  | ||||||
| 		"RetypePasswd": "Re-type password", |  | ||||||
| 	} |  | ||||||
| 	return names[field] |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *RegisterForm) Validate(errs *binding.Errors, req *http.Request, ctx martini.Context) { |  | ||||||
| 	data := ctx.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) |  | ||||||
| 	validate(errs, data, f) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type LogInForm struct { |  | ||||||
| 	UserName string `form:"username" binding:"Required;MaxSize(35)"` |  | ||||||
| 	Password string `form:"passwd" binding:"Required;MinSize(6);MaxSize(30)"` |  | ||||||
| 	Remember bool   `form:"remember"` |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *LogInForm) Name(field string) string { |  | ||||||
| 	names := map[string]string{ |  | ||||||
| 		"UserName": "Username", |  | ||||||
| 		"Password": "Password", |  | ||||||
| 	} |  | ||||||
| 	return names[field] |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *LogInForm) Validate(errs *binding.Errors, req *http.Request, ctx martini.Context) { |  | ||||||
| 	data := ctx.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) |  | ||||||
| 	validate(errs, data, f) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func GetMinMaxSize(field reflect.StructField) string { |  | ||||||
| 	for _, rule := range strings.Split(field.Tag.Get("binding"), ";") { |  | ||||||
| 		if strings.HasPrefix(rule, "MinSize(") || strings.HasPrefix(rule, "MaxSize(") { |  | ||||||
| 			return rule[8 : len(rule)-1] |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return "" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func validate(errs *binding.Errors, data base.TmplData, f Form) { |  | ||||||
| 	if errs.Count() == 0 { |  | ||||||
| 		return |  | ||||||
| 	} else if len(errs.Overall) > 0 { |  | ||||||
| 		for _, err := range errs.Overall { |  | ||||||
| 			log.Error("%s: %v", reflect.TypeOf(f), err) |  | ||||||
| 		} |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	data["HasError"] = true |  | ||||||
| 	AssignForm(f, data) |  | ||||||
| 
 |  | ||||||
| 	typ := reflect.TypeOf(f) |  | ||||||
| 	val := reflect.ValueOf(f) |  | ||||||
| 
 |  | ||||||
| 	if typ.Kind() == reflect.Ptr { |  | ||||||
| 		typ = typ.Elem() |  | ||||||
| 		val = val.Elem() |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	for i := 0; i < typ.NumField(); i++ { |  | ||||||
| 		field := typ.Field(i) |  | ||||||
| 
 |  | ||||||
| 		fieldName := field.Tag.Get("form") |  | ||||||
| 		// Allow ignored fields in the struct
 |  | ||||||
| 		if fieldName == "-" { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if err, ok := errs.Fields[field.Name]; ok { |  | ||||||
| 			data["Err_"+field.Name] = true |  | ||||||
| 			switch err { |  | ||||||
| 			case binding.BindingRequireError: |  | ||||||
| 				data["ErrorMsg"] = f.Name(field.Name) + " cannot be empty" |  | ||||||
| 			case binding.BindingAlphaDashError: |  | ||||||
| 				data["ErrorMsg"] = f.Name(field.Name) + " must be valid alpha or numeric or dash(-_) characters" |  | ||||||
| 			case binding.BindingAlphaDashDotError: |  | ||||||
| 				data["ErrorMsg"] = f.Name(field.Name) + " must be valid alpha or numeric or dash(-_) or dot characters" |  | ||||||
| 			case binding.BindingMinSizeError: |  | ||||||
| 				data["ErrorMsg"] = f.Name(field.Name) + " must contain at least " + GetMinMaxSize(field) + " characters" |  | ||||||
| 			case binding.BindingMaxSizeError: |  | ||||||
| 				data["ErrorMsg"] = f.Name(field.Name) + " must contain at most " + GetMinMaxSize(field) + " characters" |  | ||||||
| 			case binding.BindingEmailError: |  | ||||||
| 				data["ErrorMsg"] = f.Name(field.Name) + " is not a valid e-mail address" |  | ||||||
| 			case binding.BindingUrlError: |  | ||||||
| 				data["ErrorMsg"] = f.Name(field.Name) + " is not a valid URL" |  | ||||||
| 			default: |  | ||||||
| 				data["ErrorMsg"] = "Unknown error: " + err |  | ||||||
| 			} |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // AssignForm assign form values back to the template data.
 |  | ||||||
| func AssignForm(form interface{}, data base.TmplData) { |  | ||||||
| 	typ := reflect.TypeOf(form) |  | ||||||
| 	val := reflect.ValueOf(form) |  | ||||||
| 
 |  | ||||||
| 	if typ.Kind() == reflect.Ptr { |  | ||||||
| 		typ = typ.Elem() |  | ||||||
| 		val = val.Elem() |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	for i := 0; i < typ.NumField(); i++ { |  | ||||||
| 		field := typ.Field(i) |  | ||||||
| 
 |  | ||||||
| 		fieldName := field.Tag.Get("form") |  | ||||||
| 		// Allow ignored fields in the struct
 |  | ||||||
| 		if fieldName == "-" { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		data[fieldName] = val.Field(i).Interface() |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type InstallForm struct { |  | ||||||
| 	Database        string `form:"database" binding:"Required"` |  | ||||||
| 	Host            string `form:"host"` |  | ||||||
| 	User            string `form:"user"` |  | ||||||
| 	Passwd          string `form:"passwd"` |  | ||||||
| 	DatabaseName    string `form:"database_name"` |  | ||||||
| 	SslMode         string `form:"ssl_mode"` |  | ||||||
| 	DatabasePath    string `form:"database_path"` |  | ||||||
| 	RepoRootPath    string `form:"repo_path"` |  | ||||||
| 	RunUser         string `form:"run_user"` |  | ||||||
| 	Domain          string `form:"domain"` |  | ||||||
| 	AppUrl          string `form:"app_url"` |  | ||||||
| 	AdminName       string `form:"admin_name" binding:"Required;AlphaDashDot;MaxSize(30)"` |  | ||||||
| 	AdminPasswd     string `form:"admin_pwd" binding:"Required;MinSize(6);MaxSize(30)"` |  | ||||||
| 	AdminEmail      string `form:"admin_email" binding:"Required;Email;MaxSize(50)"` |  | ||||||
| 	SmtpHost        string `form:"smtp_host"` |  | ||||||
| 	SmtpEmail       string `form:"mailer_user"` |  | ||||||
| 	SmtpPasswd      string `form:"mailer_pwd"` |  | ||||||
| 	RegisterConfirm string `form:"register_confirm"` |  | ||||||
| 	MailNotify      string `form:"mail_notify"` |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *InstallForm) Name(field string) string { |  | ||||||
| 	names := map[string]string{ |  | ||||||
| 		"Database":    "Database name", |  | ||||||
| 		"AdminName":   "Admin user name", |  | ||||||
| 		"AdminPasswd": "Admin password", |  | ||||||
| 		"AdminEmail":  "Admin e-maill address", |  | ||||||
| 	} |  | ||||||
| 	return names[field] |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *InstallForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { |  | ||||||
| 	data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) |  | ||||||
| 	validate(errors, data, f) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // SignedInId returns the id of signed in user.
 |  | ||||||
| func SignedInId(header http.Header, sess session.SessionStore) int64 { |  | ||||||
| 	if !models.HasEngine { |  | ||||||
| 		return 0 |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if setting.Service.EnableReverseProxyAuth { |  | ||||||
| 		webAuthUser := header.Get(setting.ReverseProxyAuthUser) |  | ||||||
| 		if len(webAuthUser) > 0 { |  | ||||||
| 			u, err := models.GetUserByName(webAuthUser) |  | ||||||
| 			if err != nil { |  | ||||||
| 				if err != models.ErrUserNotExist { |  | ||||||
| 					log.Error("auth.user.SignedInId(GetUserByName): %v", err) |  | ||||||
| 				} |  | ||||||
| 				return 0 |  | ||||||
| 			} |  | ||||||
| 			return u.Id |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	uid := sess.Get("userId") |  | ||||||
| 	if uid == nil { |  | ||||||
| 		return 0 |  | ||||||
| 	} |  | ||||||
| 	if id, ok := uid.(int64); ok { |  | ||||||
| 		if _, err := models.GetUserById(id); err != nil { |  | ||||||
| 			if err != models.ErrUserNotExist { |  | ||||||
| 				log.Error("auth.user.SignedInId(GetUserById): %v", err) |  | ||||||
| 			} |  | ||||||
| 			return 0 |  | ||||||
| 		} |  | ||||||
| 		return id |  | ||||||
| 	} |  | ||||||
| 	return 0 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // SignedInUser returns the user object of signed user.
 |  | ||||||
| func SignedInUser(header http.Header, sess session.SessionStore) *models.User { |  | ||||||
| 	uid := SignedInId(header, sess) |  | ||||||
| 	if uid <= 0 { |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	u, err := models.GetUserById(uid) |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Error("user.SignedInUser: %v", err) |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| 	return u |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // IsSignedIn check if any user has signed in.
 |  | ||||||
| func IsSignedIn(header http.Header, sess session.SessionStore) bool { |  | ||||||
| 	return SignedInId(header, sess) > 0 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type FeedsForm struct { |  | ||||||
| 	UserId int64 `form:"userid" binding:"Required"` |  | ||||||
| 	Page   int64 `form:"p"` |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type UpdateProfileForm struct { |  | ||||||
| 	UserName string `form:"username" binding:"Required;AlphaDash;MaxSize(30)"` |  | ||||||
| 	FullName string `form:"fullname" binding:"MaxSize(40)"` |  | ||||||
| 	Email    string `form:"email" binding:"Required;Email;MaxSize(50)"` |  | ||||||
| 	Website  string `form:"website" binding:"Url;MaxSize(50)"` |  | ||||||
| 	Location string `form:"location" binding:"MaxSize(50)"` |  | ||||||
| 	Avatar   string `form:"avatar" binding:"Required;Email;MaxSize(50)"` |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *UpdateProfileForm) Name(field string) string { |  | ||||||
| 	names := map[string]string{ |  | ||||||
| 		"UserName": "Username", |  | ||||||
| 		"Email":    "E-mail address", |  | ||||||
| 		"Website":  "Website address", |  | ||||||
| 		"Location": "Location", |  | ||||||
| 		"Avatar":   "Gravatar Email", |  | ||||||
| 	} |  | ||||||
| 	return names[field] |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *UpdateProfileForm) Validate(errs *binding.Errors, req *http.Request, ctx martini.Context) { |  | ||||||
| 	data := ctx.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) |  | ||||||
| 	validate(errs, data, f) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type UpdatePasswdForm struct { |  | ||||||
| 	OldPasswd    string `form:"oldpasswd" binding:"Required;MinSize(6);MaxSize(30)"` |  | ||||||
| 	NewPasswd    string `form:"newpasswd" binding:"Required;MinSize(6);MaxSize(30)"` |  | ||||||
| 	RetypePasswd string `form:"retypepasswd"` |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *UpdatePasswdForm) Name(field string) string { |  | ||||||
| 	names := map[string]string{ |  | ||||||
| 		"OldPasswd":    "Old password", |  | ||||||
| 		"NewPasswd":    "New password", |  | ||||||
| 		"RetypePasswd": "Re-type password", |  | ||||||
| 	} |  | ||||||
| 	return names[field] |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *UpdatePasswdForm) Validate(errs *binding.Errors, req *http.Request, ctx martini.Context) { |  | ||||||
| 	data := ctx.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) |  | ||||||
| 	validate(errs, data, f) |  | ||||||
| } |  | ||||||
							
								
								
									
										98
									
								
								modules/auth/user_form.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,98 @@ | ||||||
|  | // 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 auth | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/Unknwon/macaron" | ||||||
|  | 	"github.com/macaron-contrib/i18n" | ||||||
|  | 
 | ||||||
|  | 	"github.com/gogits/gogs/modules/middleware/binding" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type InstallForm struct { | ||||||
|  | 	Database        string `form:"database" binding:"Required"` | ||||||
|  | 	Host            string `form:"host"` | ||||||
|  | 	User            string `form:"user"` | ||||||
|  | 	Passwd          string `form:"passwd"` | ||||||
|  | 	DatabaseName    string `form:"database_name"` | ||||||
|  | 	SslMode         string `form:"ssl_mode"` | ||||||
|  | 	DatabasePath    string `form:"database_path"` | ||||||
|  | 	RepoRootPath    string `form:"repo_path"` | ||||||
|  | 	RunUser         string `form:"run_user"` | ||||||
|  | 	Domain          string `form:"domain"` | ||||||
|  | 	AppUrl          string `form:"app_url"` | ||||||
|  | 	AdminName       string `form:"admin_name" binding:"Required;AlphaDashDot;MaxSize(30)"` | ||||||
|  | 	AdminPasswd     string `form:"admin_pwd" binding:"Required;MinSize(6);MaxSize(30)"` | ||||||
|  | 	AdminEmail      string `form:"admin_email" binding:"Required;Email;MaxSize(50)"` | ||||||
|  | 	SmtpHost        string `form:"smtp_host"` | ||||||
|  | 	SmtpEmail       string `form:"mailer_user"` | ||||||
|  | 	SmtpPasswd      string `form:"mailer_pwd"` | ||||||
|  | 	RegisterConfirm string `form:"register_confirm"` | ||||||
|  | 	MailNotify      string `form:"mail_notify"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f *InstallForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | ||||||
|  | 	validate(errs, ctx.Data, f, l) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //    _____   ____ _________________ ___
 | ||||||
|  | //   /  _  \ |    |   \__    ___/   |   \
 | ||||||
|  | //  /  /_\  \|    |   / |    | /    ~    \
 | ||||||
|  | // /    |    \    |  /  |    | \    Y    /
 | ||||||
|  | // \____|__  /______/   |____|  \___|_  /
 | ||||||
|  | //         \/                         \/
 | ||||||
|  | 
 | ||||||
|  | type RegisterForm struct { | ||||||
|  | 	UserName  string `form:"uname" binding:"Required;AlphaDashDot;MaxSize(35)"` | ||||||
|  | 	Email     string `form:"email" binding:"Required;Email;MaxSize(50)"` | ||||||
|  | 	Password  string `form:"password" binding:"Required;MinSize(6);MaxSize(30)"` | ||||||
|  | 	Retype    string `form:"retype"` | ||||||
|  | 	LoginType string `form:"logintype"` | ||||||
|  | 	LoginName string `form:"loginname"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f *RegisterForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | ||||||
|  | 	validate(errs, ctx.Data, f, l) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type SignInForm struct { | ||||||
|  | 	UserName string `form:"uname" binding:"Required;MaxSize(35)"` | ||||||
|  | 	Password string `form:"password" binding:"Required;MinSize(6);MaxSize(30)"` | ||||||
|  | 	Remember bool   `form:"remember"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f *SignInForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | ||||||
|  | 	validate(errs, ctx.Data, f, l) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //   __________________________________________.___ _______    ________  _________
 | ||||||
|  | //  /   _____/\_   _____/\__    ___/\__    ___/|   |\      \  /  _____/ /   _____/
 | ||||||
|  | //  \_____  \  |    __)_   |    |     |    |   |   |/   |   \/   \  ___ \_____  \
 | ||||||
|  | //  /        \ |        \  |    |     |    |   |   /    |    \    \_\  \/        \
 | ||||||
|  | // /_______  //_______  /  |____|     |____|   |___\____|__  /\______  /_______  /
 | ||||||
|  | //         \/         \/                                   \/        \/        \/
 | ||||||
|  | 
 | ||||||
|  | type UpdateProfileForm struct { | ||||||
|  | 	UserName string `form:"uname" binding:"Required;MaxSize(35)"` | ||||||
|  | 	FullName string `form:"fullname" binding:"MaxSize(40)"` | ||||||
|  | 	Email    string `form:"email" binding:"Required;Email;MaxSize(50)"` | ||||||
|  | 	Website  string `form:"website" binding:"Url;MaxSize(50)"` | ||||||
|  | 	Location string `form:"location" binding:"MaxSize(50)"` | ||||||
|  | 	Avatar   string `form:"avatar" binding:"Required;Email;MaxSize(50)"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f *UpdateProfileForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | ||||||
|  | 	validate(errs, ctx.Data, f, l) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type ChangePasswordForm struct { | ||||||
|  | 	OldPassword string `form:"old_password" binding:"Required;MinSize(6);MaxSize(30)"` | ||||||
|  | 	Password    string `form:"password" binding:"Required;MinSize(6);MaxSize(30)"` | ||||||
|  | 	Retype      string `form:"retype"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f *ChangePasswordForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | ||||||
|  | 	validate(errs, ctx.Data, f, l) | ||||||
|  | } | ||||||
|  | @ -5,9 +5,7 @@ | ||||||
| package base | package base | ||||||
| 
 | 
 | ||||||
| type ( | type ( | ||||||
| 	// Type TmplData represents data in the templates.
 | 	TplName string | ||||||
| 	TmplData map[string]interface{} |  | ||||||
| 	TplName  string |  | ||||||
| 
 | 
 | ||||||
| 	ApiJsonErr struct { | 	ApiJsonErr struct { | ||||||
| 		Message string `json:"message"` | 		Message string `json:"message"` | ||||||
|  |  | ||||||
|  | @ -51,7 +51,7 @@ var mailDomains = map[string]string{ | ||||||
| 
 | 
 | ||||||
| var TemplateFuncs template.FuncMap = map[string]interface{}{ | var TemplateFuncs template.FuncMap = map[string]interface{}{ | ||||||
| 	"GoVer": func() string { | 	"GoVer": func() string { | ||||||
| 		return runtime.Version() | 		return strings.Title(runtime.Version()) | ||||||
| 	}, | 	}, | ||||||
| 	"AppName": func() string { | 	"AppName": func() string { | ||||||
| 		return setting.AppName | 		return setting.AppName | ||||||
|  | @ -69,7 +69,8 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{ | ||||||
| 		return fmt.Sprint(time.Since(startTime).Nanoseconds()/1e6) + "ms" | 		return fmt.Sprint(time.Since(startTime).Nanoseconds()/1e6) + "ms" | ||||||
| 	}, | 	}, | ||||||
| 	"AvatarLink": AvatarLink, | 	"AvatarLink": AvatarLink, | ||||||
| 	"str2html":   Str2html, | 	"str2html":   Str2html, // TODO: Legacy
 | ||||||
|  | 	"Str2html":   Str2html, | ||||||
| 	"TimeSince":  TimeSince, | 	"TimeSince":  TimeSince, | ||||||
| 	"FileSize":   FileSize, | 	"FileSize":   FileSize, | ||||||
| 	"Subtract":   Subtract, | 	"Subtract":   Subtract, | ||||||
|  | @ -98,8 +99,11 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{ | ||||||
| 	"DiffTypeToStr":     DiffTypeToStr, | 	"DiffTypeToStr":     DiffTypeToStr, | ||||||
| 	"DiffLineTypeToStr": DiffLineTypeToStr, | 	"DiffLineTypeToStr": DiffLineTypeToStr, | ||||||
| 	"ShortSha":          ShortSha, | 	"ShortSha":          ShortSha, | ||||||
| 	"Oauth2Icon":        Oauth2Icon, | 	"Md5":               EncodeMd5, | ||||||
| 	"Oauth2Name":        Oauth2Name, | 	"ActionContent2Commits": ActionContent2Commits, | ||||||
|  | 	"Oauth2Icon":            Oauth2Icon, | ||||||
|  | 	"Oauth2Name":            Oauth2Name, | ||||||
|  | 	"CreateCaptcha":         func() string { return "" }, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type Actioner interface { | type Actioner interface { | ||||||
|  | @ -117,11 +121,11 @@ type Actioner interface { | ||||||
| func ActionIcon(opType int) string { | func ActionIcon(opType int) string { | ||||||
| 	switch opType { | 	switch opType { | ||||||
| 	case 1: // Create repository.
 | 	case 1: // Create repository.
 | ||||||
| 		return "plus-circle" | 		return "repo" | ||||||
| 	case 5, 9: // Commit repository.
 | 	case 5, 9: // Commit repository.
 | ||||||
| 		return "arrow-circle-o-right" | 		return "git-commit" | ||||||
| 	case 6: // Create issue.
 | 	case 6: // Create issue.
 | ||||||
| 		return "exclamation-circle" | 		return "issue-opened" | ||||||
| 	case 8: // Transfer repository.
 | 	case 8: // Transfer repository.
 | ||||||
| 		return "share" | 		return "share" | ||||||
| 	case 10: // Comment issue.
 | 	case 10: // Comment issue.
 | ||||||
|  | @ -131,6 +135,7 @@ func ActionIcon(opType int) string { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // TODO: Legacy
 | ||||||
| const ( | const ( | ||||||
| 	TPL_CREATE_REPO    = `<a href="/user/%s">%s</a> created repository <a href="/%s">%s</a>` | 	TPL_CREATE_REPO    = `<a href="/user/%s">%s</a> created repository <a href="/%s">%s</a>` | ||||||
| 	TPL_COMMIT_REPO    = `<a href="/user/%s">%s</a> pushed to <a href="/%s/src/%s">%s</a> at <a href="/%s">%s</a>%s` | 	TPL_COMMIT_REPO    = `<a href="/user/%s">%s</a> pushed to <a href="/%s/src/%s">%s</a> at <a href="/%s">%s</a>%s` | ||||||
|  | @ -155,6 +160,15 @@ type PushCommits struct { | ||||||
| 	Commits []*PushCommit | 	Commits []*PushCommit | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func ActionContent2Commits(act Actioner) *PushCommits { | ||||||
|  | 	var push *PushCommits | ||||||
|  | 	if err := json.Unmarshal([]byte(act.GetContent()), &push); err != nil { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	return push | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // TODO: Legacy
 | ||||||
| // ActionDesc accepts int that represents action operation type
 | // ActionDesc accepts int that represents action operation type
 | ||||||
| // and returns the description.
 | // and returns the description.
 | ||||||
| func ActionDesc(act Actioner) string { | func ActionDesc(act Actioner) string { | ||||||
|  |  | ||||||
|  | @ -14,10 +14,13 @@ import ( | ||||||
| 	"hash" | 	"hash" | ||||||
| 	"html/template" | 	"html/template" | ||||||
| 	"math" | 	"math" | ||||||
| 	"strconv" | 	r "math/rand" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/Unknwon/com" | ||||||
|  | 	"github.com/Unknwon/i18n" | ||||||
|  | 
 | ||||||
| 	"github.com/gogits/gogs/modules/setting" | 	"github.com/gogits/gogs/modules/setting" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -43,6 +46,33 @@ func GetRandomString(n int, alphabets ...byte) string { | ||||||
| 	return string(bytes) | 	return string(bytes) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // RandomCreateBytes generate random []byte by specify chars.
 | ||||||
|  | func RandomCreateBytes(n int, alphabets ...byte) []byte { | ||||||
|  | 	const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" | ||||||
|  | 	var bytes = make([]byte, n) | ||||||
|  | 	var randby bool | ||||||
|  | 	if num, err := rand.Read(bytes); num != n || err != nil { | ||||||
|  | 		r.Seed(time.Now().UnixNano()) | ||||||
|  | 		randby = true | ||||||
|  | 	} | ||||||
|  | 	for i, b := range bytes { | ||||||
|  | 		if len(alphabets) == 0 { | ||||||
|  | 			if randby { | ||||||
|  | 				bytes[i] = alphanum[r.Intn(len(alphanum))] | ||||||
|  | 			} else { | ||||||
|  | 				bytes[i] = alphanum[b%byte(len(alphanum))] | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			if randby { | ||||||
|  | 				bytes[i] = alphabets[r.Intn(len(alphabets))] | ||||||
|  | 			} else { | ||||||
|  | 				bytes[i] = alphabets[b%byte(len(alphabets))] | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return bytes | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // http://code.google.com/p/go/source/browse/pbkdf2/pbkdf2.go?repo=crypto
 | // http://code.google.com/p/go/source/browse/pbkdf2/pbkdf2.go?repo=crypto
 | ||||||
| func PBKDF2(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte { | func PBKDF2(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte { | ||||||
| 	prf := hmac.New(h, password) | 	prf := hmac.New(h, password) | ||||||
|  | @ -90,7 +120,7 @@ func VerifyTimeLimitCode(data string, minutes int, code string) bool { | ||||||
| 	// split code
 | 	// split code
 | ||||||
| 	start := code[:12] | 	start := code[:12] | ||||||
| 	lives := code[12:18] | 	lives := code[12:18] | ||||||
| 	if d, err := StrTo(lives).Int(); err == nil { | 	if d, err := com.StrTo(lives).Int(); err == nil { | ||||||
| 		minutes = d | 		minutes = d | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -134,7 +164,7 @@ func CreateTimeLimitCode(data string, minutes int, startInf interface{}) string | ||||||
| 
 | 
 | ||||||
| 	// create sha1 encode string
 | 	// create sha1 encode string
 | ||||||
| 	sh := sha1.New() | 	sh := sha1.New() | ||||||
| 	sh.Write([]byte(data + setting.SecretKey + startStr + endStr + ToStr(minutes))) | 	sh.Write([]byte(data + setting.SecretKey + startStr + endStr + com.ToStr(minutes))) | ||||||
| 	encoded := hex.EncodeToString(sh.Sum(nil)) | 	encoded := hex.EncodeToString(sh.Sum(nil)) | ||||||
| 
 | 
 | ||||||
| 	code := fmt.Sprintf("%s%06d%s", startStr, minutes, encoded) | 	code := fmt.Sprintf("%s%06d%s", startStr, minutes, encoded) | ||||||
|  | @ -240,60 +270,59 @@ func TimeSincePro(then time.Time) string { | ||||||
| 	return strings.TrimPrefix(timeStr, ", ") | 	return strings.TrimPrefix(timeStr, ", ") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // timeSince calculates the time interval and generate user-friendly string.
 | func timeSince(then time.Time, lang string) string { | ||||||
| func timeSince(then time.Time) string { |  | ||||||
| 	now := time.Now() | 	now := time.Now() | ||||||
| 
 | 
 | ||||||
| 	lbl := "ago" | 	lbl := i18n.Tr(lang, "tool.ago") | ||||||
| 	diff := now.Unix() - then.Unix() | 	diff := now.Unix() - then.Unix() | ||||||
| 	if then.After(now) { | 	if then.After(now) { | ||||||
| 		lbl = "from now" | 		lbl = i18n.Tr(lang, "tool.from_now") | ||||||
| 		diff = then.Unix() - now.Unix() | 		diff = then.Unix() - now.Unix() | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	switch { | 	switch { | ||||||
| 	case diff <= 0: | 	case diff <= 0: | ||||||
| 		return "now" | 		return i18n.Tr(lang, "tool.now") | ||||||
| 	case diff <= 2: | 	case diff <= 2: | ||||||
| 		return fmt.Sprintf("1 second %s", lbl) | 		return i18n.Tr(lang, "tool.1s", lbl) | ||||||
| 	case diff < 1*Minute: | 	case diff < 1*Minute: | ||||||
| 		return fmt.Sprintf("%d seconds %s", diff, lbl) | 		return i18n.Tr(lang, "tool.seconds", diff, lbl) | ||||||
| 
 | 
 | ||||||
| 	case diff < 2*Minute: | 	case diff < 2*Minute: | ||||||
| 		return fmt.Sprintf("1 minute %s", lbl) | 		return i18n.Tr(lang, "tool.1m", lbl) | ||||||
| 	case diff < 1*Hour: | 	case diff < 1*Hour: | ||||||
| 		return fmt.Sprintf("%d minutes %s", diff/Minute, lbl) | 		return i18n.Tr(lang, "tool.minutes", diff/Minute, lbl) | ||||||
| 
 | 
 | ||||||
| 	case diff < 2*Hour: | 	case diff < 2*Hour: | ||||||
| 		return fmt.Sprintf("1 hour %s", lbl) | 		return i18n.Tr(lang, "tool.1h", lbl) | ||||||
| 	case diff < 1*Day: | 	case diff < 1*Day: | ||||||
| 		return fmt.Sprintf("%d hours %s", diff/Hour, lbl) | 		return i18n.Tr(lang, "tool.hours", diff/Hour, lbl) | ||||||
| 
 | 
 | ||||||
| 	case diff < 2*Day: | 	case diff < 2*Day: | ||||||
| 		return fmt.Sprintf("1 day %s", lbl) | 		return i18n.Tr(lang, "tool.1d", lbl) | ||||||
| 	case diff < 1*Week: | 	case diff < 1*Week: | ||||||
| 		return fmt.Sprintf("%d days %s", diff/Day, lbl) | 		return i18n.Tr(lang, "tool.days", diff/Day, lbl) | ||||||
| 
 | 
 | ||||||
| 	case diff < 2*Week: | 	case diff < 2*Week: | ||||||
| 		return fmt.Sprintf("1 week %s", lbl) | 		return i18n.Tr(lang, "tool.1w", lbl) | ||||||
| 	case diff < 1*Month: | 	case diff < 1*Month: | ||||||
| 		return fmt.Sprintf("%d weeks %s", diff/Week, lbl) | 		return i18n.Tr(lang, "tool.weeks", diff/Week, lbl) | ||||||
| 
 | 
 | ||||||
| 	case diff < 2*Month: | 	case diff < 2*Month: | ||||||
| 		return fmt.Sprintf("1 month %s", lbl) | 		return i18n.Tr(lang, "tool.1mon", lbl) | ||||||
| 	case diff < 1*Year: | 	case diff < 1*Year: | ||||||
| 		return fmt.Sprintf("%d months %s", diff/Month, lbl) | 		return i18n.Tr(lang, "tool.months", diff/Month, lbl) | ||||||
| 
 | 
 | ||||||
| 	case diff < 2*Year: | 	case diff < 2*Year: | ||||||
| 		return fmt.Sprintf("1 year %s", lbl) | 		return i18n.Tr(lang, "tool.1y", lbl) | ||||||
| 	default: | 	default: | ||||||
| 		return fmt.Sprintf("%d years %s", diff/Year, lbl) | 		return i18n.Tr(lang, "tool.years", diff/Year, lbl) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // TimeSince calculates the time interval and generate user-friendly string.
 | // TimeSince calculates the time interval and generate user-friendly string.
 | ||||||
| func TimeSince(t time.Time) template.HTML { | func TimeSince(t time.Time, lang string) template.HTML { | ||||||
| 	return template.HTML(fmt.Sprintf(`<span class="time-since" title="%s">%s</span>`, t.Format(setting.TimeFormat), timeSince(t))) | 	return template.HTML(fmt.Sprintf(`<span class="time-since" title="%s">%s</span>`, t.Format(setting.TimeFormat), timeSince(t, lang))) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
|  | @ -445,88 +474,3 @@ func DateFormat(t time.Time, format string) string { | ||||||
| 	format = replacer.Replace(format) | 	format = replacer.Replace(format) | ||||||
| 	return t.Format(format) | 	return t.Format(format) | ||||||
| } | } | ||||||
| 
 |  | ||||||
| // convert string to specify type
 |  | ||||||
| 
 |  | ||||||
| type StrTo string |  | ||||||
| 
 |  | ||||||
| func (f StrTo) Exist() bool { |  | ||||||
| 	return string(f) != string(0x1E) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f StrTo) Int() (int, error) { |  | ||||||
| 	v, err := strconv.ParseInt(f.String(), 10, 32) |  | ||||||
| 	return int(v), err |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f StrTo) Int64() (int64, error) { |  | ||||||
| 	v, err := strconv.ParseInt(f.String(), 10, 64) |  | ||||||
| 	return int64(v), err |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f StrTo) MustInt() int { |  | ||||||
| 	v, _ := f.Int() |  | ||||||
| 	return v |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f StrTo) MustInt64() int64 { |  | ||||||
| 	v, _ := f.Int64() |  | ||||||
| 	return v |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f StrTo) String() string { |  | ||||||
| 	if f.Exist() { |  | ||||||
| 		return string(f) |  | ||||||
| 	} |  | ||||||
| 	return "" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // convert any type to string
 |  | ||||||
| func ToStr(value interface{}, args ...int) (s string) { |  | ||||||
| 	switch v := value.(type) { |  | ||||||
| 	case bool: |  | ||||||
| 		s = strconv.FormatBool(v) |  | ||||||
| 	case float32: |  | ||||||
| 		s = strconv.FormatFloat(float64(v), 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 32)) |  | ||||||
| 	case float64: |  | ||||||
| 		s = strconv.FormatFloat(v, 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 64)) |  | ||||||
| 	case int: |  | ||||||
| 		s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10)) |  | ||||||
| 	case int8: |  | ||||||
| 		s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10)) |  | ||||||
| 	case int16: |  | ||||||
| 		s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10)) |  | ||||||
| 	case int32: |  | ||||||
| 		s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10)) |  | ||||||
| 	case int64: |  | ||||||
| 		s = strconv.FormatInt(v, argInt(args).Get(0, 10)) |  | ||||||
| 	case uint: |  | ||||||
| 		s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10)) |  | ||||||
| 	case uint8: |  | ||||||
| 		s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10)) |  | ||||||
| 	case uint16: |  | ||||||
| 		s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10)) |  | ||||||
| 	case uint32: |  | ||||||
| 		s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10)) |  | ||||||
| 	case uint64: |  | ||||||
| 		s = strconv.FormatUint(v, argInt(args).Get(0, 10)) |  | ||||||
| 	case string: |  | ||||||
| 		s = v |  | ||||||
| 	case []byte: |  | ||||||
| 		s = string(v) |  | ||||||
| 	default: |  | ||||||
| 		s = fmt.Sprintf("%v", v) |  | ||||||
| 	} |  | ||||||
| 	return s |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type argInt []int |  | ||||||
| 
 |  | ||||||
| func (a argInt) Get(i int, args ...int) (r int) { |  | ||||||
| 	if i >= 0 && i < len(a) { |  | ||||||
| 		r = a[i] |  | ||||||
| 	} else if len(args) > 0 { |  | ||||||
| 		r = args[0] |  | ||||||
| 	} |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
|  |  | ||||||
							
								
								
									
										3623
									
								
								modules/bin/conf.go
									
									
									
									
									
								
							
							
						
						
							
								
								
									
										201
									
								
								modules/captcha/captcha.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,201 @@ | ||||||
|  | // 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 captcha a middleware that provides captcha service for Macaron.
 | ||||||
|  | package captcha | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"html/template" | ||||||
|  | 	"net/http" | ||||||
|  | 	"path" | ||||||
|  | 	"strings" | ||||||
|  | 
 | ||||||
|  | 	"github.com/Unknwon/macaron" | ||||||
|  | 
 | ||||||
|  | 	"github.com/gogits/cache" | ||||||
|  | 
 | ||||||
|  | 	"github.com/gogits/gogs/modules/base" | ||||||
|  | 	"github.com/gogits/gogs/modules/log" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var ( | ||||||
|  | 	defaultChars = []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	// default captcha attributes
 | ||||||
|  | 	challengeNums    = 6 | ||||||
|  | 	expiration       = 600 | ||||||
|  | 	fieldIdName      = "captcha_id" | ||||||
|  | 	fieldCaptchaName = "captcha" | ||||||
|  | 	cachePrefix      = "captcha_" | ||||||
|  | 	defaultURLPrefix = "/captcha/" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Captcha struct
 | ||||||
|  | type Captcha struct { | ||||||
|  | 	store cache.Cache | ||||||
|  | 
 | ||||||
|  | 	// url prefix for captcha image
 | ||||||
|  | 	URLPrefix string | ||||||
|  | 
 | ||||||
|  | 	// specify captcha id input field name
 | ||||||
|  | 	FieldIdName string | ||||||
|  | 	// specify captcha result input field name
 | ||||||
|  | 	FieldCaptchaName string | ||||||
|  | 
 | ||||||
|  | 	// captcha image width and height
 | ||||||
|  | 	StdWidth  int | ||||||
|  | 	StdHeight int | ||||||
|  | 
 | ||||||
|  | 	// captcha chars nums
 | ||||||
|  | 	ChallengeNums int | ||||||
|  | 
 | ||||||
|  | 	// captcha expiration seconds
 | ||||||
|  | 	Expiration int64 | ||||||
|  | 
 | ||||||
|  | 	// cache key prefix
 | ||||||
|  | 	CachePrefix string | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // generate key string
 | ||||||
|  | func (c *Captcha) key(id string) string { | ||||||
|  | 	return c.CachePrefix + id | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // generate rand chars with default chars
 | ||||||
|  | func (c *Captcha) genRandChars() []byte { | ||||||
|  | 	return base.RandomCreateBytes(c.ChallengeNums, defaultChars...) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // beego filter handler for serve captcha image
 | ||||||
|  | func (c *Captcha) Handler(ctx *macaron.Context) { | ||||||
|  | 	var chars []byte | ||||||
|  | 
 | ||||||
|  | 	id := path.Base(ctx.Req.RequestURI) | ||||||
|  | 	if i := strings.Index(id, "."); i != -1 { | ||||||
|  | 		id = id[:i] | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	key := c.key(id) | ||||||
|  | 
 | ||||||
|  | 	if v, ok := c.store.Get(key).([]byte); ok { | ||||||
|  | 		chars = v | ||||||
|  | 	} else { | ||||||
|  | 		ctx.Status(404) | ||||||
|  | 		ctx.Write([]byte("captcha not found")) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// reload captcha
 | ||||||
|  | 	if len(ctx.Query("reload")) > 0 { | ||||||
|  | 		chars = c.genRandChars() | ||||||
|  | 		if err := c.store.Put(key, chars, c.Expiration); err != nil { | ||||||
|  | 			ctx.Status(500) | ||||||
|  | 			ctx.Write([]byte("captcha reload error")) | ||||||
|  | 			log.Error(4, "Reload Create Captcha Error: %v", err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	img := NewImage(chars, c.StdWidth, c.StdHeight) | ||||||
|  | 	if _, err := img.WriteTo(ctx.RW()); err != nil { | ||||||
|  | 		log.Error(4, "Write Captcha Image Error: %v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // tempalte func for output html
 | ||||||
|  | func (c *Captcha) CreateCaptchaHtml() template.HTML { | ||||||
|  | 	value, err := c.CreateCaptcha() | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Error(4, "Create Captcha Error: %v", err) | ||||||
|  | 		return "" | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// create html
 | ||||||
|  | 	return template.HTML(fmt.Sprintf(`<input type="hidden" name="%s" value="%s">`+ | ||||||
|  | 		`<a class="captcha" href="javascript:">`+ | ||||||
|  | 		`<img onclick="this.src=('%s%s.png?reload='+(new Date()).getTime())" class="captcha-img" src="%s%s.png">`+ | ||||||
|  | 		`</a>`, c.FieldIdName, value, c.URLPrefix, value, c.URLPrefix, value)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // create a new captcha id
 | ||||||
|  | func (c *Captcha) CreateCaptcha() (string, error) { | ||||||
|  | 	// generate captcha id
 | ||||||
|  | 	id := string(base.RandomCreateBytes(15)) | ||||||
|  | 
 | ||||||
|  | 	// get the captcha chars
 | ||||||
|  | 	chars := c.genRandChars() | ||||||
|  | 
 | ||||||
|  | 	// save to store
 | ||||||
|  | 	if err := c.store.Put(c.key(id), chars, c.Expiration); err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return id, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // verify from a request
 | ||||||
|  | func (c *Captcha) VerifyReq(req *http.Request) bool { | ||||||
|  | 	req.ParseForm() | ||||||
|  | 	return c.Verify(req.Form.Get(c.FieldIdName), req.Form.Get(c.FieldCaptchaName)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // direct verify id and challenge string
 | ||||||
|  | func (c *Captcha) Verify(id string, challenge string) (success bool) { | ||||||
|  | 	if len(challenge) == 0 || len(id) == 0 { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var chars []byte | ||||||
|  | 
 | ||||||
|  | 	key := c.key(id) | ||||||
|  | 
 | ||||||
|  | 	if v, ok := c.store.Get(key).([]byte); ok && len(v) == len(challenge) { | ||||||
|  | 		chars = v | ||||||
|  | 	} else { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	defer func() { | ||||||
|  | 		// finally remove it
 | ||||||
|  | 		c.store.Delete(key) | ||||||
|  | 	}() | ||||||
|  | 
 | ||||||
|  | 	// verify challenge
 | ||||||
|  | 	for i, c := range chars { | ||||||
|  | 		if c != challenge[i]-48 { | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // create a new captcha.Captcha
 | ||||||
|  | func NewCaptcha(urlPrefix string, store cache.Cache) *Captcha { | ||||||
|  | 	cpt := &Captcha{} | ||||||
|  | 	cpt.store = store | ||||||
|  | 	cpt.FieldIdName = fieldIdName | ||||||
|  | 	cpt.FieldCaptchaName = fieldCaptchaName | ||||||
|  | 	cpt.ChallengeNums = challengeNums | ||||||
|  | 	cpt.Expiration = expiration | ||||||
|  | 	cpt.CachePrefix = cachePrefix | ||||||
|  | 	cpt.StdWidth = stdWidth | ||||||
|  | 	cpt.StdHeight = stdHeight | ||||||
|  | 
 | ||||||
|  | 	if len(urlPrefix) == 0 { | ||||||
|  | 		urlPrefix = defaultURLPrefix | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if urlPrefix[len(urlPrefix)-1] != '/' { | ||||||
|  | 		urlPrefix += "/" | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	cpt.URLPrefix = urlPrefix | ||||||
|  | 
 | ||||||
|  | 	base.TemplateFuncs["CreateCaptcha"] = cpt.CreateCaptchaHtml | ||||||
|  | 	return cpt | ||||||
|  | } | ||||||
							
								
								
									
										487
									
								
								modules/captcha/image.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,487 @@ | ||||||
|  | // 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 captcha | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"image" | ||||||
|  | 	"image/color" | ||||||
|  | 	"image/png" | ||||||
|  | 	"io" | ||||||
|  | 	"math" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	fontWidth  = 11 | ||||||
|  | 	fontHeight = 18 | ||||||
|  | 	blackChar  = 1 | ||||||
|  | 
 | ||||||
|  | 	// Standard width and height of a captcha image.
 | ||||||
|  | 	stdWidth  = 240 | ||||||
|  | 	stdHeight = 80 | ||||||
|  | 	// Maximum absolute skew factor of a single digit.
 | ||||||
|  | 	maxSkew = 0.7 | ||||||
|  | 	// Number of background circles.
 | ||||||
|  | 	circleCount = 20 | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var font = [][]byte{ | ||||||
|  | 	{ // 0
 | ||||||
|  | 		0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, | ||||||
|  | 		0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, | ||||||
|  | 		0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, | ||||||
|  | 		0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, | ||||||
|  | 		1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, | ||||||
|  | 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||||
|  | 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||||
|  | 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||||
|  | 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||||
|  | 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||||
|  | 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||||
|  | 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||||
|  | 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||||
|  | 		1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, | ||||||
|  | 		0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, | ||||||
|  | 		0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, | ||||||
|  | 		0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, | ||||||
|  | 		0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, | ||||||
|  | 	}, | ||||||
|  | 	{ // 1
 | ||||||
|  | 		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, | ||||||
|  | 		0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, | ||||||
|  | 		0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, | ||||||
|  | 		0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, | ||||||
|  | 		0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | ||||||
|  | 		0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 		0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 	}, | ||||||
|  | 	{ // 2
 | ||||||
|  | 		0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, | ||||||
|  | 		0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, | ||||||
|  | 		1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, | ||||||
|  | 		0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, | ||||||
|  | 		0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, | ||||||
|  | 		0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, | ||||||
|  | 		0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 		0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 	}, | ||||||
|  | 	{ // 3
 | ||||||
|  | 		0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, | ||||||
|  | 		1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, | ||||||
|  | 		1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, | ||||||
|  | 		0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, | ||||||
|  | 		0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, | ||||||
|  | 		1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, | ||||||
|  | 		1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, | ||||||
|  | 		0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, | ||||||
|  | 	}, | ||||||
|  | 	{ // 4
 | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, | ||||||
|  | 		0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, | ||||||
|  | 		0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, | ||||||
|  | 		0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, | ||||||
|  | 		0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, | ||||||
|  | 		0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, | ||||||
|  | 		1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, | ||||||
|  | 		1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, | ||||||
|  | 		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, | ||||||
|  | 	}, | ||||||
|  | 	{ // 5
 | ||||||
|  | 		0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, | ||||||
|  | 		0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, | ||||||
|  | 		0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 		0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 		0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 		0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 		0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, | ||||||
|  | 		0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, | ||||||
|  | 		1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, | ||||||
|  | 		1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, | ||||||
|  | 		0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, | ||||||
|  | 	}, | ||||||
|  | 	{ // 6
 | ||||||
|  | 		0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, | ||||||
|  | 		0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, | ||||||
|  | 		0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, | ||||||
|  | 		0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 		0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 		0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 		1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, | ||||||
|  | 		1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, | ||||||
|  | 		1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, | ||||||
|  | 		1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, | ||||||
|  | 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||||
|  | 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||||
|  | 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||||
|  | 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||||
|  | 		0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, | ||||||
|  | 		0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, | ||||||
|  | 		0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, | ||||||
|  | 		0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, | ||||||
|  | 	}, | ||||||
|  | 	{ // 7
 | ||||||
|  | 		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 		1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, | ||||||
|  | 		1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, | ||||||
|  | 		0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, | ||||||
|  | 		0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, | ||||||
|  | 		0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, | ||||||
|  | 	}, | ||||||
|  | 	{ // 8
 | ||||||
|  | 		0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, | ||||||
|  | 		0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, | ||||||
|  | 		0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, | ||||||
|  | 		0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, | ||||||
|  | 		0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, | ||||||
|  | 		0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, | ||||||
|  | 		0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, | ||||||
|  | 		0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, | ||||||
|  | 		0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, | ||||||
|  | 		0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, | ||||||
|  | 		0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, | ||||||
|  | 		1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, | ||||||
|  | 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||||
|  | 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||||
|  | 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||||
|  | 		1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, | ||||||
|  | 		0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, | ||||||
|  | 		0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, | ||||||
|  | 	}, | ||||||
|  | 	{ // 9
 | ||||||
|  | 		0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, | ||||||
|  | 		0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, | ||||||
|  | 		0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, | ||||||
|  | 		1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, | ||||||
|  | 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||||
|  | 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||||
|  | 		1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||||
|  | 		1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, | ||||||
|  | 		0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, | ||||||
|  | 		0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, | ||||||
|  | 		0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, | ||||||
|  | 		0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, | ||||||
|  | 		0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, | ||||||
|  | 		0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, | ||||||
|  | 	}, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type Image struct { | ||||||
|  | 	*image.Paletted | ||||||
|  | 	numWidth  int | ||||||
|  | 	numHeight int | ||||||
|  | 	dotSize   int | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | var prng = &siprng{} | ||||||
|  | 
 | ||||||
|  | // randIntn returns a pseudorandom non-negative int in range [0, n).
 | ||||||
|  | func randIntn(n int) int { | ||||||
|  | 	return prng.Intn(n) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // randInt returns a pseudorandom int in range [from, to].
 | ||||||
|  | func randInt(from, to int) int { | ||||||
|  | 	return prng.Intn(to+1-from) + from | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // randFloat returns a pseudorandom float64 in range [from, to].
 | ||||||
|  | func randFloat(from, to float64) float64 { | ||||||
|  | 	return (to-from)*prng.Float64() + from | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func randomPalette() color.Palette { | ||||||
|  | 	p := make([]color.Color, circleCount+1) | ||||||
|  | 	// Transparent color.
 | ||||||
|  | 	p[0] = color.RGBA{0xFF, 0xFF, 0xFF, 0x00} | ||||||
|  | 	// Primary color.
 | ||||||
|  | 	prim := color.RGBA{ | ||||||
|  | 		uint8(randIntn(129)), | ||||||
|  | 		uint8(randIntn(129)), | ||||||
|  | 		uint8(randIntn(129)), | ||||||
|  | 		0xFF, | ||||||
|  | 	} | ||||||
|  | 	p[1] = prim | ||||||
|  | 	// Circle colors.
 | ||||||
|  | 	for i := 2; i <= circleCount; i++ { | ||||||
|  | 		p[i] = randomBrightness(prim, 255) | ||||||
|  | 	} | ||||||
|  | 	return p | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // NewImage returns a new captcha image of the given width and height with the
 | ||||||
|  | // given digits, where each digit must be in range 0-9.
 | ||||||
|  | func NewImage(digits []byte, width, height int) *Image { | ||||||
|  | 	m := new(Image) | ||||||
|  | 	m.Paletted = image.NewPaletted(image.Rect(0, 0, width, height), randomPalette()) | ||||||
|  | 	m.calculateSizes(width, height, len(digits)) | ||||||
|  | 	// Randomly position captcha inside the image.
 | ||||||
|  | 	maxx := width - (m.numWidth+m.dotSize)*len(digits) - m.dotSize | ||||||
|  | 	maxy := height - m.numHeight - m.dotSize*2 | ||||||
|  | 	var border int | ||||||
|  | 	if width > height { | ||||||
|  | 		border = height / 5 | ||||||
|  | 	} else { | ||||||
|  | 		border = width / 5 | ||||||
|  | 	} | ||||||
|  | 	x := randInt(border, maxx-border) | ||||||
|  | 	y := randInt(border, maxy-border) | ||||||
|  | 	// Draw digits.
 | ||||||
|  | 	for _, n := range digits { | ||||||
|  | 		m.drawDigit(font[n], x, y) | ||||||
|  | 		x += m.numWidth + m.dotSize | ||||||
|  | 	} | ||||||
|  | 	// Draw strike-through line.
 | ||||||
|  | 	m.strikeThrough() | ||||||
|  | 	// Apply wave distortion.
 | ||||||
|  | 	m.distort(randFloat(5, 10), randFloat(100, 200)) | ||||||
|  | 	// Fill image with random circles.
 | ||||||
|  | 	m.fillWithCircles(circleCount, m.dotSize) | ||||||
|  | 	return m | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // encodedPNG encodes an image to PNG and returns
 | ||||||
|  | // the result as a byte slice.
 | ||||||
|  | func (m *Image) encodedPNG() []byte { | ||||||
|  | 	var buf bytes.Buffer | ||||||
|  | 	if err := png.Encode(&buf, m.Paletted); err != nil { | ||||||
|  | 		panic(err.Error()) | ||||||
|  | 	} | ||||||
|  | 	return buf.Bytes() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // WriteTo writes captcha image in PNG format into the given writer.
 | ||||||
|  | func (m *Image) WriteTo(w io.Writer) (int64, error) { | ||||||
|  | 	n, err := w.Write(m.encodedPNG()) | ||||||
|  | 	return int64(n), err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *Image) calculateSizes(width, height, ncount int) { | ||||||
|  | 	// Goal: fit all digits inside the image.
 | ||||||
|  | 	var border int | ||||||
|  | 	if width > height { | ||||||
|  | 		border = height / 4 | ||||||
|  | 	} else { | ||||||
|  | 		border = width / 4 | ||||||
|  | 	} | ||||||
|  | 	// Convert everything to floats for calculations.
 | ||||||
|  | 	w := float64(width - border*2) | ||||||
|  | 	h := float64(height - border*2) | ||||||
|  | 	// fw takes into account 1-dot spacing between digits.
 | ||||||
|  | 	fw := float64(fontWidth + 1) | ||||||
|  | 	fh := float64(fontHeight) | ||||||
|  | 	nc := float64(ncount) | ||||||
|  | 	// Calculate the width of a single digit taking into account only the
 | ||||||
|  | 	// width of the image.
 | ||||||
|  | 	nw := w / nc | ||||||
|  | 	// Calculate the height of a digit from this width.
 | ||||||
|  | 	nh := nw * fh / fw | ||||||
|  | 	// Digit too high?
 | ||||||
|  | 	if nh > h { | ||||||
|  | 		// Fit digits based on height.
 | ||||||
|  | 		nh = h | ||||||
|  | 		nw = fw / fh * nh | ||||||
|  | 	} | ||||||
|  | 	// Calculate dot size.
 | ||||||
|  | 	m.dotSize = int(nh / fh) | ||||||
|  | 	// Save everything, making the actual width smaller by 1 dot to account
 | ||||||
|  | 	// for spacing between digits.
 | ||||||
|  | 	m.numWidth = int(nw) - m.dotSize | ||||||
|  | 	m.numHeight = int(nh) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *Image) drawHorizLine(fromX, toX, y int, colorIdx uint8) { | ||||||
|  | 	for x := fromX; x <= toX; x++ { | ||||||
|  | 		m.SetColorIndex(x, y, colorIdx) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *Image) drawCircle(x, y, radius int, colorIdx uint8) { | ||||||
|  | 	f := 1 - radius | ||||||
|  | 	dfx := 1 | ||||||
|  | 	dfy := -2 * radius | ||||||
|  | 	xo := 0 | ||||||
|  | 	yo := radius | ||||||
|  | 
 | ||||||
|  | 	m.SetColorIndex(x, y+radius, colorIdx) | ||||||
|  | 	m.SetColorIndex(x, y-radius, colorIdx) | ||||||
|  | 	m.drawHorizLine(x-radius, x+radius, y, colorIdx) | ||||||
|  | 
 | ||||||
|  | 	for xo < yo { | ||||||
|  | 		if f >= 0 { | ||||||
|  | 			yo-- | ||||||
|  | 			dfy += 2 | ||||||
|  | 			f += dfy | ||||||
|  | 		} | ||||||
|  | 		xo++ | ||||||
|  | 		dfx += 2 | ||||||
|  | 		f += dfx | ||||||
|  | 		m.drawHorizLine(x-xo, x+xo, y+yo, colorIdx) | ||||||
|  | 		m.drawHorizLine(x-xo, x+xo, y-yo, colorIdx) | ||||||
|  | 		m.drawHorizLine(x-yo, x+yo, y+xo, colorIdx) | ||||||
|  | 		m.drawHorizLine(x-yo, x+yo, y-xo, colorIdx) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *Image) fillWithCircles(n, maxradius int) { | ||||||
|  | 	maxx := m.Bounds().Max.X | ||||||
|  | 	maxy := m.Bounds().Max.Y | ||||||
|  | 	for i := 0; i < n; i++ { | ||||||
|  | 		colorIdx := uint8(randInt(1, circleCount-1)) | ||||||
|  | 		r := randInt(1, maxradius) | ||||||
|  | 		m.drawCircle(randInt(r, maxx-r), randInt(r, maxy-r), r, colorIdx) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *Image) strikeThrough() { | ||||||
|  | 	maxx := m.Bounds().Max.X | ||||||
|  | 	maxy := m.Bounds().Max.Y | ||||||
|  | 	y := randInt(maxy/3, maxy-maxy/3) | ||||||
|  | 	amplitude := randFloat(5, 20) | ||||||
|  | 	period := randFloat(80, 180) | ||||||
|  | 	dx := 2.0 * math.Pi / period | ||||||
|  | 	for x := 0; x < maxx; x++ { | ||||||
|  | 		xo := amplitude * math.Cos(float64(y)*dx) | ||||||
|  | 		yo := amplitude * math.Sin(float64(x)*dx) | ||||||
|  | 		for yn := 0; yn < m.dotSize; yn++ { | ||||||
|  | 			r := randInt(0, m.dotSize) | ||||||
|  | 			m.drawCircle(x+int(xo), y+int(yo)+(yn*m.dotSize), r/2, 1) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *Image) drawDigit(digit []byte, x, y int) { | ||||||
|  | 	skf := randFloat(-maxSkew, maxSkew) | ||||||
|  | 	xs := float64(x) | ||||||
|  | 	r := m.dotSize / 2 | ||||||
|  | 	y += randInt(-r, r) | ||||||
|  | 	for yo := 0; yo < fontHeight; yo++ { | ||||||
|  | 		for xo := 0; xo < fontWidth; xo++ { | ||||||
|  | 			if digit[yo*fontWidth+xo] != blackChar { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			m.drawCircle(x+xo*m.dotSize, y+yo*m.dotSize, r, 1) | ||||||
|  | 		} | ||||||
|  | 		xs += skf | ||||||
|  | 		x = int(xs) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *Image) distort(amplude float64, period float64) { | ||||||
|  | 	w := m.Bounds().Max.X | ||||||
|  | 	h := m.Bounds().Max.Y | ||||||
|  | 
 | ||||||
|  | 	oldm := m.Paletted | ||||||
|  | 	newm := image.NewPaletted(image.Rect(0, 0, w, h), oldm.Palette) | ||||||
|  | 
 | ||||||
|  | 	dx := 2.0 * math.Pi / period | ||||||
|  | 	for x := 0; x < w; x++ { | ||||||
|  | 		for y := 0; y < h; y++ { | ||||||
|  | 			xo := amplude * math.Sin(float64(y)*dx) | ||||||
|  | 			yo := amplude * math.Cos(float64(x)*dx) | ||||||
|  | 			newm.SetColorIndex(x, y, oldm.ColorIndexAt(x+int(xo), y+int(yo))) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	m.Paletted = newm | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func randomBrightness(c color.RGBA, max uint8) color.RGBA { | ||||||
|  | 	minc := min3(c.R, c.G, c.B) | ||||||
|  | 	maxc := max3(c.R, c.G, c.B) | ||||||
|  | 	if maxc > max { | ||||||
|  | 		return c | ||||||
|  | 	} | ||||||
|  | 	n := randIntn(int(max-maxc)) - int(minc) | ||||||
|  | 	return color.RGBA{ | ||||||
|  | 		uint8(int(c.R) + n), | ||||||
|  | 		uint8(int(c.G) + n), | ||||||
|  | 		uint8(int(c.B) + n), | ||||||
|  | 		uint8(c.A), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func min3(x, y, z uint8) (m uint8) { | ||||||
|  | 	m = x | ||||||
|  | 	if y < m { | ||||||
|  | 		m = y | ||||||
|  | 	} | ||||||
|  | 	if z < m { | ||||||
|  | 		m = z | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func max3(x, y, z uint8) (m uint8) { | ||||||
|  | 	m = x | ||||||
|  | 	if y > m { | ||||||
|  | 		m = y | ||||||
|  | 	} | ||||||
|  | 	if z > m { | ||||||
|  | 		m = z | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
							
								
								
									
										42
									
								
								modules/captcha/image_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,42 @@ | ||||||
|  | // 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 captcha | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"github.com/gogits/gogs/modules/base" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type byteCounter struct { | ||||||
|  | 	n int64 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (bc *byteCounter) Write(b []byte) (int, error) { | ||||||
|  | 	bc.n += int64(len(b)) | ||||||
|  | 	return len(b), nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func BenchmarkNewImage(b *testing.B) { | ||||||
|  | 	b.StopTimer() | ||||||
|  | 	d := base.RandomCreateBytes(challengeNums, defaultChars...) | ||||||
|  | 	b.StartTimer() | ||||||
|  | 	for i := 0; i < b.N; i++ { | ||||||
|  | 		NewImage(d, stdWidth, stdHeight) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func BenchmarkImageWriteTo(b *testing.B) { | ||||||
|  | 	b.StopTimer() | ||||||
|  | 	d := base.RandomCreateBytes(challengeNums, defaultChars...) | ||||||
|  | 	b.StartTimer() | ||||||
|  | 	counter := &byteCounter{} | ||||||
|  | 	for i := 0; i < b.N; i++ { | ||||||
|  | 		img := NewImage(d, stdWidth, stdHeight) | ||||||
|  | 		img.WriteTo(counter) | ||||||
|  | 		b.SetBytes(counter.n) | ||||||
|  | 		counter.n = 0 | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										267
									
								
								modules/captcha/siprng.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,267 @@ | ||||||
|  | // 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 captcha | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"crypto/rand" | ||||||
|  | 	"encoding/binary" | ||||||
|  | 	"io" | ||||||
|  | 	"sync" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // siprng is PRNG based on SipHash-2-4.
 | ||||||
|  | type siprng struct { | ||||||
|  | 	mu          sync.Mutex | ||||||
|  | 	k0, k1, ctr uint64 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // siphash implements SipHash-2-4, accepting a uint64 as a message.
 | ||||||
|  | func siphash(k0, k1, m uint64) uint64 { | ||||||
|  | 	// Initialization.
 | ||||||
|  | 	v0 := k0 ^ 0x736f6d6570736575 | ||||||
|  | 	v1 := k1 ^ 0x646f72616e646f6d | ||||||
|  | 	v2 := k0 ^ 0x6c7967656e657261 | ||||||
|  | 	v3 := k1 ^ 0x7465646279746573 | ||||||
|  | 	t := uint64(8) << 56 | ||||||
|  | 
 | ||||||
|  | 	// Compression.
 | ||||||
|  | 	v3 ^= m | ||||||
|  | 
 | ||||||
|  | 	// Round 1.
 | ||||||
|  | 	v0 += v1 | ||||||
|  | 	v1 = v1<<13 | v1>>(64-13) | ||||||
|  | 	v1 ^= v0 | ||||||
|  | 	v0 = v0<<32 | v0>>(64-32) | ||||||
|  | 
 | ||||||
|  | 	v2 += v3 | ||||||
|  | 	v3 = v3<<16 | v3>>(64-16) | ||||||
|  | 	v3 ^= v2 | ||||||
|  | 
 | ||||||
|  | 	v0 += v3 | ||||||
|  | 	v3 = v3<<21 | v3>>(64-21) | ||||||
|  | 	v3 ^= v0 | ||||||
|  | 
 | ||||||
|  | 	v2 += v1 | ||||||
|  | 	v1 = v1<<17 | v1>>(64-17) | ||||||
|  | 	v1 ^= v2 | ||||||
|  | 	v2 = v2<<32 | v2>>(64-32) | ||||||
|  | 
 | ||||||
|  | 	// Round 2.
 | ||||||
|  | 	v0 += v1 | ||||||
|  | 	v1 = v1<<13 | v1>>(64-13) | ||||||
|  | 	v1 ^= v0 | ||||||
|  | 	v0 = v0<<32 | v0>>(64-32) | ||||||
|  | 
 | ||||||
|  | 	v2 += v3 | ||||||
|  | 	v3 = v3<<16 | v3>>(64-16) | ||||||
|  | 	v3 ^= v2 | ||||||
|  | 
 | ||||||
|  | 	v0 += v3 | ||||||
|  | 	v3 = v3<<21 | v3>>(64-21) | ||||||
|  | 	v3 ^= v0 | ||||||
|  | 
 | ||||||
|  | 	v2 += v1 | ||||||
|  | 	v1 = v1<<17 | v1>>(64-17) | ||||||
|  | 	v1 ^= v2 | ||||||
|  | 	v2 = v2<<32 | v2>>(64-32) | ||||||
|  | 
 | ||||||
|  | 	v0 ^= m | ||||||
|  | 
 | ||||||
|  | 	// Compress last block.
 | ||||||
|  | 	v3 ^= t | ||||||
|  | 
 | ||||||
|  | 	// Round 1.
 | ||||||
|  | 	v0 += v1 | ||||||
|  | 	v1 = v1<<13 | v1>>(64-13) | ||||||
|  | 	v1 ^= v0 | ||||||
|  | 	v0 = v0<<32 | v0>>(64-32) | ||||||
|  | 
 | ||||||
|  | 	v2 += v3 | ||||||
|  | 	v3 = v3<<16 | v3>>(64-16) | ||||||
|  | 	v3 ^= v2 | ||||||
|  | 
 | ||||||
|  | 	v0 += v3 | ||||||
|  | 	v3 = v3<<21 | v3>>(64-21) | ||||||
|  | 	v3 ^= v0 | ||||||
|  | 
 | ||||||
|  | 	v2 += v1 | ||||||
|  | 	v1 = v1<<17 | v1>>(64-17) | ||||||
|  | 	v1 ^= v2 | ||||||
|  | 	v2 = v2<<32 | v2>>(64-32) | ||||||
|  | 
 | ||||||
|  | 	// Round 2.
 | ||||||
|  | 	v0 += v1 | ||||||
|  | 	v1 = v1<<13 | v1>>(64-13) | ||||||
|  | 	v1 ^= v0 | ||||||
|  | 	v0 = v0<<32 | v0>>(64-32) | ||||||
|  | 
 | ||||||
|  | 	v2 += v3 | ||||||
|  | 	v3 = v3<<16 | v3>>(64-16) | ||||||
|  | 	v3 ^= v2 | ||||||
|  | 
 | ||||||
|  | 	v0 += v3 | ||||||
|  | 	v3 = v3<<21 | v3>>(64-21) | ||||||
|  | 	v3 ^= v0 | ||||||
|  | 
 | ||||||
|  | 	v2 += v1 | ||||||
|  | 	v1 = v1<<17 | v1>>(64-17) | ||||||
|  | 	v1 ^= v2 | ||||||
|  | 	v2 = v2<<32 | v2>>(64-32) | ||||||
|  | 
 | ||||||
|  | 	v0 ^= t | ||||||
|  | 
 | ||||||
|  | 	// Finalization.
 | ||||||
|  | 	v2 ^= 0xff | ||||||
|  | 
 | ||||||
|  | 	// Round 1.
 | ||||||
|  | 	v0 += v1 | ||||||
|  | 	v1 = v1<<13 | v1>>(64-13) | ||||||
|  | 	v1 ^= v0 | ||||||
|  | 	v0 = v0<<32 | v0>>(64-32) | ||||||
|  | 
 | ||||||
|  | 	v2 += v3 | ||||||
|  | 	v3 = v3<<16 | v3>>(64-16) | ||||||
|  | 	v3 ^= v2 | ||||||
|  | 
 | ||||||
|  | 	v0 += v3 | ||||||
|  | 	v3 = v3<<21 | v3>>(64-21) | ||||||
|  | 	v3 ^= v0 | ||||||
|  | 
 | ||||||
|  | 	v2 += v1 | ||||||
|  | 	v1 = v1<<17 | v1>>(64-17) | ||||||
|  | 	v1 ^= v2 | ||||||
|  | 	v2 = v2<<32 | v2>>(64-32) | ||||||
|  | 
 | ||||||
|  | 	// Round 2.
 | ||||||
|  | 	v0 += v1 | ||||||
|  | 	v1 = v1<<13 | v1>>(64-13) | ||||||
|  | 	v1 ^= v0 | ||||||
|  | 	v0 = v0<<32 | v0>>(64-32) | ||||||
|  | 
 | ||||||
|  | 	v2 += v3 | ||||||
|  | 	v3 = v3<<16 | v3>>(64-16) | ||||||
|  | 	v3 ^= v2 | ||||||
|  | 
 | ||||||
|  | 	v0 += v3 | ||||||
|  | 	v3 = v3<<21 | v3>>(64-21) | ||||||
|  | 	v3 ^= v0 | ||||||
|  | 
 | ||||||
|  | 	v2 += v1 | ||||||
|  | 	v1 = v1<<17 | v1>>(64-17) | ||||||
|  | 	v1 ^= v2 | ||||||
|  | 	v2 = v2<<32 | v2>>(64-32) | ||||||
|  | 
 | ||||||
|  | 	// Round 3.
 | ||||||
|  | 	v0 += v1 | ||||||
|  | 	v1 = v1<<13 | v1>>(64-13) | ||||||
|  | 	v1 ^= v0 | ||||||
|  | 	v0 = v0<<32 | v0>>(64-32) | ||||||
|  | 
 | ||||||
|  | 	v2 += v3 | ||||||
|  | 	v3 = v3<<16 | v3>>(64-16) | ||||||
|  | 	v3 ^= v2 | ||||||
|  | 
 | ||||||
|  | 	v0 += v3 | ||||||
|  | 	v3 = v3<<21 | v3>>(64-21) | ||||||
|  | 	v3 ^= v0 | ||||||
|  | 
 | ||||||
|  | 	v2 += v1 | ||||||
|  | 	v1 = v1<<17 | v1>>(64-17) | ||||||
|  | 	v1 ^= v2 | ||||||
|  | 	v2 = v2<<32 | v2>>(64-32) | ||||||
|  | 
 | ||||||
|  | 	// Round 4.
 | ||||||
|  | 	v0 += v1 | ||||||
|  | 	v1 = v1<<13 | v1>>(64-13) | ||||||
|  | 	v1 ^= v0 | ||||||
|  | 	v0 = v0<<32 | v0>>(64-32) | ||||||
|  | 
 | ||||||
|  | 	v2 += v3 | ||||||
|  | 	v3 = v3<<16 | v3>>(64-16) | ||||||
|  | 	v3 ^= v2 | ||||||
|  | 
 | ||||||
|  | 	v0 += v3 | ||||||
|  | 	v3 = v3<<21 | v3>>(64-21) | ||||||
|  | 	v3 ^= v0 | ||||||
|  | 
 | ||||||
|  | 	v2 += v1 | ||||||
|  | 	v1 = v1<<17 | v1>>(64-17) | ||||||
|  | 	v1 ^= v2 | ||||||
|  | 	v2 = v2<<32 | v2>>(64-32) | ||||||
|  | 
 | ||||||
|  | 	return v0 ^ v1 ^ v2 ^ v3 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // rekey sets a new PRNG key, which is read from crypto/rand.
 | ||||||
|  | func (p *siprng) rekey() { | ||||||
|  | 	var k [16]byte | ||||||
|  | 	if _, err := io.ReadFull(rand.Reader, k[:]); err != nil { | ||||||
|  | 		panic(err.Error()) | ||||||
|  | 	} | ||||||
|  | 	p.k0 = binary.LittleEndian.Uint64(k[0:8]) | ||||||
|  | 	p.k1 = binary.LittleEndian.Uint64(k[8:16]) | ||||||
|  | 	p.ctr = 1 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Uint64 returns a new pseudorandom uint64.
 | ||||||
|  | // It rekeys PRNG on the first call and every 64 MB of generated data.
 | ||||||
|  | func (p *siprng) Uint64() uint64 { | ||||||
|  | 	p.mu.Lock() | ||||||
|  | 	if p.ctr == 0 || p.ctr > 8*1024*1024 { | ||||||
|  | 		p.rekey() | ||||||
|  | 	} | ||||||
|  | 	v := siphash(p.k0, p.k1, p.ctr) | ||||||
|  | 	p.ctr++ | ||||||
|  | 	p.mu.Unlock() | ||||||
|  | 	return v | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *siprng) Int63() int64 { | ||||||
|  | 	return int64(p.Uint64() & 0x7fffffffffffffff) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *siprng) Uint32() uint32 { | ||||||
|  | 	return uint32(p.Uint64()) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *siprng) Int31() int32 { | ||||||
|  | 	return int32(p.Uint32() & 0x7fffffff) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *siprng) Intn(n int) int { | ||||||
|  | 	if n <= 0 { | ||||||
|  | 		panic("invalid argument to Intn") | ||||||
|  | 	} | ||||||
|  | 	if n <= 1<<31-1 { | ||||||
|  | 		return int(p.Int31n(int32(n))) | ||||||
|  | 	} | ||||||
|  | 	return int(p.Int63n(int64(n))) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *siprng) Int63n(n int64) int64 { | ||||||
|  | 	if n <= 0 { | ||||||
|  | 		panic("invalid argument to Int63n") | ||||||
|  | 	} | ||||||
|  | 	max := int64((1 << 63) - 1 - (1<<63)%uint64(n)) | ||||||
|  | 	v := p.Int63() | ||||||
|  | 	for v > max { | ||||||
|  | 		v = p.Int63() | ||||||
|  | 	} | ||||||
|  | 	return v % n | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *siprng) Int31n(n int32) int32 { | ||||||
|  | 	if n <= 0 { | ||||||
|  | 		panic("invalid argument to Int31n") | ||||||
|  | 	} | ||||||
|  | 	max := int32((1 << 31) - 1 - (1<<31)%uint32(n)) | ||||||
|  | 	v := p.Int31() | ||||||
|  | 	for v > max { | ||||||
|  | 		v = p.Int31() | ||||||
|  | 	} | ||||||
|  | 	return v % n | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *siprng) Float64() float64 { return float64(p.Int63()) / (1 << 63) } | ||||||
							
								
								
									
										23
									
								
								modules/captcha/siprng_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,23 @@ | ||||||
|  | // 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 captcha | ||||||
|  | 
 | ||||||
|  | import "testing" | ||||||
|  | 
 | ||||||
|  | func TestSiphash(t *testing.T) { | ||||||
|  | 	good := uint64(0xe849e8bb6ffe2567) | ||||||
|  | 	cur := siphash(0, 0, 0) | ||||||
|  | 	if cur != good { | ||||||
|  | 		t.Fatalf("siphash: expected %x, got %x", good, cur) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func BenchmarkSiprng(b *testing.B) { | ||||||
|  | 	b.SetBytes(8) | ||||||
|  | 	p := &siprng{} | ||||||
|  | 	for i := 0; i < b.N; i++ { | ||||||
|  | 		p.Uint64() | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										26
									
								
								modules/git/blob.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,26 @@ | ||||||
|  | // 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 git | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"errors" | ||||||
|  | 	"io" | ||||||
|  | 
 | ||||||
|  | 	"github.com/Unknwon/com" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type Blob struct { | ||||||
|  | 	repo *Repository | ||||||
|  | 	*TreeEntry | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (b *Blob) Data() (io.Reader, error) { | ||||||
|  | 	stdout, stderr, err := com.ExecCmdDirBytes(b.repo.Path, "git", "show", b.Id.String()) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, errors.New(string(stderr)) | ||||||
|  | 	} | ||||||
|  | 	return bytes.NewBuffer(stdout), nil | ||||||
|  | } | ||||||
							
								
								
									
										86
									
								
								modules/git/commit.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,86 @@ | ||||||
|  | // 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 git | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"container/list" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Commit represents a git commit.
 | ||||||
|  | type Commit struct { | ||||||
|  | 	Tree | ||||||
|  | 	Id            sha1 // The id of this commit object
 | ||||||
|  | 	Author        *Signature | ||||||
|  | 	Committer     *Signature | ||||||
|  | 	CommitMessage string | ||||||
|  | 
 | ||||||
|  | 	parents []sha1 // sha1 strings
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Return the commit message. Same as retrieving CommitMessage directly.
 | ||||||
|  | func (c *Commit) Message() string { | ||||||
|  | 	return c.CommitMessage | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Commit) Summary() string { | ||||||
|  | 	return strings.Split(c.CommitMessage, "\n")[0] | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Return oid of the parent number n (0-based index). Return nil if no such parent exists.
 | ||||||
|  | func (c *Commit) ParentId(n int) (id sha1, err error) { | ||||||
|  | 	if n >= len(c.parents) { | ||||||
|  | 		err = IdNotExist | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	return c.parents[n], nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Return parent number n (0-based index)
 | ||||||
|  | func (c *Commit) Parent(n int) (*Commit, error) { | ||||||
|  | 	id, err := c.ParentId(n) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	parent, err := c.repo.getCommit(id) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return parent, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Return the number of parents of the commit. 0 if this is the
 | ||||||
|  | // root commit, otherwise 1,2,...
 | ||||||
|  | func (c *Commit) ParentCount() int { | ||||||
|  | 	return len(c.parents) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Commit) CommitsBefore() (*list.List, error) { | ||||||
|  | 	return c.repo.getCommitsBefore(c.Id) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Commit) CommitsBeforeUntil(commitId string) (*list.List, error) { | ||||||
|  | 	ec, err := c.repo.GetCommit(commitId) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return c.repo.CommitsBetween(c, ec) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Commit) CommitsCount() (int, error) { | ||||||
|  | 	return c.repo.commitsCount(c.Id) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Commit) SearchCommits(keyword string) (*list.List, error) { | ||||||
|  | 	return c.repo.searchCommits(c.Id, keyword) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Commit) CommitsByRange(page int) (*list.List, error) { | ||||||
|  | 	return c.repo.commitsByRange(c.Id, page) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Commit) GetCommitOfRelPath(relPath string) (*Commit, error) { | ||||||
|  | 	return c.repo.getCommitOfRelPath(c.Id, relPath) | ||||||
|  | } | ||||||
							
								
								
									
										36
									
								
								modules/git/commit_archive.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,36 @@ | ||||||
|  | // 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 git | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 
 | ||||||
|  | 	"github.com/Unknwon/com" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type ArchiveType int | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	ZIP ArchiveType = iota + 1 | ||||||
|  | 	TARGZ | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func (c *Commit) CreateArchive(path string, archiveType ArchiveType) error { | ||||||
|  | 	var format string | ||||||
|  | 	switch archiveType { | ||||||
|  | 	case ZIP: | ||||||
|  | 		format = "zip" | ||||||
|  | 	case TARGZ: | ||||||
|  | 		format = "tar.gz" | ||||||
|  | 	default: | ||||||
|  | 		return fmt.Errorf("unknown format: %v", archiveType) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	_, stderr, err := com.ExecCmdDir(c.repo.Path, "git", "archive", "--format="+format, "-o", path, c.Id.String()) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("%s", stderr) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										27
									
								
								modules/git/repo.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,27 @@ | ||||||
|  | // 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 git | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"path/filepath" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Repository represents a Git repository.
 | ||||||
|  | type Repository struct { | ||||||
|  | 	Path string | ||||||
|  | 
 | ||||||
|  | 	commitCache map[sha1]*Commit | ||||||
|  | 	tagCache    map[sha1]*Tag | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // OpenRepository opens the repository at the given path.
 | ||||||
|  | func OpenRepository(repoPath string) (*Repository, error) { | ||||||
|  | 	repoPath, err := filepath.Abs(repoPath) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return &Repository{Path: repoPath}, nil | ||||||
|  | } | ||||||
							
								
								
									
										38
									
								
								modules/git/repo_branch.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,38 @@ | ||||||
|  | // 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 git | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"strings" | ||||||
|  | 
 | ||||||
|  | 	"github.com/Unknwon/com" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func IsBranchExist(repoPath, branchName string) bool { | ||||||
|  | 	_, _, err := com.ExecCmdDir(repoPath, "git", "show-ref", "--verify", "refs/heads/"+branchName) | ||||||
|  | 	return err == nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (repo *Repository) IsBranchExist(branchName string) bool { | ||||||
|  | 	return IsBranchExist(repo.Path, branchName) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (repo *Repository) GetBranches() ([]string, error) { | ||||||
|  | 	stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "show-ref", "--heads") | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, errors.New(stderr) | ||||||
|  | 	} | ||||||
|  | 	infos := strings.Split(stdout, "\n") | ||||||
|  | 	branches := make([]string, len(infos)-1) | ||||||
|  | 	for i, info := range infos[:len(infos)-1] { | ||||||
|  | 		parts := strings.Split(info, " ") | ||||||
|  | 		if len(parts) != 2 { | ||||||
|  | 			continue // NOTE: I should believe git will not give me wrong string.
 | ||||||
|  | 		} | ||||||
|  | 		branches[i] = strings.TrimPrefix(parts[1], "refs/heads/") | ||||||
|  | 	} | ||||||
|  | 	return branches, nil | ||||||
|  | } | ||||||
							
								
								
									
										291
									
								
								modules/git/repo_commit.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,291 @@ | ||||||
|  | // 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 git | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"container/list" | ||||||
|  | 	"errors" | ||||||
|  | 	"strings" | ||||||
|  | 	"sync" | ||||||
|  | 
 | ||||||
|  | 	"github.com/Unknwon/com" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func (repo *Repository) getCommitIdOfRef(refpath string) (string, error) { | ||||||
|  | 	stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "show-ref", "--verify", refpath) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", errors.New(stderr) | ||||||
|  | 	} | ||||||
|  | 	return strings.Split(stdout, " ")[0], nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (repo *Repository) GetCommitIdOfBranch(branchName string) (string, error) { | ||||||
|  | 	return repo.getCommitIdOfRef("refs/heads/" + branchName) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // get branch's last commit or a special commit by id string
 | ||||||
|  | func (repo *Repository) GetCommitOfBranch(branchName string) (*Commit, error) { | ||||||
|  | 	commitId, err := repo.GetCommitIdOfBranch(branchName) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return repo.GetCommit(commitId) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (repo *Repository) GetCommitIdOfTag(tagName string) (string, error) { | ||||||
|  | 	return repo.getCommitIdOfRef("refs/tags/" + tagName) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (repo *Repository) GetCommitOfTag(tagName string) (*Commit, error) { | ||||||
|  | 	commitId, err := repo.GetCommitIdOfTag(tagName) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return repo.GetCommit(commitId) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Parse commit information from the (uncompressed) raw
 | ||||||
|  | // data from the commit object.
 | ||||||
|  | // \n\n separate headers from message
 | ||||||
|  | func parseCommitData(data []byte) (*Commit, error) { | ||||||
|  | 	commit := new(Commit) | ||||||
|  | 	commit.parents = make([]sha1, 0, 1) | ||||||
|  | 	// we now have the contents of the commit object. Let's investigate...
 | ||||||
|  | 	nextline := 0 | ||||||
|  | l: | ||||||
|  | 	for { | ||||||
|  | 		eol := bytes.IndexByte(data[nextline:], '\n') | ||||||
|  | 		switch { | ||||||
|  | 		case eol > 0: | ||||||
|  | 			line := data[nextline : nextline+eol] | ||||||
|  | 			spacepos := bytes.IndexByte(line, ' ') | ||||||
|  | 			reftype := line[:spacepos] | ||||||
|  | 			switch string(reftype) { | ||||||
|  | 			case "tree": | ||||||
|  | 				id, err := NewIdFromString(string(line[spacepos+1:])) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return nil, err | ||||||
|  | 				} | ||||||
|  | 				commit.Tree.Id = id | ||||||
|  | 			case "parent": | ||||||
|  | 				// A commit can have one or more parents
 | ||||||
|  | 				oid, err := NewIdFromString(string(line[spacepos+1:])) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return nil, err | ||||||
|  | 				} | ||||||
|  | 				commit.parents = append(commit.parents, oid) | ||||||
|  | 			case "author": | ||||||
|  | 				sig, err := newSignatureFromCommitline(line[spacepos+1:]) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return nil, err | ||||||
|  | 				} | ||||||
|  | 				commit.Author = sig | ||||||
|  | 			case "committer": | ||||||
|  | 				sig, err := newSignatureFromCommitline(line[spacepos+1:]) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return nil, err | ||||||
|  | 				} | ||||||
|  | 				commit.Committer = sig | ||||||
|  | 			} | ||||||
|  | 			nextline += eol + 1 | ||||||
|  | 		case eol == 0: | ||||||
|  | 			commit.CommitMessage = string(data[nextline+1:]) | ||||||
|  | 			break l | ||||||
|  | 		default: | ||||||
|  | 			break l | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return commit, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (repo *Repository) getCommit(id sha1) (*Commit, error) { | ||||||
|  | 	if repo.commitCache != nil { | ||||||
|  | 		if c, ok := repo.commitCache[id]; ok { | ||||||
|  | 			return c, nil | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		repo.commitCache = make(map[sha1]*Commit, 10) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	data, bytErr, err := com.ExecCmdDirBytes(repo.Path, "git", "cat-file", "-p", id.String()) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, errors.New(string(bytErr)) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	commit, err := parseCommitData(data) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	commit.repo = repo | ||||||
|  | 	commit.Id = id | ||||||
|  | 
 | ||||||
|  | 	repo.commitCache[id] = commit | ||||||
|  | 	return commit, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Find the commit object in the repository.
 | ||||||
|  | func (repo *Repository) GetCommit(commitId string) (*Commit, error) { | ||||||
|  | 	id, err := NewIdFromString(commitId) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return repo.getCommit(id) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (repo *Repository) commitsCount(id sha1) (int, error) { | ||||||
|  | 	stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "rev-list", "--count", id.String()) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return 0, errors.New(stderr) | ||||||
|  | 	} | ||||||
|  | 	return com.StrTo(strings.TrimSpace(stdout)).Int() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // used only for single tree, (]
 | ||||||
|  | func (repo *Repository) CommitsBetween(last *Commit, before *Commit) (*list.List, error) { | ||||||
|  | 	l := list.New() | ||||||
|  | 	if last == nil || last.ParentCount() == 0 { | ||||||
|  | 		return l, nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var err error | ||||||
|  | 	cur := last | ||||||
|  | 	for { | ||||||
|  | 		if cur.Id.Equal(before.Id) { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 		l.PushBack(cur) | ||||||
|  | 		if cur.ParentCount() == 0 { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 		cur, err = cur.Parent(0) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return l, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (repo *Repository) commitsBefore(lock *sync.Mutex, l *list.List, parent *list.Element, id sha1, limit int) error { | ||||||
|  | 	commit, err := repo.getCommit(id) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var e *list.Element | ||||||
|  | 	if parent == nil { | ||||||
|  | 		e = l.PushBack(commit) | ||||||
|  | 	} else { | ||||||
|  | 		var in = parent | ||||||
|  | 		for { | ||||||
|  | 			if in == nil { | ||||||
|  | 				break | ||||||
|  | 			} else if in.Value.(*Commit).Id.Equal(commit.Id) { | ||||||
|  | 				return nil | ||||||
|  | 			} else { | ||||||
|  | 				if in.Next() == nil { | ||||||
|  | 					break | ||||||
|  | 				} | ||||||
|  | 				if in.Value.(*Commit).Committer.When.Equal(commit.Committer.When) { | ||||||
|  | 					break | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				if in.Value.(*Commit).Committer.When.After(commit.Committer.When) && | ||||||
|  | 					in.Next().Value.(*Commit).Committer.When.Before(commit.Committer.When) { | ||||||
|  | 					break | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			in = in.Next() | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		e = l.InsertAfter(commit, in) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var pr = parent | ||||||
|  | 	if commit.ParentCount() > 1 { | ||||||
|  | 		pr = e | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for i := 0; i < commit.ParentCount(); i++ { | ||||||
|  | 		id, err := commit.ParentId(i) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		err = repo.commitsBefore(lock, l, pr, id, 0) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (repo *Repository) CommitsCount(commitId string) (int, error) { | ||||||
|  | 	id, err := NewIdFromString(commitId) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return 0, err | ||||||
|  | 	} | ||||||
|  | 	return repo.commitsCount(id) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (repo *Repository) FileCommitsCount(branch, file string) (int, error) { | ||||||
|  | 	stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "rev-list", "--count", | ||||||
|  | 		branch, "--", file) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return 0, errors.New(stderr) | ||||||
|  | 	} | ||||||
|  | 	return com.StrTo(strings.TrimSpace(stdout)).Int() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (repo *Repository) CommitsByFileAndRange(branch, file string, page int) (*list.List, error) { | ||||||
|  | 	stdout, stderr, err := com.ExecCmdDirBytes(repo.Path, "git", "log", branch, | ||||||
|  | 		"--skip="+com.ToStr((page-1)*50), "--max-count=50", prettyLogFormat, "--", file) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, errors.New(string(stderr)) | ||||||
|  | 	} | ||||||
|  | 	return parsePrettyFormatLog(repo, stdout) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (repo *Repository) getCommitsBefore(id sha1) (*list.List, error) { | ||||||
|  | 	l := list.New() | ||||||
|  | 	lock := new(sync.Mutex) | ||||||
|  | 	err := repo.commitsBefore(lock, l, nil, id, 0) | ||||||
|  | 	return l, err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (repo *Repository) searchCommits(id sha1, keyword string) (*list.List, error) { | ||||||
|  | 	stdout, stderr, err := com.ExecCmdDirBytes(repo.Path, "git", "log", id.String(), "-100", | ||||||
|  | 		"-i", "--grep="+keyword, prettyLogFormat) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} else if len(stderr) > 0 { | ||||||
|  | 		return nil, errors.New(string(stderr)) | ||||||
|  | 	} | ||||||
|  | 	return parsePrettyFormatLog(repo, stdout) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (repo *Repository) commitsByRange(id sha1, page int) (*list.List, error) { | ||||||
|  | 	stdout, stderr, err := com.ExecCmdDirBytes(repo.Path, "git", "log", id.String(), | ||||||
|  | 		"--skip="+com.ToStr((page-1)*50), "--max-count=50", prettyLogFormat) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, errors.New(string(stderr)) | ||||||
|  | 	} | ||||||
|  | 	return parsePrettyFormatLog(repo, stdout) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (repo *Repository) getCommitOfRelPath(id sha1, relPath string) (*Commit, error) { | ||||||
|  | 	stdout, _, err := com.ExecCmdDir(repo.Path, "git", "log", "-1", prettyLogFormat, id.String(), "--", relPath) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	id, err = NewIdFromString(string(stdout)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return repo.getCommit(id) | ||||||
|  | } | ||||||
|  | @ -2,9 +2,13 @@ | ||||||
| // Use of this source code is governed by a MIT-style
 | // Use of this source code is governed by a MIT-style
 | ||||||
| // license that can be found in the LICENSE file.
 | // license that can be found in the LICENSE file.
 | ||||||
| 
 | 
 | ||||||
| package workers | package git | ||||||
| 
 | 
 | ||||||
| // Work represents a background work interface of any kind.
 | type ObjectType string | ||||||
| type Work interface { | 
 | ||||||
| 	Do() error | const ( | ||||||
| } | 	COMMIT ObjectType = "commit" | ||||||
|  | 	TREE   ObjectType = "tree" | ||||||
|  | 	BLOB   ObjectType = "blob" | ||||||
|  | 	TAG    ObjectType = "tag" | ||||||
|  | ) | ||||||
							
								
								
									
										104
									
								
								modules/git/repo_tag.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,104 @@ | ||||||
|  | // 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 git | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"strings" | ||||||
|  | 
 | ||||||
|  | 	"github.com/Unknwon/com" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func IsTagExist(repoPath, tagName string) bool { | ||||||
|  | 	_, _, err := com.ExecCmdDir(repoPath, "git", "show-ref", "--verify", "refs/tags/"+tagName) | ||||||
|  | 	return err == nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (repo *Repository) IsTagExist(tagName string) bool { | ||||||
|  | 	return IsTagExist(repo.Path, tagName) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // GetTags returns all tags of given repository.
 | ||||||
|  | func (repo *Repository) GetTags() ([]string, error) { | ||||||
|  | 	stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "tag", "-l") | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, errors.New(stderr) | ||||||
|  | 	} | ||||||
|  | 	tags := strings.Split(stdout, "\n") | ||||||
|  | 	return tags[:len(tags)-1], nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (repo *Repository) CreateTag(tagName, idStr string) error { | ||||||
|  | 	_, stderr, err := com.ExecCmdDir(repo.Path, "git", "tag", tagName, idStr) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return errors.New(stderr) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (repo *Repository) getTag(id sha1) (*Tag, error) { | ||||||
|  | 	if repo.tagCache != nil { | ||||||
|  | 		if t, ok := repo.tagCache[id]; ok { | ||||||
|  | 			return t, nil | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		repo.tagCache = make(map[sha1]*Tag, 10) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Get tag type.
 | ||||||
|  | 	tp, stderr, err := com.ExecCmdDir(repo.Path, "git", "cat-file", "-t", id.String()) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, errors.New(stderr) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Tag is a commit.
 | ||||||
|  | 	if ObjectType(tp) == COMMIT { | ||||||
|  | 		tag := &Tag{ | ||||||
|  | 			Id:     id, | ||||||
|  | 			Object: id, | ||||||
|  | 			Type:   string(COMMIT), | ||||||
|  | 			repo:   repo, | ||||||
|  | 		} | ||||||
|  | 		repo.tagCache[id] = tag | ||||||
|  | 		return tag, nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Tag with message.
 | ||||||
|  | 	data, bytErr, err := com.ExecCmdDirBytes(repo.Path, "git", "cat-file", "-p", id.String()) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, errors.New(string(bytErr)) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	tag, err := parseTagData(data) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	tag.Id = id | ||||||
|  | 	tag.repo = repo | ||||||
|  | 
 | ||||||
|  | 	repo.tagCache[id] = tag | ||||||
|  | 	return tag, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // GetTag returns a Git tag by given name.
 | ||||||
|  | func (repo *Repository) GetTag(tagName string) (*Tag, error) { | ||||||
|  | 	stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "show-ref", "--tags", tagName) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, errors.New(stderr) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	id, err := NewIdFromString(strings.Split(stdout, " ")[0]) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	tag, err := repo.getTag(id) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	tag.Name = tagName | ||||||
|  | 	return tag, nil | ||||||
|  | } | ||||||
							
								
								
									
										32
									
								
								modules/git/repo_tree.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,32 @@ | ||||||
|  | // 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 git | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 
 | ||||||
|  | 	"github.com/Unknwon/com" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Find the tree object in the repository.
 | ||||||
|  | func (repo *Repository) GetTree(idStr string) (*Tree, error) { | ||||||
|  | 	id, err := NewIdFromString(idStr) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return repo.getTree(id) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (repo *Repository) getTree(id sha1) (*Tree, error) { | ||||||
|  | 	treePath := filepathFromSHA1(repo.Path, id.String()) | ||||||
|  | 	if !com.IsFile(treePath) { | ||||||
|  | 		_, _, err := com.ExecCmdDir(repo.Path, "git", "ls-tree", id.String()) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, fmt.Errorf("repo.getTree: %v", ErrNotExist) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return NewTree(repo, id), nil | ||||||
|  | } | ||||||
							
								
								
									
										87
									
								
								modules/git/sha1.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,87 @@ | ||||||
|  | // 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 git | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"encoding/hex" | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var ( | ||||||
|  | 	IdNotExist = errors.New("sha1 id not exist") | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type sha1 [20]byte | ||||||
|  | 
 | ||||||
|  | // Return true if s has the same sha1 as caller.
 | ||||||
|  | // Support 40-length-string, []byte, sha1
 | ||||||
|  | func (id sha1) Equal(s2 interface{}) bool { | ||||||
|  | 	switch v := s2.(type) { | ||||||
|  | 	case string: | ||||||
|  | 		if len(v) != 40 { | ||||||
|  | 			return false | ||||||
|  | 		} | ||||||
|  | 		return v == id.String() | ||||||
|  | 	case []byte: | ||||||
|  | 		if len(v) != 20 { | ||||||
|  | 			return false | ||||||
|  | 		} | ||||||
|  | 		for i, v := range v { | ||||||
|  | 			if id[i] != v { | ||||||
|  | 				return false | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	case sha1: | ||||||
|  | 		for i, v := range v { | ||||||
|  | 			if id[i] != v { | ||||||
|  | 				return false | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	default: | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Return string (hex) representation of the Oid
 | ||||||
|  | func (s sha1) String() string { | ||||||
|  | 	result := make([]byte, 0, 40) | ||||||
|  | 	hexvalues := []byte("0123456789abcdef") | ||||||
|  | 	for i := 0; i < 20; i++ { | ||||||
|  | 		result = append(result, hexvalues[s[i]>>4]) | ||||||
|  | 		result = append(result, hexvalues[s[i]&0xf]) | ||||||
|  | 	} | ||||||
|  | 	return string(result) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Create a new sha1 from a 20 byte slice.
 | ||||||
|  | func NewId(b []byte) (sha1, error) { | ||||||
|  | 	var id sha1 | ||||||
|  | 	if len(b) != 20 { | ||||||
|  | 		return id, errors.New("Length must be 20") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for i := 0; i < 20; i++ { | ||||||
|  | 		id[i] = b[i] | ||||||
|  | 	} | ||||||
|  | 	return id, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Create a new sha1 from a Sha1 string of length 40.
 | ||||||
|  | func NewIdFromString(s string) (sha1, error) { | ||||||
|  | 	s = strings.TrimSpace(s) | ||||||
|  | 	var id sha1 | ||||||
|  | 	if len(s) != 40 { | ||||||
|  | 		return id, fmt.Errorf("Length must be 40") | ||||||
|  | 	} | ||||||
|  | 	b, err := hex.DecodeString(s) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return id, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return NewId(b) | ||||||
|  | } | ||||||
							
								
								
									
										40
									
								
								modules/git/signature.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,40 @@ | ||||||
|  | // 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 git | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"strconv" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Author and Committer information
 | ||||||
|  | type Signature struct { | ||||||
|  | 	Email string | ||||||
|  | 	Name  string | ||||||
|  | 	When  time.Time | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Helper to get a signature from the commit line, which looks like this:
 | ||||||
|  | //     author Patrick Gundlach <gundlach@speedata.de> 1378823654 +0200
 | ||||||
|  | // but without the "author " at the beginning (this method should)
 | ||||||
|  | // be used for author and committer.
 | ||||||
|  | //
 | ||||||
|  | // FIXME: include timezone!
 | ||||||
|  | func newSignatureFromCommitline(line []byte) (*Signature, error) { | ||||||
|  | 	sig := new(Signature) | ||||||
|  | 	emailstart := bytes.IndexByte(line, '<') | ||||||
|  | 	sig.Name = string(line[:emailstart-1]) | ||||||
|  | 	emailstop := bytes.IndexByte(line, '>') | ||||||
|  | 	sig.Email = string(line[emailstart+1 : emailstop]) | ||||||
|  | 	timestop := bytes.IndexByte(line[emailstop+2:], ' ') | ||||||
|  | 	timestring := string(line[emailstop+2 : emailstop+2+timestop]) | ||||||
|  | 	seconds, err := strconv.ParseInt(timestring, 10, 64) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	sig.When = time.Unix(seconds, 0) | ||||||
|  | 	return sig, nil | ||||||
|  | } | ||||||
							
								
								
									
										67
									
								
								modules/git/tag.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,67 @@ | ||||||
|  | // 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 git | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Tag represents a Git tag.
 | ||||||
|  | type Tag struct { | ||||||
|  | 	Name       string | ||||||
|  | 	Id         sha1 | ||||||
|  | 	repo       *Repository | ||||||
|  | 	Object     sha1 // The id of this commit object
 | ||||||
|  | 	Type       string | ||||||
|  | 	Tagger     *Signature | ||||||
|  | 	TagMessage string | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (tag *Tag) Commit() (*Commit, error) { | ||||||
|  | 	return tag.repo.getCommit(tag.Object) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Parse commit information from the (uncompressed) raw
 | ||||||
|  | // data from the commit object.
 | ||||||
|  | // \n\n separate headers from message
 | ||||||
|  | func parseTagData(data []byte) (*Tag, error) { | ||||||
|  | 	tag := new(Tag) | ||||||
|  | 	// we now have the contents of the commit object. Let's investigate...
 | ||||||
|  | 	nextline := 0 | ||||||
|  | l: | ||||||
|  | 	for { | ||||||
|  | 		eol := bytes.IndexByte(data[nextline:], '\n') | ||||||
|  | 		switch { | ||||||
|  | 		case eol > 0: | ||||||
|  | 			line := data[nextline : nextline+eol] | ||||||
|  | 			spacepos := bytes.IndexByte(line, ' ') | ||||||
|  | 			reftype := line[:spacepos] | ||||||
|  | 			switch string(reftype) { | ||||||
|  | 			case "object": | ||||||
|  | 				id, err := NewIdFromString(string(line[spacepos+1:])) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return nil, err | ||||||
|  | 				} | ||||||
|  | 				tag.Object = id | ||||||
|  | 			case "type": | ||||||
|  | 				// A commit can have one or more parents
 | ||||||
|  | 				tag.Type = string(line[spacepos+1:]) | ||||||
|  | 			case "tagger": | ||||||
|  | 				sig, err := newSignatureFromCommitline(line[spacepos+1:]) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return nil, err | ||||||
|  | 				} | ||||||
|  | 				tag.Tagger = sig | ||||||
|  | 			} | ||||||
|  | 			nextline += eol + 1 | ||||||
|  | 		case eol == 0: | ||||||
|  | 			tag.TagMessage = string(data[nextline+1:]) | ||||||
|  | 			break l | ||||||
|  | 		default: | ||||||
|  | 			break l | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return tag, nil | ||||||
|  | } | ||||||
							
								
								
									
										124
									
								
								modules/git/tree.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,124 @@ | ||||||
|  | // 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 git | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"errors" | ||||||
|  | 	"strings" | ||||||
|  | 
 | ||||||
|  | 	"github.com/Unknwon/com" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var ( | ||||||
|  | 	ErrNotExist = errors.New("error not exist") | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // A tree is a flat directory listing.
 | ||||||
|  | type Tree struct { | ||||||
|  | 	Id   sha1 | ||||||
|  | 	repo *Repository | ||||||
|  | 
 | ||||||
|  | 	// parent tree
 | ||||||
|  | 	ptree *Tree | ||||||
|  | 
 | ||||||
|  | 	entries       Entries | ||||||
|  | 	entriesParsed bool | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Parse tree information from the (uncompressed) raw
 | ||||||
|  | // data from the tree object.
 | ||||||
|  | func parseTreeData(tree *Tree, data []byte) ([]*TreeEntry, error) { | ||||||
|  | 	entries := make([]*TreeEntry, 0, 10) | ||||||
|  | 	l := len(data) | ||||||
|  | 	pos := 0 | ||||||
|  | 	for pos < l { | ||||||
|  | 		entry := new(TreeEntry) | ||||||
|  | 		entry.ptree = tree | ||||||
|  | 		step := 6 | ||||||
|  | 		switch string(data[pos : pos+step]) { | ||||||
|  | 		case "100644": | ||||||
|  | 			entry.mode = ModeBlob | ||||||
|  | 			entry.Type = BLOB | ||||||
|  | 		case "100755": | ||||||
|  | 			entry.mode = ModeExec | ||||||
|  | 			entry.Type = BLOB | ||||||
|  | 		case "120000": | ||||||
|  | 			entry.mode = ModeSymlink | ||||||
|  | 			entry.Type = BLOB | ||||||
|  | 		case "160000": | ||||||
|  | 			entry.mode = ModeCommit | ||||||
|  | 			entry.Type = COMMIT | ||||||
|  | 		case "040000": | ||||||
|  | 			entry.mode = ModeTree | ||||||
|  | 			entry.Type = TREE | ||||||
|  | 		default: | ||||||
|  | 			return nil, errors.New("unknown type: " + string(data[pos:pos+step])) | ||||||
|  | 		} | ||||||
|  | 		pos += step + 6 // Skip string type of entry type.
 | ||||||
|  | 
 | ||||||
|  | 		step = 40 | ||||||
|  | 		id, err := NewIdFromString(string(data[pos : pos+step])) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		entry.Id = id | ||||||
|  | 		pos += step + 1 // Skip half of sha1.
 | ||||||
|  | 
 | ||||||
|  | 		step = bytes.IndexByte(data[pos:], '\n') | ||||||
|  | 		entry.name = string(data[pos : pos+step]) | ||||||
|  | 		pos += step + 1 | ||||||
|  | 		entries = append(entries, entry) | ||||||
|  | 	} | ||||||
|  | 	return entries, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (t *Tree) SubTree(rpath string) (*Tree, error) { | ||||||
|  | 	if len(rpath) == 0 { | ||||||
|  | 		return t, nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	paths := strings.Split(rpath, "/") | ||||||
|  | 	var err error | ||||||
|  | 	var g = t | ||||||
|  | 	var p = t | ||||||
|  | 	var te *TreeEntry | ||||||
|  | 	for _, name := range paths { | ||||||
|  | 		te, err = p.GetTreeEntryByPath(name) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		g, err = t.repo.getTree(te.Id) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		g.ptree = p | ||||||
|  | 		p = g | ||||||
|  | 	} | ||||||
|  | 	return g, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (t *Tree) ListEntries(relpath string) (Entries, error) { | ||||||
|  | 	if t.entriesParsed { | ||||||
|  | 		return t.entries, nil | ||||||
|  | 	} | ||||||
|  | 	t.entriesParsed = true | ||||||
|  | 
 | ||||||
|  | 	stdout, _, err := com.ExecCmdDirBytes(t.repo.Path, | ||||||
|  | 		"git", "ls-tree", t.Id.String()) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	t.entries, err = parseTreeData(t, stdout) | ||||||
|  | 	return t.entries, err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func NewTree(repo *Repository, id sha1) *Tree { | ||||||
|  | 	tree := new(Tree) | ||||||
|  | 	tree.Id = id | ||||||
|  | 	tree.repo = repo | ||||||
|  | 	return tree | ||||||
|  | } | ||||||
							
								
								
									
										59
									
								
								modules/git/tree_blob.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,59 @@ | ||||||
|  | // 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 git | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"path" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func (t *Tree) GetTreeEntryByPath(relpath string) (*TreeEntry, error) { | ||||||
|  | 	if len(relpath) == 0 { | ||||||
|  | 		return &TreeEntry{ | ||||||
|  | 			Id:   t.Id, | ||||||
|  | 			Type: TREE, | ||||||
|  | 			mode: ModeTree, | ||||||
|  | 		}, nil | ||||||
|  | 		// return nil, fmt.Errorf("GetTreeEntryByPath(empty relpath): %v", ErrNotExist)
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	relpath = path.Clean(relpath) | ||||||
|  | 	parts := strings.Split(relpath, "/") | ||||||
|  | 	var err error | ||||||
|  | 	tree := t | ||||||
|  | 	for i, name := range parts { | ||||||
|  | 		if i == len(parts)-1 { | ||||||
|  | 			entries, err := tree.ListEntries(path.Dir(relpath)) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return nil, err | ||||||
|  | 			} | ||||||
|  | 			for _, v := range entries { | ||||||
|  | 				if v.name == name { | ||||||
|  | 					return v, nil | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			tree, err = tree.SubTree(name) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return nil, err | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil, fmt.Errorf("GetTreeEntryByPath: %v", ErrNotExist) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (t *Tree) GetBlobByPath(rpath string) (*Blob, error) { | ||||||
|  | 	entry, err := t.GetTreeEntryByPath(rpath) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if !entry.IsDir() { | ||||||
|  | 		return entry.Blob(), nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return nil, ErrNotExist | ||||||
|  | } | ||||||
							
								
								
									
										109
									
								
								modules/git/tree_entry.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,109 @@ | ||||||
|  | // 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 git | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"sort" | ||||||
|  | 	"strings" | ||||||
|  | 
 | ||||||
|  | 	"github.com/Unknwon/com" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type EntryMode int | ||||||
|  | 
 | ||||||
|  | // There are only a few file modes in Git. They look like unix file modes, but they can only be
 | ||||||
|  | // one of these.
 | ||||||
|  | const ( | ||||||
|  | 	ModeBlob    EntryMode = 0100644 | ||||||
|  | 	ModeExec    EntryMode = 0100755 | ||||||
|  | 	ModeSymlink EntryMode = 0120000 | ||||||
|  | 	ModeCommit  EntryMode = 0160000 | ||||||
|  | 	ModeTree    EntryMode = 0040000 | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type TreeEntry struct { | ||||||
|  | 	Id   sha1 | ||||||
|  | 	Type ObjectType | ||||||
|  | 
 | ||||||
|  | 	mode EntryMode | ||||||
|  | 	name string | ||||||
|  | 
 | ||||||
|  | 	ptree *Tree | ||||||
|  | 
 | ||||||
|  | 	commited bool | ||||||
|  | 
 | ||||||
|  | 	size  int64 | ||||||
|  | 	sized bool | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (te *TreeEntry) Name() string { | ||||||
|  | 	return te.name | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (te *TreeEntry) Size() int64 { | ||||||
|  | 	if te.IsDir() { | ||||||
|  | 		return 0 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if te.sized { | ||||||
|  | 		return te.size | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	stdout, _, err := com.ExecCmdDir(te.ptree.repo.Path, "git", "cat-file", "-s", te.Id.String()) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return 0 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	te.sized = true | ||||||
|  | 	te.size = com.StrTo(strings.TrimSpace(stdout)).MustInt64() | ||||||
|  | 	return te.size | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (te *TreeEntry) IsDir() bool { | ||||||
|  | 	return te.mode == ModeTree | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (te *TreeEntry) EntryMode() EntryMode { | ||||||
|  | 	return te.mode | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (te *TreeEntry) Blob() *Blob { | ||||||
|  | 	return &Blob{ | ||||||
|  | 		repo:      te.ptree.repo, | ||||||
|  | 		TreeEntry: te, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type Entries []*TreeEntry | ||||||
|  | 
 | ||||||
|  | var sorter = []func(t1, t2 *TreeEntry) bool{ | ||||||
|  | 	func(t1, t2 *TreeEntry) bool { | ||||||
|  | 		return t1.IsDir() && !t2.IsDir() | ||||||
|  | 	}, | ||||||
|  | 	func(t1, t2 *TreeEntry) bool { | ||||||
|  | 		return t1.name < t2.name | ||||||
|  | 	}, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (bs Entries) Len() int      { return len(bs) } | ||||||
|  | func (bs Entries) Swap(i, j int) { bs[i], bs[j] = bs[j], bs[i] } | ||||||
|  | func (bs Entries) Less(i, j int) bool { | ||||||
|  | 	t1, t2 := bs[i], bs[j] | ||||||
|  | 	var k int | ||||||
|  | 	for k = 0; k < len(sorter)-1; k++ { | ||||||
|  | 		sort := sorter[k] | ||||||
|  | 		switch { | ||||||
|  | 		case sort(t1, t2): | ||||||
|  | 			return true | ||||||
|  | 		case sort(t2, t1): | ||||||
|  | 			return false | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return sorter[k](t1, t2) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (bs Entries) Sort() { | ||||||
|  | 	sort.Sort(bs) | ||||||
|  | } | ||||||
							
								
								
									
										48
									
								
								modules/git/utils.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,48 @@ | ||||||
|  | // 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 git | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"container/list" | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | const prettyLogFormat = `--pretty=format:%H` | ||||||
|  | 
 | ||||||
|  | func parsePrettyFormatLog(repo *Repository, logByts []byte) (*list.List, error) { | ||||||
|  | 	l := list.New() | ||||||
|  | 	if len(logByts) == 0 { | ||||||
|  | 		return l, nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	parts := bytes.Split(logByts, []byte{'\n'}) | ||||||
|  | 
 | ||||||
|  | 	for _, commitId := range parts { | ||||||
|  | 		commit, err := repo.GetCommit(string(commitId)) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		l.PushBack(commit) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return l, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func RefEndName(refStr string) string { | ||||||
|  | 	index := strings.LastIndex(refStr, "/") | ||||||
|  | 	if index != -1 { | ||||||
|  | 		return refStr[index+1:] | ||||||
|  | 	} | ||||||
|  | 	return refStr | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // If the object is stored in its own file (i.e not in a pack file),
 | ||||||
|  | // this function returns the full path to the object file.
 | ||||||
|  | // It does not test if the file exists.
 | ||||||
|  | func filepathFromSHA1(rootdir, sha1 string) string { | ||||||
|  | 	return filepath.Join(rootdir, "objects", sha1[:2], sha1[2:]) | ||||||
|  | } | ||||||
							
								
								
									
										43
									
								
								modules/git/version.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,43 @@ | ||||||
|  | // 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 git | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"strings" | ||||||
|  | 
 | ||||||
|  | 	"github.com/Unknwon/com" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Version represents version of Git.
 | ||||||
|  | type Version struct { | ||||||
|  | 	Major, Minor, Patch int | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // GetVersion returns current Git version installed.
 | ||||||
|  | func GetVersion() (Version, error) { | ||||||
|  | 	stdout, stderr, err := com.ExecCmd("git", "version") | ||||||
|  | 	if err != nil { | ||||||
|  | 		return Version{}, errors.New(stderr) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	infos := strings.Split(stdout, " ") | ||||||
|  | 	if len(infos) < 3 { | ||||||
|  | 		return Version{}, errors.New("not enough output") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	v := Version{} | ||||||
|  | 	for i, s := range strings.Split(strings.TrimSpace(infos[2]), ".") { | ||||||
|  | 		switch i { | ||||||
|  | 		case 0: | ||||||
|  | 			v.Major, _ = com.StrTo(s).Int() | ||||||
|  | 		case 1: | ||||||
|  | 			v.Minor, _ = com.StrTo(s).Int() | ||||||
|  | 		case 2: | ||||||
|  | 			v.Patch, _ = com.StrTo(s).Int() | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return v, nil | ||||||
|  | } | ||||||
							
								
								
									
										73
									
								
								modules/log/console.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,73 @@ | ||||||
|  | // 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 log | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"log" | ||||||
|  | 	"os" | ||||||
|  | 	"runtime" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type Brush func(string) string | ||||||
|  | 
 | ||||||
|  | func NewBrush(color string) Brush { | ||||||
|  | 	pre := "\033[" | ||||||
|  | 	reset := "\033[0m" | ||||||
|  | 	return func(text string) string { | ||||||
|  | 		return pre + color + "m" + text + reset | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | var colors = []Brush{ | ||||||
|  | 	NewBrush("1;36"), // Trace      cyan
 | ||||||
|  | 	NewBrush("1;34"), // Debug      blue
 | ||||||
|  | 	NewBrush("1;32"), // Info       green
 | ||||||
|  | 	NewBrush("1;33"), // Warn       yellow
 | ||||||
|  | 	NewBrush("1;31"), // Error      red
 | ||||||
|  | 	NewBrush("1;35"), // Critical   purple
 | ||||||
|  | 	NewBrush("1;31"), // Fatal      red
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ConsoleWriter implements LoggerInterface and writes messages to terminal.
 | ||||||
|  | type ConsoleWriter struct { | ||||||
|  | 	lg    *log.Logger | ||||||
|  | 	Level int `json:"level"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // create ConsoleWriter returning as LoggerInterface.
 | ||||||
|  | func NewConsole() LoggerInterface { | ||||||
|  | 	return &ConsoleWriter{ | ||||||
|  | 		lg:    log.New(os.Stdout, "", log.Ldate|log.Ltime), | ||||||
|  | 		Level: TRACE, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (cw *ConsoleWriter) Init(config string) error { | ||||||
|  | 	return json.Unmarshal([]byte(config), cw) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (cw *ConsoleWriter) WriteMsg(msg string, skip, level int) error { | ||||||
|  | 	if cw.Level > level { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	if runtime.GOOS == "windows" { | ||||||
|  | 		cw.lg.Println(msg) | ||||||
|  | 	} else { | ||||||
|  | 		cw.lg.Println(colors[level](msg)) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (_ *ConsoleWriter) Destroy() { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (_ *ConsoleWriter) Flush() { | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func init() { | ||||||
|  | 	Register("console", NewConsole) | ||||||
|  | } | ||||||
							
								
								
									
										237
									
								
								modules/log/file.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,237 @@ | ||||||
|  | // 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 log | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"log" | ||||||
|  | 	"os" | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"strings" | ||||||
|  | 	"sync" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // FileLogWriter implements LoggerInterface.
 | ||||||
|  | // It writes messages by lines limit, file size limit, or time frequency.
 | ||||||
|  | type FileLogWriter struct { | ||||||
|  | 	*log.Logger | ||||||
|  | 	mw *MuxWriter | ||||||
|  | 	// The opened file
 | ||||||
|  | 	Filename string `json:"filename"` | ||||||
|  | 
 | ||||||
|  | 	Maxlines          int `json:"maxlines"` | ||||||
|  | 	maxlines_curlines int | ||||||
|  | 
 | ||||||
|  | 	// Rotate at size
 | ||||||
|  | 	Maxsize         int `json:"maxsize"` | ||||||
|  | 	maxsize_cursize int | ||||||
|  | 
 | ||||||
|  | 	// Rotate daily
 | ||||||
|  | 	Daily          bool  `json:"daily"` | ||||||
|  | 	Maxdays        int64 `json:"maxdays` | ||||||
|  | 	daily_opendate int | ||||||
|  | 
 | ||||||
|  | 	Rotate bool `json:"rotate"` | ||||||
|  | 
 | ||||||
|  | 	startLock sync.Mutex // Only one log can write to the file
 | ||||||
|  | 
 | ||||||
|  | 	Level int `json:"level"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // an *os.File writer with locker.
 | ||||||
|  | type MuxWriter struct { | ||||||
|  | 	sync.Mutex | ||||||
|  | 	fd *os.File | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // write to os.File.
 | ||||||
|  | func (l *MuxWriter) Write(b []byte) (int, error) { | ||||||
|  | 	l.Lock() | ||||||
|  | 	defer l.Unlock() | ||||||
|  | 	return l.fd.Write(b) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // set os.File in writer.
 | ||||||
|  | func (l *MuxWriter) SetFd(fd *os.File) { | ||||||
|  | 	if l.fd != nil { | ||||||
|  | 		l.fd.Close() | ||||||
|  | 	} | ||||||
|  | 	l.fd = fd | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // create a FileLogWriter returning as LoggerInterface.
 | ||||||
|  | func NewFileWriter() LoggerInterface { | ||||||
|  | 	w := &FileLogWriter{ | ||||||
|  | 		Filename: "", | ||||||
|  | 		Maxlines: 1000000, | ||||||
|  | 		Maxsize:  1 << 28, //256 MB
 | ||||||
|  | 		Daily:    true, | ||||||
|  | 		Maxdays:  7, | ||||||
|  | 		Rotate:   true, | ||||||
|  | 		Level:    TRACE, | ||||||
|  | 	} | ||||||
|  | 	// use MuxWriter instead direct use os.File for lock write when rotate
 | ||||||
|  | 	w.mw = new(MuxWriter) | ||||||
|  | 	// set MuxWriter as Logger's io.Writer
 | ||||||
|  | 	w.Logger = log.New(w.mw, "", log.Ldate|log.Ltime) | ||||||
|  | 	return w | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Init file logger with json config.
 | ||||||
|  | // config like:
 | ||||||
|  | //	{
 | ||||||
|  | //	"filename":"log/gogs.log",
 | ||||||
|  | //	"maxlines":10000,
 | ||||||
|  | //	"maxsize":1<<30,
 | ||||||
|  | //	"daily":true,
 | ||||||
|  | //	"maxdays":15,
 | ||||||
|  | //	"rotate":true
 | ||||||
|  | //	}
 | ||||||
|  | func (w *FileLogWriter) Init(config string) error { | ||||||
|  | 	if err := json.Unmarshal([]byte(config), w); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if len(w.Filename) == 0 { | ||||||
|  | 		return errors.New("config must have filename") | ||||||
|  | 	} | ||||||
|  | 	return w.StartLogger() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // start file logger. create log file and set to locker-inside file writer.
 | ||||||
|  | func (w *FileLogWriter) StartLogger() error { | ||||||
|  | 	fd, err := w.createLogFile() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	w.mw.SetFd(fd) | ||||||
|  | 	if err = w.initFd(); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (w *FileLogWriter) docheck(size int) { | ||||||
|  | 	w.startLock.Lock() | ||||||
|  | 	defer w.startLock.Unlock() | ||||||
|  | 	if w.Rotate && ((w.Maxlines > 0 && w.maxlines_curlines >= w.Maxlines) || | ||||||
|  | 		(w.Maxsize > 0 && w.maxsize_cursize >= w.Maxsize) || | ||||||
|  | 		(w.Daily && time.Now().Day() != w.daily_opendate)) { | ||||||
|  | 		if err := w.DoRotate(); err != nil { | ||||||
|  | 			fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	w.maxlines_curlines++ | ||||||
|  | 	w.maxsize_cursize += size | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // write logger message into file.
 | ||||||
|  | func (w *FileLogWriter) WriteMsg(msg string, skip, level int) error { | ||||||
|  | 	if level < w.Level { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	n := 24 + len(msg) // 24 stand for the length "2013/06/23 21:00:22 [T] "
 | ||||||
|  | 	w.docheck(n) | ||||||
|  | 	w.Logger.Println(msg) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (w *FileLogWriter) createLogFile() (*os.File, error) { | ||||||
|  | 	// Open the log file
 | ||||||
|  | 	return os.OpenFile(w.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (w *FileLogWriter) initFd() error { | ||||||
|  | 	fd := w.mw.fd | ||||||
|  | 	finfo, err := fd.Stat() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("get stat: %s\n", err) | ||||||
|  | 	} | ||||||
|  | 	w.maxsize_cursize = int(finfo.Size()) | ||||||
|  | 	w.daily_opendate = time.Now().Day() | ||||||
|  | 	if finfo.Size() > 0 { | ||||||
|  | 		content, err := ioutil.ReadFile(w.Filename) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		w.maxlines_curlines = len(strings.Split(string(content), "\n")) | ||||||
|  | 	} else { | ||||||
|  | 		w.maxlines_curlines = 0 | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // DoRotate means it need to write file in new file.
 | ||||||
|  | // new file name like xx.log.2013-01-01.2
 | ||||||
|  | func (w *FileLogWriter) DoRotate() error { | ||||||
|  | 	_, err := os.Lstat(w.Filename) | ||||||
|  | 	if err == nil { // file exists
 | ||||||
|  | 		// Find the next available number
 | ||||||
|  | 		num := 1 | ||||||
|  | 		fname := "" | ||||||
|  | 		for ; err == nil && num <= 999; num++ { | ||||||
|  | 			fname = w.Filename + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), num) | ||||||
|  | 			_, err = os.Lstat(fname) | ||||||
|  | 		} | ||||||
|  | 		// return error if the last file checked still existed
 | ||||||
|  | 		if err == nil { | ||||||
|  | 			return fmt.Errorf("rotate: cannot find free log number to rename %s\n", w.Filename) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// block Logger's io.Writer
 | ||||||
|  | 		w.mw.Lock() | ||||||
|  | 		defer w.mw.Unlock() | ||||||
|  | 
 | ||||||
|  | 		fd := w.mw.fd | ||||||
|  | 		fd.Close() | ||||||
|  | 
 | ||||||
|  | 		// close fd before rename
 | ||||||
|  | 		// Rename the file to its newfound home
 | ||||||
|  | 		if err = os.Rename(w.Filename, fname); err != nil { | ||||||
|  | 			return fmt.Errorf("Rotate: %s\n", err) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// re-start logger
 | ||||||
|  | 		if err = w.StartLogger(); err != nil { | ||||||
|  | 			return fmt.Errorf("Rotate StartLogger: %s\n", err) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		go w.deleteOldLog() | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (w *FileLogWriter) deleteOldLog() { | ||||||
|  | 	dir := filepath.Dir(w.Filename) | ||||||
|  | 	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { | ||||||
|  | 		if !info.IsDir() && info.ModTime().Unix() < (time.Now().Unix()-60*60*24*w.Maxdays) { | ||||||
|  | 			if strings.HasPrefix(filepath.Base(path), filepath.Base(w.Filename)) { | ||||||
|  | 				os.Remove(path) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		return nil | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // destroy file logger, close file writer.
 | ||||||
|  | func (w *FileLogWriter) Destroy() { | ||||||
|  | 	w.mw.fd.Close() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // flush file logger.
 | ||||||
|  | // there are no buffering messages in file logger in memory.
 | ||||||
|  | // flush file means sync file from disk.
 | ||||||
|  | func (w *FileLogWriter) Flush() { | ||||||
|  | 	w.mw.fd.Sync() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func init() { | ||||||
|  | 	Register("file", NewFileWriter) | ||||||
|  | } | ||||||
|  | @ -2,32 +2,29 @@ | ||||||
| // Use of this source code is governed by a MIT-style
 | // Use of this source code is governed by a MIT-style
 | ||||||
| // license that can be found in the LICENSE file.
 | // license that can be found in the LICENSE file.
 | ||||||
| 
 | 
 | ||||||
| // Package log is a wrapper of logs for short calling name.
 |  | ||||||
| package log | package log | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"os" | 	"os" | ||||||
| 	"path" | 	"path" | ||||||
| 
 | 	"path/filepath" | ||||||
| 	"github.com/gogits/logs" | 	"runtime" | ||||||
|  | 	"strings" | ||||||
|  | 	"sync" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
| 	loggers   []*logs.BeeLogger | 	loggers   []*Logger | ||||||
| 	GitLogger *logs.BeeLogger | 	GitLogger *Logger | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func init() { |  | ||||||
| 	NewLogger(0, "console", `{"level": 0}`) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func NewLogger(bufLen int64, mode, config string) { | func NewLogger(bufLen int64, mode, config string) { | ||||||
| 	logger := logs.NewLogger(bufLen) | 	logger := newLogger(bufLen) | ||||||
| 
 | 
 | ||||||
| 	isExist := false | 	isExist := false | ||||||
| 	for _, l := range loggers { | 	for _, l := range loggers { | ||||||
| 		if l.Adapter == mode { | 		if l.adapter == mode { | ||||||
| 			isExist = true | 			isExist = true | ||||||
| 			l = logger | 			l = logger | ||||||
| 		} | 		} | ||||||
|  | @ -35,15 +32,14 @@ func NewLogger(bufLen int64, mode, config string) { | ||||||
| 	if !isExist { | 	if !isExist { | ||||||
| 		loggers = append(loggers, logger) | 		loggers = append(loggers, logger) | ||||||
| 	} | 	} | ||||||
| 	logger.SetLogFuncCallDepth(3) |  | ||||||
| 	if err := logger.SetLogger(mode, config); err != nil { | 	if err := logger.SetLogger(mode, config); err != nil { | ||||||
| 		Fatal("Fail to set logger(%s): %v", mode, err) | 		Fatal(1, "Fail to set logger(%s): %v", mode, err) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func NewGitLogger(logPath string) { | func NewGitLogger(logPath string) { | ||||||
| 	os.MkdirAll(path.Dir(logPath), os.ModePerm) | 	os.MkdirAll(path.Dir(logPath), os.ModePerm) | ||||||
| 	GitLogger = logs.NewLogger(0) | 	GitLogger = newLogger(0) | ||||||
| 	GitLogger.SetLogger("file", fmt.Sprintf(`{"level":0,"filename":"%s","rotate":false}`, logPath)) | 	GitLogger.SetLogger("file", fmt.Sprintf(`{"level":0,"filename":"%s","rotate":false}`, logPath)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -65,28 +61,237 @@ func Info(format string, v ...interface{}) { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func Error(format string, v ...interface{}) { |  | ||||||
| 	for _, logger := range loggers { |  | ||||||
| 		logger.Error(format, v...) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func Warn(format string, v ...interface{}) { | func Warn(format string, v ...interface{}) { | ||||||
| 	for _, logger := range loggers { | 	for _, logger := range loggers { | ||||||
| 		logger.Warn(format, v...) | 		logger.Warn(format, v...) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func Critical(format string, v ...interface{}) { | func Error(skip int, format string, v ...interface{}) { | ||||||
| 	for _, logger := range loggers { | 	for _, logger := range loggers { | ||||||
| 		logger.Critical(format, v...) | 		logger.Error(skip, format, v...) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func Fatal(format string, v ...interface{}) { | func Critical(skip int, format string, v ...interface{}) { | ||||||
| 	Error(format, v...) | 	for _, logger := range loggers { | ||||||
|  | 		logger.Critical(skip, format, v...) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func Fatal(skip int, format string, v ...interface{}) { | ||||||
|  | 	Error(skip, format, v...) | ||||||
| 	for _, l := range loggers { | 	for _, l := range loggers { | ||||||
| 		l.Close() | 		l.Close() | ||||||
| 	} | 	} | ||||||
| 	os.Exit(2) | 	os.Exit(1) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // .___ _______________________________________________________  _________ ___________
 | ||||||
|  | // |   |\      \__    ___/\_   _____/\______   \_   _____/  _  \ \_   ___ \\_   _____/
 | ||||||
|  | // |   |/   |   \|    |    |    __)_  |       _/|    __)/  /_\  \/    \  \/ |    __)_
 | ||||||
|  | // |   /    |    \    |    |        \ |    |   \|     \/    |    \     \____|        \
 | ||||||
|  | // |___\____|__  /____|   /_______  / |____|_  /\___  /\____|__  /\______  /_______  /
 | ||||||
|  | //             \/                 \/         \/     \/         \/        \/        \/
 | ||||||
|  | 
 | ||||||
|  | type LogLevel int | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	TRACE = iota | ||||||
|  | 	DEBUG | ||||||
|  | 	INFO | ||||||
|  | 	WARN | ||||||
|  | 	ERROR | ||||||
|  | 	CRITICAL | ||||||
|  | 	FATAL | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // LoggerInterface represents behaviors of a logger provider.
 | ||||||
|  | type LoggerInterface interface { | ||||||
|  | 	Init(config string) error | ||||||
|  | 	WriteMsg(msg string, skip, level int) error | ||||||
|  | 	Destroy() | ||||||
|  | 	Flush() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type loggerType func() LoggerInterface | ||||||
|  | 
 | ||||||
|  | var adapters = make(map[string]loggerType) | ||||||
|  | 
 | ||||||
|  | // Register registers given logger provider to adapters.
 | ||||||
|  | func Register(name string, log loggerType) { | ||||||
|  | 	if log == nil { | ||||||
|  | 		panic("log: register provider is nil") | ||||||
|  | 	} | ||||||
|  | 	if _, dup := adapters[name]; dup { | ||||||
|  | 		panic("log: register called twice for provider \"" + name + "\"") | ||||||
|  | 	} | ||||||
|  | 	adapters[name] = log | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type logMsg struct { | ||||||
|  | 	skip, level int | ||||||
|  | 	msg         string | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Logger is default logger in beego application.
 | ||||||
|  | // it can contain several providers and log message into all providers.
 | ||||||
|  | type Logger struct { | ||||||
|  | 	adapter string | ||||||
|  | 	lock    sync.Mutex | ||||||
|  | 	level   int | ||||||
|  | 	msg     chan *logMsg | ||||||
|  | 	outputs map[string]LoggerInterface | ||||||
|  | 	quit    chan bool | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // newLogger initializes and returns a new logger.
 | ||||||
|  | func newLogger(buffer int64) *Logger { | ||||||
|  | 	l := &Logger{ | ||||||
|  | 		msg:     make(chan *logMsg, buffer), | ||||||
|  | 		outputs: make(map[string]LoggerInterface), | ||||||
|  | 		quit:    make(chan bool), | ||||||
|  | 	} | ||||||
|  | 	go l.StartLogger() | ||||||
|  | 	return l | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // SetLogger sets new logger instanse with given logger adapter and config.
 | ||||||
|  | func (l *Logger) SetLogger(adapter string, config string) error { | ||||||
|  | 	l.lock.Lock() | ||||||
|  | 	defer l.lock.Unlock() | ||||||
|  | 	if log, ok := adapters[adapter]; ok { | ||||||
|  | 		lg := log() | ||||||
|  | 		if err := lg.Init(config); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		l.outputs[adapter] = lg | ||||||
|  | 		l.adapter = adapter | ||||||
|  | 	} else { | ||||||
|  | 		panic("log: unknown adapter \"" + adapter + "\" (forgotten Register?)") | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // DelLogger removes a logger adapter instance.
 | ||||||
|  | func (l *Logger) DelLogger(adapter string) error { | ||||||
|  | 	l.lock.Lock() | ||||||
|  | 	defer l.lock.Unlock() | ||||||
|  | 	if lg, ok := l.outputs[adapter]; ok { | ||||||
|  | 		lg.Destroy() | ||||||
|  | 		delete(l.outputs, adapter) | ||||||
|  | 	} else { | ||||||
|  | 		panic("log: unknown adapter \"" + adapter + "\" (forgotten Register?)") | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *Logger) writerMsg(skip, level int, msg string) error { | ||||||
|  | 	if l.level > level { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	lm := &logMsg{ | ||||||
|  | 		skip:  skip, | ||||||
|  | 		level: level, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Only error information needs locate position for debugging.
 | ||||||
|  | 	if lm.level >= ERROR { | ||||||
|  | 		pc, file, line, ok := runtime.Caller(skip) | ||||||
|  | 		if ok { | ||||||
|  | 			// Get caller function name.
 | ||||||
|  | 			fn := runtime.FuncForPC(pc) | ||||||
|  | 			var fnName string | ||||||
|  | 			if fn == nil { | ||||||
|  | 				fnName = "?()" | ||||||
|  | 			} else { | ||||||
|  | 				fnName = strings.TrimLeft(filepath.Ext(fn.Name()), ".") + "()" | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			lm.msg = fmt.Sprintf("[%s:%d %s] %s", filepath.Base(file), line, fnName, msg) | ||||||
|  | 		} else { | ||||||
|  | 			lm.msg = msg | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		lm.msg = msg | ||||||
|  | 	} | ||||||
|  | 	l.msg <- lm | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // StartLogger starts logger chan reading.
 | ||||||
|  | func (l *Logger) StartLogger() { | ||||||
|  | 	for { | ||||||
|  | 		select { | ||||||
|  | 		case bm := <-l.msg: | ||||||
|  | 			for _, l := range l.outputs { | ||||||
|  | 				l.WriteMsg(bm.msg, bm.skip, bm.level) | ||||||
|  | 			} | ||||||
|  | 		case <-l.quit: | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Flush flushs all chan data.
 | ||||||
|  | func (l *Logger) Flush() { | ||||||
|  | 	for _, l := range l.outputs { | ||||||
|  | 		l.Flush() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Close closes logger, flush all chan data and destroy all adapter instances.
 | ||||||
|  | func (l *Logger) Close() { | ||||||
|  | 	l.quit <- true | ||||||
|  | 	for { | ||||||
|  | 		if len(l.msg) > 0 { | ||||||
|  | 			bm := <-l.msg | ||||||
|  | 			for _, l := range l.outputs { | ||||||
|  | 				l.WriteMsg(bm.msg, bm.skip, bm.level) | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	for _, l := range l.outputs { | ||||||
|  | 		l.Flush() | ||||||
|  | 		l.Destroy() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *Logger) Trace(format string, v ...interface{}) { | ||||||
|  | 	msg := fmt.Sprintf("[T] "+format, v...) | ||||||
|  | 	l.writerMsg(0, TRACE, msg) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *Logger) Debug(format string, v ...interface{}) { | ||||||
|  | 	msg := fmt.Sprintf("[D] "+format, v...) | ||||||
|  | 	l.writerMsg(0, DEBUG, msg) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *Logger) Info(format string, v ...interface{}) { | ||||||
|  | 	msg := fmt.Sprintf("[I] "+format, v...) | ||||||
|  | 	l.writerMsg(0, INFO, msg) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *Logger) Warn(format string, v ...interface{}) { | ||||||
|  | 	msg := fmt.Sprintf("[W] "+format, v...) | ||||||
|  | 	l.writerMsg(0, WARN, msg) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *Logger) Error(skip int, format string, v ...interface{}) { | ||||||
|  | 	msg := fmt.Sprintf("[E] "+format, v...) | ||||||
|  | 	l.writerMsg(skip, ERROR, msg) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *Logger) Critical(skip int, format string, v ...interface{}) { | ||||||
|  | 	msg := fmt.Sprintf("[C] "+format, v...) | ||||||
|  | 	l.writerMsg(skip, CRITICAL, msg) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *Logger) Fatal(skip int, format string, v ...interface{}) { | ||||||
|  | 	msg := fmt.Sprintf("[F] "+format, v...) | ||||||
|  | 	l.writerMsg(skip, FATAL, msg) | ||||||
|  | 	l.Close() | ||||||
|  | 	os.Exit(1) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -10,10 +10,12 @@ import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"path" | 	"path" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/Unknwon/com" | ||||||
|  | 	"github.com/Unknwon/macaron" | ||||||
|  | 
 | ||||||
| 	"github.com/gogits/gogs/models" | 	"github.com/gogits/gogs/models" | ||||||
| 	"github.com/gogits/gogs/modules/base" | 	"github.com/gogits/gogs/modules/base" | ||||||
| 	"github.com/gogits/gogs/modules/log" | 	"github.com/gogits/gogs/modules/log" | ||||||
| 	"github.com/gogits/gogs/modules/middleware" |  | ||||||
| 	"github.com/gogits/gogs/modules/setting" | 	"github.com/gogits/gogs/modules/setting" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -55,7 +57,7 @@ func GetMailTmplData(u *models.User) map[interface{}]interface{} { | ||||||
| // create a time limit code for user active
 | // create a time limit code for user active
 | ||||||
| func CreateUserActiveCode(u *models.User, startInf interface{}) string { | func CreateUserActiveCode(u *models.User, startInf interface{}) string { | ||||||
| 	minutes := setting.Service.ActiveCodeLives | 	minutes := setting.Service.ActiveCodeLives | ||||||
| 	data := base.ToStr(u.Id) + u.Email + u.LowerName + u.Passwd + u.Rands | 	data := com.ToStr(u.Id) + u.Email + u.LowerName + u.Passwd + u.Rands | ||||||
| 	code := base.CreateTimeLimitCode(data, minutes, startInf) | 	code := base.CreateTimeLimitCode(data, minutes, startInf) | ||||||
| 
 | 
 | ||||||
| 	// add tail hex username
 | 	// add tail hex username
 | ||||||
|  | @ -64,7 +66,7 @@ func CreateUserActiveCode(u *models.User, startInf interface{}) string { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Send user register mail with active code
 | // Send user register mail with active code
 | ||||||
| func SendRegisterMail(r *middleware.Render, u *models.User) { | func SendRegisterMail(r macaron.Render, u *models.User) { | ||||||
| 	code := CreateUserActiveCode(u, nil) | 	code := CreateUserActiveCode(u, nil) | ||||||
| 	subject := "Register success, Welcome" | 	subject := "Register success, Welcome" | ||||||
| 
 | 
 | ||||||
|  | @ -72,7 +74,7 @@ func SendRegisterMail(r *middleware.Render, u *models.User) { | ||||||
| 	data["Code"] = code | 	data["Code"] = code | ||||||
| 	body, err := r.HTMLString(string(AUTH_REGISTER_SUCCESS), data) | 	body, err := r.HTMLString(string(AUTH_REGISTER_SUCCESS), data) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Error("mail.SendRegisterMail(fail to render): %v", err) | 		log.Error(4, "mail.SendRegisterMail(fail to render): %v", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -83,7 +85,7 @@ func SendRegisterMail(r *middleware.Render, u *models.User) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Send email verify active email.
 | // Send email verify active email.
 | ||||||
| func SendActiveMail(r *middleware.Render, u *models.User) { | func SendActiveMail(r macaron.Render, u *models.User) { | ||||||
| 	code := CreateUserActiveCode(u, nil) | 	code := CreateUserActiveCode(u, nil) | ||||||
| 
 | 
 | ||||||
| 	subject := "Verify your e-mail address" | 	subject := "Verify your e-mail address" | ||||||
|  | @ -92,7 +94,7 @@ func SendActiveMail(r *middleware.Render, u *models.User) { | ||||||
| 	data["Code"] = code | 	data["Code"] = code | ||||||
| 	body, err := r.HTMLString(string(AUTH_ACTIVE), data) | 	body, err := r.HTMLString(string(AUTH_ACTIVE), data) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Error("mail.SendActiveMail(fail to render): %v", err) | 		log.Error(4, "mail.SendActiveMail(fail to render): %v", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -103,7 +105,7 @@ func SendActiveMail(r *middleware.Render, u *models.User) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Send reset password email.
 | // Send reset password email.
 | ||||||
| func SendResetPasswdMail(r *middleware.Render, u *models.User) { | func SendResetPasswdMail(r macaron.Render, u *models.User) { | ||||||
| 	code := CreateUserActiveCode(u, nil) | 	code := CreateUserActiveCode(u, nil) | ||||||
| 
 | 
 | ||||||
| 	subject := "Reset your password" | 	subject := "Reset your password" | ||||||
|  | @ -112,7 +114,7 @@ func SendResetPasswdMail(r *middleware.Render, u *models.User) { | ||||||
| 	data["Code"] = code | 	data["Code"] = code | ||||||
| 	body, err := r.HTMLString(string(AUTH_RESET_PASSWORD), data) | 	body, err := r.HTMLString(string(AUTH_RESET_PASSWORD), data) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Error("mail.SendResetPasswdMail(fail to render): %v", err) | 		log.Error(4, "mail.SendResetPasswdMail(fail to render): %v", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -157,7 +159,7 @@ func SendIssueNotifyMail(u, owner *models.User, repo *models.Repository, issue * | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // SendIssueMentionMail sends mail notification for who are mentioned in issue.
 | // SendIssueMentionMail sends mail notification for who are mentioned in issue.
 | ||||||
| func SendIssueMentionMail(r *middleware.Render, u, owner *models.User, | func SendIssueMentionMail(r macaron.Render, u, owner *models.User, | ||||||
| 	repo *models.Repository, issue *models.Issue, tos []string) error { | 	repo *models.Repository, issue *models.Issue, tos []string) error { | ||||||
| 
 | 
 | ||||||
| 	if len(tos) == 0 { | 	if len(tos) == 0 { | ||||||
|  | @ -182,7 +184,7 @@ func SendIssueMentionMail(r *middleware.Render, u, owner *models.User, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // SendCollaboratorMail sends mail notification to new collaborator.
 | // SendCollaboratorMail sends mail notification to new collaborator.
 | ||||||
| func SendCollaboratorMail(r *middleware.Render, u, owner *models.User, | func SendCollaboratorMail(r macaron.Render, u, owner *models.User, | ||||||
| 	repo *models.Repository) error { | 	repo *models.Repository) error { | ||||||
| 
 | 
 | ||||||
| 	subject := fmt.Sprintf("%s added you to %s", owner.Name, repo.Name) | 	subject := fmt.Sprintf("%s added you to %s", owner.Name, repo.Name) | ||||||
|  |  | ||||||
|  | @ -56,7 +56,7 @@ func processMailQueue() { | ||||||
| 				if len(msg.Info) > 0 { | 				if len(msg.Info) > 0 { | ||||||
| 					info = ", info: " + msg.Info | 					info = ", info: " + msg.Info | ||||||
| 				} | 				} | ||||||
| 				log.Error(fmt.Sprintf("Async sent email %d succeed, not send emails: %s%s err: %s", num, tos, info, err)) | 				log.Error(4, fmt.Sprintf("Async sent email %d succeed, not send emails: %s%s err: %s", num, tos, info, err)) | ||||||
| 			} else { | 			} else { | ||||||
| 				log.Trace(fmt.Sprintf("Async sent email %d succeed, sent emails: %s%s", num, tos, info)) | 				log.Trace(fmt.Sprintf("Async sent email %d succeed, sent emails: %s%s", num, tos, info)) | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|  | @ -8,7 +8,7 @@ import ( | ||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"github.com/go-martini/martini" | 	"github.com/Unknwon/macaron" | ||||||
| 
 | 
 | ||||||
| 	"github.com/gogits/gogs/modules/setting" | 	"github.com/gogits/gogs/modules/setting" | ||||||
| ) | ) | ||||||
|  | @ -20,7 +20,7 @@ type ToggleOptions struct { | ||||||
| 	DisableCsrf    bool | 	DisableCsrf    bool | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func Toggle(options *ToggleOptions) martini.Handler { | func Toggle(options *ToggleOptions) macaron.Handler { | ||||||
| 	return func(ctx *Context) { | 	return func(ctx *Context) { | ||||||
| 		// Cannot view any page before installation.
 | 		// Cannot view any page before installation.
 | ||||||
| 		if !setting.InstallLock { | 		if !setting.InstallLock { | ||||||
|  | @ -49,7 +49,7 @@ func Toggle(options *ToggleOptions) martini.Handler { | ||||||
| 				ctx.Redirect("/user/login") | 				ctx.Redirect("/user/login") | ||||||
| 				return | 				return | ||||||
| 			} else if !ctx.User.IsActive && setting.Service.RegisterEmailConfirm { | 			} else if !ctx.User.IsActive && setting.Service.RegisterEmailConfirm { | ||||||
| 				ctx.Data["Title"] = "Activate Your Account" | 				// ctx.Data["Title"] = "Activate Your Account"
 | ||||||
| 				ctx.HTML(200, "user/activate") | 				ctx.HTML(200, "user/activate") | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|  | @ -16,7 +16,8 @@ import ( | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"unicode/utf8" | 	"unicode/utf8" | ||||||
| 
 | 
 | ||||||
| 	"github.com/go-martini/martini" | 	"github.com/Unknwon/macaron" | ||||||
|  | 	"github.com/macaron-contrib/i18n" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| /* | /* | ||||||
|  | @ -37,44 +38,44 @@ import ( | ||||||
| // your own error handling, use Form or Json middleware directly.
 | // your own error handling, use Form or Json middleware directly.
 | ||||||
| // An interface pointer can be added as a second argument in order
 | // An interface pointer can be added as a second argument in order
 | ||||||
| // to map the struct to a specific interface.
 | // to map the struct to a specific interface.
 | ||||||
| func Bind(obj interface{}, ifacePtr ...interface{}) martini.Handler { | func Bind(obj interface{}, ifacePtr ...interface{}) macaron.Handler { | ||||||
| 	return func(context martini.Context, req *http.Request) { | 	return func(ctx *macaron.Context) { | ||||||
| 		contentType := req.Header.Get("Content-Type") | 		contentType := ctx.Req.Header.Get("Content-Type") | ||||||
| 
 | 
 | ||||||
| 		if strings.Contains(contentType, "form-urlencoded") { | 		if strings.Contains(contentType, "form-urlencoded") { | ||||||
| 			context.Invoke(Form(obj, ifacePtr...)) | 			ctx.Invoke(Form(obj, ifacePtr...)) | ||||||
| 		} else if strings.Contains(contentType, "multipart/form-data") { | 		} else if strings.Contains(contentType, "multipart/form-data") { | ||||||
| 			context.Invoke(MultipartForm(obj, ifacePtr...)) | 			ctx.Invoke(MultipartForm(obj, ifacePtr...)) | ||||||
| 		} else if strings.Contains(contentType, "json") { | 		} else if strings.Contains(contentType, "json") { | ||||||
| 			context.Invoke(Json(obj, ifacePtr...)) | 			ctx.Invoke(Json(obj, ifacePtr...)) | ||||||
| 		} else { | 		} else { | ||||||
| 			context.Invoke(Json(obj, ifacePtr...)) | 			ctx.Invoke(Json(obj, ifacePtr...)) | ||||||
| 			if getErrors(context).Count() > 0 { | 			if getErrors(ctx).Count() > 0 { | ||||||
| 				context.Invoke(Form(obj, ifacePtr...)) | 				ctx.Invoke(Form(obj, ifacePtr...)) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		context.Invoke(ErrorHandler) | 		ctx.Invoke(ErrorHandler) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // BindIgnErr will do the exactly same thing as Bind but without any
 | // BindIgnErr will do the exactly same thing as Bind but without any
 | ||||||
| // error handling, which user has freedom to deal with them.
 | // error handling, which user has freedom to deal with them.
 | ||||||
| // This allows user take advantages of validation.
 | // This allows user take advantages of validation.
 | ||||||
| func BindIgnErr(obj interface{}, ifacePtr ...interface{}) martini.Handler { | func BindIgnErr(obj interface{}, ifacePtr ...interface{}) macaron.Handler { | ||||||
| 	return func(context martini.Context, req *http.Request) { | 	return func(ctx *macaron.Context, req *http.Request) { | ||||||
| 		contentType := req.Header.Get("Content-Type") | 		contentType := req.Header.Get("Content-Type") | ||||||
| 
 | 
 | ||||||
| 		if strings.Contains(contentType, "form-urlencoded") { | 		if strings.Contains(contentType, "form-urlencoded") { | ||||||
| 			context.Invoke(Form(obj, ifacePtr...)) | 			ctx.Invoke(Form(obj, ifacePtr...)) | ||||||
| 		} else if strings.Contains(contentType, "multipart/form-data") { | 		} else if strings.Contains(contentType, "multipart/form-data") { | ||||||
| 			context.Invoke(MultipartForm(obj, ifacePtr...)) | 			ctx.Invoke(MultipartForm(obj, ifacePtr...)) | ||||||
| 		} else if strings.Contains(contentType, "json") { | 		} else if strings.Contains(contentType, "json") { | ||||||
| 			context.Invoke(Json(obj, ifacePtr...)) | 			ctx.Invoke(Json(obj, ifacePtr...)) | ||||||
| 		} else { | 		} else { | ||||||
| 			context.Invoke(Json(obj, ifacePtr...)) | 			ctx.Invoke(Json(obj, ifacePtr...)) | ||||||
| 			if getErrors(context).Count() > 0 { | 			if getErrors(ctx).Count() > 0 { | ||||||
| 				context.Invoke(Form(obj, ifacePtr...)) | 				ctx.Invoke(Form(obj, ifacePtr...)) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | @ -89,12 +90,12 @@ func BindIgnErr(obj interface{}, ifacePtr ...interface{}) martini.Handler { | ||||||
| // keys, for example: key=val1&key=val2&key=val3
 | // keys, for example: key=val1&key=val2&key=val3
 | ||||||
| // An interface pointer can be added as a second argument in order
 | // An interface pointer can be added as a second argument in order
 | ||||||
| // to map the struct to a specific interface.
 | // to map the struct to a specific interface.
 | ||||||
| func Form(formStruct interface{}, ifacePtr ...interface{}) martini.Handler { | func Form(formStruct interface{}, ifacePtr ...interface{}) macaron.Handler { | ||||||
| 	return func(context martini.Context, req *http.Request) { | 	return func(ctx *macaron.Context) { | ||||||
| 		ensureNotPointer(formStruct) | 		ensureNotPointer(formStruct) | ||||||
| 		formStruct := reflect.New(reflect.TypeOf(formStruct)) | 		formStruct := reflect.New(reflect.TypeOf(formStruct)) | ||||||
| 		errors := newErrors() | 		errors := newErrors() | ||||||
| 		parseErr := req.ParseForm() | 		parseErr := ctx.Req.ParseForm() | ||||||
| 
 | 
 | ||||||
| 		// Format validation of the request body or the URL would add considerable overhead,
 | 		// Format validation of the request body or the URL would add considerable overhead,
 | ||||||
| 		// and ParseForm does not complain when URL encoding is off.
 | 		// and ParseForm does not complain when URL encoding is off.
 | ||||||
|  | @ -104,14 +105,14 @@ func Form(formStruct interface{}, ifacePtr ...interface{}) martini.Handler { | ||||||
| 			errors.Overall[BindingDeserializationError] = parseErr.Error() | 			errors.Overall[BindingDeserializationError] = parseErr.Error() | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		mapForm(formStruct, req.Form, errors) | 		mapForm(formStruct, ctx.Req.Form, errors) | ||||||
| 
 | 
 | ||||||
| 		validateAndMap(formStruct, context, errors, ifacePtr...) | 		validateAndMap(formStruct, ctx, errors, ifacePtr...) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func MultipartForm(formStruct interface{}, ifacePtr ...interface{}) martini.Handler { | func MultipartForm(formStruct interface{}, ifacePtr ...interface{}) macaron.Handler { | ||||||
| 	return func(context martini.Context, req *http.Request) { | 	return func(ctx *macaron.Context) { | ||||||
| 		ensureNotPointer(formStruct) | 		ensureNotPointer(formStruct) | ||||||
| 		formStruct := reflect.New(reflect.TypeOf(formStruct)) | 		formStruct := reflect.New(reflect.TypeOf(formStruct)) | ||||||
| 		errors := newErrors() | 		errors := newErrors() | ||||||
|  | @ -119,7 +120,7 @@ func MultipartForm(formStruct interface{}, ifacePtr ...interface{}) martini.Hand | ||||||
| 		// Workaround for multipart forms returning nil instead of an error
 | 		// Workaround for multipart forms returning nil instead of an error
 | ||||||
| 		// when content is not multipart
 | 		// when content is not multipart
 | ||||||
| 		// https://code.google.com/p/go/issues/detail?id=6334
 | 		// https://code.google.com/p/go/issues/detail?id=6334
 | ||||||
| 		multipartReader, err := req.MultipartReader() | 		multipartReader, err := ctx.Req.MultipartReader() | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			errors.Overall[BindingDeserializationError] = err.Error() | 			errors.Overall[BindingDeserializationError] = err.Error() | ||||||
| 		} else { | 		} else { | ||||||
|  | @ -129,12 +130,12 @@ func MultipartForm(formStruct interface{}, ifacePtr ...interface{}) martini.Hand | ||||||
| 				errors.Overall[BindingDeserializationError] = parseErr.Error() | 				errors.Overall[BindingDeserializationError] = parseErr.Error() | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			req.MultipartForm = form | 			ctx.Req.MultipartForm = form | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		mapForm(formStruct, req.MultipartForm.Value, errors) | 		mapForm(formStruct, ctx.Req.MultipartForm.Value, errors) | ||||||
| 
 | 
 | ||||||
| 		validateAndMap(formStruct, context, errors, ifacePtr...) | 		validateAndMap(formStruct, ctx, errors, ifacePtr...) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -143,21 +144,21 @@ func MultipartForm(formStruct interface{}, ifacePtr ...interface{}) martini.Hand | ||||||
| // validated, but no error handling is actually performed here.
 | // validated, but no error handling is actually performed here.
 | ||||||
| // An interface pointer can be added as a second argument in order
 | // An interface pointer can be added as a second argument in order
 | ||||||
| // to map the struct to a specific interface.
 | // to map the struct to a specific interface.
 | ||||||
| func Json(jsonStruct interface{}, ifacePtr ...interface{}) martini.Handler { | func Json(jsonStruct interface{}, ifacePtr ...interface{}) macaron.Handler { | ||||||
| 	return func(context martini.Context, req *http.Request) { | 	return func(ctx *macaron.Context) { | ||||||
| 		ensureNotPointer(jsonStruct) | 		ensureNotPointer(jsonStruct) | ||||||
| 		jsonStruct := reflect.New(reflect.TypeOf(jsonStruct)) | 		jsonStruct := reflect.New(reflect.TypeOf(jsonStruct)) | ||||||
| 		errors := newErrors() | 		errors := newErrors() | ||||||
| 
 | 
 | ||||||
| 		if req.Body != nil { | 		if ctx.Req.Body != nil { | ||||||
| 			defer req.Body.Close() | 			defer ctx.Req.Body.Close() | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if err := json.NewDecoder(req.Body).Decode(jsonStruct.Interface()); err != nil && err != io.EOF { | 		if err := json.NewDecoder(ctx.Req.Body).Decode(jsonStruct.Interface()); err != nil && err != io.EOF { | ||||||
| 			errors.Overall[BindingDeserializationError] = err.Error() | 			errors.Overall[BindingDeserializationError] = err.Error() | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		validateAndMap(jsonStruct, context, errors, ifacePtr...) | 		validateAndMap(jsonStruct, ctx, errors, ifacePtr...) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -165,15 +166,15 @@ func Json(jsonStruct interface{}, ifacePtr ...interface{}) martini.Handler { | ||||||
| // passed in is a Validator, then the user-defined Validate method
 | // passed in is a Validator, then the user-defined Validate method
 | ||||||
| // is executed, and its errors are mapped to the context. This middleware
 | // is executed, and its errors are mapped to the context. This middleware
 | ||||||
| // performs no error handling: it merely detects them and maps them.
 | // performs no error handling: it merely detects them and maps them.
 | ||||||
| func Validate(obj interface{}) martini.Handler { | func Validate(obj interface{}) macaron.Handler { | ||||||
| 	return func(context martini.Context, req *http.Request) { | 	return func(ctx *macaron.Context, l i18n.Locale) { | ||||||
| 		errors := newErrors() | 		errors := newErrors() | ||||||
| 		validateStruct(errors, obj) | 		validateStruct(errors, obj) | ||||||
| 
 | 
 | ||||||
| 		if validator, ok := obj.(Validator); ok { | 		if validator, ok := obj.(Validator); ok { | ||||||
| 			validator.Validate(errors, req, context) | 			validator.Validate(ctx, errors, l) | ||||||
| 		} | 		} | ||||||
| 		context.Map(*errors) | 		ctx.Map(*errors) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -387,9 +388,7 @@ func setWithProperType(valueKind reflect.Kind, val string, structField reflect.V | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Don't pass in pointers to bind to. Can lead to bugs. See:
 | // Don't pass in pointers to bind to. Can lead to bugs.
 | ||||||
| // https://github.com/codegangsta/martini-contrib/issues/40
 |  | ||||||
| // https://github.com/codegangsta/martini-contrib/pull/34#issuecomment-29683659
 |  | ||||||
| func ensureNotPointer(obj interface{}) { | func ensureNotPointer(obj interface{}) { | ||||||
| 	if reflect.TypeOf(obj).Kind() == reflect.Ptr { | 	if reflect.TypeOf(obj).Kind() == reflect.Ptr { | ||||||
| 		panic("Pointers are not accepted as binding models") | 		panic("Pointers are not accepted as binding models") | ||||||
|  | @ -399,13 +398,13 @@ func ensureNotPointer(obj interface{}) { | ||||||
| // Performs validation and combines errors from validation
 | // Performs validation and combines errors from validation
 | ||||||
| // with errors from deserialization, then maps both the
 | // with errors from deserialization, then maps both the
 | ||||||
| // resulting struct and the errors to the context.
 | // resulting struct and the errors to the context.
 | ||||||
| func validateAndMap(obj reflect.Value, context martini.Context, errors *Errors, ifacePtr ...interface{}) { | func validateAndMap(obj reflect.Value, ctx *macaron.Context, errors *Errors, ifacePtr ...interface{}) { | ||||||
| 	context.Invoke(Validate(obj.Interface())) | 	ctx.Invoke(Validate(obj.Interface())) | ||||||
| 	errors.Combine(getErrors(context)) | 	errors.Combine(getErrors(ctx)) | ||||||
| 	context.Map(*errors) | 	ctx.Map(*errors) | ||||||
| 	context.Map(obj.Elem().Interface()) | 	ctx.Map(obj.Elem().Interface()) | ||||||
| 	if len(ifacePtr) > 0 { | 	if len(ifacePtr) > 0 { | ||||||
| 		context.MapTo(obj.Elem().Interface(), ifacePtr[0]) | 		ctx.MapTo(obj.Elem().Interface(), ifacePtr[0]) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -413,8 +412,8 @@ func newErrors() *Errors { | ||||||
| 	return &Errors{make(map[string]string), make(map[string]string)} | 	return &Errors{make(map[string]string), make(map[string]string)} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func getErrors(context martini.Context) Errors { | func getErrors(ctx *macaron.Context) Errors { | ||||||
| 	return context.Get(reflect.TypeOf(Errors{})).Interface().(Errors) | 	return ctx.GetVal(reflect.TypeOf(Errors{})).Interface().(Errors) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type ( | type ( | ||||||
|  | @ -422,7 +421,7 @@ type ( | ||||||
| 	// validation before the request even gets to your application.
 | 	// validation before the request even gets to your application.
 | ||||||
| 	// The Validate method will be executed during the validation phase.
 | 	// The Validate method will be executed during the validation phase.
 | ||||||
| 	Validator interface { | 	Validator interface { | ||||||
| 		Validate(*Errors, *http.Request, martini.Context) | 		Validate(*macaron.Context, *Errors, i18n.Locale) | ||||||
| 	} | 	} | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -5,42 +5,33 @@ | ||||||
| package middleware | package middleware | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"crypto/hmac" |  | ||||||
| 	"crypto/sha1" |  | ||||||
| 	"encoding/base64" |  | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"html/template" | 	"html/template" | ||||||
| 	"io" | 	"io" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"net/url" | 	"path" | ||||||
| 	"path/filepath" |  | ||||||
| 	"strconv" |  | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"github.com/go-martini/martini" | 	"github.com/Unknwon/macaron" | ||||||
| 
 | 	"github.com/macaron-contrib/i18n" | ||||||
| 	"github.com/gogits/cache" | 	"github.com/macaron-contrib/session" | ||||||
| 	"github.com/gogits/git" |  | ||||||
| 	"github.com/gogits/session" |  | ||||||
| 
 | 
 | ||||||
| 	"github.com/gogits/gogs/models" | 	"github.com/gogits/gogs/models" | ||||||
| 	"github.com/gogits/gogs/modules/auth" | 	"github.com/gogits/gogs/modules/auth" | ||||||
| 	"github.com/gogits/gogs/modules/base" | 	"github.com/gogits/gogs/modules/base" | ||||||
|  | 	"github.com/gogits/gogs/modules/git" | ||||||
| 	"github.com/gogits/gogs/modules/log" | 	"github.com/gogits/gogs/modules/log" | ||||||
| 	"github.com/gogits/gogs/modules/setting" | 	"github.com/gogits/gogs/modules/setting" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Context represents context of a request.
 | // Context represents context of a request.
 | ||||||
| type Context struct { | type Context struct { | ||||||
| 	*Render | 	*macaron.Context | ||||||
| 	c        martini.Context | 	i18n.Locale | ||||||
| 	p        martini.Params | 	Flash   *session.Flash | ||||||
| 	Req      *http.Request | 	Session session.Store | ||||||
| 	Res      http.ResponseWriter | 
 | ||||||
| 	Flash    *Flash |  | ||||||
| 	Session  session.SessionStore |  | ||||||
| 	Cache    cache.Cache |  | ||||||
| 	User     *models.User | 	User     *models.User | ||||||
| 	IsSigned bool | 	IsSigned bool | ||||||
| 
 | 
 | ||||||
|  | @ -68,7 +59,8 @@ type Context struct { | ||||||
| 			HTTPS string | 			HTTPS string | ||||||
| 			Git   string | 			Git   string | ||||||
| 		} | 		} | ||||||
| 		Mirror *models.Mirror | 		CommitsCount int | ||||||
|  | 		Mirror       *models.Mirror | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -107,12 +99,12 @@ func (ctx *Context) HasError() bool { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // HTML calls render.HTML underlying but reduce one argument.
 | // HTML calls render.HTML underlying but reduce one argument.
 | ||||||
| func (ctx *Context) HTML(status int, name base.TplName, htmlOpt ...HTMLOptions) { | func (ctx *Context) HTML(status int, name base.TplName) { | ||||||
| 	ctx.Render.HTML(status, string(name), ctx.Data, htmlOpt...) | 	ctx.Render.HTML(status, string(name), ctx.Data) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // RenderWithErr used for page has form validation but need to prompt error to users.
 | // RenderWithErr used for page has form validation but need to prompt error to users.
 | ||||||
| func (ctx *Context) RenderWithErr(msg string, tpl base.TplName, form auth.Form) { | func (ctx *Context) RenderWithErr(msg string, tpl base.TplName, form interface{}) { | ||||||
| 	if form != nil { | 	if form != nil { | ||||||
| 		auth.AssignForm(form, ctx.Data) | 		auth.AssignForm(form, ctx.Data) | ||||||
| 	} | 	} | ||||||
|  | @ -124,8 +116,8 @@ func (ctx *Context) RenderWithErr(msg string, tpl base.TplName, form auth.Form) | ||||||
| // Handle handles and logs error by given status.
 | // Handle handles and logs error by given status.
 | ||||||
| func (ctx *Context) Handle(status int, title string, err error) { | func (ctx *Context) Handle(status int, title string, err error) { | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Error("%s: %v", title, err) | 		log.Error(4, "%s: %v", title, err) | ||||||
| 		if martini.Dev != martini.Prod { | 		if macaron.Env != macaron.PROD { | ||||||
| 			ctx.Data["ErrorMsg"] = err | 			ctx.Data["ErrorMsg"] = err | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | @ -139,106 +131,6 @@ func (ctx *Context) Handle(status int, title string, err error) { | ||||||
| 	ctx.HTML(status, base.TplName(fmt.Sprintf("status/%d", status))) | 	ctx.HTML(status, base.TplName(fmt.Sprintf("status/%d", status))) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (ctx *Context) GetCookie(name string) string { |  | ||||||
| 	cookie, err := ctx.Req.Cookie(name) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return "" |  | ||||||
| 	} |  | ||||||
| 	return cookie.Value |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (ctx *Context) SetCookie(name string, value string, others ...interface{}) { |  | ||||||
| 	cookie := http.Cookie{} |  | ||||||
| 	cookie.Name = name |  | ||||||
| 	cookie.Value = value |  | ||||||
| 
 |  | ||||||
| 	if len(others) > 0 { |  | ||||||
| 		switch v := others[0].(type) { |  | ||||||
| 		case int: |  | ||||||
| 			cookie.MaxAge = v |  | ||||||
| 		case int64: |  | ||||||
| 			cookie.MaxAge = int(v) |  | ||||||
| 		case int32: |  | ||||||
| 			cookie.MaxAge = int(v) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// default "/"
 |  | ||||||
| 	if len(others) > 1 { |  | ||||||
| 		if v, ok := others[1].(string); ok && len(v) > 0 { |  | ||||||
| 			cookie.Path = v |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		cookie.Path = "/" |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// default empty
 |  | ||||||
| 	if len(others) > 2 { |  | ||||||
| 		if v, ok := others[2].(string); ok && len(v) > 0 { |  | ||||||
| 			cookie.Domain = v |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// default empty
 |  | ||||||
| 	if len(others) > 3 { |  | ||||||
| 		switch v := others[3].(type) { |  | ||||||
| 		case bool: |  | ||||||
| 			cookie.Secure = v |  | ||||||
| 		default: |  | ||||||
| 			if others[3] != nil { |  | ||||||
| 				cookie.Secure = true |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// default false. for session cookie default true
 |  | ||||||
| 	if len(others) > 4 { |  | ||||||
| 		if v, ok := others[4].(bool); ok && v { |  | ||||||
| 			cookie.HttpOnly = true |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	ctx.Res.Header().Add("Set-Cookie", cookie.String()) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Get secure cookie from request by a given key.
 |  | ||||||
| func (ctx *Context) GetSecureCookie(Secret, key string) (string, bool) { |  | ||||||
| 	val := ctx.GetCookie(key) |  | ||||||
| 	if val == "" { |  | ||||||
| 		return "", false |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	parts := strings.SplitN(val, "|", 3) |  | ||||||
| 
 |  | ||||||
| 	if len(parts) != 3 { |  | ||||||
| 		return "", false |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	vs := parts[0] |  | ||||||
| 	timestamp := parts[1] |  | ||||||
| 	sig := parts[2] |  | ||||||
| 
 |  | ||||||
| 	h := hmac.New(sha1.New, []byte(Secret)) |  | ||||||
| 	fmt.Fprintf(h, "%s%s", vs, timestamp) |  | ||||||
| 
 |  | ||||||
| 	if fmt.Sprintf("%02x", h.Sum(nil)) != sig { |  | ||||||
| 		return "", false |  | ||||||
| 	} |  | ||||||
| 	res, _ := base64.URLEncoding.DecodeString(vs) |  | ||||||
| 	return string(res), true |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Set Secure cookie for response.
 |  | ||||||
| func (ctx *Context) SetSecureCookie(Secret, name, value string, others ...interface{}) { |  | ||||||
| 	vs := base64.URLEncoding.EncodeToString([]byte(value)) |  | ||||||
| 	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10) |  | ||||||
| 	h := hmac.New(sha1.New, []byte(Secret)) |  | ||||||
| 	fmt.Fprintf(h, "%s%s", vs, timestamp) |  | ||||||
| 	sig := fmt.Sprintf("%02x", h.Sum(nil)) |  | ||||||
| 	cookie := strings.Join([]string{vs, timestamp, sig}, "|") |  | ||||||
| 	ctx.SetCookie(name, cookie, others...) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (ctx *Context) CsrfToken() string { | func (ctx *Context) CsrfToken() string { | ||||||
| 	if len(ctx.csrfToken) > 0 { | 	if len(ctx.csrfToken) > 0 { | ||||||
| 		return ctx.csrfToken | 		return ctx.csrfToken | ||||||
|  | @ -271,16 +163,16 @@ func (ctx *Context) ServeFile(file string, names ...string) { | ||||||
| 	if len(names) > 0 { | 	if len(names) > 0 { | ||||||
| 		name = names[0] | 		name = names[0] | ||||||
| 	} else { | 	} else { | ||||||
| 		name = filepath.Base(file) | 		name = path.Base(file) | ||||||
| 	} | 	} | ||||||
| 	ctx.Res.Header().Set("Content-Description", "File Transfer") | 	ctx.Resp.Header().Set("Content-Description", "File Transfer") | ||||||
| 	ctx.Res.Header().Set("Content-Type", "application/octet-stream") | 	ctx.Resp.Header().Set("Content-Type", "application/octet-stream") | ||||||
| 	ctx.Res.Header().Set("Content-Disposition", "attachment; filename="+name) | 	ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+name) | ||||||
| 	ctx.Res.Header().Set("Content-Transfer-Encoding", "binary") | 	ctx.Resp.Header().Set("Content-Transfer-Encoding", "binary") | ||||||
| 	ctx.Res.Header().Set("Expires", "0") | 	ctx.Resp.Header().Set("Expires", "0") | ||||||
| 	ctx.Res.Header().Set("Cache-Control", "must-revalidate") | 	ctx.Resp.Header().Set("Cache-Control", "must-revalidate") | ||||||
| 	ctx.Res.Header().Set("Pragma", "public") | 	ctx.Resp.Header().Set("Pragma", "public") | ||||||
| 	http.ServeFile(ctx.Res, ctx.Req, file) | 	http.ServeFile(ctx.Resp, ctx.Req, file) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interface{}) { | func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interface{}) { | ||||||
|  | @ -291,87 +183,52 @@ func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interfa | ||||||
| 			modtime = v | 			modtime = v | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	ctx.Res.Header().Set("Content-Description", "File Transfer") | 	ctx.Resp.Header().Set("Content-Description", "File Transfer") | ||||||
| 	ctx.Res.Header().Set("Content-Type", "application/octet-stream") | 	ctx.Resp.Header().Set("Content-Type", "application/octet-stream") | ||||||
| 	ctx.Res.Header().Set("Content-Disposition", "attachment; filename="+name) | 	ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+name) | ||||||
| 	ctx.Res.Header().Set("Content-Transfer-Encoding", "binary") | 	ctx.Resp.Header().Set("Content-Transfer-Encoding", "binary") | ||||||
| 	ctx.Res.Header().Set("Expires", "0") | 	ctx.Resp.Header().Set("Expires", "0") | ||||||
| 	ctx.Res.Header().Set("Cache-Control", "must-revalidate") | 	ctx.Resp.Header().Set("Cache-Control", "must-revalidate") | ||||||
| 	ctx.Res.Header().Set("Pragma", "public") | 	ctx.Resp.Header().Set("Pragma", "public") | ||||||
| 	http.ServeContent(ctx.Res, ctx.Req, name, modtime, r) | 	http.ServeContent(ctx.Resp, ctx.Req, name, modtime, r) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type Flash struct { | // Contexter initializes a classic context for a request.
 | ||||||
| 	url.Values | func Contexter() macaron.Handler { | ||||||
| 	ErrorMsg, SuccessMsg string | 	return func(c *macaron.Context, l i18n.Locale, sess session.Store, f *session.Flash) { | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *Flash) Error(msg string) { |  | ||||||
| 	f.Set("error", msg) |  | ||||||
| 	f.ErrorMsg = msg |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *Flash) Success(msg string) { |  | ||||||
| 	f.Set("success", msg) |  | ||||||
| 	f.SuccessMsg = msg |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // InitContext initializes a classic context for a request.
 |  | ||||||
| func InitContext() martini.Handler { |  | ||||||
| 	return func(res http.ResponseWriter, r *http.Request, c martini.Context, rd *Render) { |  | ||||||
| 		ctx := &Context{ | 		ctx := &Context{ | ||||||
| 			c: c, | 			Context: c, | ||||||
| 			// p:      p,
 | 			Locale:  l, | ||||||
| 			Req:    r, | 			Flash:   f, | ||||||
| 			Res:    res, | 			Session: sess, | ||||||
| 			Cache:  setting.Cache, |  | ||||||
| 			Render: rd, |  | ||||||
| 		} | 		} | ||||||
|  | 		// Cache:  setting.Cache,
 | ||||||
|  | 
 | ||||||
|  | 		// Compute current URL for real-time change language.
 | ||||||
|  | 		link := ctx.Req.RequestURI | ||||||
|  | 		i := strings.Index(link, "?") | ||||||
|  | 		if i > -1 { | ||||||
|  | 			link = link[:i] | ||||||
|  | 		} | ||||||
|  | 		ctx.Data["Link"] = link | ||||||
|  | 
 | ||||||
| 		ctx.Data["PageStartTime"] = time.Now() | 		ctx.Data["PageStartTime"] = time.Now() | ||||||
| 
 | 
 | ||||||
| 		// start session
 |  | ||||||
| 		ctx.Session = setting.SessionManager.SessionStart(res, r) |  | ||||||
| 
 |  | ||||||
| 		// Get flash.
 |  | ||||||
| 		values, err := url.ParseQuery(ctx.GetCookie("gogs_flash")) |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Error("InitContext.ParseQuery(flash): %v", err) |  | ||||||
| 		} else if len(values) > 0 { |  | ||||||
| 			ctx.Flash = &Flash{Values: values} |  | ||||||
| 			ctx.Flash.ErrorMsg = ctx.Flash.Get("error") |  | ||||||
| 			ctx.Flash.SuccessMsg = ctx.Flash.Get("success") |  | ||||||
| 			ctx.Data["Flash"] = ctx.Flash |  | ||||||
| 			ctx.SetCookie("gogs_flash", "", -1) |  | ||||||
| 		} |  | ||||||
| 		ctx.Flash = &Flash{Values: url.Values{}} |  | ||||||
| 
 |  | ||||||
| 		rw := res.(martini.ResponseWriter) |  | ||||||
| 		rw.Before(func(martini.ResponseWriter) { |  | ||||||
| 			ctx.Session.SessionRelease(res) |  | ||||||
| 
 |  | ||||||
| 			if flash := ctx.Flash.Encode(); len(flash) > 0 { |  | ||||||
| 				ctx.SetCookie("gogs_flash", flash, 0) |  | ||||||
| 			} |  | ||||||
| 		}) |  | ||||||
| 
 |  | ||||||
| 		// Get user from session if logined.
 | 		// Get user from session if logined.
 | ||||||
| 		user := auth.SignedInUser(ctx.req.Header, ctx.Session) | 		ctx.User = auth.SignedInUser(ctx.Req.Header, ctx.Session) | ||||||
| 		ctx.User = user | 		if ctx.User != nil { | ||||||
| 		ctx.IsSigned = user != nil | 			ctx.IsSigned = true | ||||||
| 
 | 			ctx.Data["IsSigned"] = ctx.IsSigned | ||||||
| 		ctx.Data["IsSigned"] = ctx.IsSigned | 			ctx.Data["SignedUser"] = ctx.User | ||||||
| 
 | 			ctx.Data["SignedUserId"] = ctx.User.Id | ||||||
| 		if user != nil { | 			ctx.Data["SignedUserName"] = ctx.User.Name | ||||||
| 			ctx.Data["SignedUser"] = user |  | ||||||
| 			ctx.Data["SignedUserId"] = user.Id |  | ||||||
| 			ctx.Data["SignedUserName"] = user.Name |  | ||||||
| 			ctx.Data["IsAdmin"] = ctx.User.IsAdmin | 			ctx.Data["IsAdmin"] = ctx.User.IsAdmin | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid.
 | 		// If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid.
 | ||||||
| 		if strings.Contains(r.Header.Get("Content-Type"), "multipart/form-data") { | 		if ctx.Req.Method == "POST" && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") { | ||||||
| 			if err = ctx.Req.ParseMultipartForm(setting.AttachmentMaxSize << 20); err != nil { // 32MB max size
 | 			if err := ctx.Req.ParseMultipartForm(setting.AttachmentMaxSize << 20); err != nil && !strings.Contains(err.Error(), "EOF") { // 32MB max size
 | ||||||
| 				ctx.Handle(500, "issue.Comment(ctx.Req.ParseMultipartForm)", err) | 				ctx.Handle(500, "ParseMultipartForm", err) | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | @ -381,7 +238,5 @@ func InitContext() martini.Handler { | ||||||
| 		ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + ctx.csrfToken + `">`) | 		ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + ctx.csrfToken + `">`) | ||||||
| 
 | 
 | ||||||
| 		c.Map(ctx) | 		c.Map(ctx) | ||||||
| 
 |  | ||||||
| 		c.Next() |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,52 +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 middleware |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"log" |  | ||||||
| 	"net/http" |  | ||||||
| 	"runtime" |  | ||||||
| 	"time" |  | ||||||
| 
 |  | ||||||
| 	"github.com/go-martini/martini" |  | ||||||
| 
 |  | ||||||
| 	"github.com/gogits/gogs/modules/setting" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| var isWindows bool |  | ||||||
| 
 |  | ||||||
| func init() { |  | ||||||
| 	isWindows = runtime.GOOS == "windows" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func Logger() martini.Handler { |  | ||||||
| 	return func(res http.ResponseWriter, req *http.Request, ctx martini.Context, log *log.Logger) { |  | ||||||
| 		if setting.DisableRouterLog { |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		start := time.Now() |  | ||||||
| 		log.Printf("Started %s %s", req.Method, req.URL.Path) |  | ||||||
| 
 |  | ||||||
| 		rw := res.(martini.ResponseWriter) |  | ||||||
| 		ctx.Next() |  | ||||||
| 
 |  | ||||||
| 		content := fmt.Sprintf("Completed %v %s in %v", rw.Status(), http.StatusText(rw.Status()), time.Since(start)) |  | ||||||
| 		if !isWindows { |  | ||||||
| 			switch rw.Status() { |  | ||||||
| 			case 200: |  | ||||||
| 				content = fmt.Sprintf("\033[1;32m%s\033[0m", content) |  | ||||||
| 			case 304: |  | ||||||
| 				content = fmt.Sprintf("\033[1;33m%s\033[0m", content) |  | ||||||
| 			case 404: |  | ||||||
| 				content = fmt.Sprintf("\033[1;31m%s\033[0m", content) |  | ||||||
| 			case 500: |  | ||||||
| 				content = fmt.Sprintf("\033[1;36m%s\033[0m", content) |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		log.Println(content) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  | @ -1,281 +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.
 |  | ||||||
| 
 |  | ||||||
| // foked from https://github.com/martini-contrib/render/blob/master/render.go
 |  | ||||||
| package middleware |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"bytes" |  | ||||||
| 	"encoding/json" |  | ||||||
| 	"fmt" |  | ||||||
| 	"html/template" |  | ||||||
| 	"io" |  | ||||||
| 	"io/ioutil" |  | ||||||
| 	"net/http" |  | ||||||
| 	"os" |  | ||||||
| 	"path/filepath" |  | ||||||
| 	"time" |  | ||||||
| 
 |  | ||||||
| 	"github.com/go-martini/martini" |  | ||||||
| 
 |  | ||||||
| 	"github.com/gogits/gogs/modules/base" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| const ( |  | ||||||
| 	ContentType    = "Content-Type" |  | ||||||
| 	ContentLength  = "Content-Length" |  | ||||||
| 	ContentJSON    = "application/json" |  | ||||||
| 	ContentHTML    = "text/html" |  | ||||||
| 	ContentXHTML   = "application/xhtml+xml" |  | ||||||
| 	defaultCharset = "UTF-8" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| var helperFuncs = template.FuncMap{ |  | ||||||
| 	"yield": func() (string, error) { |  | ||||||
| 		return "", fmt.Errorf("yield called with no layout defined") |  | ||||||
| 	}, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type Delims struct { |  | ||||||
| 	Left  string |  | ||||||
| 	Right string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type RenderOptions struct { |  | ||||||
| 	Directory       string |  | ||||||
| 	Layout          string |  | ||||||
| 	Extensions      []string |  | ||||||
| 	Funcs           []template.FuncMap |  | ||||||
| 	Delims          Delims |  | ||||||
| 	Charset         string |  | ||||||
| 	IndentJSON      bool |  | ||||||
| 	HTMLContentType string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type HTMLOptions struct { |  | ||||||
| 	Layout string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func Renderer(options ...RenderOptions) martini.Handler { |  | ||||||
| 	opt := prepareOptions(options) |  | ||||||
| 	cs := prepareCharset(opt.Charset) |  | ||||||
| 	t := compile(opt) |  | ||||||
| 	return func(res http.ResponseWriter, req *http.Request, c martini.Context) { |  | ||||||
| 		var tc *template.Template |  | ||||||
| 		if martini.Env == martini.Dev { |  | ||||||
| 
 |  | ||||||
| 			tc = compile(opt) |  | ||||||
| 		} else { |  | ||||||
| 
 |  | ||||||
| 			tc, _ = t.Clone() |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		rd := &Render{res, req, tc, opt, cs, base.TmplData{}, time.Time{}} |  | ||||||
| 
 |  | ||||||
| 		rd.Data["TmplLoadTimes"] = func() string { |  | ||||||
| 			if rd.startTime.IsZero() { |  | ||||||
| 				return "" |  | ||||||
| 			} |  | ||||||
| 			return fmt.Sprint(time.Since(rd.startTime).Nanoseconds()/1e6) + "ms" |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		c.Map(rd.Data) |  | ||||||
| 		c.Map(rd) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func prepareCharset(charset string) string { |  | ||||||
| 	if len(charset) != 0 { |  | ||||||
| 		return "; charset=" + charset |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return "; charset=" + defaultCharset |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func prepareOptions(options []RenderOptions) RenderOptions { |  | ||||||
| 	var opt RenderOptions |  | ||||||
| 	if len(options) > 0 { |  | ||||||
| 		opt = options[0] |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if len(opt.Directory) == 0 { |  | ||||||
| 		opt.Directory = "templates" |  | ||||||
| 	} |  | ||||||
| 	if len(opt.Extensions) == 0 { |  | ||||||
| 		opt.Extensions = []string{".tmpl"} |  | ||||||
| 	} |  | ||||||
| 	if len(opt.HTMLContentType) == 0 { |  | ||||||
| 		opt.HTMLContentType = ContentHTML |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return opt |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func compile(options RenderOptions) *template.Template { |  | ||||||
| 	dir := options.Directory |  | ||||||
| 	t := template.New(dir) |  | ||||||
| 	t.Delims(options.Delims.Left, options.Delims.Right) |  | ||||||
| 
 |  | ||||||
| 	template.Must(t.Parse("Martini")) |  | ||||||
| 
 |  | ||||||
| 	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { |  | ||||||
| 		r, err := filepath.Rel(dir, path) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		ext := filepath.Ext(r) |  | ||||||
| 		for _, extension := range options.Extensions { |  | ||||||
| 			if ext == extension { |  | ||||||
| 
 |  | ||||||
| 				buf, err := ioutil.ReadFile(path) |  | ||||||
| 				if err != nil { |  | ||||||
| 					panic(err) |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				name := (r[0 : len(r)-len(ext)]) |  | ||||||
| 				tmpl := t.New(filepath.ToSlash(name)) |  | ||||||
| 
 |  | ||||||
| 				for _, funcs := range options.Funcs { |  | ||||||
| 					tmpl = tmpl.Funcs(funcs) |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				template.Must(tmpl.Funcs(helperFuncs).Parse(string(buf))) |  | ||||||
| 				break |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		return nil |  | ||||||
| 	}) |  | ||||||
| 
 |  | ||||||
| 	return t |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type Render struct { |  | ||||||
| 	http.ResponseWriter |  | ||||||
| 	req             *http.Request |  | ||||||
| 	t               *template.Template |  | ||||||
| 	opt             RenderOptions |  | ||||||
| 	compiledCharset string |  | ||||||
| 
 |  | ||||||
| 	Data base.TmplData |  | ||||||
| 
 |  | ||||||
| 	startTime time.Time |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *Render) JSON(status int, v interface{}) { |  | ||||||
| 	var result []byte |  | ||||||
| 	var err error |  | ||||||
| 	if r.opt.IndentJSON { |  | ||||||
| 		result, err = json.MarshalIndent(v, "", "  ") |  | ||||||
| 	} else { |  | ||||||
| 		result, err = json.Marshal(v) |  | ||||||
| 	} |  | ||||||
| 	if err != nil { |  | ||||||
| 		http.Error(r, err.Error(), 500) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	r.Header().Set(ContentType, ContentJSON+r.compiledCharset) |  | ||||||
| 	r.WriteHeader(status) |  | ||||||
| 	r.Write(result) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *Render) JSONString(v interface{}) (string, error) { |  | ||||||
| 	var result []byte |  | ||||||
| 	var err error |  | ||||||
| 	if r.opt.IndentJSON { |  | ||||||
| 		result, err = json.MarshalIndent(v, "", "  ") |  | ||||||
| 	} else { |  | ||||||
| 		result, err = json.Marshal(v) |  | ||||||
| 	} |  | ||||||
| 	if err != nil { |  | ||||||
| 		return "", err |  | ||||||
| 	} |  | ||||||
| 	return string(result), nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *Render) renderBytes(name string, binding interface{}, htmlOpt ...HTMLOptions) (*bytes.Buffer, error) { |  | ||||||
| 	opt := r.prepareHTMLOptions(htmlOpt) |  | ||||||
| 
 |  | ||||||
| 	if len(opt.Layout) > 0 { |  | ||||||
| 		r.addYield(name, binding) |  | ||||||
| 		name = opt.Layout |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	out, err := r.execute(name, binding) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return out, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *Render) HTML(status int, name string, binding interface{}, htmlOpt ...HTMLOptions) { |  | ||||||
| 	r.startTime = time.Now() |  | ||||||
| 
 |  | ||||||
| 	out, err := r.renderBytes(name, binding, htmlOpt...) |  | ||||||
| 	if err != nil { |  | ||||||
| 		http.Error(r, err.Error(), http.StatusInternalServerError) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	r.Header().Set(ContentType, r.opt.HTMLContentType+r.compiledCharset) |  | ||||||
| 	r.WriteHeader(status) |  | ||||||
| 	io.Copy(r, out) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *Render) HTMLString(name string, binding interface{}, htmlOpt ...HTMLOptions) (string, error) { |  | ||||||
| 	if out, err := r.renderBytes(name, binding, htmlOpt...); err != nil { |  | ||||||
| 		return "", err |  | ||||||
| 	} else { |  | ||||||
| 		return out.String(), nil |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *Render) Error(status int, message ...string) { |  | ||||||
| 	r.WriteHeader(status) |  | ||||||
| 	if len(message) > 0 { |  | ||||||
| 		r.Write([]byte(message[0])) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *Render) Redirect(location string, status ...int) { |  | ||||||
| 	code := http.StatusFound |  | ||||||
| 	if len(status) == 1 { |  | ||||||
| 		code = status[0] |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	http.Redirect(r, r.req, location, code) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *Render) Template() *template.Template { |  | ||||||
| 	return r.t |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *Render) execute(name string, binding interface{}) (*bytes.Buffer, error) { |  | ||||||
| 	buf := new(bytes.Buffer) |  | ||||||
| 	return buf, r.t.ExecuteTemplate(buf, name, binding) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *Render) addYield(name string, binding interface{}) { |  | ||||||
| 	funcs := template.FuncMap{ |  | ||||||
| 		"yield": func() (template.HTML, error) { |  | ||||||
| 			buf, err := r.execute(name, binding) |  | ||||||
| 
 |  | ||||||
| 			return template.HTML(buf.String()), err |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
| 	r.t.Funcs(funcs) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *Render) prepareHTMLOptions(htmlOpt []HTMLOptions) HTMLOptions { |  | ||||||
| 	if len(htmlOpt) > 0 { |  | ||||||
| 		return htmlOpt[0] |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return HTMLOptions{ |  | ||||||
| 		Layout: r.opt.Layout, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  | @ -10,20 +10,19 @@ import ( | ||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"github.com/go-martini/martini" | 	"github.com/Unknwon/macaron" | ||||||
| 
 |  | ||||||
| 	"github.com/gogits/git" |  | ||||||
| 
 | 
 | ||||||
| 	"github.com/gogits/gogs/models" | 	"github.com/gogits/gogs/models" | ||||||
|  | 	"github.com/gogits/gogs/modules/git" | ||||||
| 	"github.com/gogits/gogs/modules/log" | 	"github.com/gogits/gogs/modules/log" | ||||||
| 	"github.com/gogits/gogs/modules/setting" | 	"github.com/gogits/gogs/modules/setting" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func RepoAssignment(redirect bool, args ...bool) martini.Handler { | func RepoAssignment(redirect bool, args ...bool) macaron.Handler { | ||||||
| 	return func(ctx *Context, params martini.Params) { | 	return func(ctx *Context) { | ||||||
| 		// valid brachname
 | 		// To valid brach name.
 | ||||||
| 		var validBranch bool | 		var validBranch bool | ||||||
| 		// display bare quick start if it is a bare repo
 | 		// To display bare quick start if it is a bare repo.
 | ||||||
| 		var displayBare bool | 		var displayBare bool | ||||||
| 
 | 
 | ||||||
| 		if len(args) >= 1 { | 		if len(args) >= 1 { | ||||||
|  | @ -35,51 +34,53 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler { | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		var ( | 		var ( | ||||||
| 			user *models.User | 			u   *models.User | ||||||
| 			err  error | 			err error | ||||||
| 		) | 		) | ||||||
| 
 | 
 | ||||||
| 		userName := params["username"] | 		userName := ctx.Params(":username") | ||||||
| 		repoName := params["reponame"] | 		repoName := ctx.Params(":reponame") | ||||||
| 		refName := params["branchname"] | 		refName := ctx.Params(":branchname") | ||||||
|  | 		if len(refName) == 0 { | ||||||
|  | 			refName = ctx.Params(":path") | ||||||
|  | 		} | ||||||
| 
 | 
 | ||||||
| 		// TODO: need more advanced onwership and access level check.
 |  | ||||||
| 		// Collaborators who have write access can be seen as owners.
 | 		// Collaborators who have write access can be seen as owners.
 | ||||||
| 		if ctx.IsSigned { | 		if ctx.IsSigned { | ||||||
| 			ctx.Repo.IsOwner, err = models.HasAccess(ctx.User.Name, userName+"/"+repoName, models.WRITABLE) | 			ctx.Repo.IsOwner, err = models.HasAccess(ctx.User.Name, userName+"/"+repoName, models.WRITABLE) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				ctx.Handle(500, "RepoAssignment(HasAccess)", err) | 				ctx.Handle(500, "HasAccess", err) | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 			ctx.Repo.IsTrueOwner = ctx.User.LowerName == strings.ToLower(userName) | 			ctx.Repo.IsTrueOwner = ctx.User.LowerName == strings.ToLower(userName) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if !ctx.Repo.IsTrueOwner { | 		if !ctx.Repo.IsTrueOwner { | ||||||
| 			user, err = models.GetUserByName(userName) | 			u, err = models.GetUserByName(userName) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				if err == models.ErrUserNotExist { | 				if err == models.ErrUserNotExist { | ||||||
| 					ctx.Handle(404, "RepoAssignment(GetUserByName)", err) | 					ctx.Handle(404, "GetUserByName", err) | ||||||
| 					return | 					return | ||||||
| 				} else if redirect { | 				} else if redirect { | ||||||
| 					ctx.Redirect("/") | 					ctx.Redirect("/") | ||||||
| 					return | 					return | ||||||
| 				} | 				} | ||||||
| 				ctx.Handle(500, "RepoAssignment(GetUserByName)", err) | 				ctx.Handle(500, "GetUserByName", err) | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
| 			user = ctx.User | 			u = ctx.User | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if user == nil { | 		if u == nil { | ||||||
| 			if redirect { | 			if redirect { | ||||||
| 				ctx.Redirect("/") | 				ctx.Redirect("/") | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 			ctx.Handle(403, "RepoAssignment", errors.New("invliad user account for single repository")) | 			ctx.Handle(404, "RepoAssignment", errors.New("invliad user account for single repository")) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 		ctx.Repo.Owner = user | 		ctx.Repo.Owner = u | ||||||
| 
 | 
 | ||||||
| 		// Organization owner team members are true owners as well.
 | 		// Organization owner team members are true owners as well.
 | ||||||
| 		if ctx.IsSigned && ctx.Repo.Owner.IsOrganization() && ctx.Repo.Owner.IsOrgOwner(ctx.User.Id) { | 		if ctx.IsSigned && ctx.Repo.Owner.IsOrganization() && ctx.Repo.Owner.IsOrgOwner(ctx.User.Id) { | ||||||
|  | @ -87,16 +88,19 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler { | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// get repository
 | 		// get repository
 | ||||||
| 		repo, err := models.GetRepositoryByName(user.Id, repoName) | 		repo, err := models.GetRepositoryByName(u.Id, repoName) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			if err == models.ErrRepoNotExist { | 			if err == models.ErrRepoNotExist { | ||||||
| 				ctx.Handle(404, "RepoAssignment", err) | 				ctx.Handle(404, "GetRepositoryByName", err) | ||||||
| 				return | 				return | ||||||
| 			} else if redirect { | 			} else if redirect { | ||||||
| 				ctx.Redirect("/") | 				ctx.Redirect("/") | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 			ctx.Handle(500, "RepoAssignment", err) | 			ctx.Handle(500, "GetRepositoryByName", err) | ||||||
|  | 			return | ||||||
|  | 		} else if err = repo.GetOwner(); err != nil { | ||||||
|  | 			ctx.Handle(500, "GetOwner", err) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | @ -108,16 +112,16 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler { | ||||||
| 		// Check access.
 | 		// Check access.
 | ||||||
| 		if repo.IsPrivate && !ctx.Repo.IsOwner { | 		if repo.IsPrivate && !ctx.Repo.IsOwner { | ||||||
| 			if ctx.User == nil { | 			if ctx.User == nil { | ||||||
| 				ctx.Handle(404, "RepoAssignment(HasAccess)", nil) | 				ctx.Handle(404, "HasAccess", nil) | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			hasAccess, err := models.HasAccess(ctx.User.Name, ctx.Repo.Owner.Name+"/"+repo.Name, models.READABLE) | 			hasAccess, err := models.HasAccess(ctx.User.Name, ctx.Repo.Owner.Name+"/"+repo.Name, models.READABLE) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				ctx.Handle(500, "RepoAssignment(HasAccess)", err) | 				ctx.Handle(500, "HasAccess", err) | ||||||
| 				return | 				return | ||||||
| 			} else if !hasAccess { | 			} else if !hasAccess { | ||||||
| 				ctx.Handle(404, "RepoAssignment(HasAccess)", nil) | 				ctx.Handle(404, "HasAccess", nil) | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | @ -127,7 +131,7 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler { | ||||||
| 		if repo.IsMirror { | 		if repo.IsMirror { | ||||||
| 			ctx.Repo.Mirror, err = models.GetMirror(repo.Id) | 			ctx.Repo.Mirror, err = models.GetMirror(repo.Id) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				ctx.Handle(500, "RepoAssignment(GetMirror)", err) | 				ctx.Handle(500, "GetMirror", err) | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 			ctx.Data["MirrorInterval"] = ctx.Repo.Mirror.Interval | 			ctx.Data["MirrorInterval"] = ctx.Repo.Mirror.Interval | ||||||
|  | @ -144,34 +148,33 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler { | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 		ctx.Repo.GitRepo = gitRepo | 		ctx.Repo.GitRepo = gitRepo | ||||||
| 		ctx.Repo.RepoLink = "/" + user.Name + "/" + repo.Name | 		ctx.Repo.RepoLink = "/" + u.Name + "/" + repo.Name | ||||||
| 
 | 
 | ||||||
| 		tags, err := ctx.Repo.GitRepo.GetTags() | 		tags, err := ctx.Repo.GitRepo.GetTags() | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			ctx.Handle(500, "RepoAssignment(GetTags))", err) | 			ctx.Handle(500, "GetTags", err) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 		ctx.Repo.Repository.NumTags = len(tags) | 		ctx.Repo.Repository.NumTags = len(tags) | ||||||
| 
 | 
 | ||||||
| 		ctx.Data["Title"] = user.Name + "/" + repo.Name | 		ctx.Data["Title"] = u.Name + "/" + repo.Name | ||||||
| 		ctx.Data["Repository"] = repo | 		ctx.Data["Repository"] = repo | ||||||
| 		ctx.Data["Owner"] = user | 		ctx.Data["Owner"] = ctx.Repo.Repository.Owner | ||||||
| 		ctx.Data["RepoLink"] = ctx.Repo.RepoLink | 		ctx.Data["RepoLink"] = ctx.Repo.RepoLink | ||||||
| 		ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner | 		ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner | ||||||
| 		ctx.Data["IsRepositoryTrueOwner"] = ctx.Repo.IsTrueOwner | 		ctx.Data["IsRepositoryTrueOwner"] = ctx.Repo.IsTrueOwner | ||||||
| 		ctx.Data["BranchName"] = "" |  | ||||||
| 
 | 
 | ||||||
| 		if setting.SshPort != 22 { | 		if setting.SshPort != 22 { | ||||||
| 			ctx.Repo.CloneLink.SSH = fmt.Sprintf("ssh://%s@%s/%s/%s.git", setting.RunUser, setting.Domain, user.LowerName, repo.LowerName) | 			ctx.Repo.CloneLink.SSH = fmt.Sprintf("ssh://%s@%s/%s/%s.git", setting.RunUser, setting.Domain, u.LowerName, repo.LowerName) | ||||||
| 		} else { | 		} else { | ||||||
| 			ctx.Repo.CloneLink.SSH = fmt.Sprintf("%s@%s:%s/%s.git", setting.RunUser, setting.Domain, user.LowerName, repo.LowerName) | 			ctx.Repo.CloneLink.SSH = fmt.Sprintf("%s@%s:%s/%s.git", setting.RunUser, setting.Domain, u.LowerName, repo.LowerName) | ||||||
| 		} | 		} | ||||||
| 		ctx.Repo.CloneLink.HTTPS = fmt.Sprintf("%s%s/%s.git", setting.AppUrl, user.LowerName, repo.LowerName) | 		ctx.Repo.CloneLink.HTTPS = fmt.Sprintf("%s%s/%s.git", setting.AppUrl, u.LowerName, repo.LowerName) | ||||||
| 		ctx.Data["CloneLink"] = ctx.Repo.CloneLink | 		ctx.Data["CloneLink"] = ctx.Repo.CloneLink | ||||||
| 
 | 
 | ||||||
| 		if ctx.Repo.Repository.IsGoget { | 		if ctx.Repo.Repository.IsGoget { | ||||||
| 			ctx.Data["GoGetLink"] = fmt.Sprintf("%s%s/%s", setting.AppUrl, user.LowerName, repo.LowerName) | 			ctx.Data["GoGetLink"] = fmt.Sprintf("%s%s/%s", setting.AppUrl, u.LowerName, repo.LowerName) | ||||||
| 			ctx.Data["GoGetImport"] = fmt.Sprintf("%s/%s/%s", setting.Domain, user.LowerName, repo.LowerName) | 			ctx.Data["GoGetImport"] = fmt.Sprintf("%s/%s/%s", setting.Domain, u.LowerName, repo.LowerName) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// when repo is bare, not valid branch
 | 		// when repo is bare, not valid branch
 | ||||||
|  | @ -211,7 +214,7 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler { | ||||||
| 						return | 						return | ||||||
| 					} | 					} | ||||||
| 				} else { | 				} else { | ||||||
| 					ctx.Handle(404, "RepoAssignment invalid repo", nil) | 					ctx.Handle(404, "RepoAssignment invalid repo", errors.New("branch or tag not exist")) | ||||||
| 					return | 					return | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
|  | @ -222,7 +225,7 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler { | ||||||
| 					} else { | 					} else { | ||||||
| 						brs, err := gitRepo.GetBranches() | 						brs, err := gitRepo.GetBranches() | ||||||
| 						if err != nil { | 						if err != nil { | ||||||
| 							ctx.Handle(500, "RepoAssignment(GetBranches))", err) | 							ctx.Handle(500, "GetBranches", err) | ||||||
| 							return | 							return | ||||||
| 						} | 						} | ||||||
| 						refName = brs[0] | 						refName = brs[0] | ||||||
|  | @ -233,6 +236,13 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler { | ||||||
| 
 | 
 | ||||||
| 			ctx.Data["IsBranch"] = ctx.Repo.IsBranch | 			ctx.Data["IsBranch"] = ctx.Repo.IsBranch | ||||||
| 			ctx.Data["IsCommit"] = ctx.Repo.IsCommit | 			ctx.Data["IsCommit"] = ctx.Repo.IsCommit | ||||||
|  | 
 | ||||||
|  | 			ctx.Repo.CommitsCount, err = ctx.Repo.Commit.CommitsCount() | ||||||
|  | 			if err != nil { | ||||||
|  | 				ctx.Handle(500, "CommitsCount", err) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		log.Debug("displayBare: %v; IsBare: %v", displayBare, ctx.Repo.Repository.IsBare) | 		log.Debug("displayBare: %v; IsBare: %v", displayBare, ctx.Repo.Repository.IsBare) | ||||||
|  | @ -240,7 +250,7 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler { | ||||||
| 		// repo is bare and display enable
 | 		// repo is bare and display enable
 | ||||||
| 		if displayBare && ctx.Repo.Repository.IsBare { | 		if displayBare && ctx.Repo.Repository.IsBare { | ||||||
| 			log.Debug("Bare repository: %s", ctx.Repo.RepoLink) | 			log.Debug("Bare repository: %s", ctx.Repo.RepoLink) | ||||||
| 			ctx.HTML(200, "repo/single_bare") | 			ctx.HTML(200, "repo/bare") | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | @ -251,9 +261,10 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler { | ||||||
| 		ctx.Data["TagName"] = ctx.Repo.TagName | 		ctx.Data["TagName"] = ctx.Repo.TagName | ||||||
| 		brs, err := ctx.Repo.GitRepo.GetBranches() | 		brs, err := ctx.Repo.GitRepo.GetBranches() | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Error("RepoAssignment(GetBranches): %v", err) | 			log.Error(4, "GetBranches: %v", err) | ||||||
| 		} | 		} | ||||||
| 		ctx.Data["Branches"] = brs | 		ctx.Data["Branches"] = brs | ||||||
|  | 		ctx.Data["BrancheCount"] = len(brs) | ||||||
| 
 | 
 | ||||||
| 		// If not branch selected, try default one.
 | 		// If not branch selected, try default one.
 | ||||||
| 		// If default branch doesn't exists, fall back to some other branch.
 | 		// If default branch doesn't exists, fall back to some other branch.
 | ||||||
|  | @ -267,11 +278,11 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler { | ||||||
| 
 | 
 | ||||||
| 		ctx.Data["BranchName"] = ctx.Repo.BranchName | 		ctx.Data["BranchName"] = ctx.Repo.BranchName | ||||||
| 		ctx.Data["CommitId"] = ctx.Repo.CommitId | 		ctx.Data["CommitId"] = ctx.Repo.CommitId | ||||||
| 		ctx.Data["IsRepositoryWatching"] = ctx.Repo.IsWatching | 		ctx.Data["IsWatchingRepo"] = ctx.Repo.IsWatching | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func RequireTrueOwner() martini.Handler { | func RequireTrueOwner() macaron.Handler { | ||||||
| 	return func(ctx *Context) { | 	return func(ctx *Context) { | ||||||
| 		if !ctx.Repo.IsTrueOwner { | 		if !ctx.Repo.IsTrueOwner { | ||||||
| 			if !ctx.IsSigned { | 			if !ctx.IsSigned { | ||||||
|  |  | ||||||
|  | @ -1,127 +0,0 @@ | ||||||
| // Copyright 2013 The Martini Authors. All rights reserved.
 |  | ||||||
| // 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 middleware |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"log" |  | ||||||
| 	"net/http" |  | ||||||
| 	"path" |  | ||||||
| 	"runtime" |  | ||||||
| 	"strings" |  | ||||||
| 
 |  | ||||||
| 	"github.com/go-martini/martini" |  | ||||||
| 
 |  | ||||||
| 	"github.com/gogits/gogs/modules/setting" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // StaticOptions is a struct for specifying configuration options for the martini.Static middleware.
 |  | ||||||
| type StaticOptions struct { |  | ||||||
| 	// Prefix is the optional prefix used to serve the static directory content
 |  | ||||||
| 	Prefix string |  | ||||||
| 	// SkipLogging will disable [Static] log messages when a static file is served.
 |  | ||||||
| 	SkipLogging bool |  | ||||||
| 	// IndexFile defines which file to serve as index if it exists.
 |  | ||||||
| 	IndexFile string |  | ||||||
| 	// Expires defines which user-defined function to use for producing a HTTP Expires Header
 |  | ||||||
| 	// https://developers.google.com/speed/docs/insights/LeverageBrowserCaching
 |  | ||||||
| 	Expires func() string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func prepareStaticOptions(options []StaticOptions) StaticOptions { |  | ||||||
| 	var opt StaticOptions |  | ||||||
| 	if len(options) > 0 { |  | ||||||
| 		opt = options[0] |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Defaults
 |  | ||||||
| 	if len(opt.IndexFile) == 0 { |  | ||||||
| 		opt.IndexFile = "index.html" |  | ||||||
| 	} |  | ||||||
| 	// Normalize the prefix if provided
 |  | ||||||
| 	if opt.Prefix != "" { |  | ||||||
| 		// Ensure we have a leading '/'
 |  | ||||||
| 		if opt.Prefix[0] != '/' { |  | ||||||
| 			opt.Prefix = "/" + opt.Prefix |  | ||||||
| 		} |  | ||||||
| 		// Remove any trailing '/'
 |  | ||||||
| 		opt.Prefix = strings.TrimRight(opt.Prefix, "/") |  | ||||||
| 	} |  | ||||||
| 	return opt |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Static returns a middleware handler that serves static files in the given directory.
 |  | ||||||
| func Static(directory string, staticOpt ...StaticOptions) martini.Handler { |  | ||||||
| 	if runtime.GOOS == "windows" { |  | ||||||
| 		if len(directory) < 2 || directory[1] != ':' { |  | ||||||
| 			directory = path.Join(setting.StaticRootPath, directory) |  | ||||||
| 		} |  | ||||||
| 	} else if !path.IsAbs(directory) { |  | ||||||
| 		directory = path.Join(setting.StaticRootPath, directory) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	dir := http.Dir(directory) |  | ||||||
| 	opt := prepareStaticOptions(staticOpt) |  | ||||||
| 
 |  | ||||||
| 	return func(res http.ResponseWriter, req *http.Request, log *log.Logger) { |  | ||||||
| 		if req.Method != "GET" && req.Method != "HEAD" { |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 		file := req.URL.Path |  | ||||||
| 		// if we have a prefix, filter requests by stripping the prefix
 |  | ||||||
| 		if opt.Prefix != "" { |  | ||||||
| 			if !strings.HasPrefix(file, opt.Prefix) { |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 			file = file[len(opt.Prefix):] |  | ||||||
| 			if file != "" && file[0] != '/' { |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		f, err := dir.Open(file) |  | ||||||
| 		if err != nil { |  | ||||||
| 			// discard the error?
 |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 		defer f.Close() |  | ||||||
| 
 |  | ||||||
| 		fi, err := f.Stat() |  | ||||||
| 		if err != nil { |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// try to serve index file
 |  | ||||||
| 		if fi.IsDir() { |  | ||||||
| 			// redirect if missing trailing slash
 |  | ||||||
| 			if !strings.HasSuffix(req.URL.Path, "/") { |  | ||||||
| 				http.Redirect(res, req, req.URL.Path+"/", http.StatusFound) |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			file = path.Join(file, opt.IndexFile) |  | ||||||
| 			f, err = dir.Open(file) |  | ||||||
| 			if err != nil { |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 			defer f.Close() |  | ||||||
| 
 |  | ||||||
| 			fi, err = f.Stat() |  | ||||||
| 			if err != nil || fi.IsDir() { |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if !opt.SkipLogging { |  | ||||||
| 			log.Println("[Static] Serving " + file) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// Add an Expires header to the static content
 |  | ||||||
| 		if opt.Expires != nil { |  | ||||||
| 			res.Header().Set("Expires", opt.Expires()) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		http.ServeContent(res, req, file, fi.ModTime(), f) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  | @ -78,7 +78,7 @@ func ExecDir(timeout time.Duration, dir, desc, cmdName string, args ...string) ( | ||||||
| 	select { | 	select { | ||||||
| 	case <-time.After(timeout): | 	case <-time.After(timeout): | ||||||
| 		if errKill := Kill(pid); errKill != nil { | 		if errKill := Kill(pid); errKill != nil { | ||||||
| 			log.Error("Exec(%d:%s): %v", pid, desc, errKill) | 			log.Error(4, "Exec(%d:%s): %v", pid, desc, errKill) | ||||||
| 		} | 		} | ||||||
| 		<-done | 		<-done | ||||||
| 		return "", ErrExecTimeout.Error(), ErrExecTimeout | 		return "", ErrExecTimeout.Error(), ErrExecTimeout | ||||||
|  |  | ||||||
|  | @ -15,12 +15,12 @@ import ( | ||||||
| 
 | 
 | ||||||
| 	"github.com/Unknwon/com" | 	"github.com/Unknwon/com" | ||||||
| 	"github.com/Unknwon/goconfig" | 	"github.com/Unknwon/goconfig" | ||||||
|  | 	"github.com/macaron-contrib/session" | ||||||
| 
 | 
 | ||||||
| 	"github.com/gogits/cache" | 	"github.com/gogits/cache" | ||||||
| 	"github.com/gogits/session" |  | ||||||
| 
 | 
 | ||||||
| 	"github.com/gogits/gogs/modules/bin" |  | ||||||
| 	"github.com/gogits/gogs/modules/log" | 	"github.com/gogits/gogs/modules/log" | ||||||
|  | 	// "github.com/gogits/gogs-ng/modules/ssh"
 | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type Scheme string | type Scheme string | ||||||
|  | @ -46,6 +46,7 @@ var ( | ||||||
| 	DisableRouterLog   bool | 	DisableRouterLog   bool | ||||||
| 	CertFile, KeyFile  string | 	CertFile, KeyFile  string | ||||||
| 	StaticRootPath     string | 	StaticRootPath     string | ||||||
|  | 	EnableGzip         bool | ||||||
| 
 | 
 | ||||||
| 	// Security settings.
 | 	// Security settings.
 | ||||||
| 	InstallLock          bool | 	InstallLock          bool | ||||||
|  | @ -93,15 +94,22 @@ var ( | ||||||
| 	// Session settings.
 | 	// Session settings.
 | ||||||
| 	SessionProvider string | 	SessionProvider string | ||||||
| 	SessionConfig   *session.Config | 	SessionConfig   *session.Config | ||||||
| 	SessionManager  *session.Manager |  | ||||||
| 
 | 
 | ||||||
| 	// Global setting objects.
 | 	// Global setting objects.
 | ||||||
| 	Cfg        *goconfig.ConfigFile | 	Cfg          *goconfig.ConfigFile | ||||||
| 	CustomPath string // Custom directory path.
 | 	ConfRootPath string | ||||||
| 	ProdMode   bool | 	CustomPath   string // Custom directory path.
 | ||||||
| 	RunUser    string | 	ProdMode     bool | ||||||
|  | 	RunUser      string | ||||||
|  | 
 | ||||||
|  | 	// I18n settings.
 | ||||||
|  | 	Langs, Names []string | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | func init() { | ||||||
|  | 	log.NewLogger(0, "console", `{"level": 0}`) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func ExecPath() (string, error) { | func ExecPath() (string, error) { | ||||||
| 	file, err := exec.LookPath(os.Args[0]) | 	file, err := exec.LookPath(os.Args[0]) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|  | @ -125,16 +133,13 @@ func WorkDir() (string, error) { | ||||||
| func NewConfigContext() { | func NewConfigContext() { | ||||||
| 	workDir, err := WorkDir() | 	workDir, err := WorkDir() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatal("Fail to get work directory: %v", err) | 		log.Fatal(4, "Fail to get work directory: %v", err) | ||||||
| 	} | 	} | ||||||
|  | 	ConfRootPath = path.Join(workDir, "conf") | ||||||
| 
 | 
 | ||||||
| 	data, err := bin.Asset("conf/app.ini") | 	Cfg, err = goconfig.LoadConfigFile(path.Join(workDir, "conf/app.ini")) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatal("Fail to read 'conf/app.ini': %v", err) | 		log.Fatal(4, "Fail to parse 'conf/app.ini': %v", err) | ||||||
| 	} |  | ||||||
| 	Cfg, err = goconfig.LoadFromData(data) |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Fatal("Fail to parse 'conf/app.ini': %v", err) |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	CustomPath = os.Getenv("GOGS_CUSTOM") | 	CustomPath = os.Getenv("GOGS_CUSTOM") | ||||||
|  | @ -145,10 +150,10 @@ func NewConfigContext() { | ||||||
| 	cfgPath := path.Join(CustomPath, "conf/app.ini") | 	cfgPath := path.Join(CustomPath, "conf/app.ini") | ||||||
| 	if com.IsFile(cfgPath) { | 	if com.IsFile(cfgPath) { | ||||||
| 		if err = Cfg.AppendFiles(cfgPath); err != nil { | 		if err = Cfg.AppendFiles(cfgPath); err != nil { | ||||||
| 			log.Fatal("Fail to load custom 'conf/app.ini': %v", err) | 			log.Fatal(4, "Fail to load custom 'conf/app.ini': %v", err) | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		log.Warn("No custom 'conf/app.ini' found") | 		log.Warn("No custom 'conf/app.ini' found, please go to '/install'") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	AppName = Cfg.MustValue("", "APP_NAME", "Gogs: Go Git Service") | 	AppName = Cfg.MustValue("", "APP_NAME", "Gogs: Go Git Service") | ||||||
|  | @ -169,6 +174,7 @@ func NewConfigContext() { | ||||||
| 	DisableRouterLog = Cfg.MustBool("server", "DISABLE_ROUTER_LOG") | 	DisableRouterLog = Cfg.MustBool("server", "DISABLE_ROUTER_LOG") | ||||||
| 	StaticRootPath = Cfg.MustValue("server", "STATIC_ROOT_PATH", workDir) | 	StaticRootPath = Cfg.MustValue("server", "STATIC_ROOT_PATH", workDir) | ||||||
| 	LogRootPath = Cfg.MustValue("log", "ROOT_PATH", path.Join(workDir, "log")) | 	LogRootPath = Cfg.MustValue("log", "ROOT_PATH", path.Join(workDir, "log")) | ||||||
|  | 	EnableGzip = Cfg.MustBool("server", "ENABLE_GZIP") | ||||||
| 
 | 
 | ||||||
| 	InstallLock = Cfg.MustBool("security", "INSTALL_LOCK") | 	InstallLock = Cfg.MustBool("security", "INSTALL_LOCK") | ||||||
| 	SecretKey = Cfg.MustValue("security", "SECRET_KEY") | 	SecretKey = Cfg.MustValue("security", "SECRET_KEY") | ||||||
|  | @ -177,8 +183,8 @@ func NewConfigContext() { | ||||||
| 	CookieRememberName = Cfg.MustValue("security", "COOKIE_REMEMBER_NAME") | 	CookieRememberName = Cfg.MustValue("security", "COOKIE_REMEMBER_NAME") | ||||||
| 	ReverseProxyAuthUser = Cfg.MustValue("security", "REVERSE_PROXY_AUTHENTICATION_USER", "X-WEBAUTH-USER") | 	ReverseProxyAuthUser = Cfg.MustValue("security", "REVERSE_PROXY_AUTHENTICATION_USER", "X-WEBAUTH-USER") | ||||||
| 
 | 
 | ||||||
| 	AttachmentPath = Cfg.MustValue("attachment", "PATH", "files/attachments") | 	AttachmentPath = Cfg.MustValue("attachment", "PATH", "data/attachments") | ||||||
| 	AttachmentAllowedTypes = Cfg.MustValue("attachment", "ALLOWED_TYPES", "*/*") | 	AttachmentAllowedTypes = Cfg.MustValue("attachment", "ALLOWED_TYPES", "image/jpeg|image/png") | ||||||
| 	AttachmentMaxSize = Cfg.MustInt64("attachment", "MAX_SIZE", 32) | 	AttachmentMaxSize = Cfg.MustInt64("attachment", "MAX_SIZE", 32) | ||||||
| 	AttachmentMaxFiles = Cfg.MustInt("attachment", "MAX_FILES", 10) | 	AttachmentMaxFiles = Cfg.MustInt("attachment", "MAX_FILES", 10) | ||||||
| 	AttachmentEnabled = Cfg.MustBool("attachment", "ENABLE", true) | 	AttachmentEnabled = Cfg.MustBool("attachment", "ENABLE", true) | ||||||
|  | @ -233,7 +239,7 @@ func NewConfigContext() { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err = os.MkdirAll(AttachmentPath, os.ModePerm); err != nil { | 	if err = os.MkdirAll(AttachmentPath, os.ModePerm); err != nil { | ||||||
| 		log.Fatal("Could not create directory %s: %s", AttachmentPath, err) | 		log.Fatal(4, "Could not create directory %s: %s", AttachmentPath, err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	RunUser = Cfg.MustValue("", "RUN_USER") | 	RunUser = Cfg.MustValue("", "RUN_USER") | ||||||
|  | @ -243,13 +249,13 @@ func NewConfigContext() { | ||||||
| 	} | 	} | ||||||
| 	// Does not check run user when the install lock is off.
 | 	// Does not check run user when the install lock is off.
 | ||||||
| 	if InstallLock && RunUser != curUser { | 	if InstallLock && RunUser != curUser { | ||||||
| 		log.Fatal("Expect user(%s) but current user is: %s", RunUser, curUser) | 		log.Fatal(4, "Expect user(%s) but current user is: %s", RunUser, curUser) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Determine and create root git reposiroty path.
 | 	// Determine and create root git reposiroty path.
 | ||||||
| 	homeDir, err := com.HomeDir() | 	homeDir, err := com.HomeDir() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatal("Fail to get home directory: %v", err) | 		log.Fatal(4, "Fail to get home directory: %v", err) | ||||||
| 	} | 	} | ||||||
| 	RepoRootPath = Cfg.MustValue("repository", "ROOT", filepath.Join(homeDir, "gogs-repositories")) | 	RepoRootPath = Cfg.MustValue("repository", "ROOT", filepath.Join(homeDir, "gogs-repositories")) | ||||||
| 	if !filepath.IsAbs(RepoRootPath) { | 	if !filepath.IsAbs(RepoRootPath) { | ||||||
|  | @ -259,13 +265,16 @@ func NewConfigContext() { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err = os.MkdirAll(RepoRootPath, os.ModePerm); err != nil { | 	if err = os.MkdirAll(RepoRootPath, os.ModePerm); err != nil { | ||||||
| 		log.Fatal("Fail to create repository root path(%s): %v", RepoRootPath, err) | 		log.Fatal(4, "Fail to create repository root path(%s): %v", RepoRootPath, err) | ||||||
| 	} | 	} | ||||||
| 	ScriptType = Cfg.MustValue("repository", "SCRIPT_TYPE", "bash") | 	ScriptType = Cfg.MustValue("repository", "SCRIPT_TYPE", "bash") | ||||||
| 
 | 
 | ||||||
| 	PictureService = Cfg.MustValueRange("picture", "SERVICE", "server", | 	PictureService = Cfg.MustValueRange("picture", "SERVICE", "server", | ||||||
| 		[]string{"server"}) | 		[]string{"server"}) | ||||||
| 	DisableGravatar = Cfg.MustBool("picture", "DISABLE_GRAVATAR") | 	DisableGravatar = Cfg.MustBool("picture", "DISABLE_GRAVATAR") | ||||||
|  | 
 | ||||||
|  | 	Langs = Cfg.MustValueArray("i18n", "LANGS", ",") | ||||||
|  | 	Names = Cfg.MustValueArray("i18n", "NAMES", ",") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var Service struct { | var Service struct { | ||||||
|  | @ -308,7 +317,7 @@ func newLogService() { | ||||||
| 		mode = strings.TrimSpace(mode) | 		mode = strings.TrimSpace(mode) | ||||||
| 		modeSec := "log." + mode | 		modeSec := "log." + mode | ||||||
| 		if _, err := Cfg.GetSection(modeSec); err != nil { | 		if _, err := Cfg.GetSection(modeSec); err != nil { | ||||||
| 			log.Fatal("Unknown log mode: %s", mode) | 			log.Fatal(4, "Unknown log mode: %s", mode) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// Log level.
 | 		// Log level.
 | ||||||
|  | @ -316,7 +325,7 @@ func newLogService() { | ||||||
| 			[]string{"Trace", "Debug", "Info", "Warn", "Error", "Critical"}) | 			[]string{"Trace", "Debug", "Info", "Warn", "Error", "Critical"}) | ||||||
| 		level, ok := logLevels[levelName] | 		level, ok := logLevels[levelName] | ||||||
| 		if !ok { | 		if !ok { | ||||||
| 			log.Fatal("Unknown log level: %s", levelName) | 			log.Fatal(4, "Unknown log level: %s", levelName) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// Generate log configuration.
 | 		// Generate log configuration.
 | ||||||
|  | @ -371,15 +380,15 @@ func newCacheService() { | ||||||
| 	case "memory": | 	case "memory": | ||||||
| 		CacheConfig = fmt.Sprintf(`{"interval":%d}`, Cfg.MustInt("cache", "INTERVAL", 60)) | 		CacheConfig = fmt.Sprintf(`{"interval":%d}`, Cfg.MustInt("cache", "INTERVAL", 60)) | ||||||
| 	case "redis", "memcache": | 	case "redis", "memcache": | ||||||
| 		CacheConfig = fmt.Sprintf(`{"conn":"%s"}`, Cfg.MustValue("cache", "HOST")) | 		CacheConfig = fmt.Sprintf(`{"conn":"%s"}`, strings.Trim(Cfg.MustValue("cache", "HOST"), "\" ")) | ||||||
| 	default: | 	default: | ||||||
| 		log.Fatal("Unknown cache adapter: %s", CacheAdapter) | 		log.Fatal(4, "Unknown cache adapter: %s", CacheAdapter) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	var err error | 	var err error | ||||||
| 	Cache, err = cache.NewCache(CacheAdapter, CacheConfig) | 	Cache, err = cache.NewCache(CacheAdapter, CacheConfig) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatal("Init cache system failed, adapter: %s, config: %s, %v\n", | 		log.Fatal(4, "Init cache system failed, adapter: %s, config: %s, %v\n", | ||||||
| 			CacheAdapter, CacheConfig, err) | 			CacheAdapter, CacheConfig, err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -391,12 +400,12 @@ func newSessionService() { | ||||||
| 		[]string{"memory", "file", "redis", "mysql"}) | 		[]string{"memory", "file", "redis", "mysql"}) | ||||||
| 
 | 
 | ||||||
| 	SessionConfig = new(session.Config) | 	SessionConfig = new(session.Config) | ||||||
| 	SessionConfig.ProviderConfig = Cfg.MustValue("session", "PROVIDER_CONFIG") | 	SessionConfig.ProviderConfig = strings.Trim(Cfg.MustValue("session", "PROVIDER_CONFIG"), "\" ") | ||||||
| 	SessionConfig.CookieName = Cfg.MustValue("session", "COOKIE_NAME", "i_like_gogits") | 	SessionConfig.CookieName = Cfg.MustValue("session", "COOKIE_NAME", "i_like_gogits") | ||||||
| 	SessionConfig.CookieSecure = Cfg.MustBool("session", "COOKIE_SECURE") | 	SessionConfig.Secure = Cfg.MustBool("session", "COOKIE_SECURE") | ||||||
| 	SessionConfig.EnableSetCookie = Cfg.MustBool("session", "ENABLE_SET_COOKIE", true) | 	SessionConfig.EnableSetCookie = Cfg.MustBool("session", "ENABLE_SET_COOKIE", true) | ||||||
| 	SessionConfig.GcIntervalTime = Cfg.MustInt64("session", "GC_INTERVAL_TIME", 86400) | 	SessionConfig.Gclifetime = Cfg.MustInt64("session", "GC_INTERVAL_TIME", 86400) | ||||||
| 	SessionConfig.SessionLifeTime = Cfg.MustInt64("session", "SESSION_LIFE_TIME", 86400) | 	SessionConfig.Maxlifetime = Cfg.MustInt64("session", "SESSION_LIFE_TIME", 86400) | ||||||
| 	SessionConfig.SessionIDHashFunc = Cfg.MustValueRange("session", "SESSION_ID_HASHFUNC", | 	SessionConfig.SessionIDHashFunc = Cfg.MustValueRange("session", "SESSION_ID_HASHFUNC", | ||||||
| 		"sha1", []string{"sha1", "sha256", "md5"}) | 		"sha1", []string{"sha1", "sha256", "md5"}) | ||||||
| 	SessionConfig.SessionIDHashKey = Cfg.MustValue("session", "SESSION_ID_HASHKEY") | 	SessionConfig.SessionIDHashKey = Cfg.MustValue("session", "SESSION_ID_HASHKEY") | ||||||
|  | @ -405,14 +414,6 @@ func newSessionService() { | ||||||
| 		os.MkdirAll(path.Dir(SessionConfig.ProviderConfig), os.ModePerm) | 		os.MkdirAll(path.Dir(SessionConfig.ProviderConfig), os.ModePerm) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	var err error |  | ||||||
| 	SessionManager, err = session.NewManager(SessionProvider, *SessionConfig) |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Fatal("Init session system failed, provider: %s, %v", |  | ||||||
| 			SessionProvider, err) |  | ||||||
| 	} |  | ||||||
| 	go SessionManager.GC() |  | ||||||
| 
 |  | ||||||
| 	log.Info("Session Service Enabled") | 	log.Info("Session Service Enabled") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -494,4 +495,5 @@ func NewServices() { | ||||||
| 	newRegisterMailService() | 	newRegisterMailService() | ||||||
| 	newNotifyMailService() | 	newNotifyMailService() | ||||||
| 	newWebhookService() | 	newWebhookService() | ||||||
|  | 	// ssh.Listen("2022")
 | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										119
									
								
								modules/ssh/ssh.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,119 @@ | ||||||
|  | // 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.
 | ||||||
|  | 
 | ||||||
|  | // Prototype, git client looks like do not recognize req.Reply.
 | ||||||
|  | package ssh | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"net" | ||||||
|  | 	"os" | ||||||
|  | 	"os/exec" | ||||||
|  | 	"strings" | ||||||
|  | 
 | ||||||
|  | 	"code.google.com/p/go.crypto/ssh" | ||||||
|  | 
 | ||||||
|  | 	"github.com/Unknwon/com" | ||||||
|  | 
 | ||||||
|  | 	"github.com/gogits/gogs/modules/log" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func handleServerConn(keyId string, chans <-chan ssh.NewChannel) { | ||||||
|  | 	for newChan := range chans { | ||||||
|  | 		if newChan.ChannelType() != "session" { | ||||||
|  | 			newChan.Reject(ssh.UnknownChannelType, "unknown channel type") | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		channel, requests, err := newChan.Accept() | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.Error(3, "Could not accept channel: %v", err) | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		go func(in <-chan *ssh.Request) { | ||||||
|  | 			defer channel.Close() | ||||||
|  | 			for req := range in { | ||||||
|  | 				ok, payload := false, strings.TrimLeft(string(req.Payload), "\x00") | ||||||
|  | 				fmt.Println("Request:", req.Type, req.WantReply, payload) | ||||||
|  | 				switch req.Type { | ||||||
|  | 				case "env": | ||||||
|  | 					args := strings.Split(strings.Replace(payload, "\x00", "", -1), "\v") | ||||||
|  | 					if len(args) != 2 { | ||||||
|  | 						break | ||||||
|  | 					} | ||||||
|  | 					args[0] = strings.TrimLeft(args[0], "\x04") | ||||||
|  | 					_, _, err := com.ExecCmdBytes("env", args[0]+"="+args[1]) | ||||||
|  | 					if err != nil { | ||||||
|  | 						log.Error(3, "env: %v", err) | ||||||
|  | 						channel.Stderr().Write([]byte(err.Error())) | ||||||
|  | 						break | ||||||
|  | 					} | ||||||
|  | 					ok = true | ||||||
|  | 				case "exec": | ||||||
|  | 					os.Setenv("SSH_ORIGINAL_COMMAND", strings.TrimLeft(payload, "'(")) | ||||||
|  | 					log.Info("Payload: %v", strings.TrimLeft(payload, "'(")) | ||||||
|  | 					cmd := exec.Command("/Users/jiahuachen/Applications/Go/src/github.com/gogits/gogs-ng/gogs-ng", "serv", "key-"+keyId) | ||||||
|  | 					cmd.Stdout = channel | ||||||
|  | 					cmd.Stdin = channel | ||||||
|  | 					cmd.Stderr = channel.Stderr() | ||||||
|  | 					if err := cmd.Run(); err != nil { | ||||||
|  | 						log.Error(3, "exec: %v", err) | ||||||
|  | 					} else { | ||||||
|  | 						ok = true | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 				fmt.Println("Done:", ok) | ||||||
|  | 				req.Reply(ok, nil) // BUG: Git on Mac seems not know this reply and hang?
 | ||||||
|  | 			} | ||||||
|  | 			fmt.Println("Done!!!") | ||||||
|  | 		}(requests) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func listen(config *ssh.ServerConfig, port string) { | ||||||
|  | 	listener, err := net.Listen("tcp", "0.0.0.0:"+port) | ||||||
|  | 	if err != nil { | ||||||
|  | 		panic(err) | ||||||
|  | 	} | ||||||
|  | 	for { | ||||||
|  | 		// Once a ServerConfig has been configured, connections can be accepted.
 | ||||||
|  | 		conn, err := listener.Accept() | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.Error(3, "Fail to accept incoming connection: %v", err) | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		// Before use, a handshake must be performed on the incoming net.Conn.
 | ||||||
|  | 		sConn, chans, reqs, err := ssh.NewServerConn(conn, config) | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.Error(3, "Fail to handshake: %v", err) | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		// The incoming Request channel must be serviced.
 | ||||||
|  | 		go ssh.DiscardRequests(reqs) | ||||||
|  | 		go handleServerConn(sConn.Permissions.Extensions["key-id"], chans) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Listen starts a SSH server listens on given port.
 | ||||||
|  | func Listen(port string) { | ||||||
|  | 	config := &ssh.ServerConfig{ | ||||||
|  | 		PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { | ||||||
|  | 			// keyCache[string(ssh.MarshalAuthorizedKey(key))] = 2
 | ||||||
|  | 			return &ssh.Permissions{Extensions: map[string]string{"key-id": "2"}}, nil | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	privateBytes, err := ioutil.ReadFile("/Users/jiahuachen/.ssh/id_rsa") | ||||||
|  | 	if err != nil { | ||||||
|  | 		panic("failed to load private key") | ||||||
|  | 	} | ||||||
|  | 	private, err := ssh.ParsePrivateKey(privateBytes) | ||||||
|  | 	if err != nil { | ||||||
|  | 		panic("failed to parse private key") | ||||||
|  | 	} | ||||||
|  | 	config.AddHostKey(private) | ||||||
|  | 
 | ||||||
|  | 	go listen(config, port) | ||||||
|  | } | ||||||
							
								
								
									
										1
									
								
								public/css/github.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1 @@ | ||||||
|  | .hljs{display:block;overflow-x:auto;padding:.5em;color:#333;background:#f8f8f8}.hljs-comment,.hljs-template_comment,.diff .hljs-header,.hljs-javadoc{color:#998;font-style:italic}.hljs-keyword,.css .rule .hljs-keyword,.hljs-winutils,.javascript .hljs-title,.nginx .hljs-title,.hljs-subst,.hljs-request,.hljs-status{color:#333;font-weight:bold}.hljs-number,.hljs-hexcolor,.ruby .hljs-constant{color:#099}.hljs-string,.hljs-tag .hljs-value,.hljs-phpdoc,.tex .hljs-formula{color:#d14}.hljs-title,.hljs-id,.coffeescript .hljs-params,.scss .hljs-preprocessor{color:#900;font-weight:bold}.javascript .hljs-title,.lisp .hljs-title,.clojure .hljs-title,.hljs-subst{font-weight:normal}.hljs-class .hljs-title,.haskell .hljs-type,.vhdl .hljs-literal,.tex .hljs-command{color:#458;font-weight:bold}.hljs-tag,.hljs-tag .hljs-title,.hljs-rules .hljs-property,.django .hljs-tag .hljs-keyword{color:#000080;font-weight:normal}.hljs-attribute,.hljs-variable,.lisp .hljs-body{color:#008080}.hljs-regexp{color:#009926}.hljs-symbol,.ruby .hljs-symbol .hljs-string,.lisp .hljs-keyword,.tex .hljs-special,.hljs-prompt{color:#990073}.hljs-built_in,.lisp .hljs-title,.clojure .hljs-built_in{color:#0086b3}.hljs-preprocessor,.hljs-pragma,.hljs-pi,.hljs-doctype,.hljs-shebang,.hljs-cdata{color:#999;font-weight:bold}.hljs-deletion{background:#fdd}.hljs-addition{background:#dfd}.diff .hljs-change{background:#0086b3}.hljs-chunk{color:#aaa} | ||||||
|  | @ -1836,4 +1836,11 @@ body { | ||||||
| 
 | 
 | ||||||
| #issue-create-form #attached { | #issue-create-form #attached { | ||||||
|     margin-bottom: 0; |     margin-bottom: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #submit-error { | ||||||
|  |     display: none; | ||||||
|  |     padding: 10px 15px 15px 15px; | ||||||
|  |     font-weight: bold; | ||||||
|  |     text-align: center; | ||||||
| } | } | ||||||
| Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 9.5 KiB | 
| Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB | 
| Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.1 KiB | 
| Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 12 KiB | 
| Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 10 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/img/gogs-lg.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 96 KiB | 
							
								
								
									
										174
									
								
								public/js/app.js
									
									
									
									
									
								
							
							
						
						|  | @ -520,6 +520,50 @@ function initIssue() { | ||||||
|         }); |         }); | ||||||
|     }()); |     }()); | ||||||
| 
 | 
 | ||||||
|  |     // store unsend text in session storage.
 | ||||||
|  |     (function() { | ||||||
|  |         var $textArea = $("#issue-content,#issue-reply-content"); | ||||||
|  |         var current = ""; | ||||||
|  | 
 | ||||||
|  |         if ($textArea == null || !('sessionStorage' in window)) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         var path = location.pathname.split("/"); | ||||||
|  |         var key = "issue-" + path[1] + "-" + path[2] + "-"; | ||||||
|  | 
 | ||||||
|  |         if (/\/issues\/\d+$/.test(location.pathname)) { | ||||||
|  |             key = key + path[4]; | ||||||
|  |         } else { | ||||||
|  |             key = key + "new"; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if ($textArea.val() !== undefined && $textArea.val() !== "") { | ||||||
|  |             sessionStorage.setItem(key, $textArea.val()); | ||||||
|  |         } else { | ||||||
|  |             $textArea.val(sessionStorage.getItem(key) || ""); | ||||||
|  | 
 | ||||||
|  |             if ($textArea.attr("id") == "issue-reply-content") { | ||||||
|  |                 var $closeBtn = $('#issue-close-btn'); | ||||||
|  |                 var $openBtn = $('#issue-open-btn'); | ||||||
|  |      | ||||||
|  |                 if ($textArea.val().length) { | ||||||
|  |                     $closeBtn.val($closeBtn.data("text")); | ||||||
|  |                     $openBtn.val($openBtn.data("text")); | ||||||
|  |                 } else { | ||||||
|  |                     $closeBtn.val($closeBtn.data("origin")); | ||||||
|  |                     $openBtn.val($openBtn.data("origin")); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         $textArea.on("keyup", function() { | ||||||
|  |             if ($textArea.val() !== current) { | ||||||
|  |                 sessionStorage.setItem(key, current = $textArea.val()); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     }()); | ||||||
|  | 
 | ||||||
|     // Preview for images.
 |     // Preview for images.
 | ||||||
|     (function() { |     (function() { | ||||||
|         var $hoverElement = $("<div></div>"); |         var $hoverElement = $("<div></div>"); | ||||||
|  | @ -536,7 +580,7 @@ function initIssue() { | ||||||
|         var over = function() { |         var over = function() { | ||||||
|             var $this = $(this); |             var $this = $(this); | ||||||
| 
 | 
 | ||||||
|             if ($this.text().match(/\.(png|jpg|jpeg|gif)$/i) == false) { |             if ((/\.(png|jpg|jpeg|gif)$/i).test($this.text()) == false) { | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | @ -579,23 +623,135 @@ function initIssue() { | ||||||
|         var $attachedList = $("#attached-list"); |         var $attachedList = $("#attached-list"); | ||||||
|         var $addButton = $("#attachments-button"); |         var $addButton = $("#attachments-button"); | ||||||
| 
 | 
 | ||||||
|         var fileInput = $("#attachments-input")[0]; |         var files = []; | ||||||
|  | 
 | ||||||
|  |         var fileInput = document.getElementById("attachments-input"); | ||||||
|  |          | ||||||
|  |         if (fileInput === null) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         $attachedList.on("click", "span.attachment-remove", function(event) { | ||||||
|  |             var $parent = $(this).parent(); | ||||||
|  | 
 | ||||||
|  |             files.splice($parent.data("index"), 1); | ||||||
|  |             $parent.remove(); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         var clickedButton = undefined; | ||||||
|  | 
 | ||||||
|  |         $("button,input[type=\"submit\"]", fileInput.form).on("click", function() { | ||||||
|  |             clickedButton = this; | ||||||
|  | 
 | ||||||
|  |             var $button = $(this); | ||||||
|  | 
 | ||||||
|  |             $button.removeClass("btn-success"); | ||||||
|  |             $button.addClass("btn-warning"); | ||||||
|  | 
 | ||||||
|  |             $button.text("Submiting..."); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |          fileInput.form.addEventListener("submit", function(event) { | ||||||
|  |             event.stopImmediatePropagation(); | ||||||
|  |             event.preventDefault(); | ||||||
|  | 
 | ||||||
|  |             //var data = new FormData(this);
 | ||||||
|  | 
 | ||||||
|  |             // Internet Explorer ... -_-
 | ||||||
|  |             var data = new FormData(); | ||||||
|  | 
 | ||||||
|  |             $.each($("[name]", this), function(i, e) { | ||||||
|  |                 if (e.name == "attachments" || e.type == "submit") { | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 data.append(e.name, $(e).val()); | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |             data.append(clickedButton.name, $(clickedButton).val()); | ||||||
|  | 
 | ||||||
|  |             files.forEach(function(file) { | ||||||
|  |                 data.append("attachments", file); | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |             var xhr = new XMLHttpRequest(); | ||||||
|  | 
 | ||||||
|  |             xhr.addEventListener("error", function() { | ||||||
|  |                 debugger; | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |             xhr.addEventListener("load", function() { | ||||||
|  |                 var response = xhr.response; | ||||||
|  | 
 | ||||||
|  |                 if (typeof response == "string") { | ||||||
|  |                     try { | ||||||
|  |                         response = JSON.parse(response); | ||||||
|  |                     } catch (err) { | ||||||
|  |                         response = { ok: false, error: "Could not parse JSON" }; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if (response.ok === false) { | ||||||
|  |                     $("#submit-error").text(response.error); | ||||||
|  |                     $("#submit-error").show(); | ||||||
|  | 
 | ||||||
|  |                     var $button = $(clickedButton); | ||||||
|  | 
 | ||||||
|  |                     $button.removeClass("btn-warning"); | ||||||
|  |                     $button.addClass("btn-danger"); | ||||||
|  | 
 | ||||||
|  |                     $button.text("An error encoured!") | ||||||
|  | 
 | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if (!('sessionStorage' in window)) { | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 var path = location.pathname.split("/"); | ||||||
|  |                 var key = "issue-" + path[1] + "-" + path[2] + "-"; | ||||||
|  | 
 | ||||||
|  |                 if (/\/issues\/\d+$/.test(location.pathname)) { | ||||||
|  |                     key = key + path[4]; | ||||||
|  |                 } else { | ||||||
|  |                     key = key + "new"; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 sessionStorage.removeItem(key); | ||||||
|  |                 window.location.href = response.data; | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |             xhr.open("POST", this.action, true); | ||||||
|  |             xhr.send(data); | ||||||
|  |              | ||||||
|  |             return false; | ||||||
|  |         }); | ||||||
| 
 | 
 | ||||||
|         fileInput.addEventListener("change", function(event) { |         fileInput.addEventListener("change", function(event) { | ||||||
|             $attachedList.empty(); |  | ||||||
|             $attachedList.append("<b>Attachments:</b> "); |  | ||||||
| 
 |  | ||||||
|             for (var index = 0; index < fileInput.files.length; index++) { |             for (var index = 0; index < fileInput.files.length; index++) { | ||||||
|                 var file = fileInput.files[index]; |                 var file = fileInput.files[index]; | ||||||
| 
 | 
 | ||||||
|  |                 if (files.indexOf(file) > -1) { | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|                 var $span = $("<span></span>"); |                 var $span = $("<span></span>"); | ||||||
| 
 | 
 | ||||||
|                 $span.addClass("label"); |                 $span.addClass("label"); | ||||||
|                 $span.addClass("label-default"); |                 $span.addClass("label-default"); | ||||||
| 
 | 
 | ||||||
|                 $span.append(file.name.toLowerCase()); |                 $span.data("index", files.length); | ||||||
|  | 
 | ||||||
|  |                 $span.append(file.name); | ||||||
|  |                 $span.append(" <span class=\"attachment-remove fa fa-times-circle\"></span>"); | ||||||
|  | 
 | ||||||
|                 $attachedList.append($span); |                 $attachedList.append($span); | ||||||
|  | 
 | ||||||
|  |                 files.push(file); | ||||||
|             } |             } | ||||||
|  | 
 | ||||||
|  |             this.value = ""; | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         $addButton.on("click", function() { |         $addButton.on("click", function() { | ||||||
|  | @ -828,11 +984,17 @@ function initIssue() { | ||||||
|                     $(item).addClass("no-checked"); |                     $(item).addClass("no-checked"); | ||||||
| 
 | 
 | ||||||
|                     $("#label-" + id, $labels).remove(); |                     $("#label-" + id, $labels).remove(); | ||||||
|  |                      | ||||||
|  |                     if ($labels.children(".label-item").length == 0) { | ||||||
|  |                         $labels.append("<p>None yet</p>"); | ||||||
|  |                     } | ||||||
|                 } else { |                 } else { | ||||||
|                     $(item).prepend('<span class="check pull-left"><i class="fa fa-check"></i></span>'); |                     $(item).prepend('<span class="check pull-left"><i class="fa fa-check"></i></span>'); | ||||||
| 
 | 
 | ||||||
|                     $(item).removeClass("no-checked"); |                     $(item).removeClass("no-checked"); | ||||||
|                     $(item).addClass("checked"); |                     $(item).addClass("checked"); | ||||||
|  |                      | ||||||
|  |                     $("p:not([class])", $labels).remove(); | ||||||
| 
 | 
 | ||||||
|                     var $l = $("<p></p>"); |                     var $l = $("<p></p>"); | ||||||
|                     var c = $("span.color", item).css("background-color"); |                     var c = $("span.color", item).css("background-color"); | ||||||
|  |  | ||||||