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/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) | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										2
									
								
								gogs.go
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								gogs.go
									
									
									
									
									
								
							|  | @ -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,10 +35,12 @@ func Toggle(options *ToggleOptions) macaron.Handler { | |||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		if !options.DisableCsrf && ctx.Req.Method == "POST" && !ctx.CsrfTokenValid() { | ||||
| 			ctx.Error(403, "CSRF token does not match") | ||||
| 		if !options.SignOutRequire && !options.DisableCsrf && ctx.Req.Method == "POST" { | ||||
| 			csrf.Validate(ctx.Context, ctx.csrf) | ||||
| 			if ctx.Written() { | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if options.SignInRequire { | ||||
| 			if !ctx.IsSigned { | ||||
|  |  | |||
|  | @ -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 | ||||
| 	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> | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue