mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-12-21 12:44:49 -05:00
Convert captcha, cache, csrf as middlewares
This commit is contained in:
parent
3428baa3b5
commit
7bbf644dd5
17 changed files with 46 additions and 1122 deletions
30
cmd/web.go
30
cmd/web.go
|
@ -14,6 +14,9 @@ import (
|
||||||
|
|
||||||
"github.com/Unknwon/macaron"
|
"github.com/Unknwon/macaron"
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
|
"github.com/macaron-contrib/cache"
|
||||||
|
"github.com/macaron-contrib/captcha"
|
||||||
|
"github.com/macaron-contrib/csrf"
|
||||||
"github.com/macaron-contrib/i18n"
|
"github.com/macaron-contrib/i18n"
|
||||||
"github.com/macaron-contrib/session"
|
"github.com/macaron-contrib/session"
|
||||||
|
|
||||||
|
@ -21,7 +24,6 @@ import (
|
||||||
"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"
|
||||||
|
@ -78,10 +80,20 @@ func newMacaron() *macaron.Macaron {
|
||||||
Names: setting.Names,
|
Names: setting.Names,
|
||||||
Redirect: true,
|
Redirect: true,
|
||||||
}))
|
}))
|
||||||
|
m.Use(cache.Cacher(cache.Options{
|
||||||
|
Adapter: setting.CacheAdapter,
|
||||||
|
Interval: setting.CacheInternal,
|
||||||
|
Conn: setting.CacheConn,
|
||||||
|
}))
|
||||||
|
m.Use(captcha.Captchaer())
|
||||||
m.Use(session.Sessioner(session.Options{
|
m.Use(session.Sessioner(session.Options{
|
||||||
Provider: setting.SessionProvider,
|
Provider: setting.SessionProvider,
|
||||||
Config: *setting.SessionConfig,
|
Config: *setting.SessionConfig,
|
||||||
}))
|
}))
|
||||||
|
m.Use(csrf.Generate(csrf.Options{
|
||||||
|
Secret: setting.SecretKey,
|
||||||
|
SetCookie: true,
|
||||||
|
}))
|
||||||
m.Use(middleware.Contexter())
|
m.Use(middleware.Contexter())
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
@ -121,16 +133,12 @@ func runWeb(*cli.Context) {
|
||||||
// Repositories.
|
// Repositories.
|
||||||
r.Get("/orgs/:org/repos/search", v1.SearchOrgRepositoreis)
|
r.Get("/orgs/:org/repos/search", v1.SearchOrgRepositoreis)
|
||||||
|
|
||||||
r.Any("**", func(ctx *middleware.Context) {
|
r.Any("/*", func(ctx *middleware.Context) {
|
||||||
ctx.JSON(404, &base.ApiJsonErr{"Not Found", v1.DOC_URL})
|
ctx.JSON(404, &base.ApiJsonErr{"Not Found", v1.DOC_URL})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
avt := avatar.CacheServer("public/img/avatar/", "public/img/avatar_default.jpg")
|
|
||||||
os.MkdirAll("public/img/avatar/", os.ModePerm)
|
|
||||||
m.Get("/avatar/:hash", avt.ServeHTTP)
|
|
||||||
|
|
||||||
// User routers.
|
// User routers.
|
||||||
m.Group("/user", func(r *macaron.Router) {
|
m.Group("/user", func(r *macaron.Router) {
|
||||||
r.Get("/login", user.SignIn)
|
r.Get("/login", user.SignIn)
|
||||||
|
@ -165,10 +173,10 @@ func runWeb(*cli.Context) {
|
||||||
|
|
||||||
m.Get("/user/:username", ignSignIn, user.Profile) // TODO: Legacy
|
m.Get("/user/:username", ignSignIn, user.Profile) // TODO: Legacy
|
||||||
|
|
||||||
// Captcha service.
|
// Gravatar service.
|
||||||
cpt := captcha.NewCaptcha("/captcha/", setting.Cache)
|
avt := avatar.CacheServer("public/img/avatar/", "public/img/avatar_default.jpg")
|
||||||
m.Map(cpt)
|
os.MkdirAll("public/img/avatar/", os.ModePerm)
|
||||||
m.Get("/captcha/*", cpt.Handler)
|
m.Get("/avatar/:hash", avt.ServeHTTP)
|
||||||
|
|
||||||
adminReq := middleware.Toggle(&middleware.ToggleOptions{SignInRequire: true, AdminRequire: true})
|
adminReq := middleware.Toggle(&middleware.ToggleOptions{SignInRequire: true, AdminRequire: true})
|
||||||
|
|
||||||
|
@ -199,7 +207,7 @@ func runWeb(*cli.Context) {
|
||||||
m.Get("/:username", ignSignIn, user.Profile)
|
m.Get("/:username", ignSignIn, user.Profile)
|
||||||
|
|
||||||
if macaron.Env == macaron.DEV {
|
if macaron.Env == macaron.DEV {
|
||||||
m.Get("/template/**", dev.TemplatePreview)
|
m.Get("/template/*", dev.TemplatePreview)
|
||||||
dev.RegisterDebugRoutes(m)
|
dev.RegisterDebugRoutes(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
2
gogs.go
2
gogs.go
|
@ -17,7 +17,7 @@ import (
|
||||||
"github.com/gogits/gogs/modules/setting"
|
"github.com/gogits/gogs/modules/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
const APP_VER = "0.4.7.0730 Alpha"
|
const APP_VER = "0.4.7.0731 Alpha"
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
|
|
|
@ -103,7 +103,6 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{
|
||||||
"ActionContent2Commits": ActionContent2Commits,
|
"ActionContent2Commits": ActionContent2Commits,
|
||||||
"Oauth2Icon": Oauth2Icon,
|
"Oauth2Icon": Oauth2Icon,
|
||||||
"Oauth2Name": Oauth2Name,
|
"Oauth2Name": Oauth2Name,
|
||||||
"CreateCaptcha": func() string { return "" },
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Actioner interface {
|
type Actioner interface {
|
||||||
|
|
|
@ -13,7 +13,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash"
|
"hash"
|
||||||
"math"
|
"math"
|
||||||
r "math/rand"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -45,33 +44,6 @@ 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)
|
||||||
|
|
|
@ -1,201 +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 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
|
|
||||||
}
|
|
|
@ -1,487 +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 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
|
|
||||||
}
|
|
|
@ -1,42 +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 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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,267 +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 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) }
|
|
|
@ -1,23 +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 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()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Unknwon/macaron"
|
"github.com/Unknwon/macaron"
|
||||||
|
"github.com/macaron-contrib/csrf"
|
||||||
|
|
||||||
"github.com/gogits/gogs/modules/setting"
|
"github.com/gogits/gogs/modules/setting"
|
||||||
)
|
)
|
||||||
|
@ -34,9 +35,11 @@ func Toggle(options *ToggleOptions) macaron.Handler {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !options.DisableCsrf && ctx.Req.Method == "POST" && !ctx.CsrfTokenValid() {
|
if !options.SignOutRequire && !options.DisableCsrf && ctx.Req.Method == "POST" {
|
||||||
ctx.Error(403, "CSRF token does not match")
|
csrf.Validate(ctx.Context, ctx.csrf)
|
||||||
return
|
if ctx.Written() {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.SignInRequire {
|
if options.SignInRequire {
|
||||||
|
|
|
@ -14,6 +14,8 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Unknwon/macaron"
|
"github.com/Unknwon/macaron"
|
||||||
|
"github.com/macaron-contrib/cache"
|
||||||
|
"github.com/macaron-contrib/csrf"
|
||||||
"github.com/macaron-contrib/i18n"
|
"github.com/macaron-contrib/i18n"
|
||||||
"github.com/macaron-contrib/session"
|
"github.com/macaron-contrib/session"
|
||||||
|
|
||||||
|
@ -29,14 +31,14 @@ import (
|
||||||
type Context struct {
|
type Context struct {
|
||||||
*macaron.Context
|
*macaron.Context
|
||||||
i18n.Locale
|
i18n.Locale
|
||||||
|
Cache cache.Cache
|
||||||
|
csrf csrf.CSRF
|
||||||
Flash *session.Flash
|
Flash *session.Flash
|
||||||
Session session.Store
|
Session session.Store
|
||||||
|
|
||||||
User *models.User
|
User *models.User
|
||||||
IsSigned bool
|
IsSigned bool
|
||||||
|
|
||||||
csrfToken string
|
|
||||||
|
|
||||||
Repo struct {
|
Repo struct {
|
||||||
IsOwner bool
|
IsOwner bool
|
||||||
IsTrueOwner bool
|
IsTrueOwner bool
|
||||||
|
@ -70,10 +72,6 @@ func (ctx *Context) Query(name string) string {
|
||||||
return ctx.Req.Form.Get(name)
|
return ctx.Req.Form.Get(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// func (ctx *Context) Param(name string) string {
|
|
||||||
// return ctx.p[name]
|
|
||||||
// }
|
|
||||||
|
|
||||||
// HasError returns true if error occurs in form validation.
|
// HasError returns true if error occurs in form validation.
|
||||||
func (ctx *Context) HasApiError() bool {
|
func (ctx *Context) HasApiError() bool {
|
||||||
hasErr, ok := ctx.Data["HasError"]
|
hasErr, ok := ctx.Data["HasError"]
|
||||||
|
@ -131,33 +129,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) CsrfToken() string {
|
|
||||||
if len(ctx.csrfToken) > 0 {
|
|
||||||
return ctx.csrfToken
|
|
||||||
}
|
|
||||||
|
|
||||||
token := ctx.GetCookie("_csrf")
|
|
||||||
if len(token) == 0 {
|
|
||||||
token = base.GetRandomString(30)
|
|
||||||
ctx.SetCookie("_csrf", token)
|
|
||||||
}
|
|
||||||
ctx.csrfToken = token
|
|
||||||
return token
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctx *Context) CsrfTokenValid() bool {
|
|
||||||
token := ctx.Query("_csrf")
|
|
||||||
if token == "" {
|
|
||||||
token = ctx.Req.Header.Get("X-Csrf-Token")
|
|
||||||
}
|
|
||||||
if token == "" {
|
|
||||||
return false
|
|
||||||
} else if ctx.csrfToken != token {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctx *Context) ServeFile(file string, names ...string) {
|
func (ctx *Context) ServeFile(file string, names ...string) {
|
||||||
var name string
|
var name string
|
||||||
if len(names) > 0 {
|
if len(names) > 0 {
|
||||||
|
@ -195,14 +166,15 @@ func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interfa
|
||||||
|
|
||||||
// Contexter initializes a classic context for a request.
|
// Contexter initializes a classic context for a request.
|
||||||
func Contexter() macaron.Handler {
|
func Contexter() macaron.Handler {
|
||||||
return func(c *macaron.Context, l i18n.Locale, sess session.Store, f *session.Flash) {
|
return func(c *macaron.Context, l i18n.Locale, cache cache.Cache, sess session.Store, f *session.Flash, x csrf.CSRF) {
|
||||||
ctx := &Context{
|
ctx := &Context{
|
||||||
Context: c,
|
Context: c,
|
||||||
Locale: l,
|
Locale: l,
|
||||||
|
Cache: cache,
|
||||||
|
csrf: x,
|
||||||
Flash: f,
|
Flash: f,
|
||||||
Session: sess,
|
Session: sess,
|
||||||
}
|
}
|
||||||
// Cache: setting.Cache,
|
|
||||||
|
|
||||||
// Compute current URL for real-time change language.
|
// Compute current URL for real-time change language.
|
||||||
link := ctx.Req.RequestURI
|
link := ctx.Req.RequestURI
|
||||||
|
@ -231,9 +203,8 @@ func Contexter() macaron.Handler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// get or create csrf token
|
ctx.Data["CsrfToken"] = x.GetToken()
|
||||||
ctx.Data["CsrfToken"] = ctx.CsrfToken()
|
ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + x.GetToken() + `">`)
|
||||||
ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + ctx.csrfToken + `">`)
|
|
||||||
|
|
||||||
c.Map(ctx)
|
c.Map(ctx)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,6 @@ import (
|
||||||
"github.com/Unknwon/goconfig"
|
"github.com/Unknwon/goconfig"
|
||||||
"github.com/macaron-contrib/session"
|
"github.com/macaron-contrib/session"
|
||||||
|
|
||||||
"github.com/gogits/cache"
|
|
||||||
|
|
||||||
"github.com/gogits/gogs/modules/log"
|
"github.com/gogits/gogs/modules/log"
|
||||||
// "github.com/gogits/gogs-ng/modules/ssh"
|
// "github.com/gogits/gogs-ng/modules/ssh"
|
||||||
)
|
)
|
||||||
|
@ -80,9 +78,9 @@ var (
|
||||||
AttachmentEnabled bool
|
AttachmentEnabled bool
|
||||||
|
|
||||||
// Cache settings.
|
// Cache settings.
|
||||||
Cache cache.Cache
|
CacheAdapter string
|
||||||
CacheAdapter string
|
CacheInternal int
|
||||||
CacheConfig string
|
CacheConn string
|
||||||
|
|
||||||
EnableRedis bool
|
EnableRedis bool
|
||||||
EnableMemcache bool
|
EnableMemcache bool
|
||||||
|
@ -325,20 +323,13 @@ func newCacheService() {
|
||||||
|
|
||||||
switch CacheAdapter {
|
switch CacheAdapter {
|
||||||
case "memory":
|
case "memory":
|
||||||
CacheConfig = fmt.Sprintf(`{"interval":%d}`, Cfg.MustInt("cache", "INTERVAL", 60))
|
CacheInternal = Cfg.MustInt("cache", "INTERVAL", 60)
|
||||||
case "redis", "memcache":
|
case "redis", "memcache":
|
||||||
CacheConfig = fmt.Sprintf(`{"conn":"%s"}`, strings.Trim(Cfg.MustValue("cache", "HOST"), "\" "))
|
CacheConn = strings.Trim(Cfg.MustValue("cache", "HOST"), "\" ")
|
||||||
default:
|
default:
|
||||||
log.Fatal(4, "Unknown cache adapter: %s", CacheAdapter)
|
log.Fatal(4, "Unknown cache adapter: %s", CacheAdapter)
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
|
||||||
Cache, err = cache.NewCache(CacheAdapter, CacheConfig)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(4, "Init cache system failed, adapter: %s, config: %s, %v\n",
|
|
||||||
CacheAdapter, CacheConfig, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("Cache Service Enabled")
|
log.Info("Cache Service Enabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -263,7 +263,8 @@ func Config(ctx *middleware.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Data["CacheAdapter"] = setting.CacheAdapter
|
ctx.Data["CacheAdapter"] = setting.CacheAdapter
|
||||||
ctx.Data["CacheConfig"] = setting.CacheConfig
|
ctx.Data["CacheInternal"] = setting.CacheInternal
|
||||||
|
ctx.Data["CacheConn"] = setting.CacheConn
|
||||||
|
|
||||||
ctx.Data["SessionProvider"] = setting.SessionProvider
|
ctx.Data["SessionProvider"] = setting.SessionProvider
|
||||||
ctx.Data["SessionConfig"] = setting.SessionConfig
|
ctx.Data["SessionConfig"] = setting.SessionConfig
|
||||||
|
|
|
@ -8,10 +8,11 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/macaron-contrib/captcha"
|
||||||
|
|
||||||
"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/captcha"
|
|
||||||
"github.com/gogits/gogs/modules/log"
|
"github.com/gogits/gogs/modules/log"
|
||||||
// "github.com/gogits/gogs/modules/mailer"
|
// "github.com/gogits/gogs/modules/mailer"
|
||||||
"github.com/gogits/gogs/modules/middleware"
|
"github.com/gogits/gogs/modules/middleware"
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
0.4.7.0730 Alpha
|
0.4.7.0731 Alpha
|
|
@ -2,7 +2,6 @@
|
||||||
{{template "ng/base/header" .}}
|
{{template "ng/base/header" .}}
|
||||||
<div id="sign-wrapper">
|
<div id="sign-wrapper">
|
||||||
<form class="form-align form panel sign-panel sign-form container panel-radius" id="sign-up-form" action="/user/login" method="post">
|
<form class="form-align form panel sign-panel sign-form container panel-radius" id="sign-up-form" action="/user/login" method="post">
|
||||||
{{.CsrfTokenHtml}}
|
|
||||||
<div class="panel-header">
|
<div class="panel-header">
|
||||||
<h2>{{.i18n.Tr "sign_in"}}</h2>
|
<h2>{{.i18n.Tr "sign_in"}}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
{{template "ng/base/header" .}}
|
{{template "ng/base/header" .}}
|
||||||
<div id="sign-wrapper">
|
<div id="sign-wrapper">
|
||||||
<form class="form-align form panel panel-radius sign-panel sign-form container" id="sign-up-form" action="/user/sign_up" method="post">
|
<form class="form-align form panel panel-radius sign-panel sign-form container" id="sign-up-form" action="/user/sign_up" method="post">
|
||||||
{{.CsrfTokenHtml}}
|
|
||||||
<div class="panel-header">
|
<div class="panel-header">
|
||||||
<h2>{{.i18n.Tr "sign_up"}}</h2>
|
<h2>{{.i18n.Tr "sign_up"}}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
@ -29,7 +28,7 @@
|
||||||
</p>
|
</p>
|
||||||
<p class="field">
|
<p class="field">
|
||||||
<label></label>
|
<label></label>
|
||||||
{{CreateCaptcha}}
|
{{.Captcha.CreateHtml}}
|
||||||
</p>
|
</p>
|
||||||
<p class="field">
|
<p class="field">
|
||||||
<label class="req" for="captcha">{{.i18n.Tr "captcha"}}</label>
|
<label class="req" for="captcha">{{.i18n.Tr "captcha"}}</label>
|
||||||
|
|
Loading…
Reference in a new issue