server.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
5package captcha
6
7import (
8 "http"
9 "os"
10 "path"
11 "strconv"
12 "strings"
13)
14
15type captchaHandler struct {
16 imgWidth int
17 imgHeight int
18}
19
20// Server returns a handler that serves HTTP requests with image or
21// audio representations of captchas. Image dimensions are accepted as
22// arguments. The server decides which captcha to serve based on the last URL
23// path component: file name part must contain a captcha id, file extension —
24// its format (PNG or WAV).
25//
26// For example, for file name "LBm5vMjHDtdUfaWYXiQX.png" it serves an image captcha
27// with id "LBm5vMjHDtdUfaWYXiQX", and for "LBm5vMjHDtdUfaWYXiQX.wav" it serves the
28// same captcha in audio format.
29//
30// To serve a captcha as a downloadable file, the URL must be constructed in
31// such a way as if the file to serve is in the "download" subdirectory:
32// "/download/LBm5vMjHDtdUfaWYXiQX.wav".
33//
34// To reload captcha (get a different solution for the same captcha id), append
35// "?reload=x" to URL, where x may be anything (for example, current time or a
36// random number to make browsers refetch an image instead of loading it from
37// cache).
38//
39// By default, the Server serves audio in English language. To serve audio
40// captcha in one of the other supported languages, append "lang" value, for
41// example, "?lang=ru".
42func Server(imgWidth, imgHeight int) http.Handler {
43 return &captchaHandler{imgWidth, imgHeight}
44}
45
46func (h *captchaHandler) serve(w http.ResponseWriter, id, ext string, lang string, download bool) os.Error {
47 if download {
48 w.Header().Set("Content-Type", "application/octet-stream")
49 }
50 switch ext {
51 case ".png":
52 if !download {
53 w.Header().Set("Content-Type", "image/png")
54 }
55 return WriteImage(w, id, h.imgWidth, h.imgHeight)
56 case ".wav":
57 //XXX(dchest) Workaround for Chrome: it wants content-length,
58 //or else will start playing NOT from the beginning.
59 //Filed issue: http://code.google.com/p/chromium/issues/detail?id=80565
60 d := globalStore.Get(id, false)
61 if d == nil {
62 return ErrNotFound
63 }
64 a := NewAudio(d, lang)
65 if !download {
66 w.Header().Set("Content-Type", "audio/x-wav")
67 }
68 w.Header().Set("Content-Length", strconv.Itoa(a.EncodedLen()))
69 _, err := a.WriteTo(w)
70 return err
71 }
72 return ErrNotFound
73}
74
75func (h *captchaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
76 dir, file := path.Split(r.URL.Path)
77 ext := path.Ext(file)
78 id := file[:len(file)-len(ext)]
79 if ext == "" || id == "" {
80 http.NotFound(w, r)
81 return
82 }
83 if r.FormValue("reload") != "" {
84 Reload(id)
85 }
86 lang := strings.ToLower(r.FormValue("lang"))
87 download := path.Base(dir) == "download"
88 if h.serve(w, id, ext, lang, download) == ErrNotFound {
89 http.NotFound(w, r)
90 }
91 // Ignore other errors.
92}