all repos — captcha @ 90a7bd4b43b3a24663fb6a57ddfe33e4b291d971

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

Fix for Go weekly.2012-01-15.
Dmitry Chestnykh dmitry@codingrobots.com
Mon, 16 Jan 2012 15:11:18 +0100
commit

90a7bd4b43b3a24663fb6a57ddfe33e4b291d971

parent

31ad576274dd153db41c6e9ba9d78b92a22708a7

8 files changed, 32 insertions(+), 37 deletions(-)

jump to
M audio.goaudio.go

@@ -7,10 +7,10 @@

import ( "bytes" "encoding/binary" - "math" - "os" - "rand" "io" + + "math" + "math/rand" ) const sampleRate = 8000 // Hz

@@ -24,7 +24,7 @@ endingBeepSound = changeSpeed(beepSound, 1.4)

} type Audio struct { - body *bytes.Buffer + body *bytes.Buffer digitSounds [][]byte }

@@ -79,7 +79,7 @@ }

// WriteTo writes captcha audio in WAVE format into the given io.Writer, and // returns the number of bytes written and an error if any. -func (a *Audio) WriteTo(w io.Writer) (n int64, err os.Error) { +func (a *Audio) WriteTo(w io.Writer) (n int64, err error) { // Calculate padded length of PCM chunk data. bodyLen := uint32(a.body.Len()) paddedBodyLen := bodyLen
M captcha.gocaptcha.go

@@ -47,8 +47,8 @@ package captcha

import ( "bytes" + "errors" "io" - "os" ) const (

@@ -62,7 +62,7 @@ Expiration = 10 * 60 // 10 minutes

) var ( - ErrNotFound = os.NewError("captcha: id not found") + ErrNotFound = errors.New("captcha: id not found") // globalStore is a shared storage for captchas, generated by New function. globalStore = NewMemoryStore(CollectNum, Expiration) )

@@ -104,7 +104,7 @@ }

// WriteImage writes PNG-encoded image representation of the captcha with the // given id. The image will have the given width and height. -func WriteImage(w io.Writer, id string, width, height int) os.Error { +func WriteImage(w io.Writer, id string, width, height int) error { d := globalStore.Get(id, false) if d == nil { return ErrNotFound

@@ -116,7 +116,7 @@

// WriteAudio writes WAV-encoded audio representation of the captcha with the // given id and the given language. If there are no sounds for the given // language, English is used. -func WriteAudio(w io.Writer, id string, lang string) os.Error { +func WriteAudio(w io.Writer, id string, lang string) error { d := globalStore.Get(id, false) if d == nil { return ErrNotFound
M example/main.goexample/main.go

@@ -8,10 +8,10 @@

import ( "fmt" "github.com/dchest/captcha" - "http" "io" "log" - "template" + "net/http" + "text/template" ) var formTemplate = template.Must(template.New("example").Parse(formTemplateSrc))

@@ -22,12 +22,12 @@ http.NotFound(w, r)

return } d := struct { - CaptchaId string + CaptchaId string }{ captcha.New(), } if err := formTemplate.Execute(w, &d); err != nil { - http.Error(w, err.String(), http.StatusInternalServerError) + http.Error(w, err.Error(), http.StatusInternalServerError) } }
M image.goimage.go

@@ -10,8 +10,7 @@ "image/color"

"image/png" "io" "math" - "os" - "rand" + "math/rand" ) const (

@@ -86,7 +85,7 @@ // method returns 0 instead of the actual bytes written because png.Encode

// doesn't report this. // WriteTo writes captcha image in PNG format into the given writer. -func (m *Image) WriteTo(w io.Writer) (int64, os.Error) { +func (m *Image) WriteTo(w io.Writer) (int64, error) { return 0, png.Encode(w, m.Paletted) }
M image_test.goimage_test.go

@@ -4,16 +4,13 @@ // license that can be found in the LICENSE file.

package captcha -import ( - "os" - "testing" -) +import "testing" type byteCounter struct { n int64 } -func (bc *byteCounter) Write(b []byte) (int, os.Error) { +func (bc *byteCounter) Write(b []byte) (int, error) { bc.n += int64(len(b)) return len(b), nil }
M random.gorandom.go

@@ -7,7 +7,7 @@

import ( crand "crypto/rand" "io" - "rand" + "math/rand" "time" )

@@ -18,7 +18,7 @@ // idChars are characters allowed in captcha id.

var idChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") func init() { - rand.Seed(time.Nanoseconds()) + rand.Seed(time.Now().UnixNano()) } // RandomDigits returns a byte slice of the given length containing

@@ -32,7 +32,7 @@ // randomBytes returns a byte slice of the given length read from CSPRNG.

func randomBytes(length int) (b []byte) { b = make([]byte, length) if _, err := io.ReadFull(crand.Reader, b); err != nil { - panic("captcha: error reading random source: " + err.String()) + panic("captcha: error reading random source: " + err.Error()) } return }
M server.goserver.go

@@ -5,8 +5,7 @@

package captcha import ( - "http" - "os" + "net/http" "path" "strconv" "strings"

@@ -39,11 +38,11 @@ //

// By default, the Server serves audio in English language. To serve audio // captcha in one of the other supported languages, append "lang" value, for // example, "?lang=ru". -func Server(imgWidth, imgHeight int) http.Handler { +func Server(imgWidth, imgHeight int) http.Handler { return &captchaHandler{imgWidth, imgHeight} } -func (h *captchaHandler) serve(w http.ResponseWriter, id, ext string, lang string, download bool) os.Error { +func (h *captchaHandler) serve(w http.ResponseWriter, id, ext string, lang string, download bool) error { if download { w.Header().Set("Content-Type", "application/octet-stream") }
M store.gostore.go

@@ -30,7 +30,7 @@ // expValue stores timestamp and id of captchas. It is used in the list inside

// memoryStore for indexing generated captchas by timestamp to enable garbage // collection of expired captchas. type idByTimeValue struct { - timestamp int64 + timestamp time.Time id string }

@@ -44,13 +44,13 @@ numStored int

// Number of saved items that triggers collection. collectNum int // Expiration time of captchas. - expiration int64 + expiration time.Duration } // NewMemoryStore returns a new standard memory store for captchas with the -// given collection threshold and expiration time in seconds. The returned +// given collection threshold and expiration time (duration). The returned // store must be registered with SetCustomStore to replace the default one. -func NewMemoryStore(collectNum int, expiration int64) Store { +func NewMemoryStore(collectNum int, expiration time.Duration) Store { s := new(memoryStore) s.digitsById = make(map[string][]byte) s.idByTime = list.New()

@@ -62,7 +62,7 @@

func (s *memoryStore) Set(id string, digits []byte) { s.mu.Lock() s.digitsById[id] = digits - s.idByTime.PushBack(idByTimeValue{time.Seconds(), id}) + s.idByTime.PushBack(idByTimeValue{time.Now(), id}) s.numStored++ if s.numStored <= s.collectNum { s.mu.Unlock()

@@ -86,7 +86,7 @@ if !ok {

return } if clear { - s.digitsById[id] = nil, false + delete(s.digitsById, id) // XXX(dchest) Index (s.idByTime) will be cleaned when // collecting expired captchas. Can't clean it here, because // we don't store reference to expValue in the map.

@@ -96,7 +96,7 @@ return

} func (s *memoryStore) collect() { - now := time.Seconds() + now := time.Now() s.mu.Lock() defer s.mu.Unlock() s.numStored = 0

@@ -105,8 +105,8 @@ ev, ok := e.Value.(idByTimeValue)

if !ok { return } - if ev.timestamp+s.expiration < now { - s.digitsById[ev.id] = nil, false + if ev.timestamp.Add(s.expiration).Before(now) { + delete(s.digitsById, ev.id) next := e.Next() s.idByTime.Remove(e) e = next