all repos — captcha @ 90e4f7499bd2ad1e8c4e92fb54e97d79fc53e6b5

Go package captcha implements generation and verification of image and audio CAPTCHAs.

captcha.go (view raw)

  1package captcha
  2
  3import (
  4	"bytes"
  5	"crypto/rand"
  6	"github.com/dchest/uniuri"
  7	"io"
  8	"os"
  9)
 10
 11const (
 12	// Standard number of digits in captcha.
 13	StdLength = 6
 14	// The number of captchas created that triggers garbage collection
 15	StdCollectNum = 100
 16	// Expiration time of captchas
 17	StdExpiration = 2*60 // 2 minutes
 18
 19)
 20
 21var ErrNotFound = os.NewError("captcha with the given id not found")
 22
 23// globalStore is a shared storage for captchas, generated by New function.
 24var globalStore = newStore(StdCollectNum, StdExpiration)
 25
 26// RandomDigits returns a byte slice of the given length containing random
 27// digits in range 0-9.
 28func RandomDigits(length int) []byte {
 29	d := make([]byte, length)
 30	if _, err := io.ReadFull(rand.Reader, d); err != nil {
 31		panic(err)
 32	}
 33	for i := range d {
 34		d[i] %= 10
 35	}
 36	return d
 37}
 38
 39// New creates a new captcha of the given length, saves it in the internal
 40// storage, and returns its id.
 41func New(length int) (id string) {
 42	id = uniuri.New()
 43	globalStore.saveCaptcha(id, RandomDigits(length))
 44	return
 45}
 46
 47// Reload generates and remembers new digits for the given captcha id.  This
 48// function returns false if there is no captcha with the given id.
 49//
 50// After calling this function, the image or audio presented to a user must be
 51// refreshed to show the new captcha representation (WriteImage and WriteAudio
 52// will write the new one).
 53func Reload(id string) bool {
 54	old := globalStore.getDigits(id)
 55	if old == nil {
 56		return false
 57	}
 58	globalStore.saveCaptcha(id, RandomDigits(len(old)))
 59	return true
 60}
 61
 62// WriteImage writes PNG-encoded image representation of the captcha with the
 63// given id. The image will have the given width and height.
 64func WriteImage(w io.Writer, id string, width, height int) os.Error {
 65	d := globalStore.getDigits(id)
 66	if d == nil {
 67		return ErrNotFound
 68	}
 69	_, err := NewImage(d, width, height).WriteTo(w)
 70	return err
 71}
 72
 73// WriteAudio writes WAV-encoded audio representation of the captcha with the
 74// given id.
 75func WriteAudio(w io.Writer, id string) os.Error {
 76	d := globalStore.getDigits(id)
 77	if d == nil {
 78		return ErrNotFound
 79	}
 80	_, err := NewAudio(d).WriteTo(w)
 81	return err
 82}
 83
 84// Verify returns true if the given digits are the ones that were used to
 85// create the given captcha id.
 86// 
 87// The function deletes the captcha with the given id from the internal
 88// storage, so that the same captcha can't be verified anymore.
 89func Verify(id string, digits []byte) bool {
 90	reald := globalStore.getDigitsClear(id)
 91	if reald == nil {
 92		return false
 93	}
 94	return bytes.Equal(digits, reald)
 95}
 96
 97// Collect deletes expired and used captchas from the internal
 98// storage. It is called automatically by New function every CollectNum
 99// generated captchas, but still exported to enable freeing memory manually if
100// needed.
101//
102// Collection is launched in a new goroutine, so this function returns
103// immediately.
104func Collect() {
105	go globalStore.collect()
106}