captcha.go 2.3 KB

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