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