captcha.go 2.6 KB

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