108 lignes
2 Kio
Go
108 lignes
2 Kio
Go
package captcha
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/dchest/captcha"
|
|
)
|
|
|
|
const lifetime = time.Minute * 20
|
|
|
|
var (
|
|
server = captcha.Server(captcha.StdWidth, captcha.StdHeight)
|
|
captchas = captchaMap{
|
|
m: make(map[string]store, 64),
|
|
}
|
|
|
|
ErrInvalidCaptcha = errors.New("invalid captcha")
|
|
)
|
|
|
|
func init() {
|
|
captcha.SetCustomStore(captcha.NewMemoryStore(1<<10, lifetime))
|
|
|
|
go func() {
|
|
t := time.Tick(time.Minute)
|
|
for {
|
|
<-t
|
|
captchas.cleanUp()
|
|
}
|
|
}()
|
|
}
|
|
|
|
// Captcha is to be embedded into any form struct requiring a captcha
|
|
type Captcha struct {
|
|
CaptchaID, Solution string
|
|
}
|
|
|
|
// Captchas are IP-specific and need eventual cleanup
|
|
type captchaMap struct {
|
|
sync.Mutex
|
|
m map[string]store
|
|
}
|
|
|
|
type store struct {
|
|
id string
|
|
created time.Time
|
|
}
|
|
|
|
// Returns a captcha id by IP. If a captcha for this IP already exists, it is
|
|
// reloaded and returned. Otherwise, a new captcha is created.
|
|
func (n *captchaMap) get(ip string) string {
|
|
n.Lock()
|
|
defer n.Unlock()
|
|
|
|
old, ok := n.m[ip]
|
|
|
|
// No existing captcha, it expired or this IP already used the captcha
|
|
if !ok || !captcha.Reload(old.id) {
|
|
id := captcha.New()
|
|
n.m[ip] = store{
|
|
id: id,
|
|
created: time.Now(),
|
|
}
|
|
return id
|
|
}
|
|
|
|
old.created = time.Now()
|
|
n.m[ip] = old
|
|
return old.id
|
|
}
|
|
|
|
// Remove expired ip -> captchaID mappings
|
|
func (n *captchaMap) cleanUp() {
|
|
n.Lock()
|
|
defer n.Unlock()
|
|
|
|
till := time.Now().Add(-lifetime)
|
|
for ip, c := range n.m {
|
|
if c.created.Before(till) {
|
|
delete(n.m, ip)
|
|
}
|
|
}
|
|
}
|
|
|
|
// GetID returns a new or previous captcha id by IP
|
|
func GetID(ip string) string {
|
|
return captchas.get(ip)
|
|
}
|
|
|
|
// Extract a Captcha struct from an HTML form
|
|
func Extract(r *http.Request) Captcha {
|
|
return Captcha{
|
|
CaptchaID: r.FormValue("captchaID"),
|
|
Solution: r.FormValue("solution"),
|
|
}
|
|
}
|
|
|
|
// ServeFiles serves captcha images and audio
|
|
func ServeFiles(w http.ResponseWriter, r *http.Request) {
|
|
server.ServeHTTP(w, r)
|
|
}
|
|
|
|
// Authenticate check's if a captcha solution is valid
|
|
func Authenticate(req Captcha) bool {
|
|
return captcha.VerifyString(req.CaptchaID, req.Solution)
|
|
}
|