| @@ -14,6 +14,9 @@ import ( | |||
| "github.com/Unknwon/macaron" | |||
| "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/session" | |||
| @@ -21,7 +24,6 @@ import ( | |||
| "github.com/gogits/gogs/modules/auth/apiv1" | |||
| "github.com/gogits/gogs/modules/avatar" | |||
| "github.com/gogits/gogs/modules/base" | |||
| "github.com/gogits/gogs/modules/captcha" | |||
| "github.com/gogits/gogs/modules/log" | |||
| "github.com/gogits/gogs/modules/middleware" | |||
| "github.com/gogits/gogs/modules/middleware/binding" | |||
| @@ -78,10 +80,20 @@ func newMacaron() *macaron.Macaron { | |||
| Names: setting.Names, | |||
| 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{ | |||
| Provider: setting.SessionProvider, | |||
| Config: *setting.SessionConfig, | |||
| })) | |||
| m.Use(csrf.Generate(csrf.Options{ | |||
| Secret: setting.SecretKey, | |||
| SetCookie: true, | |||
| })) | |||
| m.Use(middleware.Contexter()) | |||
| return m | |||
| } | |||
| @@ -121,16 +133,12 @@ func runWeb(*cli.Context) { | |||
| // Repositories. | |||
| 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}) | |||
| }) | |||
| }) | |||
| }) | |||
| 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. | |||
| m.Group("/user", func(r *macaron.Router) { | |||
| r.Get("/login", user.SignIn) | |||
| @@ -165,10 +173,10 @@ func runWeb(*cli.Context) { | |||
| m.Get("/user/:username", ignSignIn, user.Profile) // TODO: Legacy | |||
| // Captcha service. | |||
| cpt := captcha.NewCaptcha("/captcha/", setting.Cache) | |||
| m.Map(cpt) | |||
| m.Get("/captcha/*", cpt.Handler) | |||
| // Gravatar service. | |||
| avt := avatar.CacheServer("public/img/avatar/", "public/img/avatar_default.jpg") | |||
| os.MkdirAll("public/img/avatar/", os.ModePerm) | |||
| m.Get("/avatar/:hash", avt.ServeHTTP) | |||
| adminReq := middleware.Toggle(&middleware.ToggleOptions{SignInRequire: true, AdminRequire: true}) | |||
| @@ -199,7 +207,7 @@ func runWeb(*cli.Context) { | |||
| m.Get("/:username", ignSignIn, user.Profile) | |||
| if macaron.Env == macaron.DEV { | |||
| m.Get("/template/**", dev.TemplatePreview) | |||
| m.Get("/template/*", dev.TemplatePreview) | |||
| dev.RegisterDebugRoutes(m) | |||
| } | |||
| @@ -17,7 +17,7 @@ import ( | |||
| "github.com/gogits/gogs/modules/setting" | |||
| ) | |||
| const APP_VER = "0.4.7.0730 Alpha" | |||
| const APP_VER = "0.4.7.0731 Alpha" | |||
| func init() { | |||
| runtime.GOMAXPROCS(runtime.NumCPU()) | |||
| @@ -103,7 +103,6 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{ | |||
| "ActionContent2Commits": ActionContent2Commits, | |||
| "Oauth2Icon": Oauth2Icon, | |||
| "Oauth2Name": Oauth2Name, | |||
| "CreateCaptcha": func() string { return "" }, | |||
| } | |||
| type Actioner interface { | |||
| @@ -13,7 +13,6 @@ import ( | |||
| "fmt" | |||
| "hash" | |||
| "math" | |||
| r "math/rand" | |||
| "strings" | |||
| "time" | |||
| @@ -45,33 +44,6 @@ func GetRandomString(n int, alphabets ...byte) string { | |||
| 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 | |||
| func PBKDF2(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte { | |||
| 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" | |||
| "github.com/Unknwon/macaron" | |||
| "github.com/macaron-contrib/csrf" | |||
| "github.com/gogits/gogs/modules/setting" | |||
| ) | |||
| @@ -34,9 +35,11 @@ func Toggle(options *ToggleOptions) macaron.Handler { | |||
| return | |||
| } | |||
| if !options.DisableCsrf && ctx.Req.Method == "POST" && !ctx.CsrfTokenValid() { | |||
| ctx.Error(403, "CSRF token does not match") | |||
| return | |||
| if !options.SignOutRequire && !options.DisableCsrf && ctx.Req.Method == "POST" { | |||
| csrf.Validate(ctx.Context, ctx.csrf) | |||
| if ctx.Written() { | |||
| return | |||
| } | |||
| } | |||
| if options.SignInRequire { | |||
| @@ -14,6 +14,8 @@ import ( | |||
| "time" | |||
| "github.com/Unknwon/macaron" | |||
| "github.com/macaron-contrib/cache" | |||
| "github.com/macaron-contrib/csrf" | |||
| "github.com/macaron-contrib/i18n" | |||
| "github.com/macaron-contrib/session" | |||
| @@ -29,14 +31,14 @@ import ( | |||
| type Context struct { | |||
| *macaron.Context | |||
| i18n.Locale | |||
| Cache cache.Cache | |||
| csrf csrf.CSRF | |||
| Flash *session.Flash | |||
| Session session.Store | |||
| User *models.User | |||
| IsSigned bool | |||
| csrfToken string | |||
| Repo struct { | |||
| IsOwner bool | |||
| IsTrueOwner bool | |||
| @@ -70,10 +72,6 @@ func (ctx *Context) Query(name string) string { | |||
| 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. | |||
| func (ctx *Context) HasApiError() bool { | |||
| 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))) | |||
| } | |||
| 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) { | |||
| var name string | |||
| 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. | |||
| 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{ | |||
| Context: c, | |||
| Locale: l, | |||
| Cache: cache, | |||
| csrf: x, | |||
| Flash: f, | |||
| Session: sess, | |||
| } | |||
| // Cache: setting.Cache, | |||
| // Compute current URL for real-time change language. | |||
| link := ctx.Req.RequestURI | |||
| @@ -231,9 +203,8 @@ func Contexter() macaron.Handler { | |||
| } | |||
| } | |||
| // get or create csrf token | |||
| ctx.Data["CsrfToken"] = ctx.CsrfToken() | |||
| ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + ctx.csrfToken + `">`) | |||
| ctx.Data["CsrfToken"] = x.GetToken() | |||
| ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + x.GetToken() + `">`) | |||
| c.Map(ctx) | |||
| } | |||
| @@ -16,8 +16,6 @@ import ( | |||
| "github.com/Unknwon/goconfig" | |||
| "github.com/macaron-contrib/session" | |||
| "github.com/gogits/cache" | |||
| "github.com/gogits/gogs/modules/log" | |||
| // "github.com/gogits/gogs-ng/modules/ssh" | |||
| ) | |||
| @@ -80,9 +78,9 @@ var ( | |||
| AttachmentEnabled bool | |||
| // Cache settings. | |||
| Cache cache.Cache | |||
| CacheAdapter string | |||
| CacheConfig string | |||
| CacheAdapter string | |||
| CacheInternal int | |||
| CacheConn string | |||
| EnableRedis bool | |||
| EnableMemcache bool | |||
| @@ -325,20 +323,13 @@ func newCacheService() { | |||
| switch CacheAdapter { | |||
| case "memory": | |||
| CacheConfig = fmt.Sprintf(`{"interval":%d}`, Cfg.MustInt("cache", "INTERVAL", 60)) | |||
| CacheInternal = Cfg.MustInt("cache", "INTERVAL", 60) | |||
| case "redis", "memcache": | |||
| CacheConfig = fmt.Sprintf(`{"conn":"%s"}`, strings.Trim(Cfg.MustValue("cache", "HOST"), "\" ")) | |||
| CacheConn = strings.Trim(Cfg.MustValue("cache", "HOST"), "\" ") | |||
| default: | |||
| 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") | |||
| } | |||
| @@ -263,7 +263,8 @@ func Config(ctx *middleware.Context) { | |||
| } | |||
| 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["SessionConfig"] = setting.SessionConfig | |||
| @@ -8,10 +8,11 @@ import ( | |||
| "net/url" | |||
| "strings" | |||
| "github.com/macaron-contrib/captcha" | |||
| "github.com/gogits/gogs/models" | |||
| "github.com/gogits/gogs/modules/auth" | |||
| "github.com/gogits/gogs/modules/base" | |||
| "github.com/gogits/gogs/modules/captcha" | |||
| "github.com/gogits/gogs/modules/log" | |||
| // "github.com/gogits/gogs/modules/mailer" | |||
| "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" .}} | |||
| <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"> | |||
| {{.CsrfTokenHtml}} | |||
| <div class="panel-header"> | |||
| <h2>{{.i18n.Tr "sign_in"}}</h2> | |||
| </div> | |||
| @@ -2,7 +2,6 @@ | |||
| {{template "ng/base/header" .}} | |||
| <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"> | |||
| {{.CsrfTokenHtml}} | |||
| <div class="panel-header"> | |||
| <h2>{{.i18n.Tr "sign_up"}}</h2> | |||
| </div> | |||
| @@ -29,7 +28,7 @@ | |||
| </p> | |||
| <p class="field"> | |||
| <label></label> | |||
| {{CreateCaptcha}} | |||
| {{.Captcha.CreateHtml}} | |||
| </p> | |||
| <p class="field"> | |||
| <label class="req" for="captcha">{{.i18n.Tr "captcha"}}</label> | |||