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 [![wercker status](https://app.wercker.com/status/ad0bdb0bc450ac6f09bc56b9640a50aa/s/ "wercker status")](https://app.wercker.com/project/bykey/ad0bdb0bc450ac6f09bc56b9640a50aa) [![Build Status](https://drone.io/github.com/gogits/gogs/status.png)](https://drone.io/github.com/gogits/gogs/latest)
|
Gogs - Go Git Service [![wercker status](https://app.wercker.com/status/ad0bdb0bc450ac6f09bc56b9640a50aa/s/ "wercker status")](https://app.wercker.com/project/bykey/ad0bdb0bc450ac6f09bc56b9640a50aa) [![Build Status](https://drone.io/github.com/gogits/gogs/status.png)](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.
|
||||||
|
|
||||||
![Demo](http://gowalker.org/public/gogs_demo.gif)
|
![Demo](http://gowalker.org/public/gogs_demo.gif)
|
||||||
|
|
||||||
##### 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 [![wercker status](https://app.wercker.com/status/ad0bdb0bc450ac6f09bc56b9640a50aa/s/ "wercker status")](https://app.wercker.com/project/bykey/ad0bdb0bc450ac6f09bc56b9640a50aa) [![Build Status](https://drone.io/github.com/gogits/gogs/status.png)](https://drone.io/github.com/gogits/gogs/latest)
|
Gogs - Go Git Service [![wercker status](https://app.wercker.com/status/ad0bdb0bc450ac6f09bc56b9640a50aa/s/ "wercker status")](https://app.wercker.com/project/bykey/ad0bdb0bc450ac6f09bc56b9640a50aa) [![Build Status](https://drone.io/github.com/gogits/gogs/status.png)](https://drone.io/github.com/gogits/gogs/latest)
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
Gogs(Go Git Service) 是一个由 Go 语言编写的自助 Git 托管服务。
|
Gogs(Go Git Service) 是一个基于 Go 语言的自助 Git 服务。
|
||||||
|
|
||||||
![Demo](http://gowalker.org/public/gogs_demo.gif)
|
![Demo](http://gowalker.org/public/gogs_demo.gif)
|
||||||
|
|
||||||
##### 当前版本: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");
|
||||||
|
|