all repos — captcha @ d7cb604d49c3777d159741e1df389c6c540310f8

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

captcha.go (view raw)

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