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 and returns a pointer to an image of CAPTCHA. 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 expireIn time.Time } func NewBaseCaptcha(expiry time.Duration) *BaseCaptcha { return &BaseCaptcha{ expireIn: ExpiryDate(expiry), } } func (c *BaseCaptcha) Image(style string) *image.Image { return nil } // Answer generates an integer answer for a CAPTCHA or just returns // an existing one. func (c *BaseCaptcha) Answer() Answer { if c.answer == "" { c.answer = NewIntAnswer() } return c.answer } // Solve sets solved field to true if given answer is right and returns a result // of a check. 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.expireIn } // 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) }