all repos — captcha @ 9d0ab4e0254bd6f8ce9aa950ebb90956b1764fc9

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

captcha.go (view raw)

  1// Package captcha implements generation and verification of image and audio
  2// CAPTCHAs.
  3//
  4// A captcha solution is the sequence of digits 0-9 with the defined length.
  5// There are two captcha representations: image and audio.
  6//
  7// An image representation is a PNG-encoded image with the solution printed on
  8// it in such a way that makes it hard for computers to solve it using OCR.
  9//
 10// An audio representation is a WAVE-encoded (8 kHz unsigned 8-bit) sound with
 11// the spoken solution (currently in English and Russian). To make it hard for
 12// computers to solve audio captcha, the voice that pronounces numbers has
 13// random speed and pitch, and there is a randomly generated background noise
 14// mixed into the sound.
 15//
 16// This package doesn't require external files or libraries to generate captcha
 17// representations; it is self-contained.
 18//
 19// To make captchas one-time, the package includes a memory storage that stores
 20// captcha ids, their solutions, and expiration time. Used captchas are removed
 21// from the store immediately after calling Verify or VerifyString, while
 22// unused captchas (user loaded a page with captcha, but didn't submit the
 23// form) are collected automatically after the predefined expiration time.
 24// Developers can also provide custom store (for example, which saves captcha
 25// ids and solutions in database) by implementing Store interface and
 26// registering the object with SetCustomStore.
 27//
 28// Captchas are created by calling New, which returns the captcha id.  Their
 29// representations, though, are created on-the-fly by calling WriteImage or
 30// WriteAudio functions. Created representations are not stored anywhere, so
 31// subsequent calls to these functions with the same id will write the same
 32// captcha solution, but with a different random representation. Reload
 33// function will create a new different solution for the provided captcha,
 34// allowing users to "reload" captcha if they can't solve the displayed one
 35// without reloading the whole page.  Verify and VerifyString are used to
 36// verify that the given solution is the right one for the given captcha id.
 37//
 38// Server provides an http.Handler which can serve image and audio
 39// representations of captchas automatically from the URL. It can also be used
 40// to reload captchas.  Refer to Server function documentation for details, or
 41// take a look at the example in "example" subdirectory.
 42package captcha
 43
 44import (
 45	"bytes"
 46	"io"
 47	"os"
 48)
 49
 50const (
 51	// Default number of digits in captcha solution.
 52	DefaultLen = 6
 53	// The number of captchas created that triggers garbage collection used
 54	// by default store.
 55	CollectNum = 100
 56	// Expiration time of captchas used by default store.
 57	Expiration = 10 * 60 // 10 minutes
 58)
 59
 60var (
 61	ErrNotFound = os.NewError("captcha: id not found")
 62	// globalStore is a shared storage for captchas, generated by New function.
 63	globalStore = NewMemoryStore(CollectNum, Expiration)
 64)
 65
 66// SetCustomStore sets custom storage for captchas, replacing the default
 67// memory store. This function must be called before generating any captchas.
 68func SetCustomStore(s Store) {
 69	globalStore = s
 70}
 71
 72// New creates a new captcha with the standard length, saves it in the internal
 73// storage and returns its id.
 74func New() string {
 75	return NewLen(DefaultLen)
 76}
 77
 78// NewLen is just like New, but accepts length of a captcha solution as the
 79// argument.
 80func NewLen(length int) (id string) {
 81	id = randomId()
 82	globalStore.Set(id, RandomDigits(length))
 83	return
 84}
 85
 86// Reload generates and remembers new digits for the given captcha id.  This
 87// function returns false if there is no captcha with the given id.
 88//
 89// After calling this function, the image or audio presented to a user must be
 90// refreshed to show the new captcha representation (WriteImage and WriteAudio
 91// will write the new one).
 92func Reload(id string) bool {
 93	old := globalStore.Get(id, false)
 94	if old == nil {
 95		return false
 96	}
 97	globalStore.Set(id, RandomDigits(len(old)))
 98	return true
 99}
100
101// WriteImage writes PNG-encoded image representation of the captcha with the
102// given id. The image will have the given width and height.
103func WriteImage(w io.Writer, id string, width, height int) os.Error {
104	d := globalStore.Get(id, false)
105	if d == nil {
106		return ErrNotFound
107	}
108	_, err := NewImage(d, width, height).WriteTo(w)
109	return err
110}
111
112// WriteAudio writes WAV-encoded audio representation of the captcha with the
113// given id and the given language. If there are no sounds for the given
114// language, English is used.
115func WriteAudio(w io.Writer, id string, lang string) os.Error {
116	d := globalStore.Get(id, false)
117	if d == nil {
118		return ErrNotFound
119	}
120	_, err := NewAudio(d, lang).WriteTo(w)
121	return err
122}
123
124// Verify returns true if the given digits are the ones that were used to
125// create the given captcha id.
126// 
127// The function deletes the captcha with the given id from the internal
128// storage, so that the same captcha can't be verified anymore.
129func Verify(id string, digits []byte) bool {
130	if digits == nil || len(digits) == 0 {
131		return false
132	}
133	reald := globalStore.Get(id, true)
134	if reald == nil {
135		return false
136	}
137	return bytes.Equal(digits, reald)
138}
139
140// VerifyString is like Verify, but accepts a string of digits.  It removes
141// spaces and commas from the string, but any other characters, apart from
142// digits and listed above, will cause the function to return false.
143func VerifyString(id string, digits string) bool {
144	if digits == "" {
145		return false
146	}
147	ns := make([]byte, len(digits))
148	for i := range ns {
149		d := digits[i]
150		switch {
151		case '0' <= d && d <= '9':
152			ns[i] = d - '0'
153		case d == ' ' || d == ',':
154			// ignore
155		default:
156			return false
157		}
158	}
159	return Verify(id, ns)
160}