package captcha import ( "crypto/rand" "image" "math/big" "time" ) const ( maxIntAnswer = 999999 ) type Answer string func NewIntAnswer() Answer { ans, _ := rand.Int(rand.Reader, big.NewInt(maxIntAnswer)) return (Answer(ans.String())) } // Captcha interface that should be implemented by a CAPTCHA. type Captcha interface { // Image generates an image of a CAPTCHA according to a passed style // and returns a pointer to it. Image(style string) *image.Image // Answer returns a pregenerated answer. Answer() Answer // Solve compares a stored answer with a passed one. Solve(answer Answer) bool // IsSolved returns if a CAPTCHA is solved or not. IsSolved() bool // Expiry returns a date after what CAPTCHA will expire. Expiry() time.Time } // BaseCaptcha is a base implementation of a CAPTCHA. // // All derivatives that embed this struct only need to implement // an Image() method. type BaseCaptcha struct { answer Answer solved bool expiry time.Time } func NewBaseCaptcha(expiry time.Duration) *BaseCaptcha { return &BaseCaptcha{ expiry: ExpiryDate(expiry), } } func (c *BaseCaptcha) Image(style string) *image.Image { return nil } func (c *BaseCaptcha) Answer() Answer { if c.answer == "" { c.answer = NewIntAnswer() } return c.answer } func (c *BaseCaptcha) Solve(answer Answer) bool { c.solved = c.answer == answer return c.solved } func (c *BaseCaptcha) IsSolved() bool { return c.solved } func (c *BaseCaptcha) Expiry() time.Time { return c.expiry } // ExpiryDate returns a date when CAPTCHA expires. It adds a passed // expiry duration to a current time. func ExpiryDate(expiry time.Duration) time.Time { return time.Now().Add(expiry) }