// Copyright 2012 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. /* QR data layout qr/ upload/ id.png id.fix flag/ id */ // TODO: Random seed taken from GET for caching, repeatability. // TODO: Flag for abuse button + some kind of dashboard. // TODO: +1 button on web page? permalink? // TODO: Flag for abuse button on permalinks too? // TODO: Make the page prettier. // TODO: Cache headers. package web import ( "bytes" "crypto/md5" "encoding/base64" "encoding/json" "fmt" "html/template" "image" "image/color" _ "image/gif" _ "image/jpeg" "image/png" "io" "math/rand" "net/http" "net/url" "os" "sort" "strconv" "strings" "time" "github.com/mattermost/rsc/appfs/fs" "github.com/mattermost/rsc/gf256" "github.com/mattermost/rsc/qr" "github.com/mattermost/rsc/qr/coding" "github.com/mattermost/rsc/qr/web/resize" ) func runTemplate(c *fs.Context, w http.ResponseWriter, name string, data interface{}) { t := template.New("main") main, _, err := c.Read(name) if err != nil { panic(err) } style, _, _ := c.Read("style.html") main = append(main, style...) _, err = t.Parse(string(main)) if err != nil { panic(err) } var buf bytes.Buffer if err := t.Execute(&buf, &data); err != nil { panic(err) } w.Write(buf.Bytes()) } func isImgName(s string) bool { if len(s) != 32 { return false } for i := 0; i < len(s); i++ { if '0' <= s[i] && s[i] <= '9' || 'a' <= s[i] && s[i] <= 'f' { continue } return false } return true } func isTagName(s string) bool { if len(s) != 16 { return false } for i := 0; i < len(s); i++ { if '0' <= s[i] && s[i] <= '9' || 'a' <= s[i] && s[i] <= 'f' { continue } return false } return true } // Draw is the handler for drawing a QR code. func Draw(w http.ResponseWriter, req *http.Request) { ctxt := fs.NewContext(req) url := req.FormValue("url") if url == "" { url = "http://swtch.com/qr" } if req.FormValue("upload") == "1" { upload(w, req, url) return } t0 := time.Now() img := req.FormValue("i") if !isImgName(img) { img = "pjw" } if req.FormValue("show") == "png" { i := loadSize(ctxt, img, 48) var buf bytes.Buffer png.Encode(&buf, i) w.Write(buf.Bytes()) return } if req.FormValue("flag") == "1" { flag(w, req, img, ctxt) return } if req.FormValue("x") == "" { var data = struct { Name string URL string }{ Name: img, URL: url, } runTemplate(ctxt, w, "qr/main.html", &data) return } arg := func(s string) int { x, _ := strconv.Atoi(req.FormValue(s)); return x } targ := makeTarg(ctxt, img, 17+4*arg("v")+arg("z")) m := &Image{ Name: img, Dx: arg("x"), Dy: arg("y"), URL: req.FormValue("u"), Version: arg("v"), Mask: arg("m"), RandControl: arg("r") > 0, Dither: arg("i") > 0, OnlyDataBits: arg("d") > 0, SaveControl: arg("c") > 0, Scale: arg("scale"), Target: targ, Seed: int64(arg("s")), Rotation: arg("o"), Size: arg("z"), } if m.Version > 8 { m.Version = 8 } if m.Scale == 0 { if arg("l") > 1 { m.Scale = 8 } else { m.Scale = 4 } } if m.Version >= 12 && m.Scale >= 4 { m.Scale /= 2 } if arg("l") == 1 { data, err := json.Marshal(m) if err != nil { panic(err) } h := md5.New() h.Write(data) tag := fmt.Sprintf("%x", h.Sum(nil))[:16] if err := ctxt.Write("qrsave/"+tag, data); err != nil { panic(err) } http.Redirect(w, req, "/qr/show/" + tag, http.StatusTemporaryRedirect) return } if err := m.Encode(req); err != nil { fmt.Fprintf(w, "%s\n", err) return } var dat []byte switch { case m.SaveControl: dat = m.Control default: dat = m.Code.PNG() } if arg("l") > 0 { w.Header().Set("Content-Type", "image/png") w.Write(dat) return } w.Header().Set("Content-Type", "text/html; charset=utf-8") fmt.Fprint(w, "

") fmt.Fprintf(w, "
\n", m.Link()) fmt.Fprintf(w, "
\n") fmt.Fprintf(w, "
%v
\n", time.Now().Sub(t0)) } func (m *Image) Small() bool { return 8*(17+4*int(m.Version)) < 512 } func (m *Image) Link() string { s := fmt.Sprint b := func(v bool) string { if v { return "1" } return "0" } val := url.Values{ "i": {m.Name}, "x": {s(m.Dx)}, "y": {s(m.Dy)}, "z": {s(m.Size)}, "u": {m.URL}, "v": {s(m.Version)}, "m": {s(m.Mask)}, "r": {b(m.RandControl)}, "t": {b(m.Dither)}, "d": {b(m.OnlyDataBits)}, "c": {b(m.SaveControl)}, "s": {s(m.Seed)}, } return "/qr/draw?" + val.Encode() } // Show is the handler for showing a stored QR code. func Show(w http.ResponseWriter, req *http.Request) { ctxt := fs.NewContext(req) tag := req.URL.Path[len("/qr/show/"):] png := strings.HasSuffix(tag, ".png") if png { tag = tag[:len(tag)-len(".png")] } if !isTagName(tag) { fmt.Fprintf(w, "Sorry, QR code not found\n") return } if req.FormValue("flag") == "1" { flag(w, req, tag, ctxt) return } data, _, err := ctxt.Read("qrsave/" + tag) if err != nil { fmt.Fprintf(w, "Sorry, QR code not found.\n") return } var m Image if err := json.Unmarshal(data, &m); err != nil { panic(err) } m.Tag = tag switch req.FormValue("size") { case "big": m.Scale *= 2 case "small": m.Scale /= 2 } if png { if err := m.Encode(req); err != nil { panic(err) return } w.Header().Set("Cache-Control", "public, max-age=3600") w.Write(m.Code.PNG()) return } w.Header().Set("Cache-Control", "public, max-age=300") runTemplate(ctxt, w, "qr/permalink.html", &m) } func upload(w http.ResponseWriter, req *http.Request, link string) { // Upload of a new image. // Copied from Moustachio demo. f, _, err := req.FormFile("image") if err != nil { fmt.Fprintf(w, "You need to select an image to upload.\n") return } defer f.Close() i, _, err := image.Decode(f) if err != nil { panic(err) } // Convert image to 128x128 gray+alpha. b := i.Bounds() const max = 128 // If it's gigantic, it's more efficient to downsample first // and then resize; resizing will smooth out the roughness. var i1 *image.RGBA if b.Dx() > 4*max || b.Dy() > 4*max { w, h := 2*max, 2*max if b.Dx() > b.Dy() { h = b.Dy() * h / b.Dx() } else { w = b.Dx() * w / b.Dy() } i1 = resize.Resample(i, b, w, h) } else { // "Resample" to same size, just to convert to RGBA. i1 = resize.Resample(i, b, b.Dx(), b.Dy()) } b = i1.Bounds() // Encode to PNG. dx, dy := 128, 128 if b.Dx() > b.Dy() { dy = b.Dy() * dx / b.Dx() } else { dx = b.Dx() * dy / b.Dy() } i128 := resize.ResizeRGBA(i1, i1.Bounds(), dx, dy) var buf bytes.Buffer if err := png.Encode(&buf, i128); err != nil { panic(err) } h := md5.New() h.Write(buf.Bytes()) tag := fmt.Sprintf("%x", h.Sum(nil))[:32] ctxt := fs.NewContext(req) if err := ctxt.Write("qr/upload/"+tag+".png", buf.Bytes()); err != nil { panic(err) } // Redirect with new image tag. // Redirect to draw with new image tag. http.Redirect(w, req, req.URL.Path+"?"+url.Values{"i": {tag}, "url": {link}}.Encode(), 302) } func flag(w http.ResponseWriter, req *http.Request, img string, ctxt *fs.Context) { if !isImgName(img) && !isTagName(img) { fmt.Fprintf(w, "Invalid image.\n") return } data, _, _ := ctxt.Read("qr/flag/" + img) data = append(data, '!') ctxt.Write("qr/flag/" + img, data) fmt.Fprintf(w, "Thank you. The image has been reported.\n") } func loadSize(ctxt *fs.Context, name string, max int) *image.RGBA { data, _, err := ctxt.Read("qr/upload/" + name + ".png") if err != nil { panic(err) } i, _, err := image.Decode(bytes.NewBuffer(data)) if err != nil { panic(err) } b := i.Bounds() dx, dy := max, max if b.Dx() > b.Dy() { dy = b.Dy() * dx / b.Dx() } else { dx = b.Dx() * dy / b.Dy() } var irgba *image.RGBA switch i := i.(type) { case *image.RGBA: irgba = resize.ResizeRGBA(i, i.Bounds(), dx, dy) case *image.NRGBA: irgba = resize.ResizeNRGBA(i, i.Bounds(), dx, dy) } return irgba } func makeTarg(ctxt *fs.Context, name string, max int) [][]int { i := loadSize(ctxt, name, max) b := i.Bounds() dx, dy := b.Dx(), b.Dy() targ := make([][]int, dy) arr := make([]int, dx*dy) for y := 0; y < dy; y++ { targ[y], arr = arr[:dx], arr[dx:] row := targ[y] for x := 0; x < dx; x++ { p := i.Pix[y*i.Stride+4*x:] r, g, b, a := p[0], p[1], p[2], p[3] if a == 0 { row[x] = -1 } else { row[x] = int((299*uint32(r) + 587*uint32(g) + 114*uint32(b) + 500) / 1000) } } } return targ } type Image struct { Name string Target [][]int Dx int Dy int URL string Tag string Version int Mask int Scale int Rotation int Size int // RandControl says to pick the pixels randomly. RandControl bool Seed int64 // Dither says to dither instead of using threshold pixel layout. Dither bool // OnlyDataBits says to use only data bits, not check bits. OnlyDataBits bool // Code is the final QR code. Code *qr.Code // Control is a PNG showing the pixels that we controlled. // Pixels we don't control are grayed out. SaveControl bool Control []byte } type Pixinfo struct { X int Y int Pix coding.Pixel Targ byte DTarg int Contrast int HardZero bool Block *BitBlock Bit uint } type Pixorder struct { Off int Priority int } type byPriority []Pixorder func (x byPriority) Len() int { return len(x) } func (x byPriority) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (x byPriority) Less(i, j int) bool { return x[i].Priority > x[j].Priority } func (m *Image) target(x, y int) (targ byte, contrast int) { tx := x + m.Dx ty := y + m.Dy if ty < 0 || ty >= len(m.Target) || tx < 0 || tx >= len(m.Target[ty]) { return 255, -1 } v0 := m.Target[ty][tx] if v0 < 0 { return 255, -1 } targ = byte(v0) n := 0 sum := 0 sumsq := 0 const del = 5 for dy := -del; dy <= del; dy++ { for dx := -del; dx <= del; dx++ { if 0 <= ty+dy && ty+dy < len(m.Target) && 0 <= tx+dx && tx+dx < len(m.Target[ty+dy]) { v := m.Target[ty+dy][tx+dx] sum += v sumsq += v * v n++ } } } avg := sum / n contrast = sumsq/n - avg*avg return } func (m *Image) rotate(p *coding.Plan, rot int) { if rot == 0 { return } N := len(p.Pixel) pix := make([][]coding.Pixel, N) apix := make([]coding.Pixel, N*N) for i := range pix { pix[i], apix = apix[:N], apix[N:] } switch rot { case 0: // ok case 1: for y := 0; y < N; y++ { for x := 0; x < N; x++ { pix[y][x] = p.Pixel[x][N-1-y] } } case 2: for y := 0; y < N; y++ { for x := 0; x < N; x++ { pix[y][x] = p.Pixel[N-1-y][N-1-x] } } case 3: for y := 0; y < N; y++ { for x := 0; x < N; x++ { pix[y][x] = p.Pixel[N-1-x][y] } } } p.Pixel = pix } func (m *Image) Encode(req *http.Request) error { p, err := coding.NewPlan(coding.Version(m.Version), coding.L, coding.Mask(m.Mask)) if err != nil { return err } m.rotate(p, m.Rotation) rand := rand.New(rand.NewSource(m.Seed)) // QR parameters. nd := p.DataBytes / p.Blocks nc := p.CheckBytes / p.Blocks extra := p.DataBytes - nd*p.Blocks rs := gf256.NewRSEncoder(coding.Field, nc) // Build information about pixels, indexed by data/check bit number. pixByOff := make([]Pixinfo, (p.DataBytes+p.CheckBytes)*8) expect := make([][]bool, len(p.Pixel)) for y, row := range p.Pixel { expect[y] = make([]bool, len(row)) for x, pix := range row { targ, contrast := m.target(x, y) if m.RandControl && contrast >= 0 { contrast = rand.Intn(128) + 64*((x+y)%2) + 64*((x+y)%3%2) } expect[y][x] = pix&coding.Black != 0 if r := pix.Role(); r == coding.Data || r == coding.Check { pixByOff[pix.Offset()] = Pixinfo{X: x, Y: y, Pix: pix, Targ: targ, Contrast: contrast} } } } Again: // Count fixed initial data bits, prepare template URL. url := m.URL + "#" var b coding.Bits coding.String(url).Encode(&b, p.Version) coding.Num("").Encode(&b, p.Version) bbit := b.Bits() dbit := p.DataBytes*8 - bbit if dbit < 0 { return fmt.Errorf("cannot encode URL into available bits") } num := make([]byte, dbit/10*3) for i := range num { num[i] = '0' } b.Pad(dbit) b.Reset() coding.String(url).Encode(&b, p.Version) coding.Num(num).Encode(&b, p.Version) b.AddCheckBytes(p.Version, p.Level) data := b.Bytes() doff := 0 // data offset coff := 0 // checksum offset mbit := bbit + dbit/10*10 // Choose pixels. bitblocks := make([]*BitBlock, p.Blocks) for blocknum := 0; blocknum < p.Blocks; blocknum++ { if blocknum == p.Blocks-extra { nd++ } bdata := data[doff/8 : doff/8+nd] cdata := data[p.DataBytes+coff/8 : p.DataBytes+coff/8+nc] bb := newBlock(nd, nc, rs, bdata, cdata) bitblocks[blocknum] = bb // Determine which bits in this block we can try to edit. lo, hi := 0, nd*8 if lo < bbit-doff { lo = bbit - doff if lo > hi { lo = hi } } if hi > mbit-doff { hi = mbit - doff if hi < lo { hi = lo } } // Preserve [0, lo) and [hi, nd*8). for i := 0; i < lo; i++ { if !bb.canSet(uint(i), (bdata[i/8]>>uint(7-i&7))&1) { return fmt.Errorf("cannot preserve required bits") } } for i := hi; i < nd*8; i++ { if !bb.canSet(uint(i), (bdata[i/8]>>uint(7-i&7))&1) { return fmt.Errorf("cannot preserve required bits") } } // Can edit [lo, hi) and checksum bits to hit target. // Determine which ones to try first. order := make([]Pixorder, (hi-lo)+nc*8) for i := lo; i < hi; i++ { order[i-lo].Off = doff + i } for i := 0; i < nc*8; i++ { order[hi-lo+i].Off = p.DataBytes*8 + coff + i } if m.OnlyDataBits { order = order[:hi-lo] } for i := range order { po := &order[i] po.Priority = pixByOff[po.Off].Contrast<<8 | rand.Intn(256) } sort.Sort(byPriority(order)) const mark = false for i := range order { po := &order[i] pinfo := &pixByOff[po.Off] bval := pinfo.Targ if bval < 128 { bval = 1 } else { bval = 0 } pix := pinfo.Pix if pix&coding.Invert != 0 { bval ^= 1 } if pinfo.HardZero { bval = 0 } var bi int if pix.Role() == coding.Data { bi = po.Off - doff } else { bi = po.Off - p.DataBytes*8 - coff + nd*8 } if bb.canSet(uint(bi), bval) { pinfo.Block = bb pinfo.Bit = uint(bi) if mark { p.Pixel[pinfo.Y][pinfo.X] = coding.Black } } else { if pinfo.HardZero { panic("hard zero") } if mark { p.Pixel[pinfo.Y][pinfo.X] = 0 } } } bb.copyOut() const cheat = false for i := 0; i < nd*8; i++ { pinfo := &pixByOff[doff+i] pix := p.Pixel[pinfo.Y][pinfo.X] if bb.B[i/8]&(1<= 128 { // want white pval = 0 v = 255 } bval := pval // bit value if pix&coding.Invert != 0 { bval ^= 1 } if pinfo.HardZero && bval != 0 { bval ^= 1 pval ^= 1 v ^= 255 } // Set pixel value as we want it. pinfo.Block.reset(pinfo.Bit, bval) _, _ = x, y err := targ - v if x+1 < len(row) { addDither(pixByOff, row[x+1], err*7/16) } if false && y+1 < len(p.Pixel) { if x > 0 { addDither(pixByOff, p.Pixel[y+1][x-1], err*3/16) } addDither(pixByOff, p.Pixel[y+1][x], err*5/16) if x+1 < len(row) { addDither(pixByOff, p.Pixel[y+1][x+1], err*1/16) } } } } for _, bb := range bitblocks { bb.copyOut() } } noops := 0 // Copy numbers back out. for i := 0; i < dbit/10; i++ { // Pull out 10 bits. v := 0 for j := 0; j < 10; j++ { bi := uint(bbit + 10*i + j) v <<= 1 v |= int((data[bi/8] >> (7 - bi&7)) & 1) } // Turn into 3 digits. if v >= 1000 { // Oops - too many 1 bits. // We know the 512, 256, 128, 64, 32 bits are all set. // Pick one at random to clear. This will break some // checksum bits, but so be it. println("oops", i, v) pinfo := &pixByOff[bbit+10*i+3] // TODO random pinfo.Contrast = 1e9 >> 8 pinfo.HardZero = true noops++ } num[i*3+0] = byte(v/100 + '0') num[i*3+1] = byte(v/10%10 + '0') num[i*3+2] = byte(v%10 + '0') } if noops > 0 { goto Again } var b1 coding.Bits coding.String(url).Encode(&b1, p.Version) coding.Num(num).Encode(&b1, p.Version) b1.AddCheckBytes(p.Version, p.Level) if !bytes.Equal(b.Bytes(), b1.Bytes()) { fmt.Printf("mismatch\n%d %x\n%d %x\n", len(b.Bytes()), b.Bytes(), len(b1.Bytes()), b1.Bytes()) panic("byte mismatch") } cc, err := p.Encode(coding.String(url), coding.Num(num)) if err != nil { return err } if !m.Dither { for y, row := range expect { for x, pix := range row { if cc.Black(x, y) != pix { println("mismatch", x, y, p.Pixel[y][x].String()) } } } } m.Code = &qr.Code{Bitmap: cc.Bitmap, Size: cc.Size, Stride: cc.Stride, Scale: m.Scale} if m.SaveControl { m.Control = pngEncode(makeImage(req, "", "", 0, cc.Size, 4, m.Scale, func(x, y int) (rgba uint32) { pix := p.Pixel[y][x] if pix.Role() == coding.Data || pix.Role() == coding.Check { pinfo := &pixByOff[pix.Offset()] if pinfo.Block != nil { if cc.Black(x, y) { return 0x000000ff } return 0xffffffff } } if cc.Black(x, y) { return 0x3f3f3fff } return 0xbfbfbfff })) } return nil } func addDither(pixByOff []Pixinfo, pix coding.Pixel, err int) { if pix.Role() != coding.Data && pix.Role() != coding.Check { return } pinfo := &pixByOff[pix.Offset()] println("add", pinfo.X, pinfo.Y, pinfo.DTarg, err) pinfo.DTarg += err } func readTarget(name string) ([][]int, error) { f, err := os.Open(name) if err != nil { return nil, err } m, err := png.Decode(f) if err != nil { return nil, fmt.Errorf("decode %s: %v", name, err) } rect := m.Bounds() target := make([][]int, rect.Dy()) for i := range target { target[i] = make([]int, rect.Dx()) } for y, row := range target { for x := range row { a := int(color.RGBAModel.Convert(m.At(x, y)).(color.RGBA).A) t := int(color.GrayModel.Convert(m.At(x, y)).(color.Gray).Y) if a == 0 { t = -1 } row[x] = t } } return target, nil } type BitBlock struct { DataBytes int CheckBytes int B []byte M [][]byte Tmp []byte RS *gf256.RSEncoder bdata []byte cdata []byte } func newBlock(nd, nc int, rs *gf256.RSEncoder, dat, cdata []byte) *BitBlock { b := &BitBlock{ DataBytes: nd, CheckBytes: nc, B: make([]byte, nd+nc), Tmp: make([]byte, nc), RS: rs, bdata: dat, cdata: cdata, } copy(b.B, dat) rs.ECC(b.B[:nd], b.B[nd:]) b.check() if !bytes.Equal(b.Tmp, cdata) { panic("cdata") } b.M = make([][]byte, nd*8) for i := range b.M { row := make([]byte, nd+nc) b.M[i] = row for j := range row { row[j] = 0 } row[i/8] = 1 << (7 - uint(i%8)) rs.ECC(row[:nd], row[nd:]) } return b } func (b *BitBlock) check() { b.RS.ECC(b.B[:b.DataBytes], b.Tmp) if !bytes.Equal(b.B[b.DataBytes:], b.Tmp) { fmt.Printf("ecc mismatch\n%x\n%x\n", b.B[b.DataBytes:], b.Tmp) panic("mismatch") } } func (b *BitBlock) reset(bi uint, bval byte) { if (b.B[bi/8]>>(7-bi&7))&1 == bval { // already has desired bit return } // rows that have already been set m := b.M[len(b.M):cap(b.M)] for _, row := range m { if row[bi/8]&(1<<(7-bi&7)) != 0 { // Found it. for j, v := range row { b.B[j] ^= v } return } } panic("reset of unset bit") } func (b *BitBlock) canSet(bi uint, bval byte) bool { found := false m := b.M for j, row := range m { if row[bi/8]&(1<<(7-bi&7)) == 0 { continue } if !found { found = true if j != 0 { m[0], m[j] = m[j], m[0] } continue } for k := range row { row[k] ^= m[0][k] } } if !found { return false } targ := m[0] // Subtract from saved-away rows too. for _, row := range m[len(m):cap(m)] { if row[bi/8]&(1<<(7-bi&7)) == 0 { continue } for k := range row { row[k] ^= targ[k] } } // Found a row with bit #bi == 1 and cut that bit from all the others. // Apply to data and remove from m. if (b.B[bi/8]>>(7-bi&7))&1 != bval { for j, v := range targ { b.B[j] ^= v } } b.check() n := len(m) - 1 m[0], m[n] = m[n], m[0] b.M = m[:n] for _, row := range b.M { if row[bi/8]&(1<<(7-bi&7)) != 0 { panic("did not reduce") } } return true } func (b *BitBlock) copyOut() { b.check() copy(b.bdata, b.B[:b.DataBytes]) copy(b.cdata, b.B[b.DataBytes:]) } func showtable(w http.ResponseWriter, b *BitBlock, gray func(int) bool) { nd := b.DataBytes nc := b.CheckBytes fmt.Fprintf(w, "\n") line := func() { fmt.Fprintf(w, "\n") for i := 0; i < (nd+nc)*8; i++ { fmt.Fprintf(w, "> uint(7-i&7) & 1 if gray(i) { fmt.Fprintf(w, " class='gray'") } fmt.Fprintf(w, ">") if v == 1 { fmt.Fprintf(w, "1") } } line() } m := b.M[len(b.M):cap(b.M)] for i := len(m) - 1; i >= 0; i-- { dorow(m[i]) } m = b.M for _, row := range b.M { dorow(row) } fmt.Fprintf(w, "
\n", (nd+nc)*8) } line() dorow := func(row []byte) { fmt.Fprintf(w, "
\n") } func BitsTable(w http.ResponseWriter, req *http.Request) { nd := 2 nc := 2 fmt.Fprintf(w, ` `) rs := gf256.NewRSEncoder(coding.Field, nc) dat := make([]byte, nd+nc) b := newBlock(nd, nc, rs, dat[:nd], dat[nd:]) for i := 0; i < nd*8; i++ { b.canSet(uint(i), 0) } showtable(w, b, func(i int) bool { return i < nd*8 }) b = newBlock(nd, nc, rs, dat[:nd], dat[nd:]) for j := 0; j < (nd+nc)*8; j += 2 { b.canSet(uint(j), 0) } showtable(w, b, func(i int) bool { return i%2 == 0 }) }