├── README.md ├── vendor ├── alipay │ ├── alipay_test.go │ ├── utils.go │ ├── readme.md │ ├── native.go │ ├── alipay_v1.go │ └── alipay.go └── odeke-em │ ├── qr │ ├── png_test.go │ ├── qr.go │ ├── coding │ │ ├── qr_test.go │ │ ├── gen.go │ │ └── qr.go │ ├── libqrencode │ │ └── qrencode.go │ ├── web │ │ ├── resize │ │ │ └── resize.go │ │ ├── pic.go │ │ └── play.go │ └── png.go │ ├── appfs │ ├── fs │ │ ├── local.go │ │ └── fs.go │ └── proto │ │ └── data.go │ └── gf256 │ ├── blog_test.go │ ├── gf256_test.go │ └── gf256.go ├── controller ├── wxpay.go └── alipay.go └── models └── Wxpay ├── OrderqueryReq.go ├── WXPayNotifyReq.go └── UnifyOrderReq.go /README.md: -------------------------------------------------------------------------------- 1 | # payment 2 | ####使用golang写的微信和支付宝的接口处理,参考了已有的微信和支付宝的go实现 3 | -------------------------------------------------------------------------------- /vendor/alipay/alipay_test.go: -------------------------------------------------------------------------------- 1 | package alipay 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestV1(t *testing.T) { 8 | AlipayPartner = "0" 9 | AlipayKey = "0" 10 | WebReturnUrl = "none" 11 | WebNotifyUrl = "none" 12 | WebSellerEmail = "huangziyi@wokugame.com" 13 | form := CreateAlipaySign("123", 99.8, "翱翔大空", "充值100") 14 | if form == "" { 15 | t.Error("v1错误") 16 | } 17 | } 18 | 19 | func TestNew(t *testing.T) { 20 | alipay := Client{ 21 | Partner: "", // 合作者ID 22 | Key: "", // 合作者私钥 23 | ReturnUrl: "", // 同步返回地址 24 | NotifyUrl: "", // 网站异步返回地址 25 | Email: "", // 网站卖家邮箱地址 26 | } 27 | form := alipay.Form(Options{ 28 | OrderId: "123", 29 | Fee: 99.8, 30 | NickName: "翱翔大空", 31 | Subject: "充值100", 32 | }) 33 | if form == "" { 34 | t.Error("最新接口错误") 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /vendor/odeke-em/qr/png_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package qr 6 | 7 | import ( 8 | "bytes" 9 | "image" 10 | "image/color" 11 | "image/png" 12 | "io/ioutil" 13 | "testing" 14 | ) 15 | 16 | func TestPNG(t *testing.T) { 17 | c, err := Encode("hello, world", L) 18 | if err != nil { 19 | t.Fatal(err) 20 | } 21 | pngdat := c.PNG() 22 | if true { 23 | ioutil.WriteFile("x.png", pngdat, 0666) 24 | } 25 | m, err := png.Decode(bytes.NewBuffer(pngdat)) 26 | if err != nil { 27 | t.Fatal(err) 28 | } 29 | gm := m.(*image.Gray) 30 | 31 | scale := c.Scale 32 | siz := c.Size 33 | nbad := 0 34 | for y := 0; y < scale*(8+siz); y++ { 35 | for x := 0; x < scale*(8+siz); x++ { 36 | v := byte(255) 37 | if c.Black(x/scale-4, y/scale-4) { 38 | v = 0 39 | } 40 | if gv := gm.At(x, y).(color.Gray).Y; gv != v { 41 | t.Errorf("%d,%d = %d, want %d", x, y, gv, v) 42 | if nbad++; nbad >= 20 { 43 | t.Fatalf("too many bad pixels") 44 | } 45 | } 46 | } 47 | } 48 | } 49 | 50 | func BenchmarkPNG(b *testing.B) { 51 | c, err := Encode("0123456789012345678901234567890123456789", L) 52 | if err != nil { 53 | panic(err) 54 | } 55 | var bytes []byte 56 | for i := 0; i < b.N; i++ { 57 | bytes = c.PNG() 58 | } 59 | b.SetBytes(int64(len(bytes))) 60 | } 61 | 62 | func BenchmarkImagePNG(b *testing.B) { 63 | c, err := Encode("0123456789012345678901234567890123456789", L) 64 | if err != nil { 65 | panic(err) 66 | } 67 | var buf bytes.Buffer 68 | for i := 0; i < b.N; i++ { 69 | buf.Reset() 70 | png.Encode(&buf, c.Image()) 71 | } 72 | b.SetBytes(int64(buf.Len())) 73 | } 74 | -------------------------------------------------------------------------------- /controller/wxpay.go: -------------------------------------------------------------------------------- 1 | package Payment 2 | 3 | import ( 4 | "encoding/base64" 5 | "fmt" 6 | "odeke-em/qr" 7 | "os" 8 | "payment/models/Wxpay" 9 | "strconv" 10 | "time" 11 | 12 | "github.com/astaxie/beego" 13 | ) 14 | 15 | type WxpayController struct { 16 | beego.Controller 17 | } 18 | 19 | func (this *WxpayController) Native() { 20 | orderNumber := this.Ctx.Input.Param(":id") //获取订单号 21 | payAmount := this.GetString("price") //获取价格 22 | params := make(map[string]interface{}) 23 | params["body"] = "****company-" + orderNumber //显示标题 24 | params["out_trade_no"] = orderNumber 25 | params["total_fee"] = payAmount 26 | params["product_id"] = orderNumber 27 | params["attach"] = "abc" //自定义参数 28 | 29 | var modwx Wxpay.UnifyOrderReq 30 | res := modwx.CreateOrder(this.Ctx, params) 31 | 32 | this.Data["data"] = res 33 | //拿到数据之后,需要生成二维码。 34 | this.Data["Image"] = Img(res.Code_url) 35 | 36 | this.TplName = "Wxpay/index.tpl" 37 | } 38 | 39 | func (this *WxpayController) Notify() { 40 | var notifyReq Wxpay.WXPayNotifyReq 41 | res := notifyReq.WxpayCallback(this.Ctx) 42 | //beego.Debug("res",res) 43 | if res != nil { 44 | //这里可以组织res的数据 处理自己的业务逻辑: 45 | sendData := make(map[string]interface{}) 46 | sendData["id"] = res["out_trade_no"] 47 | sendData["trade_no"] = res["transaction_id"] 48 | paid_time, _ := time.Parse("20060102150405", res["time_end"].(string)) 49 | paid_timestr := paid_time.Format("2006-01-02 15:04:05") 50 | sendData["paid_time"] = paid_timestr 51 | sendData["payment_type"] = "wxpay" 52 | intfee := res["cash_fee"].(int) 53 | floatfee := float64(intfee) 54 | cashfee := floatfee / 100 55 | sendData["payment_amount"] = strconv.FormatFloat(cashfee, 'f', 2, 32) 56 | 57 | //api(sendData)...自己的逻辑处理 58 | // 59 | 60 | } 61 | } 62 | 63 | func Img(url string) string { 64 | code, err := qr.Encode(url, qr.H) 65 | if err != nil { 66 | fmt.Println(err) 67 | os.Exit(1) 68 | } 69 | imgByte := code.PNG() 70 | str := base64.StdEncoding.EncodeToString(imgByte) 71 | 72 | return str 73 | } 74 | -------------------------------------------------------------------------------- /vendor/odeke-em/appfs/fs/local.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package fs 6 | 7 | import ( 8 | "io/ioutil" 9 | "net/http" 10 | "os" 11 | "path/filepath" 12 | 13 | "code.google.com/p/rsc/appfs/proto" 14 | ) 15 | 16 | type context struct{} 17 | 18 | type cacheKey struct{} 19 | 20 | func newContext(req *http.Request) *Context { 21 | return &Context{} 22 | } 23 | 24 | func (*context) cacheRead(ckey CacheKey, path string) (CacheKey, []byte, bool) { 25 | return ckey, nil, false 26 | } 27 | 28 | func (*context) cacheWrite(ckey CacheKey, data []byte) { 29 | } 30 | 31 | func (*context) read(path string) ([]byte, *proto.FileInfo, error) { 32 | p := filepath.Join(Root, path) 33 | dir, err := os.Stat(p) 34 | if err != nil { 35 | return nil, nil, err 36 | } 37 | fi := &proto.FileInfo{ 38 | Name: dir.Name(), 39 | ModTime: dir.ModTime(), 40 | Size: dir.Size(), 41 | IsDir: dir.IsDir(), 42 | } 43 | data, err := ioutil.ReadFile(p) 44 | return data, fi, err 45 | } 46 | 47 | func (*context) write(path string, data []byte) error { 48 | p := filepath.Join(Root, path) 49 | return ioutil.WriteFile(p, data, 0666) 50 | } 51 | 52 | func (*context) remove(path string) error { 53 | p := filepath.Join(Root, path) 54 | return os.Remove(p) 55 | } 56 | 57 | func (*context) mkdir(path string) error { 58 | p := filepath.Join(Root, path) 59 | fi, err := os.Stat(p) 60 | if err == nil && fi.IsDir() { 61 | return nil 62 | } 63 | return os.Mkdir(p, 0777) 64 | } 65 | 66 | func (*context) readdir(path string) ([]proto.FileInfo, error) { 67 | p := filepath.Join(Root, path) 68 | dirs, err := ioutil.ReadDir(p) 69 | if err != nil { 70 | return nil, err 71 | } 72 | var out []proto.FileInfo 73 | for _, dir := range dirs { 74 | out = append(out, proto.FileInfo{ 75 | Name: dir.Name(), 76 | ModTime: dir.ModTime(), 77 | Size: dir.Size(), 78 | IsDir: dir.IsDir(), 79 | }) 80 | } 81 | return out, nil 82 | } 83 | -------------------------------------------------------------------------------- /vendor/odeke-em/appfs/proto/data.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package proto defines the protocol between appfs client and server. 6 | package proto 7 | 8 | import "time" 9 | 10 | // An Auth appears, JSON-encoded, as the X-Appfs-Auth header line, 11 | // to authenticate a request made to the file server. 12 | // The authentication scheme could be made more sophisticated, but since 13 | // we are already forcing the use of TLS, a plain password is fine for now. 14 | type Auth struct { 15 | Password string 16 | } 17 | 18 | // GET /.appfs/stat/path returns the metadata for a file or directory, 19 | // a JSON-encoded FileInfo. 20 | const StatURL = "/.appfs/stat/" 21 | 22 | // GET /.appfs/read/path returns the content of the file or directory. 23 | // The body of the response is the raw file or directory content. 24 | // The content of a directory is a sequence of JSON-encoded FileInfo. 25 | const ReadURL = "/.appfs/read/" 26 | 27 | // POST to /.appfs/write/path writes new data to a file. 28 | // The X-Appfs-SHA1 header is the SHA1 hash of the data. 29 | // The body of the request is the raw file content. 30 | const WriteURL = "/.appfs/write/" 31 | 32 | // POST to /.appfs/mount initializes the file system if it does not 33 | // yet exist in the datastore. 34 | const MkfsURL = "/.appfs/mkfs" 35 | 36 | // POST to /.appfs/create/path creates a new file or directory. 37 | // The named path must not already exist; its parent must exist. 38 | // The query parameter dir=1 indicates that a directory should be created. 39 | const CreateURL = "/.appfs/create/" 40 | 41 | // POST to /.appfs/remove/path removes the file or directory. 42 | // A directory must be empty to be removed. 43 | const RemoveURL = "/.appfs/remove/" 44 | 45 | // A FileInfo is a directory entry. 46 | type FileInfo struct { 47 | Name string // final path element 48 | ModTime time.Time 49 | Size int64 50 | IsDir bool 51 | } 52 | 53 | // PostContentType is the Content-Type for POSTed data. 54 | // There is no encoding or framing: it is just raw data bytes. 55 | const PostContentType = "x-appfs/raw" 56 | -------------------------------------------------------------------------------- /vendor/alipay/utils.go: -------------------------------------------------------------------------------- 1 | package alipay 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | "encoding/json" 7 | "strconv" 8 | "strings" 9 | "github.com/astaxie/beego" 10 | ) 11 | 12 | type AlipayParameters struct { 13 | InputCharset string `json:"_input_charset"` //网站编码 14 | Body string `json:"body"` //订单描述 15 | Extra_common_param string `json:"extra_common_param"` 16 | NotifyUrl string `json:"notify_url"` //异步通知页面 17 | OutTradeNo string `json:"out_trade_no"` //订单唯一id 18 | Partner string `json:"partner"` //合作者身份ID 19 | PaymentType uint8 `json:"payment_type"` //支付类型 1:商品购买 20 | ReturnUrl string `json:"return_url"` //回调url 21 | SellerEmail string `json:"seller_email"` //卖家支付宝邮箱 22 | Service string `json:"service"` //接口名称 23 | Subject string `json:"subject"` //商品名称 24 | TotalFee float32 `json:"total_fee"` //总价 25 | Sign string `json:"sign"` //签名,生成签名时忽略 26 | SignType string `json:"sign_type"` //签名类型,生成签名时忽略 27 | } 28 | 29 | // 按照支付宝规则生成sign 30 | func sign(param interface{}) string { 31 | //解析为字节数组 32 | paramBytes, err := json.Marshal(param) 33 | if err != nil { 34 | return "" 35 | } 36 | 37 | //重组字符串 38 | var sign string 39 | oldString := string(paramBytes) 40 | 41 | //为保证签名前特殊字符串没有被转码,这里解码一次 42 | oldString = strings.Replace(oldString, `\u003c`, "<", -1) 43 | oldString = strings.Replace(oldString, `\u003e`, ">", -1) 44 | 45 | //去除特殊标点 46 | oldString = strings.Replace(oldString, "\"", "", -1) 47 | oldString = strings.Replace(oldString, "{", "", -1) 48 | oldString = strings.Replace(oldString, "}", "", -1) 49 | paramArray := strings.Split(oldString, ",") 50 | 51 | for _, v := range paramArray { 52 | detail := strings.SplitN(v, ":", 2) 53 | //排除sign和sign_type 54 | if detail[0] != "sign" && detail[0] != "sign_type" { 55 | //total_fee转化为2位小数 56 | if detail[0] == "total_fee" { 57 | number, _ := strconv.ParseFloat(detail[1], 32) 58 | detail[1] = strconv.FormatFloat(number, 'f', 2, 64) 59 | } 60 | if sign == "" { 61 | sign = detail[0] + "=" + detail[1] 62 | } else { 63 | sign += "&" + detail[0] + "=" + detail[1] 64 | } 65 | } 66 | } 67 | 68 | //追加密钥 69 | sign += beego.AppConfig.String("alikey") 70 | 71 | //md5加密 72 | m := md5.New() 73 | m.Write([]byte(sign)) 74 | sign = hex.EncodeToString(m.Sum(nil)) 75 | return sign 76 | } 77 | -------------------------------------------------------------------------------- /controller/alipay.go: -------------------------------------------------------------------------------- 1 | package Payment 2 | 3 | import ( 4 | "alipay" 5 | "strconv" 6 | "strings" 7 | "github.com/astaxie/beego" 8 | ) 9 | 10 | type AlipayController struct { 11 | beego.Controller 12 | } 13 | 14 | func newClient() *alipay.Client { 15 | return &alipay.Client{ 16 | Partner: beego.AppConfig.String("alipartner"), // 合作者ID 17 | Key: beego.AppConfig.String("alikey"), // 合作者私钥 18 | ReturnUrl: "http://" + beego.AppConfig.String("domainurl") + "/alipay/return", // 同步返回地址 19 | NotifyUrl: "http://" + beego.AppConfig.String("domainurl") + "/alipay/notify", // 网站异步返回地址 20 | Email: beego.AppConfig.String("aliemail"), // 网站卖家邮箱地址 21 | } 22 | } 23 | 24 | func (this *AlipayController) Native() { 25 | orderNo := this.GetString("orderNo") //获取自己的订单号 26 | schemestr := this.Ctx.Input.Site() 27 | alipayClient := newClient() 28 | fee, _ := strconv.ParseFloat("100.5")//价格转换 29 | ots := alipay.Options{ 30 | OrderId: orderNo, 31 | Fee: float32(fee), 32 | NickName: "ricky", 33 | Subject: "基域站订单" + orderNo, 34 | Extra_common_param: schemestr, //加上自己需要用到的参数 35 | } 36 | form := alipayClient.Form(ots) 37 | res := map[string]interface{}{"form": form} 38 | this.Data["json"] = res 39 | this.ServeJSON() 40 | } 41 | 42 | func (this *AlipayController) Return() { 43 | alipayClient := newClient() 44 | result := alipayClient.Return(&this.Controller) 45 | //beego.Debug("notify", result) 46 | if result.Status == 1 { //付款成功,处理订单 47 | //处理订单 48 | if result.Extra_common_param != "" { 49 | url := typestr[1] + "/order/detail/" + result.OrderNo 50 | this.Ctx.Redirect(302, url) 51 | } 52 | } else { 53 | res := map[string]interface{}{"msg": "来源验证失败"} 54 | this.Data["json"] = res 55 | this.ServeJSON() 56 | } 57 | } 58 | 59 | func (this *AlipayController) Notify() { 60 | alipayClient := newClient() 61 | result := alipayClient.Notify(&this.Controller) 62 | 63 | timetest := this.GetString("gmt_payment") 64 | if result.Status == 1 { //付款成功,处理订单 65 | sendData := make(map[string]interface{}) 66 | sendData["id"] = result.OrderNo 67 | sendData["trade_no"] = result.TradeNo 68 | sendData["paid_time"] = timetest 69 | sendData["payment_type"] = "alipay" 70 | sendData["payment_amount"] = result.TotalFee 71 | //这里处理自己的业务逻辑 72 | if result.Extra_common_param != "" { 73 | //your method 例如修改数据库中订单的状态为付款。。 74 | } 75 | 76 | } 77 | } -------------------------------------------------------------------------------- /vendor/alipay/readme.md: -------------------------------------------------------------------------------- 1 | # golang版支付宝SDK! 2 | 3 | 做最好的支付宝sdk,大家的支持就是作者最大的动力! 4 | 5 | ## 安装 6 | 7 | ~~~ go 8 | go get github.com/ascoders/alipay 9 | ~~~ 10 | 11 | ## 初始化 12 | 13 | ~~~ go 14 | alipay := alipay.Client{ 15 | Partner : "", // 合作者ID 16 | Key : "", // 合作者私钥 17 | ReturnUrl : "", // 同步返回地址 18 | NotifyUrl : "", // 网站异步返回地址 19 | Email : "", // 网站卖家邮箱地址 20 | } 21 | ~~~ 22 | 23 | alipay升级到2.0版本,[api_v1.0](doc/v1.md)依旧兼容 24 | 25 | ## 生成提交表单 26 | 27 | ~~~ go 28 | form := alipay.Form(alipay.Options{ 29 | OrderId: "123", // 唯一订单号 30 | Fee: 99.8, // 价格 31 | NickName: "翱翔大空", // 用户昵称,支付页面显示用 32 | Subject: "充值100", // 支付描述,支付页面显示用 33 | }) 34 | ~~~ 35 | 36 | 将form输出到页面上,会自动跳转至支付宝收银台。 37 | 38 | ## 回调处理 39 | 40 | 回调分为`同步`和`异步`,支付成功后跳转至商户页面成为`同步回调`,跳转地址在`ReturnUrl`参数中配置。支付宝每隔10 10 30 ... 秒发送一次异步请求称之为`异步回调`,通知地址在`NotifyUrl`中配置。 41 | 42 | #### 同步回调(依赖beego) 43 | 44 | 注意这里需要解析get请求参数,为了自动获取,请传入beego的`&this.Controller` 45 | 46 | ~~~ go 47 | func (this *ApiController) Return() { 48 | result := alipay.Return(&this.Controller) 49 | if result.Status == 1 { //付款成功,处理订单 50 | //处理订单 51 | } 52 | } 53 | ~~~ 54 | 55 | 参数: 56 | 57 | - Partner // 合作者ID 58 | - Key // 合作者私钥 59 | - ReturnUrl // 同步返回地址 60 | - NotifyUrl // 网站异步返回地址 61 | - Email // 网站卖家邮箱地址 62 | 63 | 至于为什么没有给充值金额参数,因为金额不能代表问题,例如打折或者做活动,请自行查询订单表,根据业务逻辑进行处理。 64 | 65 | #### 同步回调(无依赖) 66 | 67 | 感谢@atnet提供的代码,让alipay支持原生http请求,脱离推框架的依赖。 68 | 69 | ~~~ go 70 | func ReturnHandle(w http.ResponseWriter, r *http.Request) { 71 | result := alipay.NativeReturn(r) 72 | if result.Status == 1 { //付款成功,处理订单 73 | //处理订单 74 | } 75 | } 76 | ~~~ 77 | 78 | #### 异步回调(依赖beego) 79 | 80 | ~~~ go 81 | func (this *ApiController) Notify() { 82 | result := alipay.Notify(&this.Controller) 83 | if result.Status == 1 { //付款成功,处理订单 84 | //处理订单 85 | } 86 | } 87 | ~~~ 88 | 89 | > 异步回调的返回参数与同步回调相同。 90 | 91 | #### 异步回调(无依赖) 92 | 93 | ~~~ go 94 | func NotifyHandle(w http.ResponseWriter, r *http.Request) { 95 | result := alipay.NativeNotify(r) 96 | if result.Status == 1 { //付款成功,处理订单 97 | //处理订单 98 | } 99 | } 100 | ~~~ 101 | 102 | ## 支付流程介绍 103 | 104 | 支付宝流程基本就四步:初始化、构造请求用户付款、同步跳转付款、异步post接收付款请求。 105 | 106 | 用户在您的网站点击支付按钮后,您的网站需要经历下述操作: 107 | 108 | 1.为该用户生成一个订单存入数据库,保存了该订单的**唯一标识符**,它可以是数据库的唯一id;该订单触发用户id;该订单充值用户id;充值金额;订单创建时间。上述是最基本要存储的订单数据。 109 | 110 | 2.调用此SDK让用户跳转到支付宝付款页面。 111 | 112 | 3.等待用户完成付款,此时如果用户没有关闭付款页面,大约3秒后会跳转到你网站指定的**同步回调页面**,如果用户关闭了网页,支付宝也会多次异步通知你的**异步回调页面**,这一切都是为了告诉你用户完成了付款。 113 | 114 | 4.处理订单,在**同步回调页面**和**异步回调页面**调用此SDK,获取该订单ID,在数据库中查出并给相应账号充值(之后发邮件通知等等),一定要注意防止订单**重复充值**,你可以标记订单的active解决此问题。 -------------------------------------------------------------------------------- /vendor/odeke-em/gf256/blog_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // This file contains a straightforward implementation of 6 | // Reed-Solomon encoding, along with a benchmark. 7 | // It goes with http://research.swtch.com/field. 8 | // 9 | // For an optimized implementation, see gf256.go. 10 | 11 | package gf256 12 | 13 | import ( 14 | "bytes" 15 | "fmt" 16 | "testing" 17 | ) 18 | 19 | // BlogECC writes to check the error correcting code bytes 20 | // for data using the given Reed-Solomon parameters. 21 | func BlogECC(rs *RSEncoder, m []byte, check []byte) { 22 | if len(check) < rs.c { 23 | panic("gf256: invalid check byte length") 24 | } 25 | if rs.c == 0 { 26 | return 27 | } 28 | 29 | // The check bytes are the remainder after dividing 30 | // data padded with c zeros by the generator polynomial. 31 | 32 | // p = data padded with c zeros. 33 | var p []byte 34 | n := len(m) + rs.c 35 | if len(rs.p) >= n { 36 | p = rs.p 37 | } else { 38 | p = make([]byte, n) 39 | } 40 | copy(p, m) 41 | for i := len(m); i < len(p); i++ { 42 | p[i] = 0 43 | } 44 | 45 | gen := rs.gen 46 | 47 | // Divide p by gen, leaving the remainder in p[len(data):]. 48 | // p[0] is the most significant term in p, and 49 | // gen[0] is the most significant term in the generator. 50 | for i := 0; i < len(m); i++ { 51 | k := f.Mul(p[i], f.Inv(gen[0])) // k = pi / g0 52 | // p -= k·g 53 | for j, g := range gen { 54 | p[i+j] = f.Add(p[i+j], f.Mul(k, g)) 55 | } 56 | } 57 | 58 | copy(check, p[len(m):]) 59 | rs.p = p 60 | } 61 | 62 | func BenchmarkBlogECC(b *testing.B) { 63 | data := []byte{0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11} 64 | check := []byte{0x29, 0x41, 0xb3, 0x93, 0x8, 0xe8, 0xa3, 0xe7, 0x63, 0x8f} 65 | out := make([]byte, len(check)) 66 | rs := NewRSEncoder(f, len(check)) 67 | for i := 0; i < b.N; i++ { 68 | BlogECC(rs, data, out) 69 | } 70 | b.SetBytes(int64(len(data))) 71 | if !bytes.Equal(out, check) { 72 | fmt.Printf("have %#v want %#v\n", out, check) 73 | } 74 | } 75 | 76 | func TestBlogECC(t *testing.T) { 77 | data := []byte{0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11} 78 | check := []byte{0xa5, 0x24, 0xd4, 0xc1, 0xed, 0x36, 0xc7, 0x87, 0x2c, 0x55} 79 | out := make([]byte, len(check)) 80 | rs := NewRSEncoder(f, len(check)) 81 | BlogECC(rs, data, out) 82 | if !bytes.Equal(out, check) { 83 | t.Errorf("have %x want %x", out, check) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /vendor/odeke-em/qr/qr.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | Package qr encodes QR codes. 7 | */ 8 | package qr 9 | 10 | import ( 11 | "errors" 12 | "image" 13 | "image/color" 14 | 15 | "odeke-em/qr/coding" 16 | ) 17 | 18 | // A Level denotes a QR error correction level. 19 | // From least to most tolerant of errors, they are L, M, Q, H. 20 | type Level int 21 | 22 | const ( 23 | L Level = iota // 20% redundant 24 | M // 38% redundant 25 | Q // 55% redundant 26 | H // 65% redundant 27 | ) 28 | 29 | // Encode returns an encoding of text at the given error correction level. 30 | func Encode(text string, level Level) (*Code, error) { 31 | // Pick data encoding, smallest first. 32 | // We could split the string and use different encodings 33 | // but that seems like overkill for now. 34 | var enc coding.Encoding 35 | switch { 36 | case coding.Num(text).Check() == nil: 37 | enc = coding.Num(text) 38 | case coding.Alpha(text).Check() == nil: 39 | enc = coding.Alpha(text) 40 | default: 41 | enc = coding.String(text) 42 | } 43 | 44 | // Pick size. 45 | l := coding.Level(level) 46 | var v coding.Version 47 | for v = coding.MinVersion; ; v++ { 48 | if v > coding.MaxVersion { 49 | return nil, errors.New("text too long to encode as QR") 50 | } 51 | if enc.Bits(v) <= v.DataBytes(l)*8 { 52 | break 53 | } 54 | } 55 | 56 | // Build and execute plan. 57 | p, err := coding.NewPlan(v, l, 0) 58 | if err != nil { 59 | return nil, err 60 | } 61 | cc, err := p.Encode(enc) 62 | if err != nil { 63 | return nil, err 64 | } 65 | 66 | // TODO: Pick appropriate mask. 67 | 68 | return &Code{cc.Bitmap, cc.Size, cc.Stride, 8}, nil 69 | } 70 | 71 | // A Code is a square pixel grid. 72 | // It implements image.Image and direct PNG encoding. 73 | type Code struct { 74 | Bitmap []byte // 1 is black, 0 is white 75 | Size int // number of pixels on a side 76 | Stride int // number of bytes per row 77 | Scale int // number of image pixels per QR pixel 78 | } 79 | 80 | // Black returns true if the pixel at (x,y) is black. 81 | func (c *Code) Black(x, y int) bool { 82 | return 0 <= x && x < c.Size && 0 <= y && y < c.Size && 83 | c.Bitmap[y*c.Stride+x/8]&(1<这种,所以这里需要replace一下 73 | str_req = strings.Replace(str_req, "OrderqueryReq", "xml", -1) 74 | bytes_req = []byte(str_req) 75 | 76 | //发送unified order请求. 77 | req, err := http.NewRequest("POST", query_order_req, bytes.NewReader(bytes_req)) 78 | if err != nil { 79 | fmt.Println("New Http Request发生错误,原因:", err) 80 | return xmlResp 81 | } 82 | req.Header.Set("Accept", "application/xml") 83 | //这里的http header的设置是必须设置的. 84 | req.Header.Set("Content-Type", "application/xml;charset=utf-8") 85 | 86 | c := http.Client{} 87 | resp, _err := c.Do(req) 88 | if _err != nil { 89 | fmt.Println("请求微信支付查询发送错误, 原因:", _err) 90 | return xmlResp 91 | } 92 | 93 | //xmlResp :=UnifyOrderResp{} 94 | body, _ := ioutil.ReadAll(resp.Body) 95 | _err = xml.Unmarshal(body, &xmlResp) 96 | if xmlResp.Return_code == "FAIL" { 97 | fmt.Println("微信支付统查询不成功,原因:", xmlResp.Return_msg) 98 | return xmlResp 99 | } 100 | //beego.Debug("xmlResp",xmlResp) 101 | 102 | return xmlResp 103 | 104 | } 105 | -------------------------------------------------------------------------------- /vendor/odeke-em/qr/coding/qr_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package coding 6 | 7 | import ( 8 | "bytes" 9 | "testing" 10 | 11 | "odeke-em/gf256" 12 | "github.com/odeke-em//rsc/qr/libqrencode" 13 | ) 14 | 15 | func test(t *testing.T, v Version, l Level, text ...Encoding) bool { 16 | s := "" 17 | ty := libqrencode.EightBit 18 | switch x := text[0].(type) { 19 | case String: 20 | s = string(x) 21 | case Alpha: 22 | s = string(x) 23 | ty = libqrencode.Alphanumeric 24 | case Num: 25 | s = string(x) 26 | ty = libqrencode.Numeric 27 | } 28 | key, err := libqrencode.Encode(libqrencode.Version(v), libqrencode.Level(l), ty, s) 29 | if err != nil { 30 | t.Errorf("libqrencode.Encode(%v, %v, %d, %#q): %v", v, l, ty, s, err) 31 | return false 32 | } 33 | mask := (^key.Pixel[8][2]&1)<<2 | (key.Pixel[8][3]&1)<<1 | (^key.Pixel[8][4] & 1) 34 | p, err := NewPlan(v, l, Mask(mask)) 35 | if err != nil { 36 | t.Errorf("NewPlan(%v, L, %d): %v", v, err, mask) 37 | return false 38 | } 39 | if len(p.Pixel) != len(key.Pixel) { 40 | t.Errorf("%v: NewPlan uses %dx%d, libqrencode uses %dx%d", v, len(p.Pixel), len(p.Pixel), len(key.Pixel), len(key.Pixel)) 41 | return false 42 | } 43 | c, err := p.Encode(text...) 44 | if err != nil { 45 | t.Errorf("Encode: %v", err) 46 | return false 47 | } 48 | badpix := 0 49 | Pixel: 50 | for y, prow := range p.Pixel { 51 | for x, pix := range prow { 52 | pix &^= Black 53 | if c.Black(x, y) { 54 | pix |= Black 55 | } 56 | 57 | keypix := key.Pixel[y][x] 58 | want := Pixel(0) 59 | switch { 60 | case keypix&libqrencode.Finder != 0: 61 | want = Position.Pixel() 62 | case keypix&libqrencode.Alignment != 0: 63 | want = Alignment.Pixel() 64 | case keypix&libqrencode.Timing != 0: 65 | want = Timing.Pixel() 66 | case keypix&libqrencode.Format != 0: 67 | want = Format.Pixel() 68 | want |= OffsetPixel(pix.Offset()) // sic 69 | want |= pix & Invert 70 | case keypix&libqrencode.PVersion != 0: 71 | want = PVersion.Pixel() 72 | case keypix&libqrencode.DataECC != 0: 73 | if pix.Role() == Check || pix.Role() == Extra { 74 | want = pix.Role().Pixel() 75 | } else { 76 | want = Data.Pixel() 77 | } 78 | want |= OffsetPixel(pix.Offset()) 79 | want |= pix & Invert 80 | default: 81 | want = Unused.Pixel() 82 | } 83 | if keypix&libqrencode.Black != 0 { 84 | want |= Black 85 | } 86 | if pix != want { 87 | t.Errorf("%v/%v: Pixel[%d][%d] = %v, want %v %#x", v, mask, y, x, pix, want, keypix) 88 | if badpix++; badpix >= 100 { 89 | t.Errorf("stopping after %d bad pixels", badpix) 90 | break Pixel 91 | } 92 | } 93 | } 94 | } 95 | return badpix == 0 96 | } 97 | 98 | var input = []Encoding{ 99 | String("hello"), 100 | Num("1"), 101 | Num("12"), 102 | Num("123"), 103 | Alpha("AB"), 104 | Alpha("ABC"), 105 | } 106 | 107 | func TestVersion(t *testing.T) { 108 | badvers := 0 109 | Version: 110 | for v := Version(1); v <= 40; v++ { 111 | for l := L; l <= H; l++ { 112 | for _, in := range input { 113 | if !test(t, v, l, in) { 114 | if badvers++; badvers >= 10 { 115 | t.Errorf("stopping after %d bad versions", badvers) 116 | break Version 117 | } 118 | } 119 | } 120 | } 121 | } 122 | } 123 | 124 | func TestEncode(t *testing.T) { 125 | data := []byte{0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11} 126 | check := []byte{0xa5, 0x24, 0xd4, 0xc1, 0xed, 0x36, 0xc7, 0x87, 0x2c, 0x55} 127 | rs := gf256.NewRSEncoder(Field, len(check)) 128 | out := make([]byte, len(check)) 129 | rs.ECC(data, out) 130 | if !bytes.Equal(out, check) { 131 | t.Errorf("have %x want %x", out, check) 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /vendor/odeke-em/qr/libqrencode/qrencode.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package libqrencode wraps the C libqrencode library. 6 | // The qr package (in this package's parent directory) 7 | // does not use any C wrapping. This code is here only 8 | // for use during that package's tests. 9 | package libqrencode 10 | 11 | /* 12 | #cgo LDFLAGS: -lqrencode 13 | #include 14 | */ 15 | import "C" 16 | 17 | import ( 18 | "fmt" 19 | "image" 20 | "image/color" 21 | "unsafe" 22 | ) 23 | 24 | type Version int 25 | 26 | type Mode int 27 | 28 | const ( 29 | Numeric Mode = C.QR_MODE_NUM 30 | Alphanumeric Mode = C.QR_MODE_AN 31 | EightBit Mode = C.QR_MODE_8 32 | ) 33 | 34 | type Level int 35 | 36 | const ( 37 | L Level = C.QR_ECLEVEL_L 38 | M Level = C.QR_ECLEVEL_M 39 | Q Level = C.QR_ECLEVEL_Q 40 | H Level = C.QR_ECLEVEL_H 41 | ) 42 | 43 | type Pixel int 44 | 45 | const ( 46 | Black Pixel = 1 << iota 47 | DataECC 48 | Format 49 | PVersion 50 | Timing 51 | Alignment 52 | Finder 53 | NonData 54 | ) 55 | 56 | type Code struct { 57 | Version int 58 | Width int 59 | Pixel [][]Pixel 60 | Scale int 61 | } 62 | 63 | func (*Code) ColorModel() color.Model { 64 | return color.RGBAModel 65 | } 66 | 67 | func (c *Code) Bounds() image.Rectangle { 68 | d := (c.Width + 8) * c.Scale 69 | return image.Rect(0, 0, d, d) 70 | } 71 | 72 | var ( 73 | white color.Color = color.RGBA{0xFF, 0xFF, 0xFF, 0xFF} 74 | black color.Color = color.RGBA{0x00, 0x00, 0x00, 0xFF} 75 | blue color.Color = color.RGBA{0x00, 0x00, 0x80, 0xFF} 76 | red color.Color = color.RGBA{0xFF, 0x40, 0x40, 0xFF} 77 | yellow color.Color = color.RGBA{0xFF, 0xFF, 0x00, 0xFF} 78 | gray color.Color = color.RGBA{0x80, 0x80, 0x80, 0xFF} 79 | green color.Color = color.RGBA{0x22, 0x8B, 0x22, 0xFF} 80 | ) 81 | 82 | func (c *Code) At(x, y int) color.Color { 83 | x = x/c.Scale - 4 84 | y = y/c.Scale - 4 85 | if 0 <= x && x < c.Width && 0 <= y && y < c.Width { 86 | switch p := c.Pixel[y][x]; { 87 | case p&Black == 0: 88 | // nothing 89 | case p&DataECC != 0: 90 | return black 91 | case p&Format != 0: 92 | return blue 93 | case p&PVersion != 0: 94 | return red 95 | case p&Timing != 0: 96 | return yellow 97 | case p&Alignment != 0: 98 | return gray 99 | case p&Finder != 0: 100 | return green 101 | } 102 | } 103 | return white 104 | } 105 | 106 | type Chunk struct { 107 | Mode Mode 108 | Text string 109 | } 110 | 111 | func Encode(version Version, level Level, mode Mode, text string) (*Code, error) { 112 | return EncodeChunk(version, level, Chunk{mode, text}) 113 | } 114 | 115 | func EncodeChunk(version Version, level Level, chunk ...Chunk) (*Code, error) { 116 | qi, err := C.QRinput_new2(C.int(version), C.QRecLevel(level)) 117 | if qi == nil { 118 | return nil, fmt.Errorf("QRinput_new2: %v", err) 119 | } 120 | defer C.QRinput_free(qi) 121 | for _, ch := range chunk { 122 | data := []byte(ch.Text) 123 | n, err := C.QRinput_append(qi, C.QRencodeMode(ch.Mode), C.int(len(data)), (*C.uchar)(&data[0])) 124 | if n < 0 { 125 | return nil, fmt.Errorf("QRinput_append %q: %v", data, err) 126 | } 127 | } 128 | 129 | qc, err := C.QRcode_encodeInput(qi) 130 | if qc == nil { 131 | return nil, fmt.Errorf("QRinput_encodeInput: %v", err) 132 | } 133 | 134 | c := &Code{ 135 | Version: int(qc.version), 136 | Width: int(qc.width), 137 | Scale: 16, 138 | } 139 | pix := make([]Pixel, c.Width*c.Width) 140 | cdat := (*[1000 * 1000]byte)(unsafe.Pointer(qc.data))[:len(pix)] 141 | for i := range pix { 142 | pix[i] = Pixel(cdat[i]) 143 | } 144 | c.Pixel = make([][]Pixel, c.Width) 145 | for i := range c.Pixel { 146 | c.Pixel[i] = pix[i*c.Width : (i+1)*c.Width] 147 | } 148 | return c, nil 149 | } 150 | -------------------------------------------------------------------------------- /models/Wxpay/WXPayNotifyReq.go: -------------------------------------------------------------------------------- 1 | package Wxpay 2 | 3 | import ( 4 | _ "bytes" 5 | _ "crypto/md5" 6 | _ "crypto/rand" 7 | _ "encoding/hex" 8 | "encoding/xml" 9 | "fmt" 10 | "io/ioutil" 11 | _ "net/http" 12 | _ "reflect" 13 | _ "sort" 14 | _ "strconv" 15 | "strings" 16 | _ "time" 17 | 18 | _ "KiWiFi/gate.cloud/models" 19 | _ "gatecloud" 20 | "github.com/astaxie/beego" 21 | "github.com/astaxie/beego/context" 22 | _ "github.com/bitly/go-simplejson" 23 | //"encoding/json" 24 | ) 25 | 26 | type WXPayNotifyReq struct { 27 | Return_code string `xml:"return_code"` 28 | Return_msg string `xml:"return_msg"` 29 | Appid string `xml:"appid"` 30 | Mch_id string `xml:"mch_id"` 31 | Nonce string `xml:"nonce_str"` 32 | Sign string `xml:"sign"` 33 | Result_code string `xml:"result_code"` 34 | Openid string `xml:"openid"` 35 | Is_subscribe string `xml:"is_subscribe"` 36 | Trade_type string `xml:"trade_type"` 37 | Bank_type string `xml:"bank_type"` 38 | Total_fee int `xml:"total_fee"` 39 | Fee_type string `xml:"fee_type"` 40 | Cash_fee int `xml:"cash_fee"` 41 | Cash_fee_Type string `xml:"cash_fee_type"` 42 | Transaction_id string `xml:"transaction_id"` 43 | Out_trade_no string `xml:"out_trade_no"` 44 | Attach string `xml:"attach"` 45 | Time_end string `xml:"time_end"` 46 | } 47 | 48 | type WXPayNotifyResp struct { 49 | Return_code string `xml:"return_code"` 50 | Return_msg string `xml:"return_msg"` 51 | } 52 | 53 | func (o *WXPayNotifyReq) WxpayCallback(ctx *context.Context) map[string]interface{} { 54 | 55 | body, err := ioutil.ReadAll(ctx.Input.Context.Request.Body) 56 | if err != nil { 57 | fmt.Println("读取http body失败,原因!", err) 58 | } 59 | fmt.Println("微信支付异步通知,HTTP Body:", string(body)) 60 | var mr WXPayNotifyReq 61 | err = xml.Unmarshal(body, &mr) 62 | if err != nil { 63 | fmt.Println("解析HTTP Body格式到xml失败,原因!", err) 64 | } 65 | 66 | //beego.Debug("body",body) 67 | 68 | var reqMap map[string]interface{} 69 | reqMap = make(map[string]interface{}, 0) 70 | 71 | reqMap["return_code"] = mr.Return_code 72 | reqMap["return_msg"] = mr.Return_msg 73 | reqMap["appid"] = mr.Appid 74 | reqMap["mch_id"] = mr.Mch_id 75 | reqMap["nonce_str"] = mr.Nonce 76 | reqMap["result_code"] = mr.Result_code 77 | reqMap["openid"] = mr.Openid 78 | reqMap["is_subscribe"] = mr.Is_subscribe 79 | reqMap["trade_type"] = mr.Trade_type 80 | reqMap["bank_type"] = mr.Bank_type 81 | reqMap["total_fee"] = mr.Total_fee 82 | reqMap["fee_type"] = mr.Fee_type 83 | reqMap["cash_fee"] = mr.Cash_fee 84 | reqMap["cash_fee_type"] = mr.Cash_fee_Type 85 | reqMap["transaction_id"] = mr.Transaction_id 86 | reqMap["out_trade_no"] = mr.Out_trade_no 87 | reqMap["attach"] = mr.Attach 88 | reqMap["time_end"] = mr.Time_end 89 | 90 | var resp WXPayNotifyResp 91 | 92 | //进行签名校验 93 | if WxpayVerifySign(reqMap, mr.Sign) { 94 | //这里就可以更新我们的后台数据库了,其他业务逻辑同理。 95 | //beego.Debug("succes","succes") 96 | resp.Return_code = "SUCCESS" 97 | resp.Return_msg = "OK" 98 | ctx.WriteString("SUCCESS") 99 | return reqMap 100 | } else { 101 | resp.Return_code = "FAIL" 102 | resp.Return_msg = "failed to verify sign, please retry!" 103 | } 104 | 105 | //结果返回,微信要求如果成功需要返回return_code "SUCCESS" 106 | bytes, _err := xml.Marshal(resp) 107 | strResp := strings.Replace(string(bytes), "WXPayNotifyResp", "xml", -1) 108 | if _err != nil { 109 | fmt.Println("xml编码失败,原因:", _err) 110 | //return 111 | } 112 | ////beego.Debug("sa",strResp) 113 | ctx.WriteString(strResp) 114 | 115 | //c.Ctx.ResponseWriter.WriteHeader(200) 116 | //fmt.Fprint(c.Ctx.ResponseWriter, strResp) 117 | 118 | return reqMap 119 | } 120 | 121 | //微信支付签名验证函数 122 | func WxpayVerifySign(needVerifyM map[string]interface{}, sign string) bool { 123 | appkey := beego.AppConfig.String("wxappkey") 124 | signCalc := WxpayCalcSign(needVerifyM, appkey) 125 | 126 | //beego.Debug("计算出来的sign: ", signCalc) 127 | //beego.Debug("微信异步通知sign: ", sign) 128 | if sign == signCalc { 129 | fmt.Println("签名校验通过!") 130 | return true 131 | } 132 | 133 | fmt.Println("签名校验失败!") 134 | return false 135 | } -------------------------------------------------------------------------------- /vendor/odeke-em/qr/web/resize/resize.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package resize 6 | 7 | import ( 8 | "image" 9 | "image/color" 10 | ) 11 | 12 | // average convert the sums to averages and returns the result. 13 | func average(sum []uint64, w, h int, n uint64) *image.RGBA { 14 | ret := image.NewRGBA(image.Rect(0, 0, w, h)) 15 | for y := 0; y < h; y++ { 16 | for x := 0; x < w; x++ { 17 | index := 4 * (y*w + x) 18 | pix := ret.Pix[y*ret.Stride+x*4:] 19 | pix[0] = uint8(sum[index+0] / n) 20 | pix[1] = uint8(sum[index+1] / n) 21 | pix[2] = uint8(sum[index+2] / n) 22 | pix[3] = uint8(sum[index+3] / n) 23 | } 24 | } 25 | return ret 26 | } 27 | 28 | // ResizeRGBA returns a scaled copy of the RGBA image slice r of m. 29 | // The returned image has width w and height h. 30 | func ResizeRGBA(m *image.RGBA, r image.Rectangle, w, h int) *image.RGBA { 31 | ww, hh := uint64(w), uint64(h) 32 | dx, dy := uint64(r.Dx()), uint64(r.Dy()) 33 | // See comment in Resize. 34 | n, sum := dx*dy, make([]uint64, 4*w*h) 35 | for y := r.Min.Y; y < r.Max.Y; y++ { 36 | pix := m.Pix[(y-r.Min.Y)*m.Stride:] 37 | for x := r.Min.X; x < r.Max.X; x++ { 38 | // Get the source pixel. 39 | p := pix[(x-r.Min.X)*4:] 40 | r64 := uint64(p[0]) 41 | g64 := uint64(p[1]) 42 | b64 := uint64(p[2]) 43 | a64 := uint64(p[3]) 44 | // Spread the source pixel over 1 or more destination rows. 45 | py := uint64(y) * hh 46 | for remy := hh; remy > 0; { 47 | qy := dy - (py % dy) 48 | if qy > remy { 49 | qy = remy 50 | } 51 | // Spread the source pixel over 1 or more destination columns. 52 | px := uint64(x) * ww 53 | index := 4 * ((py/dy)*ww + (px / dx)) 54 | for remx := ww; remx > 0; { 55 | qx := dx - (px % dx) 56 | if qx > remx { 57 | qx = remx 58 | } 59 | qxy := qx * qy 60 | sum[index+0] += r64 * qxy 61 | sum[index+1] += g64 * qxy 62 | sum[index+2] += b64 * qxy 63 | sum[index+3] += a64 * qxy 64 | index += 4 65 | px += qx 66 | remx -= qx 67 | } 68 | py += qy 69 | remy -= qy 70 | } 71 | } 72 | } 73 | return average(sum, w, h, n) 74 | } 75 | 76 | // ResizeNRGBA returns a scaled copy of the RGBA image slice r of m. 77 | // The returned image has width w and height h. 78 | func ResizeNRGBA(m *image.NRGBA, r image.Rectangle, w, h int) *image.RGBA { 79 | ww, hh := uint64(w), uint64(h) 80 | dx, dy := uint64(r.Dx()), uint64(r.Dy()) 81 | // See comment in Resize. 82 | n, sum := dx*dy, make([]uint64, 4*w*h) 83 | for y := r.Min.Y; y < r.Max.Y; y++ { 84 | pix := m.Pix[(y-r.Min.Y)*m.Stride:] 85 | for x := r.Min.X; x < r.Max.X; x++ { 86 | // Get the source pixel. 87 | p := pix[(x-r.Min.X)*4:] 88 | r64 := uint64(p[0]) 89 | g64 := uint64(p[1]) 90 | b64 := uint64(p[2]) 91 | a64 := uint64(p[3]) 92 | r64 = (r64 * a64) / 255 93 | g64 = (g64 * a64) / 255 94 | b64 = (b64 * a64) / 255 95 | // Spread the source pixel over 1 or more destination rows. 96 | py := uint64(y) * hh 97 | for remy := hh; remy > 0; { 98 | qy := dy - (py % dy) 99 | if qy > remy { 100 | qy = remy 101 | } 102 | // Spread the source pixel over 1 or more destination columns. 103 | px := uint64(x) * ww 104 | index := 4 * ((py/dy)*ww + (px / dx)) 105 | for remx := ww; remx > 0; { 106 | qx := dx - (px % dx) 107 | if qx > remx { 108 | qx = remx 109 | } 110 | qxy := qx * qy 111 | sum[index+0] += r64 * qxy 112 | sum[index+1] += g64 * qxy 113 | sum[index+2] += b64 * qxy 114 | sum[index+3] += a64 * qxy 115 | index += 4 116 | px += qx 117 | remx -= qx 118 | } 119 | py += qy 120 | remy -= qy 121 | } 122 | } 123 | } 124 | return average(sum, w, h, n) 125 | } 126 | 127 | // Resample returns a resampled copy of the image slice r of m. 128 | // The returned image has width w and height h. 129 | func Resample(m image.Image, r image.Rectangle, w, h int) *image.RGBA { 130 | if w < 0 || h < 0 { 131 | return nil 132 | } 133 | if w == 0 || h == 0 || r.Dx() <= 0 || r.Dy() <= 0 { 134 | return image.NewRGBA(image.Rect(0, 0, w, h)) 135 | } 136 | curw, curh := r.Dx(), r.Dy() 137 | img := image.NewRGBA(image.Rect(0, 0, w, h)) 138 | for y := 0; y < h; y++ { 139 | for x := 0; x < w; x++ { 140 | // Get a source pixel. 141 | subx := x * curw / w 142 | suby := y * curh / h 143 | r32, g32, b32, a32 := m.At(subx, suby).RGBA() 144 | r := uint8(r32 >> 8) 145 | g := uint8(g32 >> 8) 146 | b := uint8(b32 >> 8) 147 | a := uint8(a32 >> 8) 148 | img.SetRGBA(x, y, color.RGBA{r, g, b, a}) 149 | } 150 | } 151 | return img 152 | } 153 | -------------------------------------------------------------------------------- /vendor/odeke-em/gf256/gf256_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gf256 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "testing" 11 | ) 12 | 13 | var f = NewField(0x11d, 2) // x^8 + x^4 + x^3 + x^2 + 1 14 | 15 | func TestBasic(t *testing.T) { 16 | if f.Exp(0) != 1 || f.Exp(1) != 2 || f.Exp(255) != 1 { 17 | panic("bad Exp") 18 | } 19 | } 20 | 21 | func TestECC(t *testing.T) { 22 | data := []byte{0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11} 23 | check := []byte{0xa5, 0x24, 0xd4, 0xc1, 0xed, 0x36, 0xc7, 0x87, 0x2c, 0x55} 24 | out := make([]byte, len(check)) 25 | rs := NewRSEncoder(f, len(check)) 26 | rs.ECC(data, out) 27 | if !bytes.Equal(out, check) { 28 | t.Errorf("have %x want %x", out, check) 29 | } 30 | } 31 | 32 | func TestLinear(t *testing.T) { 33 | d1 := []byte{0x00, 0x00} 34 | c1 := []byte{0x00, 0x00} 35 | out := make([]byte, len(c1)) 36 | rs := NewRSEncoder(f, len(c1)) 37 | if rs.ECC(d1, out); !bytes.Equal(out, c1) { 38 | t.Errorf("ECBytes(%x, %d) = %x, want 0", d1, len(c1), out) 39 | } 40 | d2 := []byte{0x00, 0x01} 41 | c2 := make([]byte, 2) 42 | rs.ECC(d2, c2) 43 | d3 := []byte{0x00, 0x02} 44 | c3 := make([]byte, 2) 45 | rs.ECC(d3, c3) 46 | cx := make([]byte, 2) 47 | for i := range cx { 48 | cx[i] = c2[i] ^ c3[i] 49 | } 50 | d4 := []byte{0x00, 0x03} 51 | c4 := make([]byte, 2) 52 | rs.ECC(d4, c4) 53 | if !bytes.Equal(cx, c4) { 54 | t.Errorf("ECBytes(%x, 2) = %x\nECBytes(%x, 2) = %x\nxor = %x\nECBytes(%x, 2) = %x", 55 | d2, c2, d3, c3, cx, d4, c4) 56 | } 57 | } 58 | 59 | func TestGaussJordan(t *testing.T) { 60 | rs := NewRSEncoder(f, 2) 61 | m := make([][]byte, 16) 62 | for i := range m { 63 | m[i] = make([]byte, 4) 64 | m[i][i/8] = 1 << uint(i%8) 65 | rs.ECC(m[i][:2], m[i][2:]) 66 | } 67 | if false { 68 | fmt.Printf("---\n") 69 | for _, row := range m { 70 | fmt.Printf("%x\n", row) 71 | } 72 | } 73 | b := []uint{0, 1, 2, 3, 12, 13, 14, 15, 20, 21, 22, 23, 24, 25, 26, 27} 74 | for i := 0; i < 16; i++ { 75 | bi := b[i] 76 | if m[i][bi/8]&(1<<(7-bi%8)) == 0 { 77 | for j := i + 1; ; j++ { 78 | if j >= len(m) { 79 | t.Errorf("lost track for %d", bi) 80 | break 81 | } 82 | if m[j][bi/8]&(1<<(7-bi%8)) != 0 { 83 | m[i], m[j] = m[j], m[i] 84 | break 85 | } 86 | } 87 | } 88 | for j := i + 1; j < len(m); j++ { 89 | if m[j][bi/8]&(1<<(7-bi%8)) != 0 { 90 | for k := range m[j] { 91 | m[j][k] ^= m[i][k] 92 | } 93 | } 94 | } 95 | } 96 | if false { 97 | fmt.Printf("---\n") 98 | for _, row := range m { 99 | fmt.Printf("%x\n", row) 100 | } 101 | } 102 | for i := 15; i >= 0; i-- { 103 | bi := b[i] 104 | for j := i - 1; j >= 0; j-- { 105 | if m[j][bi/8]&(1<<(7-bi%8)) != 0 { 106 | for k := range m[j] { 107 | m[j][k] ^= m[i][k] 108 | } 109 | } 110 | } 111 | } 112 | if false { 113 | fmt.Printf("---\n") 114 | for _, row := range m { 115 | fmt.Printf("%x", row) 116 | out := make([]byte, 2) 117 | if rs.ECC(row[:2], out); !bytes.Equal(out, row[2:]) { 118 | fmt.Printf(" - want %x", out) 119 | } 120 | fmt.Printf("\n") 121 | } 122 | } 123 | } 124 | 125 | func BenchmarkECC(b *testing.B) { 126 | data := []byte{0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11} 127 | check := []byte{0x29, 0x41, 0xb3, 0x93, 0x8, 0xe8, 0xa3, 0xe7, 0x63, 0x8f} 128 | out := make([]byte, len(check)) 129 | rs := NewRSEncoder(f, len(check)) 130 | for i := 0; i < b.N; i++ { 131 | rs.ECC(data, out) 132 | } 133 | b.SetBytes(int64(len(data))) 134 | if !bytes.Equal(out, check) { 135 | fmt.Printf("have %#v want %#v\n", out, check) 136 | } 137 | } 138 | 139 | func TestGen(t *testing.T) { 140 | for i := 0; i < 256; i++ { 141 | _, lg := f.gen(i) 142 | if lg[0] != 0 { 143 | t.Errorf("#%d: %x", i, lg) 144 | } 145 | } 146 | } 147 | 148 | func TestReducible(t *testing.T) { 149 | var count = []int{1, 2, 3, 6, 9, 18, 30, 56, 99, 186} // oeis.org/A1037 150 | for i, want := range count { 151 | n := 0 152 | for p := 1 << uint(i+2); p < 1<= 0x200 || reducible(poly) { 23 | panic("gf256: invalid polynomial: " + strconv.Itoa(poly)) 24 | } 25 | 26 | var f Field 27 | x := 1 28 | for i := 0; i < 255; i++ { 29 | if x == 1 && i != 0 { 30 | panic("gf256: invalid generator " + strconv.Itoa(α) + 31 | " for polynomial " + strconv.Itoa(poly)) 32 | } 33 | f.exp[i] = byte(x) 34 | f.exp[i+255] = byte(x) 35 | f.log[x] = byte(i) 36 | x = mul(x, α, poly) 37 | } 38 | f.log[0] = 255 39 | for i := 0; i < 255; i++ { 40 | if f.log[f.exp[i]] != byte(i) { 41 | panic("bad log") 42 | } 43 | if f.log[f.exp[i+255]] != byte(i) { 44 | panic("bad log") 45 | } 46 | } 47 | for i := 1; i < 256; i++ { 48 | if f.exp[f.log[i]] != byte(i) { 49 | panic("bad log") 50 | } 51 | } 52 | 53 | return &f 54 | } 55 | 56 | // nbit returns the number of significant in p. 57 | func nbit(p int) uint { 58 | n := uint(0) 59 | for ; p > 0; p >>= 1 { 60 | n++ 61 | } 62 | return n 63 | } 64 | 65 | // polyDiv divides the polynomial p by q and returns the remainder. 66 | func polyDiv(p, q int) int { 67 | np := nbit(p) 68 | nq := nbit(q) 69 | for ; np >= nq; np-- { 70 | if p&(1<<(np-1)) != 0 { 71 | p ^= q << (np - nq) 72 | } 73 | } 74 | return p 75 | } 76 | 77 | // mul returns the product x*y mod poly, a GF(256) multiplication. 78 | func mul(x, y, poly int) int { 79 | z := 0 80 | for x > 0 { 81 | if x&1 != 0 { 82 | z ^= y 83 | } 84 | x >>= 1 85 | y <<= 1 86 | if y&0x100 != 0 { 87 | y ^= poly 88 | } 89 | } 90 | return z 91 | } 92 | 93 | // reducible reports whether p is reducible. 94 | func reducible(p int) bool { 95 | // Multiplying n-bit * n-bit produces (2n-1)-bit, 96 | // so if p is reducible, one of its factors must be 97 | // of np/2+1 bits or fewer. 98 | np := nbit(p) 99 | for q := 2; q < 1<<(np/2+1); q++ { 100 | if polyDiv(p, q) == 0 { 101 | return true 102 | } 103 | } 104 | return false 105 | } 106 | 107 | // Add returns the sum of x and y in the field. 108 | func (f *Field) Add(x, y byte) byte { 109 | return x ^ y 110 | } 111 | 112 | // Exp returns the base-α exponential of e in the field. 113 | // If e < 0, Exp returns 0. 114 | func (f *Field) Exp(e int) byte { 115 | if e < 0 { 116 | return 0 117 | } 118 | return f.exp[e%255] 119 | } 120 | 121 | // Log returns the base-α logarithm of x in the field. 122 | // If x == 0, Log returns -1. 123 | func (f *Field) Log(x byte) int { 124 | if x == 0 { 125 | return -1 126 | } 127 | return int(f.log[x]) 128 | } 129 | 130 | // Inv returns the multiplicative inverse of x in the field. 131 | // If x == 0, Inv returns 0. 132 | func (f *Field) Inv(x byte) byte { 133 | if x == 0 { 134 | return 0 135 | } 136 | return f.exp[255-f.log[x]] 137 | } 138 | 139 | // Mul returns the product of x and y in the field. 140 | func (f *Field) Mul(x, y byte) byte { 141 | if x == 0 || y == 0 { 142 | return 0 143 | } 144 | return f.exp[int(f.log[x])+int(f.log[y])] 145 | } 146 | 147 | // An RSEncoder implements Reed-Solomon encoding 148 | // over a given field using a given number of error correction bytes. 149 | type RSEncoder struct { 150 | f *Field 151 | c int 152 | gen []byte 153 | lgen []byte 154 | p []byte 155 | } 156 | 157 | func (f *Field) gen(e int) (gen, lgen []byte) { 158 | // p = 1 159 | p := make([]byte, e+1) 160 | p[e] = 1 161 | 162 | for i := 0; i < e; i++ { 163 | // p *= (x + Exp(i)) 164 | // p[j] = p[j]*Exp(i) + p[j+1]. 165 | c := f.Exp(i) 166 | for j := 0; j < e; j++ { 167 | p[j] = f.Mul(p[j], c) ^ p[j+1] 168 | } 169 | p[e] = f.Mul(p[e], c) 170 | } 171 | 172 | // lp = log p. 173 | lp := make([]byte, e+1) 174 | for i, c := range p { 175 | if c == 0 { 176 | lp[i] = 255 177 | } else { 178 | lp[i] = byte(f.Log(c)) 179 | } 180 | } 181 | 182 | return p, lp 183 | } 184 | 185 | // NewRSEncoder returns a new Reed-Solomon encoder 186 | // over the given field and number of error correction bytes. 187 | func NewRSEncoder(f *Field, c int) *RSEncoder { 188 | gen, lgen := f.gen(c) 189 | return &RSEncoder{f: f, c: c, gen: gen, lgen: lgen} 190 | } 191 | 192 | // ECC writes to check the error correcting code bytes 193 | // for data using the given Reed-Solomon parameters. 194 | func (rs *RSEncoder) ECC(data []byte, check []byte) { 195 | if len(check) < rs.c { 196 | panic("gf256: invalid check byte length") 197 | } 198 | if rs.c == 0 { 199 | return 200 | } 201 | 202 | // The check bytes are the remainder after dividing 203 | // data padded with c zeros by the generator polynomial. 204 | 205 | // p = data padded with c zeros. 206 | var p []byte 207 | n := len(data) + rs.c 208 | if len(rs.p) >= n { 209 | p = rs.p 210 | } else { 211 | p = make([]byte, n) 212 | } 213 | copy(p, data) 214 | for i := len(data); i < len(p); i++ { 215 | p[i] = 0 216 | } 217 | 218 | // Divide p by gen, leaving the remainder in p[len(data):]. 219 | // p[0] is the most significant term in p, and 220 | // gen[0] is the most significant term in the generator, 221 | // which is always 1. 222 | // To avoid repeated work, we store various values as 223 | // lv, not v, where lv = log[v]. 224 | f := rs.f 225 | lgen := rs.lgen[1:] 226 | for i := 0; i < len(data); i++ { 227 | c := p[i] 228 | if c == 0 { 229 | continue 230 | } 231 | q := p[i+1:] 232 | exp := f.exp[f.log[c]:] 233 | for j, lg := range lgen { 234 | if lg != 255 { // lgen uses 255 for log 0 235 | q[j] ^= exp[lg] 236 | } 237 | } 238 | } 239 | copy(check, p[len(data):]) 240 | rs.p = p 241 | } 242 | -------------------------------------------------------------------------------- /models/Wxpay/UnifyOrderReq.go: -------------------------------------------------------------------------------- 1 | package Wxpay 2 | 3 | import ( 4 | "bytes" 5 | "crypto/md5" 6 | "crypto/rand" 7 | "encoding/hex" 8 | "encoding/xml" 9 | "fmt" 10 | "io/ioutil" 11 | "net/http" 12 | "sort" 13 | "strconv" 14 | "strings" 15 | "time" 16 | 17 | "github.com/astaxie/beego" 18 | "github.com/astaxie/beego/context" 19 | ) 20 | 21 | type UnifyOrderReq struct { 22 | Attach string `xml:"attach"` 23 | Appid string `xml:"appid"` 24 | Body string `xml:"body"` 25 | Detail string `xml:"detail"` 26 | Fee_type string `xml:"fee_type"` 27 | Goods_tag string `xml:"goods_tag"` 28 | Mch_id string `xml:"mch_id"` 29 | Nonce_str string `xml:"nonce_str"` 30 | Notify_url string `xml:"notify_url"` 31 | Product_id string `xml:"product_id"` 32 | Time_start string `xml:"time_start"` 33 | Time_expire string `xml:"time_expire"` 34 | Trade_type string `xml:"trade_type"` 35 | Spbill_create_ip string `xml:"spbill_create_ip"` 36 | Total_fee int `xml:"total_fee"` 37 | Out_trade_no string `xml:"out_trade_no"` 38 | Sign string `xml:"sign"` 39 | } 40 | 41 | type UnifyOrderResp struct { 42 | Return_code string `xml:"return_code"` 43 | Return_msg string `xml:"return_msg"` 44 | Attach string `xml:"attach"` 45 | Appid string `xml:"appid"` 46 | Mch_id string `xml:"mch_id"` 47 | Nonce_str string `xml:"nonce_str"` 48 | Sign string `xml:"sign"` 49 | Result_code string `xml:"result_code"` 50 | Prepay_id string `xml:"prepay_id"` 51 | Trade_type string `xml:"trade_type"` 52 | Code_url string `xml:"code_url"` 53 | } 54 | 55 | //因为我在国外,所以需要和国内时间转换一致 56 | func GetCurrentTime() time.Time { 57 | location := time.FixedZone("Asia/Shanghai", +8*60*60) 58 | abnow := time.Now().UTC() 59 | _now := abnow.In(location) 60 | return _now 61 | } 62 | func TimeConvert(span int) string { 63 | _now := GetCurrentTime() 64 | if span == 2 { 65 | _now = _now.Add(time.Hour * 2) 66 | } 67 | return _now.Format("20060102150405") 68 | } 69 | 70 | func (o *UnifyOrderReq) CreateOrder(ctx *context.Context, param map[string]interface{}) UnifyOrderResp { 71 | xmlResp := UnifyOrderResp{} 72 | unify_order_req := "https://api.mch.weixin.qq.com/pay/unifiedorder" 73 | var yourReq UnifyOrderReq 74 | yourReq.Attach = param["attach"].(string) 75 | yourReq.Appid = beego.AppConfig.String("wxappid") //微信开放平台我们创建出来的app的app id 76 | yourReq.Body = param["body"].(string) 77 | yourReq.Detail = "某某某网络科技有限公司" 78 | yourReq.Fee_type = "CNY" 79 | yourReq.Goods_tag = "WXG" 80 | yourReq.Mch_id = beego.AppConfig.String("wxmchid") 81 | yourReq.Nonce_str = randStr(32, "alphanum") 82 | yourReq.Notify_url = "http://" + beego.AppConfig.String("domainurl") + "/weixin/notify" //异步返回的地址 83 | yourReq.Product_id = param["product_id"].(string) 84 | yourReq.Time_start = TimeConvert(1) 85 | yourReq.Time_expire = TimeConvert(2) 86 | yourReq.Trade_type = "NATIVE" 87 | yourReq.Spbill_create_ip = ctx.Input.IP() 88 | 89 | totalFee, _ := strconv.ParseFloat(param["total_fee"].(string), 64) 90 | totalfeeint := totalFee * 100 91 | yourReq.Total_fee = int(totalfeeint) //单位是分,这里是1毛钱 92 | //beego.Debug("totalfee*100",totalfeeint) 93 | //beego.Debug("totalfee*int",int(totalfeeint)) 94 | //beego.Debug("totalfee*1your",yourReq.Total_fee) 95 | yourReq.Out_trade_no = param["out_trade_no"].(string) 96 | 97 | //beego.Debug("yourReqEntity",yourReq) 98 | var m map[string]interface{} 99 | m = make(map[string]interface{}, 0) 100 | m["attach"] = yourReq.Attach 101 | m["appid"] = yourReq.Appid 102 | m["body"] = yourReq.Body 103 | m["detail"] = yourReq.Detail 104 | m["fee_type"] = yourReq.Fee_type 105 | m["goods_tag"] = yourReq.Goods_tag 106 | m["mch_id"] = yourReq.Mch_id 107 | m["nonce_str"] = yourReq.Nonce_str 108 | m["notify_url"] = yourReq.Notify_url 109 | m["product_id"] = yourReq.Product_id 110 | m["time_start"] = yourReq.Time_start 111 | m["time_expire"] = yourReq.Time_expire 112 | m["trade_type"] = yourReq.Trade_type 113 | m["spbill_create_ip"] = yourReq.Spbill_create_ip 114 | m["total_fee"] = yourReq.Total_fee 115 | m["out_trade_no"] = yourReq.Out_trade_no 116 | appkey := beego.AppConfig.String("wxappkey") 117 | yourReq.Sign = WxpayCalcSign(m, appkey) //这个是计算wxpay签名的函数上面已贴出 118 | 119 | //beego.Debug("yourReq",yourReq) 120 | bytes_req, err := xml.Marshal(yourReq) 121 | if err != nil { 122 | fmt.Println("以xml形式编码发送错误, 原因:", err) 123 | return xmlResp 124 | } 125 | 126 | str_req := string(bytes_req) 127 | //wxpay的unifiedorder接口需要http body中xmldoc的根节点是这种,所以这里需要replace一下 128 | str_req = strings.Replace(str_req, "UnifyOrderReq", "xml", -1) 129 | bytes_req = []byte(str_req) 130 | 131 | //发送unified order请求. 132 | req, err := http.NewRequest("POST", unify_order_req, bytes.NewReader(bytes_req)) 133 | if err != nil { 134 | fmt.Println("New Http Request发生错误,原因:", err) 135 | return xmlResp 136 | } 137 | req.Header.Set("Accept", "application/xml") 138 | //这里的http header的设置是必须设置的. 139 | req.Header.Set("Content-Type", "application/xml;charset=utf-8") 140 | 141 | c := http.Client{} 142 | resp, _err := c.Do(req) 143 | if _err != nil { 144 | fmt.Println("请求微信支付统一下单接口发送错误, 原因:", _err) 145 | return xmlResp 146 | } 147 | 148 | //xmlResp :=UnifyOrderResp{} 149 | body, _ := ioutil.ReadAll(resp.Body) 150 | _err = xml.Unmarshal(body, &xmlResp) 151 | if xmlResp.Return_code == "FAIL" { 152 | fmt.Println("微信支付统一下单不成功,原因:", xmlResp.Return_msg) 153 | return xmlResp 154 | } 155 | //beego.Debug("xmlResp",xmlResp) 156 | //这里已经得到微信支付的prepay id,需要返给客户端,由客户端继续完成支付流程 157 | fmt.Println("微信支付统一下单成功,预支付单号:", xmlResp.Prepay_id) 158 | return xmlResp 159 | 160 | } 161 | 162 | func randStr(strSize int, randType string) string { 163 | 164 | var dictionary string 165 | 166 | if randType == "alphanum" { 167 | dictionary = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 168 | } 169 | 170 | if randType == "alpha" { 171 | dictionary = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 172 | } 173 | 174 | if randType == "number" { 175 | dictionary = "0123456789" 176 | } 177 | 178 | var bytes = make([]byte, strSize) 179 | rand.Read(bytes) 180 | for k, v := range bytes { 181 | bytes[k] = dictionary[v%byte(len(dictionary))] 182 | } 183 | return string(bytes) 184 | } 185 | 186 | func WxpayCalcSign(mReq map[string]interface{}, key string) (sign string) { 187 | fmt.Println("微信支付签名计算, API KEY:", key) 188 | //STEP 1, 对key进行升序排序. 189 | sorted_keys := make([]string, 0) 190 | for k, _ := range mReq { 191 | sorted_keys = append(sorted_keys, k) 192 | } 193 | 194 | sort.Strings(sorted_keys) 195 | 196 | //STEP2, 对key=value的键值对用&连接起来,略过空值 197 | var signStrings string 198 | for _, k := range sorted_keys { 199 | fmt.Printf("k=%v, v=%v\n", k, mReq[k]) 200 | value := fmt.Sprintf("%v", mReq[k]) 201 | if value != "" { 202 | signStrings = signStrings + k + "=" + value + "&" 203 | } 204 | } 205 | 206 | //STEP3, 在键值对的最后加上key=API_KEY 207 | if key != "" { 208 | signStrings = signStrings + "key=" + key 209 | } 210 | 211 | //STEP4, 进行MD5签名并且将所有字符转为大写. 212 | md5Ctx := md5.New() 213 | md5Ctx.Write([]byte(signStrings)) 214 | cipherStr := md5Ctx.Sum(nil) 215 | upperSign := strings.ToUpper(hex.EncodeToString(cipherStr)) 216 | return upperSign 217 | } 218 | -------------------------------------------------------------------------------- /vendor/alipay/native.go: -------------------------------------------------------------------------------- 1 | // author : jarryliu 2 | // date : 2015-07-28 02:22 3 | 4 | package alipay 5 | 6 | import ( 7 | "crypto/md5" 8 | "encoding/hex" 9 | "io/ioutil" 10 | "net/http" 11 | "net/url" 12 | "regexp" 13 | "strings" 14 | ) 15 | 16 | type AliPayParameters struct { 17 | InputCharset string `json:"_input_charset"` //网站编码 18 | Body string `json:"body"` //订单描述 19 | NotifyUrl string `json:"notify_url"` //异步通知页面 20 | OutTradeNo string `json:"out_trade_no"` //订单唯一id 21 | Partner string `json:"partner"` //合作者身份ID 22 | PaymentType uint8 `json:"payment_type"` //支付类型 1:商品购买 23 | ReturnUrl string `json:"return_url"` //回调url 24 | SellerEmail string `json:"seller_email"` //卖家支付宝邮箱 25 | Service string `json:"service"` //接口名称 26 | Subject string `json:"subject"` //商品名称 27 | TotalFee float32 `json:"total_fee"` //总价 28 | Sign string `json:"sign"` //签名,生成签名时忽略 29 | SignType string `json:"sign_type"` //签名类型,生成签名时忽略 30 | } 31 | 32 | /* 被动接收支付宝同步跳转的页面 */ 33 | func (this *Client) NativeReturn(r *http.Request) Result { 34 | var result Result 35 | 36 | //实例化参数 37 | param := map[string]string{ 38 | "body": "", //描述 39 | "buyer_email": "", //买家账号 40 | "buyer_id": "", //买家ID 41 | "exterface": "", 42 | "is_success": "", //交易是否成功 43 | "notify_id": "", //通知校验id 44 | "notify_time": "", //校验时间 45 | "notify_type": "", //校验类型 46 | "out_trade_no": "", //在网站中唯一id 47 | "payment_type": "", //支付类型 48 | "seller_email": "", //卖家账号 49 | "seller_id": "", //卖家id 50 | "subject": "", //商品名称 51 | "total_fee": "", //总价 52 | "trade_no": "", //支付宝交易号 53 | "trade_status": "", //交易状态 TRADE_FINISHED或TRADE_SUCCESS表示交易成功 54 | "sign": "", //签名 55 | "sign_type": "", //签名类型 56 | } 57 | 58 | //解析表单内容,失败返回错误代码-3 59 | form := r.URL.Query() 60 | for k, _ := range param { 61 | param[k] = form.Get(k) 62 | } 63 | 64 | result.OrderNo = param["out_trade_no"] 65 | result.TradeNo = param["trade_no"] 66 | 67 | //如果最基本的网站交易号为空,返回错误代码-1 68 | if result.OrderNo == "" { //不存在交易号 69 | result.Status = -1 70 | return result 71 | } 72 | //生成签名 73 | sign := sign(param) 74 | //对比签名是否相同 75 | if sign == param["sign"] { //只有相同才说明该订单成功了 76 | //判断订单是否已完成 77 | tradeStatus := param["trade_status"] 78 | if tradeStatus == "TRADE_FINISHED" || tradeStatus == "TRADE_SUCCESS" { //交易成功 79 | result.Status = 1 80 | } else { //交易未完成,返回错误代码-4 81 | result.Status = -4 82 | } 83 | } else { //签名认证失败,返回错误代码-2 84 | result.Status = -2 85 | } 86 | 87 | //位置错误类型-5 88 | if result.Status == 0 { 89 | result.Status = -5 90 | } 91 | 92 | return result 93 | } 94 | 95 | /* 被动接收支付宝异步通知 */ 96 | func (this *Client) NativeNotify(r *http.Request) Result { 97 | 98 | // /pay/notify/104_alipay?discount=0.00&payment_type=1&subject=%E5%9C%A8%E7%BA%BF%E6%94%AF%E4%BB%98%E8%AE%A2%E5%8D%95&trade_no=2015072800001000810060741985&buyer_email=***&gmt_create=2015-07-28%2001:24:19%C2%ACify_type=trade_status_sync&quantity=1&out_trade_no=146842585&seller_id=2088021187655650%C2%ACify_time=2015-07-28%2001:24:29&body=%E8%AE%A2%E5%8D%95%E5%8F%B7%EF%BC%9A146842585&trade_status=TRADE_SUCCESS&is_total_fee_adjust=N&total_fee=0.01&gmt_payment=2015-07-28%2001:24:29&seller_email=***&price=0.01&buyer_id=2088302384317810%C2%ACify_id=75e570fcc802c637d8cf1fdaa8677d046i&use_coupon=N&sign_type=MD5&sign=*** 99 | var result Result 100 | body, _ := ioutil.ReadAll(r.Body) 101 | bodyStr := string(body) 102 | 103 | if bodyStr == "" { 104 | /* 105 | if len(r.URL.RawPath) != 0 { 106 | bodyStr = r.URL.RawQuery[1:] 107 | } 108 | */ 109 | result.Status = -4 110 | return result 111 | } 112 | 113 | //从body里读取参数,用&切割 114 | postArray := strings.Split(bodyStr, "&") 115 | 116 | //实例化url 117 | urls := &url.Values{} 118 | 119 | //保存传参的sign 120 | var paramSign string 121 | var sign string 122 | 123 | //如果字符串中包含sec_id说明是手机端的异步通知 124 | if strings.Index(bodyStr, `alipay.wap.trade.create.direct`) == -1 { //快捷支付 125 | for _, v := range postArray { 126 | detail := strings.Split(v, "=") 127 | 128 | //使用=切割字符串 去除sign和sign_type 129 | if detail[0] == "sign" || detail[0] == "sign_type" { 130 | if detail[0] == "sign" { 131 | paramSign = detail[1] 132 | } 133 | continue 134 | } else { 135 | urls.Add(detail[0], detail[1]) 136 | } 137 | } 138 | 139 | // url解码 140 | urlDecode, _ := url.QueryUnescape(urls.Encode()) 141 | sign, _ = url.QueryUnescape(urlDecode) 142 | } else { // 手机网页支付 143 | // 手机字符串加密顺序 144 | mobileOrder := []string{"service", "v", "sec_id", "notify_data"} 145 | for _, v := range mobileOrder { 146 | for _, value := range postArray { 147 | detail := strings.Split(value, "=") 148 | // 保存sign 149 | if detail[0] == "sign" { 150 | paramSign = detail[1] 151 | } else { 152 | // 如果满足当前v 153 | if detail[0] == v { 154 | if sign == "" { 155 | sign = detail[0] + "=" + detail[1] 156 | } else { 157 | sign += "&" + detail[0] + "=" + detail[1] 158 | } 159 | } 160 | } 161 | } 162 | } 163 | sign, _ = url.QueryUnescape(sign) 164 | 165 | //获取之间的request_token 166 | re, _ := regexp.Compile("\\") 167 | rt := re.FindAllString(sign, 1) 168 | trade_status := strings.Replace(rt[0], "", "", -1) 169 | trade_status = strings.Replace(trade_status, "", "", -1) 170 | urls.Add("trade_status", trade_status) 171 | 172 | //获取之间的request_token 173 | re, _ = regexp.Compile("\\") 174 | rt = re.FindAllString(sign, 1) 175 | out_trade_no := strings.Replace(rt[0], "", "", -1) 176 | out_trade_no = strings.Replace(out_trade_no, "", "", -1) 177 | urls.Add("out_trade_no", out_trade_no) 178 | 179 | //获取之间的request_token 180 | re, _ = regexp.Compile("\\") 181 | rt = re.FindAllString(sign, 1) 182 | buyer_email := strings.Replace(rt[0], "", "", -1) 183 | buyer_email = strings.Replace(buyer_email, "", "", -1) 184 | urls.Add("buyer_email", buyer_email) 185 | 186 | //获取之间的request_token 187 | re, _ = regexp.Compile("\\") 188 | rt = re.FindAllString(sign, 1) 189 | trade_no := strings.Replace(rt[0], "", "", -1) 190 | trade_no = strings.Replace(trade_no, "", "", -1) 191 | urls.Add("trade_no", trade_no) 192 | } 193 | //追加密钥 194 | sign += this.Key 195 | 196 | //md5加密 197 | m := md5.New() 198 | m.Write([]byte(sign)) 199 | sign = hex.EncodeToString(m.Sum(nil)) 200 | 201 | result.OrderNo = urls.Get("out_trade_no") 202 | result.TradeNo = urls.Get("trade_no") 203 | //fee ,_ := strconv.ParseFloat(urls.Get("total_fee"),32) 204 | //payResult.Fee = float32(fee) 205 | 206 | if paramSign == sign { //传进的签名等于计算出的签名,说明请求合法 207 | //判断订单是否已完成 208 | if urls.Get("trade_status") == "TRADE_FINISHED" || urls.Get("trade_status") == "TRADE_SUCCESS" { //交易成功 209 | result.Status = 1 210 | } 211 | } else { 212 | //签名不符,错误代码-1 213 | result.Status = -1 214 | } 215 | 216 | return result 217 | } 218 | -------------------------------------------------------------------------------- /vendor/alipay/alipay_v1.go: -------------------------------------------------------------------------------- 1 | // @authors ascoders 2 | // 保持旧api可用 3 | // 依赖beego 4 | 5 | package alipay 6 | 7 | import ( 8 | "crypto/md5" 9 | "encoding/hex" 10 | "github.com/astaxie/beego" 11 | "net/url" 12 | "regexp" 13 | "strconv" 14 | "strings" 15 | ) 16 | 17 | var ( 18 | AlipayPartner string //合作者ID 19 | AlipayKey string //合作者私钥 20 | WebReturnUrl string //网站同步返回地址 21 | WebNotifyUrl string //网站异步返回地址 22 | WebSellerEmail string //网站卖家邮箱地址 23 | ) 24 | 25 | /* 生成支付宝即时到帐提交表单html代码 26 | * @params string 订单唯一id 27 | * @params int 价格 28 | * @params int 获得代金券的数量 29 | * @params string 充值账户的名称 30 | * @params string 充值描述 31 | */ 32 | func CreateAlipaySign(orderId string, fee float32, nickname string, subject string) string { 33 | //实例化参数 34 | param := &AlipayParameters{} 35 | param.InputCharset = "utf-8" 36 | param.Body = "为" + nickname + "充值" + strconv.FormatFloat(float64(fee), 'f', 2, 32) + "元" 37 | param.NotifyUrl = WebNotifyUrl 38 | param.OutTradeNo = orderId 39 | param.Partner = AlipayPartner 40 | param.PaymentType = 1 41 | param.ReturnUrl = WebReturnUrl 42 | param.SellerEmail = WebSellerEmail 43 | param.Service = "create_direct_pay_by_user" 44 | param.Subject = subject 45 | param.TotalFee = fee 46 | 47 | //生成签名 48 | sign := sign(param) 49 | 50 | //追加参数 51 | param.Sign = sign 52 | param.SignType = "MD5" 53 | 54 | //生成自动提交form 55 | return ` 56 | 71 | 74 | ` 75 | } 76 | 77 | /* 被动接收支付宝同步跳转的页面 */ 78 | func AlipayReturn(contro *beego.Controller) (int, string, string, string) { 79 | //列举全部传参 80 | type Params struct { 81 | Body string `form:"body" json:"body"` //描述 82 | BuyerEmail string `form:"buyer_email" json:"buyer_email"` //买家账号 83 | BuyerId string `form:"buyer_id" json:"buyer_id"` //买家ID 84 | Exterface string `form:"exterface" json:"exterface"` //接口名称 85 | IsSuccess string `form:"is_success" json:"is_success"` //交易是否成功 86 | NotifyId string `form:"notify_id" json:"notify_id"` //通知校验id 87 | NotifyTime string `form:"notify_time" json:"notify_time"` //校验时间 88 | NotifyType string `form:"notify_type" json:"notify_type"` //校验类型 89 | OutTradeNo string `form:"out_trade_no" json:"out_trade_no"` //在网站中唯一id 90 | PaymentType uint8 `form:"payment_type" json:"payment_type"` //支付类型 91 | SellerEmail string `form:"seller_email" json:"seller_email"` //卖家账号 92 | SellerId string `form:"seller_id" json:"seller_id"` //卖家id 93 | Subject string `form:"subject" json:"subject"` //商品名称 94 | TotalFee string `form:"total_fee" json:"total_fee"` //总价 95 | TradeNo string `form:"trade_no" json:"trade_no"` //支付宝交易号 96 | TradeStatus string `form:"trade_status" json:"trade_status"` //交易状态 TRADE_FINISHED或TRADE_SUCCESS表示交易成功 97 | Sign string `form:"sign" json:"sign"` //签名 98 | SignType string `form:"sign_type" json:"sign_type"` //签名类型 99 | } 100 | 101 | //实例化参数 102 | param := &Params{} 103 | 104 | //解析表单内容,失败返回错误代码-3 105 | if err := contro.ParseForm(param); err != nil { 106 | return -3, "", "", "" 107 | } 108 | //如果最基本的网站交易号为空,返回错误代码-1 109 | if param.OutTradeNo == "" { //不存在交易号 110 | return -1, "", "", "" 111 | } else { 112 | //生成签名 113 | sign := sign(param) 114 | 115 | //对比签名是否相同 116 | if sign == param.Sign { //只有相同才说明该订单成功了 117 | //判断订单是否已完成 118 | if param.TradeStatus == "TRADE_FINISHED" || param.TradeStatus == "TRADE_SUCCESS" { //交易成功 119 | return 1, param.OutTradeNo, param.BuyerEmail, param.TradeNo 120 | } else { //交易未完成,返回错误代码-4 121 | return -4, "", "", "" 122 | } 123 | } else { //签名认证失败,返回错误代码-2 124 | return -2, "", "", "" 125 | } 126 | } 127 | 128 | //位置错误类型-5 129 | return -5, "", "", "" 130 | } 131 | 132 | /* 被动接收支付宝异步通知 */ 133 | func AlipayNotify(contro *beego.Controller) (int, string, string, string) { 134 | //从body里读取参数,用&切割 135 | postArray := strings.Split(string(contro.Ctx.Input.CopyBody(beego.BConfig.MaxMemory)), "&") 136 | 137 | //实例化url 138 | urls := &url.Values{} 139 | 140 | //保存传参的sign 141 | var paramSign string 142 | var sign string 143 | 144 | //如果字符串中包含sec_id说明是手机端的异步通知 145 | if strings.Index(string(contro.Ctx.Input.CopyBody(beego.BConfig.MaxMemory)), `alipay.wap.trade.create.direct`) == -1 { //快捷支付 146 | for _, v := range postArray { 147 | detail := strings.Split(v, "=") 148 | 149 | //使用=切割字符串 去除sign和sign_type 150 | if detail[0] == "sign" || detail[0] == "sign_type" { 151 | if detail[0] == "sign" { 152 | paramSign = detail[1] 153 | } 154 | continue 155 | } else { 156 | urls.Add(detail[0], detail[1]) 157 | } 158 | } 159 | 160 | // url解码 161 | urlDecode, _ := url.QueryUnescape(urls.Encode()) 162 | sign, _ = url.QueryUnescape(urlDecode) 163 | } else { // 手机网页支付 164 | // 手机字符串加密顺序 165 | mobileOrder := []string{"service", "v", "sec_id", "notify_data"} 166 | for _, v := range mobileOrder { 167 | for _, value := range postArray { 168 | detail := strings.Split(value, "=") 169 | // 保存sign 170 | if detail[0] == "sign" { 171 | paramSign = detail[1] 172 | } else { 173 | // 如果满足当前v 174 | if detail[0] == v { 175 | if sign == "" { 176 | sign = detail[0] + "=" + detail[1] 177 | } else { 178 | sign += "&" + detail[0] + "=" + detail[1] 179 | } 180 | } 181 | } 182 | } 183 | } 184 | sign, _ = url.QueryUnescape(sign) 185 | 186 | //获取之间的request_token 187 | re, _ := regexp.Compile("\\") 188 | rt := re.FindAllString(sign, 1) 189 | trade_status := strings.Replace(rt[0], "", "", -1) 190 | trade_status = strings.Replace(trade_status, "", "", -1) 191 | urls.Add("trade_status", trade_status) 192 | 193 | //获取之间的request_token 194 | re, _ = regexp.Compile("\\") 195 | rt = re.FindAllString(sign, 1) 196 | out_trade_no := strings.Replace(rt[0], "", "", -1) 197 | out_trade_no = strings.Replace(out_trade_no, "", "", -1) 198 | urls.Add("out_trade_no", out_trade_no) 199 | 200 | //获取之间的request_token 201 | re, _ = regexp.Compile("\\") 202 | rt = re.FindAllString(sign, 1) 203 | buyer_email := strings.Replace(rt[0], "", "", -1) 204 | buyer_email = strings.Replace(buyer_email, "", "", -1) 205 | urls.Add("buyer_email", buyer_email) 206 | 207 | //获取之间的request_token 208 | re, _ = regexp.Compile("\\") 209 | rt = re.FindAllString(sign, 1) 210 | trade_no := strings.Replace(rt[0], "", "", -1) 211 | trade_no = strings.Replace(trade_no, "", "", -1) 212 | urls.Add("trade_no", trade_no) 213 | } 214 | //追加密钥 215 | sign += AlipayKey 216 | 217 | //md5加密 218 | m := md5.New() 219 | m.Write([]byte(sign)) 220 | sign = hex.EncodeToString(m.Sum(nil)) 221 | if paramSign == sign { //传进的签名等于计算出的签名,说明请求合法 222 | //判断订单是否已完成 223 | if urls.Get("trade_status") == "TRADE_FINISHED" || urls.Get("trade_status") == "TRADE_SUCCESS" { //交易成功 224 | contro.Ctx.WriteString("success") 225 | return 1, urls.Get("out_trade_no"), urls.Get("buyer_email"), urls.Get("trade_no") 226 | } else { 227 | contro.Ctx.WriteString("error") 228 | } 229 | } else { 230 | contro.Ctx.WriteString("error") 231 | //签名不符,错误代码-1 232 | return -1, "", "", "" 233 | } 234 | //未知错误-2 235 | return -2, "", "", "" 236 | } 237 | -------------------------------------------------------------------------------- /vendor/odeke-em/appfs/fs/fs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package fs is an indirection layer, allowing code to use a 6 | // file system without knowing whether it is the host file system 7 | // (running without App Engine) or the datastore-based app 8 | // file system (running on App Engine). 9 | // 10 | // When compiled locally, fs refers to files in the local file system, 11 | // and the cache saves nothing. 12 | // 13 | // When compiled for App Engine, fs uses the appfs file system 14 | // and the memcache-based cache. 15 | package fs 16 | 17 | import ( 18 | "bytes" 19 | "encoding/gob" 20 | "fmt" 21 | "io" 22 | "log" 23 | "net/http" 24 | "os" 25 | "time" 26 | 27 | "code.google.com/p/rsc/appfs/proto" 28 | ) 29 | 30 | type AppEngine interface { 31 | NewContext(req *http.Request) interface{} 32 | CacheRead(ctxt interface{}, name, path string) (key interface{}, data []byte, found bool) 33 | CacheWrite(ctxt, key interface{}, data []byte) 34 | Read(ctxt interface{}, path string) ([]byte, *proto.FileInfo, error) 35 | Write(ctxt interface{}, path string, data []byte) error 36 | Remove(ctxt interface{}, path string) error 37 | Mkdir(ctxt interface{}, path string) error 38 | ReadDir(ctxt interface{}, path string) ([]proto.FileInfo, error) 39 | Criticalf(ctxt interface{}, format string, args ...interface{}) 40 | User(ctxt interface{}) string 41 | } 42 | 43 | var ae AppEngine 44 | 45 | func Register(impl AppEngine) { 46 | ae = impl 47 | } 48 | 49 | // Root is the root of the local file system. It has no effect on App Engine. 50 | var Root = "." 51 | 52 | // A Context is an opaque context that is needed to perform file system 53 | // operations. Each context is associated with a single HTTP request. 54 | type Context struct { 55 | context 56 | ae interface{} 57 | } 58 | 59 | // NewContext returns a context associated with the given HTTP request. 60 | func NewContext(req *http.Request) *Context { 61 | if ae != nil { 62 | ctxt := ae.NewContext(req) 63 | return &Context{ae: ctxt} 64 | } 65 | return newContext(req) 66 | } 67 | 68 | // A CacheKey is an opaque cache key that can be used to store new entries 69 | // in the cache. To ensure that the cache remains consistent with the underlying 70 | // file system, the correct procedure is: 71 | // 72 | // 1. Use CacheRead (or CacheLoad) to attempt to load the entry. If it succeeds, use it. 73 | // If not, continue, saving the CacheKey. 74 | // 75 | // 2. Read from the file system and construct the entry that would have 76 | // been in the cache. In order to be consistent, all the file system reads 77 | // should only refer to parts of the file system in the tree rooted at the path 78 | // passed to CacheRead. 79 | // 80 | // 3. Save the entry using CacheWrite (or CacheStore), using the key that was 81 | // created by the CacheRead (or CacheLoad) executed before reading from the 82 | // file system. 83 | // 84 | type CacheKey struct { 85 | cacheKey 86 | ae interface{} 87 | } 88 | 89 | // CacheRead reads from cache the entry with the given name and path. 90 | // The path specifies the scope of information stored in the cache entry. 91 | // An entry is invalidated by a write to any location in the file tree rooted at path. 92 | // The name is an uninterpreted identifier to distinguish the cache entry 93 | // from other entries using the same path. 94 | // 95 | // If it finds a cache entry, CacheRead returns the data and found=true. 96 | // If it does not find a cache entry, CacheRead returns data=nil and found=false. 97 | // Either way, CacheRead returns an appropriate cache key for storing to the 98 | // cache entry using CacheWrite. 99 | func (c *Context) CacheRead(name, path string) (ckey CacheKey, data []byte, found bool) { 100 | if ae != nil { 101 | key, data, found := ae.CacheRead(c.ae, name, path) 102 | return CacheKey{ae: key}, data, found 103 | } 104 | return c.cacheRead(ckey, path) 105 | } 106 | 107 | // CacheLoad uses CacheRead to load gob-encoded data and decodes it into value. 108 | func (c *Context) CacheLoad(name, path string, value interface{}) (ckey CacheKey, found bool) { 109 | ckey, data, found := c.CacheRead(name, path) 110 | if found { 111 | if err := gob.NewDecoder(bytes.NewBuffer(data)).Decode(value); err != nil { 112 | c.Criticalf("gob Decode: %v", err) 113 | found = false 114 | } 115 | } 116 | return 117 | } 118 | 119 | // CacheWrite writes an entry to the cache with the given key, path, and data. 120 | // The cache entry will be invalidated the next time the file tree rooted at path is 121 | // modified in anyway. 122 | func (c *Context) CacheWrite(ckey CacheKey, data []byte) { 123 | if ae != nil { 124 | ae.CacheWrite(c.ae, ckey.ae, data) 125 | return 126 | } 127 | c.cacheWrite(ckey, data) 128 | } 129 | 130 | // CacheStore uses CacheWrite to save the gob-encoded form of value. 131 | func (c *Context) CacheStore(ckey CacheKey, value interface{}) { 132 | var buf bytes.Buffer 133 | if err := gob.NewEncoder(&buf).Encode(value); err != nil { 134 | c.Criticalf("gob Encode: %v", err) 135 | return 136 | } 137 | c.CacheWrite(ckey, buf.Bytes()) 138 | } 139 | 140 | // Read returns the data associated with the file named by path. 141 | // It is a copy and can be modified without affecting the file. 142 | func (c *Context) Read(path string) ([]byte, *proto.FileInfo, error) { 143 | if ae != nil { 144 | return ae.Read(c.ae, path) 145 | } 146 | return c.read(path) 147 | } 148 | 149 | // Write replaces the data associated with the file named by path. 150 | func (c *Context) Write(path string, data []byte) error { 151 | if ae != nil { 152 | return ae.Write(c.ae, path, data) 153 | } 154 | return c.write(path, data) 155 | } 156 | 157 | // Remove removes the file named by path. 158 | func (c *Context) Remove(path string) error { 159 | if ae != nil { 160 | return ae.Remove(c.ae, path) 161 | } 162 | return c.remove(path) 163 | } 164 | 165 | // Mkdir creates a directory with the given path. 166 | // If the path already exists and is a directory, Mkdir returns no error. 167 | func (c *Context) Mkdir(path string) error { 168 | if ae != nil { 169 | return ae.Mkdir(c.ae, path) 170 | } 171 | return c.mkdir(path) 172 | } 173 | 174 | // ReadDir returns the contents of the directory named by the path. 175 | func (c *Context) ReadDir(path string) ([]proto.FileInfo, error) { 176 | if ae != nil { 177 | return ae.ReadDir(c.ae, path) 178 | } 179 | return c.readdir(path) 180 | } 181 | 182 | // ServeFile serves the named file as the response to the HTTP request. 183 | func (c *Context) ServeFile(w http.ResponseWriter, req *http.Request, name string) { 184 | root := &httpFS{c, name} 185 | http.FileServer(root).ServeHTTP(w, req) 186 | } 187 | 188 | // Criticalf logs the message at critical priority. 189 | func (c *Context) Criticalf(format string, args ...interface{}) { 190 | if ae != nil { 191 | ae.Criticalf(c.ae, format, args...) 192 | } 193 | log.Printf(format, args...) 194 | } 195 | 196 | // User returns the name of the user running the request. 197 | func (c *Context) User() string { 198 | if ae != nil { 199 | return ae.User(c.ae) 200 | } 201 | return os.Getenv("USER") 202 | } 203 | 204 | type httpFS struct { 205 | c *Context 206 | name string 207 | } 208 | 209 | type httpFile struct { 210 | data []byte 211 | fi *proto.FileInfo 212 | off int 213 | } 214 | 215 | func (h *httpFS) Open(_ string) (http.File, error) { 216 | data, fi, err := h.c.Read(h.name) 217 | if err != nil { 218 | return nil, err 219 | } 220 | return &httpFile{data, fi, 0}, nil 221 | } 222 | 223 | func (f *httpFile) Close() error { 224 | return nil 225 | } 226 | 227 | type fileInfo struct { 228 | p *proto.FileInfo 229 | } 230 | 231 | func (f *fileInfo) IsDir() bool { return f.p.IsDir } 232 | func (f *fileInfo) Name() string { return f.p.Name } 233 | func (f *fileInfo) ModTime() time.Time { return f.p.ModTime } 234 | func (f *fileInfo) Size() int64 { return f.p.Size } 235 | func (f *fileInfo) Sys() interface{} { return f.p } 236 | func (f *fileInfo) Mode() os.FileMode { 237 | if f.p.IsDir { 238 | return os.ModeDir | 0777 239 | } 240 | return 0666 241 | } 242 | 243 | func (f *httpFile) Stat() (os.FileInfo, error) { 244 | return &fileInfo{f.fi}, nil 245 | } 246 | 247 | func (f *httpFile) Readdir(count int) ([]os.FileInfo, error) { 248 | return nil, fmt.Errorf("no directory") 249 | } 250 | 251 | func (f *httpFile) Read(data []byte) (int, error) { 252 | if f.off >= len(f.data) { 253 | return 0, io.EOF 254 | } 255 | n := copy(data, f.data[f.off:]) 256 | f.off += n 257 | return n, nil 258 | } 259 | 260 | func (f *httpFile) Seek(offset int64, whence int) (int64, error) { 261 | off := int(offset) 262 | if int64(off) != offset { 263 | return 0, fmt.Errorf("invalid offset") 264 | } 265 | switch whence { 266 | case 1: 267 | off += f.off 268 | case 2: 269 | off += len(f.data) 270 | } 271 | f.off = off 272 | return int64(off), nil 273 | } 274 | -------------------------------------------------------------------------------- /vendor/alipay/alipay.go: -------------------------------------------------------------------------------- 1 | // @authors ascoders 2 | 3 | package alipay 4 | 5 | import ( 6 | "crypto/md5" 7 | "encoding/hex" 8 | "github.com/astaxie/beego" 9 | "net/url" 10 | "regexp" 11 | "strconv" 12 | "strings" 13 | _"io/ioutil" 14 | ) 15 | 16 | type Client struct { 17 | Partner string // 合作者ID 18 | Key string // 合作者私钥 19 | ReturnUrl string // 同步返回地址 20 | NotifyUrl string // 网站异步返回地址 21 | Email string // 网站卖家邮箱地址 22 | } 23 | 24 | type Result struct { 25 | // 状态 26 | Status int 27 | // 本网站订单号 28 | OrderNo string 29 | // 支付宝交易号 30 | TradeNo string 31 | // 买家支付宝账号 32 | BuyerEmail string 33 | // 错误提示 34 | Message string 35 | 36 | GmtPayment string 37 | 38 | TotalFee string 39 | 40 | Extra_common_param string 41 | } 42 | 43 | // 生成订单的参数 44 | type Options struct { 45 | OrderId string // 订单唯一id 46 | Fee float32 // 价格 47 | NickName string // 充值账户名称 48 | Subject string // 充值描述 49 | Extra_common_param string 50 | } 51 | 52 | /* 生成支付宝即时到帐提交表单html代码 */ 53 | func (this *Client) Form(opts Options) string { 54 | //实例化参数 55 | //beego.Debug("formopts",opts) 56 | param := &AlipayParameters{} 57 | param.InputCharset = "utf-8" 58 | param.Body = "为" + opts.NickName + "充值" + strconv.FormatFloat(float64(opts.Fee), 'f', 2, 32) + "元" 59 | param.NotifyUrl = this.NotifyUrl 60 | param.OutTradeNo = opts.OrderId 61 | param.Partner = this.Partner 62 | param.PaymentType = 1 63 | param.ReturnUrl = this.ReturnUrl 64 | param.SellerEmail = this.Email 65 | param.Service = "create_direct_pay_by_user" 66 | param.Subject = opts.Subject 67 | param.TotalFee = opts.Fee 68 | param.Extra_common_param = opts.Extra_common_param 69 | //生成签名 70 | sign := sign(param) 71 | 72 | //追加参数 73 | param.Sign = sign 74 | param.SignType = "MD5" 75 | 76 | //生成自动提交form 77 | return ` 78 | 94 | 97 | ` 98 | } 99 | 100 | /* 被动接收支付宝同步跳转的页面 */ 101 | func (this *Client) Return(contro *beego.Controller) *Result { 102 | // 列举全部传参 103 | type Params struct { 104 | Body string `form:"body" json:"body"` // 描述 105 | BuyerEmail string `form:"buyer_email" json:"buyer_email"` // 买家账号 106 | BuyerId string `form:"buyer_id" json:"buyer_id"` // 买家ID 107 | Exterface string `form:"exterface" json:"exterface"` // 接口名称 108 | Extra_common_param string `form:"extra_common_param" json:"extra_common_param"` // 签名类型 109 | IsSuccess string `form:"is_success" json:"is_success"` // 交易是否成功 110 | NotifyId string `form:"notify_id" json:"notify_id"` // 通知校验id 111 | NotifyTime string `form:"notify_time" json:"notify_time"` // 校验时间 112 | NotifyType string `form:"notify_type" json:"notify_type"` // 校验类型 113 | OutTradeNo string `form:"out_trade_no" json:"out_trade_no"` // 在网站中唯一id 114 | PaymentType uint8 `form:"payment_type" json:"payment_type"` // 支付类型 115 | SellerEmail string `form:"seller_email" json:"seller_email"` // 卖家账号 116 | SellerId string `form:"seller_id" json:"seller_id"` // 卖家id 117 | Subject string `form:"subject" json:"subject"` // 商品名称 118 | TotalFee string `form:"total_fee" json:"total_fee"` // 总价 119 | TradeNo string `form:"trade_no" json:"trade_no"` // 支付宝交易号 120 | TradeStatus string `form:"trade_status" json:"trade_status"` // 交易状态 TRADE_FINISHED或TRADE_SUCCESS表示交易成功 121 | Sign string `form:"sign" json:"sign"` // 签名 122 | SignType string `form:"sign_type" json:"sign_type"` 123 | } 124 | 125 | // 实例化参数 126 | param := &Params{} 127 | 128 | // 结果 129 | result := &Result{} 130 | 131 | // 解析表单内容,失败返回错误代码-3 132 | if err := contro.ParseForm(param); err != nil { 133 | result.Status = -3 134 | result.Message = "解析表单失败" 135 | return result 136 | } 137 | // 如果最基本的网站交易号为空,返回错误代码-1 138 | if param.OutTradeNo == "" { //不存在交易号 139 | result.Status = -1 140 | result.Message = "站交易号为空" 141 | return result 142 | } else { 143 | // 生成签名 144 | sign := sign(param) 145 | 146 | // 对比签名是否相同 147 | if sign == param.Sign { //只有相同才说明该订单成功了 148 | // 判断订单是否已完成 149 | if param.TradeStatus == "TRADE_FINISHED" || param.TradeStatus == "TRADE_SUCCESS" { //交易成功 150 | result.Status = 1 151 | result.OrderNo = param.OutTradeNo 152 | result.TradeNo = param.TradeNo 153 | result.BuyerEmail = param.BuyerEmail 154 | result.Extra_common_param = param.Extra_common_param 155 | return result 156 | } else { // 交易未完成,返回错误代码-4 157 | result.Status = -4 158 | result.Message = "交易未完成" 159 | return result 160 | } 161 | } else { // 签名认证失败,返回错误代码-2 162 | result.Status = -2 163 | result.Message = "签名认证失败" 164 | return result 165 | } 166 | } 167 | 168 | // 位置错误类型-5 169 | result.Status = -5 170 | result.Message = "位置错误" 171 | return result 172 | } 173 | 174 | /* 被动接收支付宝异步通知 */ 175 | func (this *Client) Notify(contro *beego.Controller) *Result { 176 | // 从body里读取参数,用&切割 177 | 178 | postArray := strings.Split(string(contro.Ctx.Input.RequestBody), "&") 179 | 180 | // 实例化url 181 | urls := &url.Values{} 182 | 183 | // 保存传参的sign 184 | var paramSign string 185 | var sign string 186 | 187 | // 如果字符串中包含sec_id说明是手机端的异步通知 188 | if strings.Index(string(contro.Ctx.Input.CopyBody(beego.BConfig.MaxMemory)), `alipay.wap.trade.create.direct`) == -1 { // 快捷支付 189 | for _, v := range postArray { 190 | detail := strings.Split(v, "=") 191 | 192 | // 使用=切割字符串 去除sign和sign_type 193 | if detail[0] == "sign" || detail[0] == "sign_type" { 194 | if detail[0] == "sign" { 195 | paramSign = detail[1] 196 | } 197 | continue 198 | } else { 199 | urls.Add(detail[0], detail[1]) 200 | } 201 | } 202 | 203 | // url解码 204 | urlDecode, _ := url.QueryUnescape(urls.Encode()) 205 | sign, _ = url.QueryUnescape(urlDecode) 206 | } else { // 手机网页支付 207 | // 手机字符串加密顺序 208 | mobileOrder := []string{"service", "v", "sec_id", "notify_data"} 209 | for _, v := range mobileOrder { 210 | for _, value := range postArray { 211 | detail := strings.Split(value, "=") 212 | // 保存sign 213 | if detail[0] == "sign" { 214 | paramSign = detail[1] 215 | } else { 216 | // 如果满足当前v 217 | if detail[0] == v { 218 | if sign == "" { 219 | sign = detail[0] + "=" + detail[1] 220 | } else { 221 | sign += "&" + detail[0] + "=" + detail[1] 222 | } 223 | } 224 | } 225 | } 226 | } 227 | sign, _ = url.QueryUnescape(sign) 228 | 229 | // 获取之间的request_token 230 | re, _ := regexp.Compile("\\") 231 | rt := re.FindAllString(sign, 1) 232 | trade_status := strings.Replace(rt[0], "", "", -1) 233 | trade_status = strings.Replace(trade_status, "", "", -1) 234 | urls.Add("trade_status", trade_status) 235 | 236 | // 获取之间的request_token 237 | re, _ = regexp.Compile("\\") 238 | rt = re.FindAllString(sign, 1) 239 | out_trade_no := strings.Replace(rt[0], "", "", -1) 240 | out_trade_no = strings.Replace(out_trade_no, "", "", -1) 241 | urls.Add("out_trade_no", out_trade_no) 242 | 243 | // 获取之间的request_token 244 | re, _ = regexp.Compile("\\") 245 | rt = re.FindAllString(sign, 1) 246 | buyer_email := strings.Replace(rt[0], "", "", -1) 247 | buyer_email = strings.Replace(buyer_email, "", "", -1) 248 | urls.Add("buyer_email", buyer_email) 249 | 250 | // 获取之间的request_token 251 | re, _ = regexp.Compile("\\") 252 | rt = re.FindAllString(sign, 1) 253 | trade_no := strings.Replace(rt[0], "", "", -1) 254 | trade_no = strings.Replace(trade_no, "", "", -1) 255 | urls.Add("trade_no", trade_no) 256 | } 257 | // 追加密钥 258 | sign += this.Key 259 | 260 | // 返回参数 261 | result := &Result{} 262 | 263 | // md5加密 264 | m := md5.New() 265 | m.Write([]byte(sign)) 266 | sign = hex.EncodeToString(m.Sum(nil)) 267 | if paramSign == sign { // 传进的签名等于计算出的签名,说明请求合法 268 | // 判断订单是否已完成 269 | if urls.Get("trade_status") == "TRADE_FINISHED" || urls.Get("trade_status") == "TRADE_SUCCESS" { //交易成功 270 | contro.Ctx.WriteString("success") 271 | result.Status = 1 272 | result.OrderNo = urls.Get("out_trade_no") 273 | result.TradeNo = urls.Get("trade_no") 274 | result.BuyerEmail = urls.Get("buyer_email") 275 | result.GmtPayment = urls.Get("gmt_payment") 276 | result.TotalFee = urls.Get("price") 277 | result.Extra_common_param = urls.Get("extra_common_param") 278 | return result 279 | } else { 280 | contro.Ctx.WriteString("error") 281 | } 282 | } else { 283 | contro.Ctx.WriteString("error") 284 | // 签名不符,错误代码-1 285 | result.Status = -1 286 | result.Message = "签名不符" 287 | return result 288 | } 289 | // 未知错误-2 290 | result.Status = -2 291 | result.Message = "未知错误" 292 | return result 293 | } 294 | -------------------------------------------------------------------------------- /vendor/odeke-em/qr/png.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package qr 6 | 7 | // PNG writer for QR codes. 8 | 9 | import ( 10 | "bytes" 11 | "encoding/binary" 12 | "hash" 13 | "hash/crc32" 14 | ) 15 | 16 | // PNG returns a PNG image displaying the code. 17 | // 18 | // PNG uses a custom encoder tailored to QR codes. 19 | // Its compressed size is about 2x away from optimal, 20 | // but it runs about 20x faster than calling png.Encode 21 | // on c.Image(). 22 | func (c *Code) PNG() []byte { 23 | var p pngWriter 24 | return p.encode(c) 25 | } 26 | 27 | type pngWriter struct { 28 | tmp [16]byte 29 | wctmp [4]byte 30 | buf bytes.Buffer 31 | zlib bitWriter 32 | crc hash.Hash32 33 | } 34 | 35 | var pngHeader = []byte("\x89PNG\r\n\x1a\n") 36 | 37 | func (w *pngWriter) encode(c *Code) []byte { 38 | scale := c.Scale 39 | siz := c.Size 40 | 41 | w.buf.Reset() 42 | 43 | // Header 44 | w.buf.Write(pngHeader) 45 | 46 | // Header block 47 | binary.BigEndian.PutUint32(w.tmp[0:4], uint32((siz+8)*scale)) 48 | binary.BigEndian.PutUint32(w.tmp[4:8], uint32((siz+8)*scale)) 49 | w.tmp[8] = 1 // 1-bit 50 | w.tmp[9] = 0 // gray 51 | w.tmp[10] = 0 52 | w.tmp[11] = 0 53 | w.tmp[12] = 0 54 | w.writeChunk("IHDR", w.tmp[:13]) 55 | 56 | // Comment 57 | w.writeChunk("tEXt", comment) 58 | 59 | // Data 60 | w.zlib.writeCode(c) 61 | w.writeChunk("IDAT", w.zlib.bytes.Bytes()) 62 | 63 | // End 64 | w.writeChunk("IEND", nil) 65 | 66 | return w.buf.Bytes() 67 | } 68 | 69 | var comment = []byte("Software\x00QR-PNG http://qr.swtch.com/") 70 | 71 | func (w *pngWriter) writeChunk(name string, data []byte) { 72 | if w.crc == nil { 73 | w.crc = crc32.NewIEEE() 74 | } 75 | binary.BigEndian.PutUint32(w.wctmp[0:4], uint32(len(data))) 76 | w.buf.Write(w.wctmp[0:4]) 77 | w.crc.Reset() 78 | copy(w.wctmp[0:4], name) 79 | w.buf.Write(w.wctmp[0:4]) 80 | w.crc.Write(w.wctmp[0:4]) 81 | w.buf.Write(data) 82 | w.crc.Write(data) 83 | crc := w.crc.Sum32() 84 | binary.BigEndian.PutUint32(w.wctmp[0:4], crc) 85 | w.buf.Write(w.wctmp[0:4]) 86 | } 87 | 88 | func (b *bitWriter) writeCode(c *Code) { 89 | const ftNone = 0 90 | 91 | b.adler32.Reset() 92 | b.bytes.Reset() 93 | b.nbit = 0 94 | 95 | scale := c.Scale 96 | siz := c.Size 97 | 98 | // zlib header 99 | b.tmp[0] = 0x78 100 | b.tmp[1] = 0 101 | b.tmp[1] += uint8(31 - (uint16(b.tmp[0])<<8+uint16(b.tmp[1]))%31) 102 | b.bytes.Write(b.tmp[0:2]) 103 | 104 | // Start flate block. 105 | b.writeBits(1, 1, false) // final block 106 | b.writeBits(1, 2, false) // compressed, fixed Huffman tables 107 | 108 | // White border. 109 | // First row. 110 | b.byte(ftNone) 111 | n := (scale*(siz+8) + 7) / 8 112 | b.byte(255) 113 | b.repeat(n-1, 1) 114 | // 4*scale rows total. 115 | b.repeat((4*scale-1)*(1+n), 1+n) 116 | 117 | for i := 0; i < 4*scale; i++ { 118 | b.adler32.WriteNByte(ftNone, 1) 119 | b.adler32.WriteNByte(255, n) 120 | } 121 | 122 | row := make([]byte, 1+n) 123 | for y := 0; y < siz; y++ { 124 | row[0] = ftNone 125 | j := 1 126 | var z uint8 127 | nz := 0 128 | for x := -4; x < siz+4; x++ { 129 | // Raw data. 130 | for i := 0; i < scale; i++ { 131 | z <<= 1 132 | if !c.Black(x, y) { 133 | z |= 1 134 | } 135 | if nz++; nz == 8 { 136 | row[j] = z 137 | j++ 138 | nz = 0 139 | } 140 | } 141 | } 142 | if j < len(row) { 143 | row[j] = z 144 | } 145 | for _, z := range row { 146 | b.byte(z) 147 | } 148 | 149 | // Scale-1 copies. 150 | b.repeat((scale-1)*(1+n), 1+n) 151 | 152 | b.adler32.WriteN(row, scale) 153 | } 154 | 155 | // White border. 156 | // First row. 157 | b.byte(ftNone) 158 | b.byte(255) 159 | b.repeat(n-1, 1) 160 | // 4*scale rows total. 161 | b.repeat((4*scale-1)*(1+n), 1+n) 162 | 163 | for i := 0; i < 4*scale; i++ { 164 | b.adler32.WriteNByte(ftNone, 1) 165 | b.adler32.WriteNByte(255, n) 166 | } 167 | 168 | // End of block. 169 | b.hcode(256) 170 | b.flushBits() 171 | 172 | // adler32 173 | binary.BigEndian.PutUint32(b.tmp[0:], b.adler32.Sum32()) 174 | b.bytes.Write(b.tmp[0:4]) 175 | } 176 | 177 | // A bitWriter is a write buffer for bit-oriented data like deflate. 178 | type bitWriter struct { 179 | bytes bytes.Buffer 180 | bit uint32 181 | nbit uint 182 | 183 | tmp [4]byte 184 | adler32 adigest 185 | } 186 | 187 | func (b *bitWriter) writeBits(bit uint32, nbit uint, rev bool) { 188 | // reverse, for huffman codes 189 | if rev { 190 | br := uint32(0) 191 | for i := uint(0); i < nbit; i++ { 192 | br |= ((bit >> i) & 1) << (nbit - 1 - i) 193 | } 194 | bit = br 195 | } 196 | b.bit |= bit << b.nbit 197 | b.nbit += nbit 198 | for b.nbit >= 8 { 199 | b.bytes.WriteByte(byte(b.bit)) 200 | b.bit >>= 8 201 | b.nbit -= 8 202 | } 203 | } 204 | 205 | func (b *bitWriter) flushBits() { 206 | if b.nbit > 0 { 207 | b.bytes.WriteByte(byte(b.bit)) 208 | b.nbit = 0 209 | b.bit = 0 210 | } 211 | } 212 | 213 | func (b *bitWriter) hcode(v int) { 214 | /* 215 | Lit Value Bits Codes 216 | --------- ---- ----- 217 | 0 - 143 8 00110000 through 218 | 10111111 219 | 144 - 255 9 110010000 through 220 | 111111111 221 | 256 - 279 7 0000000 through 222 | 0010111 223 | 280 - 287 8 11000000 through 224 | 11000111 225 | */ 226 | switch { 227 | case v <= 143: 228 | b.writeBits(uint32(v)+0x30, 8, true) 229 | case v <= 255: 230 | b.writeBits(uint32(v-144)+0x190, 9, true) 231 | case v <= 279: 232 | b.writeBits(uint32(v-256)+0, 7, true) 233 | case v <= 287: 234 | b.writeBits(uint32(v-280)+0xc0, 8, true) 235 | default: 236 | panic("invalid hcode") 237 | } 238 | } 239 | 240 | func (b *bitWriter) byte(x byte) { 241 | b.hcode(int(x)) 242 | } 243 | 244 | func (b *bitWriter) codex(c int, val int, nx uint) { 245 | b.hcode(c + val>>nx) 246 | b.writeBits(uint32(val)&(1<= 258+3; n -= 258 { 251 | b.repeat1(258, d) 252 | } 253 | if n > 258 { 254 | // 258 < n < 258+3 255 | b.repeat1(10, d) 256 | b.repeat1(n-10, d) 257 | return 258 | } 259 | if n < 3 { 260 | panic("invalid flate repeat") 261 | } 262 | b.repeat1(n, d) 263 | } 264 | 265 | func (b *bitWriter) repeat1(n, d int) { 266 | /* 267 | Extra Extra Extra 268 | Code Bits Length(s) Code Bits Lengths Code Bits Length(s) 269 | ---- ---- ------ ---- ---- ------- ---- ---- ------- 270 | 257 0 3 267 1 15,16 277 4 67-82 271 | 258 0 4 268 1 17,18 278 4 83-98 272 | 259 0 5 269 2 19-22 279 4 99-114 273 | 260 0 6 270 2 23-26 280 4 115-130 274 | 261 0 7 271 2 27-30 281 5 131-162 275 | 262 0 8 272 2 31-34 282 5 163-194 276 | 263 0 9 273 3 35-42 283 5 195-226 277 | 264 0 10 274 3 43-50 284 5 227-257 278 | 265 1 11,12 275 3 51-58 285 0 258 279 | 266 1 13,14 276 3 59-66 280 | */ 281 | switch { 282 | case n <= 10: 283 | b.codex(257, n-3, 0) 284 | case n <= 18: 285 | b.codex(265, n-11, 1) 286 | case n <= 34: 287 | b.codex(269, n-19, 2) 288 | case n <= 66: 289 | b.codex(273, n-35, 3) 290 | case n <= 130: 291 | b.codex(277, n-67, 4) 292 | case n <= 257: 293 | b.codex(281, n-131, 5) 294 | case n == 258: 295 | b.hcode(285) 296 | default: 297 | panic("invalid repeat length") 298 | } 299 | 300 | /* 301 | Extra Extra Extra 302 | Code Bits Dist Code Bits Dist Code Bits Distance 303 | ---- ---- ---- ---- ---- ------ ---- ---- -------- 304 | 0 0 1 10 4 33-48 20 9 1025-1536 305 | 1 0 2 11 4 49-64 21 9 1537-2048 306 | 2 0 3 12 5 65-96 22 10 2049-3072 307 | 3 0 4 13 5 97-128 23 10 3073-4096 308 | 4 1 5,6 14 6 129-192 24 11 4097-6144 309 | 5 1 7,8 15 6 193-256 25 11 6145-8192 310 | 6 2 9-12 16 7 257-384 26 12 8193-12288 311 | 7 2 13-16 17 7 385-512 27 12 12289-16384 312 | 8 3 17-24 18 8 513-768 28 13 16385-24576 313 | 9 3 25-32 19 8 769-1024 29 13 24577-32768 314 | */ 315 | if d <= 4 { 316 | b.writeBits(uint32(d-1), 5, true) 317 | } else if d <= 32768 { 318 | nbit := uint(16) 319 | for d <= 1<<(nbit-1) { 320 | nbit-- 321 | } 322 | v := uint32(d - 1) 323 | v &^= 1 << (nbit - 1) // top bit is implicit 324 | code := uint32(2*nbit - 2) // second bit is low bit of code 325 | code |= v >> (nbit - 2) 326 | v &^= 1 << (nbit - 2) 327 | b.writeBits(code, 5, true) 328 | // rest of bits follow 329 | b.writeBits(uint32(v), nbit-2, false) 330 | } else { 331 | panic("invalid repeat distance") 332 | } 333 | } 334 | 335 | func (b *bitWriter) run(v byte, n int) { 336 | if n == 0 { 337 | return 338 | } 339 | b.byte(v) 340 | if n-1 < 3 { 341 | for i := 0; i < n-1; i++ { 342 | b.byte(v) 343 | } 344 | } else { 345 | b.repeat(n-1, 1) 346 | } 347 | } 348 | 349 | type adigest struct { 350 | a, b uint32 351 | } 352 | 353 | func (d *adigest) Reset() { d.a, d.b = 1, 0 } 354 | 355 | const amod = 65521 356 | 357 | func aupdate(a, b uint32, pi byte, n int) (aa, bb uint32) { 358 | // TODO(rsc): 6g doesn't do magic multiplies for b %= amod, 359 | // only for b = b%amod. 360 | 361 | // invariant: a, b < amod 362 | if pi == 0 { 363 | b += uint32(n%amod) * a 364 | b = b % amod 365 | return a, b 366 | } 367 | 368 | // n times: 369 | // a += pi 370 | // b += a 371 | // is same as 372 | // b += n*a + n*(n+1)/2*pi 373 | // a += n*pi 374 | m := uint32(n) 375 | b += (m % amod) * a 376 | b = b % amod 377 | b += (m * (m + 1) / 2) % amod * uint32(pi) 378 | b = b % amod 379 | a += (m % amod) * uint32(pi) 380 | a = a % amod 381 | return a, b 382 | } 383 | 384 | func afinish(a, b uint32) uint32 { 385 | return b<<16 | a 386 | } 387 | 388 | func (d *adigest) WriteN(p []byte, n int) { 389 | for i := 0; i < n; i++ { 390 | for _, pi := range p { 391 | d.a, d.b = aupdate(d.a, d.b, pi, 1) 392 | } 393 | } 394 | } 395 | 396 | func (d *adigest) WriteNByte(pi byte, n int) { 397 | d.a, d.b = aupdate(d.a, d.b, pi, n) 398 | } 399 | 400 | func (d *adigest) Sum32() uint32 { return afinish(d.a, d.b) } 401 | -------------------------------------------------------------------------------- /vendor/odeke-em/qr/web/pic.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "image" 7 | "image/color" 8 | "image/draw" 9 | "image/png" 10 | "net/http" 11 | "strconv" 12 | "strings" 13 | 14 | "github.com/golang/freetype" 15 | "odeke-em/appfs/fs" 16 | "odeke-em/qr" 17 | "odeke-em/qr/coding" 18 | ) 19 | 20 | func makeImage(req *http.Request, caption, font string, pt, size, border, scale int, f func(x, y int) uint32) *image.RGBA { 21 | d := (size + 2*border) * scale 22 | csize := 0 23 | if caption != "" { 24 | if pt == 0 { 25 | pt = 11 26 | } 27 | csize = pt * 2 28 | } 29 | c := image.NewRGBA(image.Rect(0, 0, d, d+csize)) 30 | 31 | // white 32 | u := &image.Uniform{C: color.White} 33 | draw.Draw(c, c.Bounds(), u, image.ZP, draw.Src) 34 | 35 | for y := 0; y < size; y++ { 36 | for x := 0; x < size; x++ { 37 | r := image.Rect((x+border)*scale, (y+border)*scale, (x+border+1)*scale, (y+border+1)*scale) 38 | rgba := f(x, y) 39 | u.C = color.RGBA{byte(rgba >> 24), byte(rgba >> 16), byte(rgba >> 8), byte(rgba)} 40 | draw.Draw(c, r, u, image.ZP, draw.Src) 41 | } 42 | } 43 | 44 | if csize != 0 { 45 | if font == "" { 46 | font = "data/luxisr.ttf" 47 | } 48 | ctxt := fs.NewContext(req) 49 | dat, _, err := ctxt.Read(font) 50 | if err != nil { 51 | panic(err) 52 | } 53 | tfont, err := freetype.ParseFont(dat) 54 | if err != nil { 55 | panic(err) 56 | } 57 | ft := freetype.NewContext() 58 | ft.SetDst(c) 59 | ft.SetDPI(100) 60 | ft.SetFont(tfont) 61 | ft.SetFontSize(float64(pt)) 62 | ft.SetSrc(image.NewUniform(color.Black)) 63 | ft.SetClip(image.Rect(0, 0, 0, 0)) 64 | wid, err := ft.DrawString(caption, freetype.Pt(0, 0)) 65 | if err != nil { 66 | panic(err) 67 | } 68 | p := freetype.Pt(d, d+3*pt/2) 69 | p.X -= wid.X 70 | p.X /= 2 71 | ft.SetClip(c.Bounds()) 72 | ft.DrawString(caption, p) 73 | } 74 | 75 | return c 76 | } 77 | 78 | func makeFrame(req *http.Request, font string, pt, vers, l, scale, dots int) image.Image { 79 | lev := coding.Level(l) 80 | p, err := coding.NewPlan(coding.Version(vers), lev, 0) 81 | if err != nil { 82 | panic(err) 83 | } 84 | 85 | nd := p.DataBytes / p.Blocks 86 | nc := p.CheckBytes / p.Blocks 87 | extra := p.DataBytes - nd*p.Blocks 88 | 89 | cap := fmt.Sprintf("QR v%d, %s", vers, lev) 90 | if dots > 0 { 91 | cap = fmt.Sprintf("QR v%d order, from bottom right", vers) 92 | } 93 | m := makeImage(req, cap, font, pt, len(p.Pixel), 0, scale, func(x, y int) uint32 { 94 | pix := p.Pixel[y][x] 95 | switch pix.Role() { 96 | case coding.Data: 97 | if dots > 0 { 98 | return 0xffffffff 99 | } 100 | off := int(pix.Offset() / 8) 101 | nd := nd 102 | var i int 103 | for i = 0; i < p.Blocks; i++ { 104 | if i == extra { 105 | nd++ 106 | } 107 | if off < nd { 108 | break 109 | } 110 | off -= nd 111 | } 112 | return blockColors[i%len(blockColors)] 113 | case coding.Check: 114 | if dots > 0 { 115 | return 0xffffffff 116 | } 117 | i := (int(pix.Offset()/8) - p.DataBytes) / nc 118 | return dark(blockColors[i%len(blockColors)]) 119 | } 120 | if pix&coding.Black != 0 { 121 | return 0x000000ff 122 | } 123 | return 0xffffffff 124 | }) 125 | 126 | if dots > 0 { 127 | b := m.Bounds() 128 | for y := 0; y <= len(p.Pixel); y++ { 129 | for x := 0; x < b.Dx(); x++ { 130 | m.SetRGBA(x, y*scale-(y/len(p.Pixel)), color.RGBA{127, 127, 127, 255}) 131 | } 132 | } 133 | for x := 0; x <= len(p.Pixel); x++ { 134 | for y := 0; y < b.Dx(); y++ { 135 | m.SetRGBA(x*scale-(x/len(p.Pixel)), y, color.RGBA{127, 127, 127, 255}) 136 | } 137 | } 138 | order := make([]image.Point, (p.DataBytes+p.CheckBytes)*8+1) 139 | for y, row := range p.Pixel { 140 | for x, pix := range row { 141 | if r := pix.Role(); r != coding.Data && r != coding.Check { 142 | continue 143 | } 144 | // draw.Draw(m, m.Bounds().Add(image.Pt(x*scale, y*scale)), dot, image.ZP, draw.Over) 145 | order[pix.Offset()] = image.Point{x*scale + scale/2, y*scale + scale/2} 146 | } 147 | } 148 | 149 | for mode := 0; mode < 2; mode++ { 150 | for i, p := range order { 151 | q := order[i+1] 152 | if q.X == 0 { 153 | break 154 | } 155 | line(m, p, q, mode) 156 | } 157 | } 158 | } 159 | return m 160 | } 161 | 162 | func line(m *image.RGBA, p, q image.Point, mode int) { 163 | x := 0 164 | y := 0 165 | dx := q.X - p.X 166 | dy := q.Y - p.Y 167 | xsign := +1 168 | ysign := +1 169 | if dx < 0 { 170 | xsign = -1 171 | dx = -dx 172 | } 173 | if dy < 0 { 174 | ysign = -1 175 | dy = -dy 176 | } 177 | pt := func() { 178 | switch mode { 179 | case 0: 180 | for dx := -2; dx <= 2; dx++ { 181 | for dy := -2; dy <= 2; dy++ { 182 | if dy*dx <= -4 || dy*dx >= 4 { 183 | continue 184 | } 185 | m.SetRGBA(p.X+x*xsign+dx, p.Y+y*ysign+dy, color.RGBA{255, 192, 192, 255}) 186 | } 187 | } 188 | 189 | case 1: 190 | m.SetRGBA(p.X+x*xsign, p.Y+y*ysign, color.RGBA{128, 0, 0, 255}) 191 | } 192 | } 193 | if dx > dy { 194 | for x < dx || y < dy { 195 | pt() 196 | x++ 197 | if float64(x)*float64(dy)/float64(dx)-float64(y) > 0.5 { 198 | y++ 199 | } 200 | } 201 | } else { 202 | for x < dx || y < dy { 203 | pt() 204 | y++ 205 | if float64(y)*float64(dx)/float64(dy)-float64(x) > 0.5 { 206 | x++ 207 | } 208 | } 209 | } 210 | pt() 211 | } 212 | 213 | func pngEncode(c image.Image) []byte { 214 | var b bytes.Buffer 215 | png.Encode(&b, c) 216 | return b.Bytes() 217 | } 218 | 219 | // Frame handles a request for a single QR frame. 220 | func Frame(w http.ResponseWriter, req *http.Request) { 221 | arg := func(s string) int { x, _ := strconv.Atoi(req.FormValue(s)); return x } 222 | v := arg("v") 223 | scale := arg("scale") 224 | if scale == 0 { 225 | scale = 8 226 | } 227 | 228 | w.Header().Set("Cache-Control", "public, max-age=3600") 229 | w.Write(pngEncode(makeFrame(req, req.FormValue("font"), arg("pt"), v, arg("l"), scale, arg("dots")))) 230 | } 231 | 232 | // Frames handles a request for multiple QR frames. 233 | func Frames(w http.ResponseWriter, req *http.Request) { 234 | vs := strings.Split(req.FormValue("v"), ",") 235 | 236 | arg := func(s string) int { x, _ := strconv.Atoi(req.FormValue(s)); return x } 237 | scale := arg("scale") 238 | if scale == 0 { 239 | scale = 8 240 | } 241 | font := req.FormValue("font") 242 | pt := arg("pt") 243 | dots := arg("dots") 244 | 245 | var images []image.Image 246 | l := arg("l") 247 | for _, v := range vs { 248 | l := l 249 | if i := strings.Index(v, "."); i >= 0 { 250 | l, _ = strconv.Atoi(v[i+1:]) 251 | v = v[:i] 252 | } 253 | vv, _ := strconv.Atoi(v) 254 | images = append(images, makeFrame(req, font, pt, vv, l, scale, dots)) 255 | } 256 | 257 | b := images[len(images)-1].Bounds() 258 | 259 | dx := arg("dx") 260 | if dx == 0 { 261 | dx = b.Dx() 262 | } 263 | x, y := 0, 0 264 | xmax := 0 265 | sep := arg("sep") 266 | if sep == 0 { 267 | sep = 10 268 | } 269 | var points []image.Point 270 | for i, m := range images { 271 | if x > 0 { 272 | x += sep 273 | } 274 | if x > 0 && x+m.Bounds().Dx() > dx { 275 | y += sep + images[i-1].Bounds().Dy() 276 | x = 0 277 | } 278 | points = append(points, image.Point{x, y}) 279 | x += m.Bounds().Dx() 280 | if x > xmax { 281 | xmax = x 282 | } 283 | 284 | } 285 | 286 | c := image.NewRGBA(image.Rect(0, 0, xmax, y+b.Dy())) 287 | for i, m := range images { 288 | draw.Draw(c, c.Bounds().Add(points[i]), m, image.ZP, draw.Src) 289 | } 290 | 291 | w.Header().Set("Cache-Control", "public, max-age=3600") 292 | w.Write(pngEncode(c)) 293 | } 294 | 295 | // Mask handles a request for a single QR mask. 296 | func Mask(w http.ResponseWriter, req *http.Request) { 297 | arg := func(s string) int { x, _ := strconv.Atoi(req.FormValue(s)); return x } 298 | v := arg("v") 299 | m := arg("m") 300 | scale := arg("scale") 301 | if scale == 0 { 302 | scale = 8 303 | } 304 | 305 | w.Header().Set("Cache-Control", "public, max-age=3600") 306 | w.Write(pngEncode(makeMask(req, req.FormValue("font"), arg("pt"), v, m, scale))) 307 | } 308 | 309 | // Masks handles a request for multiple QR masks. 310 | func Masks(w http.ResponseWriter, req *http.Request) { 311 | arg := func(s string) int { x, _ := strconv.Atoi(req.FormValue(s)); return x } 312 | v := arg("v") 313 | scale := arg("scale") 314 | if scale == 0 { 315 | scale = 8 316 | } 317 | font := req.FormValue("font") 318 | pt := arg("pt") 319 | var mm []image.Image 320 | for m := 0; m < 8; m++ { 321 | mm = append(mm, makeMask(req, font, pt, v, m, scale)) 322 | } 323 | dx := mm[0].Bounds().Dx() 324 | dy := mm[0].Bounds().Dy() 325 | 326 | sep := arg("sep") 327 | if sep == 0 { 328 | sep = 10 329 | } 330 | c := image.NewRGBA(image.Rect(0, 0, (dx+sep)*4-sep, (dy+sep)*2-sep)) 331 | for m := 0; m < 8; m++ { 332 | x := (m % 4) * (dx + sep) 333 | y := (m / 4) * (dy + sep) 334 | draw.Draw(c, c.Bounds().Add(image.Pt(x, y)), mm[m], image.ZP, draw.Src) 335 | } 336 | 337 | w.Header().Set("Cache-Control", "public, max-age=3600") 338 | w.Write(pngEncode(c)) 339 | } 340 | 341 | var maskName = []string{ 342 | "(x+y) % 2", 343 | "y % 2", 344 | "x % 3", 345 | "(x+y) % 3", 346 | "(y/2 + x/3) % 2", 347 | "xy%2 + xy%3", 348 | "(xy%2 + xy%3) % 2", 349 | "(xy%3 + (x+y)%2) % 2", 350 | } 351 | 352 | func makeMask(req *http.Request, font string, pt int, vers, mask, scale int) image.Image { 353 | p, err := coding.NewPlan(coding.Version(vers), coding.L, coding.Mask(mask)) 354 | if err != nil { 355 | panic(err) 356 | } 357 | m := makeImage(req, maskName[mask], font, pt, len(p.Pixel), 0, scale, func(x, y int) uint32 { 358 | pix := p.Pixel[y][x] 359 | switch pix.Role() { 360 | case coding.Data, coding.Check: 361 | if pix&coding.Invert != 0 { 362 | return 0x000000ff 363 | } 364 | } 365 | return 0xffffffff 366 | }) 367 | return m 368 | } 369 | 370 | var blockColors = []uint32{ 371 | 0x7777ffff, 372 | 0xffff77ff, 373 | 0xff7777ff, 374 | 0x77ffffff, 375 | 0x1e90ffff, 376 | 0xffffe0ff, 377 | 0x8b6969ff, 378 | 0x77ff77ff, 379 | 0x9b30ffff, 380 | 0x00bfffff, 381 | 0x90e890ff, 382 | 0xfff68fff, 383 | 0xffec8bff, 384 | 0xffa07aff, 385 | 0xffa54fff, 386 | 0xeee8aaff, 387 | 0x98fb98ff, 388 | 0xbfbfbfff, 389 | 0x54ff9fff, 390 | 0xffaeb9ff, 391 | 0xb23aeeff, 392 | 0xbbffffff, 393 | 0x7fffd4ff, 394 | 0xff7a7aff, 395 | 0x00007fff, 396 | } 397 | 398 | func dark(x uint32) uint32 { 399 | r, g, b, a := byte(x>>24), byte(x>>16), byte(x>>8), byte(x) 400 | r = r/2 + r/4 401 | g = g/2 + g/4 402 | b = b/2 + b/4 403 | return uint32(r)<<24 | uint32(g)<<16 | uint32(b)<<8 | uint32(a) 404 | } 405 | 406 | func clamp(x int) byte { 407 | if x < 0 { 408 | return 0 409 | } 410 | if x > 255 { 411 | return 255 412 | } 413 | return byte(x) 414 | } 415 | 416 | func max(x, y int) int { 417 | if x > y { 418 | return x 419 | } 420 | return y 421 | } 422 | 423 | // Arrow handles a request for an arrow pointing in a given direction. 424 | func Arrow(w http.ResponseWriter, req *http.Request) { 425 | arg := func(s string) int { x, _ := strconv.Atoi(req.FormValue(s)); return x } 426 | dir := arg("dir") 427 | size := arg("size") 428 | if size == 0 { 429 | size = 50 430 | } 431 | del := size / 10 432 | 433 | m := image.NewRGBA(image.Rect(0, 0, size, size)) 434 | 435 | if dir == 4 { 436 | draw.Draw(m, m.Bounds(), image.Black, image.ZP, draw.Src) 437 | draw.Draw(m, image.Rect(5, 5, size-5, size-5), image.White, image.ZP, draw.Src) 438 | } 439 | 440 | pt := func(x, y int, c color.RGBA) { 441 | switch dir { 442 | case 0: 443 | m.SetRGBA(x, y, c) 444 | case 1: 445 | m.SetRGBA(y, size-1-x, c) 446 | case 2: 447 | m.SetRGBA(size-1-x, size-1-y, c) 448 | case 3: 449 | m.SetRGBA(size-1-y, x, c) 450 | } 451 | } 452 | 453 | for y := 0; y < size/2; y++ { 454 | for x := 0; x < del && x < y; x++ { 455 | pt(x, y, color.RGBA{0, 0, 0, 255}) 456 | } 457 | for x := del; x < y-del; x++ { 458 | pt(x, y, color.RGBA{128, 128, 255, 255}) 459 | } 460 | for x := max(y-del, 0); x <= y; x++ { 461 | pt(x, y, color.RGBA{0, 0, 0, 255}) 462 | } 463 | } 464 | for y := size / 2; y < size; y++ { 465 | for x := 0; x < del && x < size-1-y; x++ { 466 | pt(x, y, color.RGBA{0, 0, 0, 255}) 467 | } 468 | for x := del; x < size-1-y-del; x++ { 469 | pt(x, y, color.RGBA{128, 128, 192, 255}) 470 | } 471 | for x := max(size-1-y-del, 0); x <= size-1-y; x++ { 472 | pt(x, y, color.RGBA{0, 0, 0, 255}) 473 | } 474 | } 475 | 476 | w.Header().Set("Cache-Control", "public, max-age=3600") 477 | w.Write(pngEncode(m)) 478 | } 479 | 480 | // Encode encodes a string using the given version, level, and mask. 481 | func Encode(w http.ResponseWriter, req *http.Request) { 482 | val := func(s string) int { 483 | v, _ := strconv.Atoi(req.FormValue(s)) 484 | return v 485 | } 486 | 487 | l := coding.Level(val("l")) 488 | v := coding.Version(val("v")) 489 | enc := coding.String(req.FormValue("t")) 490 | m := coding.Mask(val("m")) 491 | 492 | p, err := coding.NewPlan(v, l, m) 493 | if err != nil { 494 | panic(err) 495 | } 496 | cc, err := p.Encode(enc) 497 | if err != nil { 498 | panic(err) 499 | } 500 | 501 | c := &qr.Code{Bitmap: cc.Bitmap, Size: cc.Size, Stride: cc.Stride, Scale: 8} 502 | w.Header().Set("Content-Type", "image/png") 503 | w.Header().Set("Cache-Control", "public, max-age=3600") 504 | w.Write(c.PNG()) 505 | } 506 | -------------------------------------------------------------------------------- /vendor/odeke-em/qr/coding/qr.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package coding implements low-level QR coding details. 6 | package coding 7 | 8 | import ( 9 | "fmt" 10 | "strconv" 11 | "strings" 12 | 13 | "odeke-em/gf256" 14 | ) 15 | 16 | // Field is the field for QR error correction. 17 | var Field = gf256.NewField(0x11d, 2) 18 | 19 | // A Version represents a QR version. 20 | // The version specifies the size of the QR code: 21 | // a QR code with version v has 4v+17 pixels on a side. 22 | // Versions number from 1 to 40: the larger the version, 23 | // the more information the code can store. 24 | type Version int 25 | 26 | const MinVersion = 1 27 | const MaxVersion = 40 28 | 29 | func (v Version) String() string { 30 | return strconv.Itoa(int(v)) 31 | } 32 | 33 | func (v Version) sizeClass() int { 34 | if v <= 9 { 35 | return 0 36 | } 37 | if v <= 26 { 38 | return 1 39 | } 40 | return 2 41 | } 42 | 43 | // DataBytes returns the number of data bytes that can be 44 | // stored in a QR code with the given version and level. 45 | func (v Version) DataBytes(l Level) int { 46 | vt := &vtab[v] 47 | lev := &vt.level[l] 48 | return vt.bytes - lev.nblock*lev.check 49 | } 50 | 51 | // Encoding implements a QR data encoding scheme. 52 | // The implementations--Numeric, Alphanumeric, and String--specify 53 | // the character set and the mapping from UTF-8 to code bits. 54 | // The more restrictive the mode, the fewer code bits are needed. 55 | type Encoding interface { 56 | Check() error 57 | Bits(v Version) int 58 | Encode(b *Bits, v Version) 59 | } 60 | 61 | type Bits struct { 62 | b []byte 63 | nbit int 64 | } 65 | 66 | func (b *Bits) Reset() { 67 | b.b = b.b[:0] 68 | b.nbit = 0 69 | } 70 | 71 | func (b *Bits) Bits() int { 72 | return b.nbit 73 | } 74 | 75 | func (b *Bits) Bytes() []byte { 76 | if b.nbit%8 != 0 { 77 | panic("fractional byte") 78 | } 79 | return b.b 80 | } 81 | 82 | func (b *Bits) Append(p []byte) { 83 | if b.nbit%8 != 0 { 84 | panic("fractional byte") 85 | } 86 | b.b = append(b.b, p...) 87 | b.nbit += 8 * len(p) 88 | } 89 | 90 | func (b *Bits) Write(v uint, nbit int) { 91 | for nbit > 0 { 92 | n := nbit 93 | if n > 8 { 94 | n = 8 95 | } 96 | if b.nbit%8 == 0 { 97 | b.b = append(b.b, 0) 98 | } else { 99 | m := -b.nbit & 7 100 | if n > m { 101 | n = m 102 | } 103 | } 104 | b.nbit += n 105 | sh := uint(nbit - n) 106 | b.b[len(b.b)-1] |= uint8(v >> sh << uint(-b.nbit&7)) 107 | v -= v >> sh << sh 108 | nbit -= n 109 | } 110 | } 111 | 112 | // Num is the encoding for numeric data. 113 | // The only valid characters are the decimal digits 0 through 9. 114 | type Num string 115 | 116 | func (s Num) String() string { 117 | return fmt.Sprintf("Num(%#q)", string(s)) 118 | } 119 | 120 | func (s Num) Check() error { 121 | for _, c := range s { 122 | if c < '0' || '9' < c { 123 | return fmt.Errorf("non-numeric string %#q", string(s)) 124 | } 125 | } 126 | return nil 127 | } 128 | 129 | var numLen = [3]int{10, 12, 14} 130 | 131 | func (s Num) Bits(v Version) int { 132 | return 4 + numLen[v.sizeClass()] + (10*len(s)+2)/3 133 | } 134 | 135 | func (s Num) Encode(b *Bits, v Version) { 136 | b.Write(1, 4) 137 | b.Write(uint(len(s)), numLen[v.sizeClass()]) 138 | var i int 139 | for i = 0; i+3 <= len(s); i += 3 { 140 | w := uint(s[i]-'0')*100 + uint(s[i+1]-'0')*10 + uint(s[i+2]-'0') 141 | b.Write(w, 10) 142 | } 143 | switch len(s) - i { 144 | case 1: 145 | w := uint(s[i] - '0') 146 | b.Write(w, 4) 147 | case 2: 148 | w := uint(s[i]-'0')*10 + uint(s[i+1]-'0') 149 | b.Write(w, 7) 150 | } 151 | } 152 | 153 | // Alpha is the encoding for alphanumeric data. 154 | // The valid characters are 0-9A-Z$%*+-./: and space. 155 | type Alpha string 156 | 157 | const alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:" 158 | 159 | func (s Alpha) String() string { 160 | return fmt.Sprintf("Alpha(%#q)", string(s)) 161 | } 162 | 163 | func (s Alpha) Check() error { 164 | for _, c := range s { 165 | if strings.IndexRune(alphabet, c) < 0 { 166 | return fmt.Errorf("non-alphanumeric string %#q", string(s)) 167 | } 168 | } 169 | return nil 170 | } 171 | 172 | var alphaLen = [3]int{9, 11, 13} 173 | 174 | func (s Alpha) Bits(v Version) int { 175 | return 4 + alphaLen[v.sizeClass()] + (11*len(s)+1)/2 176 | } 177 | 178 | func (s Alpha) Encode(b *Bits, v Version) { 179 | b.Write(2, 4) 180 | b.Write(uint(len(s)), alphaLen[v.sizeClass()]) 181 | var i int 182 | for i = 0; i+2 <= len(s); i += 2 { 183 | w := uint(strings.IndexRune(alphabet, rune(s[i])))*45 + 184 | uint(strings.IndexRune(alphabet, rune(s[i+1]))) 185 | b.Write(w, 11) 186 | } 187 | 188 | if i < len(s) { 189 | w := uint(strings.IndexRune(alphabet, rune(s[i]))) 190 | b.Write(w, 6) 191 | } 192 | } 193 | 194 | // String is the encoding for 8-bit data. All bytes are valid. 195 | type String string 196 | 197 | func (s String) String() string { 198 | return fmt.Sprintf("String(%#q)", string(s)) 199 | } 200 | 201 | func (s String) Check() error { 202 | return nil 203 | } 204 | 205 | var stringLen = [3]int{8, 16, 16} 206 | 207 | func (s String) Bits(v Version) int { 208 | return 4 + stringLen[v.sizeClass()] + 8*len(s) 209 | } 210 | 211 | func (s String) Encode(b *Bits, v Version) { 212 | b.Write(4, 4) 213 | b.Write(uint(len(s)), stringLen[v.sizeClass()]) 214 | for i := 0; i < len(s); i++ { 215 | b.Write(uint(s[i]), 8) 216 | } 217 | } 218 | 219 | // A Pixel describes a single pixel in a QR code. 220 | type Pixel uint32 221 | 222 | const ( 223 | Black Pixel = 1 << iota 224 | Invert 225 | ) 226 | 227 | func (p Pixel) Offset() uint { 228 | return uint(p >> 6) 229 | } 230 | 231 | func OffsetPixel(o uint) Pixel { 232 | return Pixel(o << 6) 233 | } 234 | 235 | func (r PixelRole) Pixel() Pixel { 236 | return Pixel(r << 2) 237 | } 238 | 239 | func (p Pixel) Role() PixelRole { 240 | return PixelRole(p>>2) & 15 241 | } 242 | 243 | func (p Pixel) String() string { 244 | s := p.Role().String() 245 | if p&Black != 0 { 246 | s += "+black" 247 | } 248 | if p&Invert != 0 { 249 | s += "+invert" 250 | } 251 | s += "+" + strconv.FormatUint(uint64(p.Offset()), 10) 252 | return s 253 | } 254 | 255 | // A PixelRole describes the role of a QR pixel. 256 | type PixelRole uint32 257 | 258 | const ( 259 | _ PixelRole = iota 260 | Position // position squares (large) 261 | Alignment // alignment squares (small) 262 | Timing // timing strip between position squares 263 | Format // format metadata 264 | PVersion // version pattern 265 | Unused // unused pixel 266 | Data // data bit 267 | Check // error correction check bit 268 | Extra 269 | ) 270 | 271 | var roles = []string{ 272 | "", 273 | "position", 274 | "alignment", 275 | "timing", 276 | "format", 277 | "pversion", 278 | "unused", 279 | "data", 280 | "check", 281 | "extra", 282 | } 283 | 284 | func (r PixelRole) String() string { 285 | if Position <= r && r <= Check { 286 | return roles[r] 287 | } 288 | return strconv.Itoa(int(r)) 289 | } 290 | 291 | // A Level represents a QR error correction level. 292 | // From least to most tolerant of errors, they are L, M, Q, H. 293 | type Level int 294 | 295 | const ( 296 | L Level = iota 297 | M 298 | Q 299 | H 300 | ) 301 | 302 | func (l Level) String() string { 303 | if L <= l && l <= H { 304 | return "LMQH"[l : l+1] 305 | } 306 | return strconv.Itoa(int(l)) 307 | } 308 | 309 | // A Code is a square pixel grid. 310 | type Code struct { 311 | Bitmap []byte // 1 is black, 0 is white 312 | Size int // number of pixels on a side 313 | Stride int // number of bytes per row 314 | } 315 | 316 | func (c *Code) Black(x, y int) bool { 317 | return 0 <= x && x < c.Size && 0 <= y && y < c.Size && 318 | c.Bitmap[y*c.Stride+x/8]&(1<= pad { 394 | break 395 | } 396 | b.Write(0x11, 8) 397 | } 398 | } 399 | } 400 | 401 | func (b *Bits) AddCheckBytes(v Version, l Level) { 402 | nd := v.DataBytes(l) 403 | if b.nbit < nd*8 { 404 | b.Pad(nd*8 - b.nbit) 405 | } 406 | if b.nbit != nd*8 { 407 | panic("qr: too much data") 408 | } 409 | 410 | dat := b.Bytes() 411 | vt := &vtab[v] 412 | lev := &vt.level[l] 413 | db := nd / lev.nblock 414 | extra := nd % lev.nblock 415 | chk := make([]byte, lev.check) 416 | rs := gf256.NewRSEncoder(Field, lev.check) 417 | for i := 0; i < lev.nblock; i++ { 418 | if i == lev.nblock-extra { 419 | db++ 420 | } 421 | rs.ECC(dat[:db], chk) 422 | b.Append(chk) 423 | dat = dat[db:] 424 | } 425 | 426 | if len(b.Bytes()) != vt.bytes { 427 | panic("qr: internal error") 428 | } 429 | } 430 | 431 | func (p *Plan) Encode(text ...Encoding) (*Code, error) { 432 | var b Bits 433 | for _, t := range text { 434 | if err := t.Check(); err != nil { 435 | return nil, err 436 | } 437 | t.Encode(&b, p.Version) 438 | } 439 | if b.Bits() > p.DataBytes*8 { 440 | return nil, fmt.Errorf("cannot encode %d bits into %d-bit code", b.Bits(), p.DataBytes*8) 441 | } 442 | b.AddCheckBytes(p.Version, p.Level) 443 | bytes := b.Bytes() 444 | 445 | // Now we have the checksum bytes and the data bytes. 446 | // Construct the actual code. 447 | c := &Code{Size: len(p.Pixel), Stride: (len(p.Pixel) + 7) &^ 7} 448 | c.Bitmap = make([]byte, c.Stride*c.Size) 449 | crow := c.Bitmap 450 | for _, row := range p.Pixel { 451 | for x, pix := range row { 452 | switch pix.Role() { 453 | case Data, Check: 454 | o := pix.Offset() 455 | if bytes[o/8]&(1< 40 { 539 | return nil, fmt.Errorf("invalid QR version %d", int(v)) 540 | } 541 | siz := 17 + int(v)*4 542 | m := grid(siz) 543 | p.Pixel = m 544 | 545 | // Timing markers (overwritten by boxes). 546 | const ti = 6 // timing is in row/column 6 (counting from 0) 547 | for i := range m { 548 | p := Timing.Pixel() 549 | if i&1 == 0 { 550 | p |= Black 551 | } 552 | m[i][ti] = p 553 | m[ti][i] = p 554 | } 555 | 556 | // Position boxes. 557 | posBox(m, 0, 0) 558 | posBox(m, siz-7, 0) 559 | posBox(m, 0, siz-7) 560 | 561 | // Alignment boxes. 562 | info := &vtab[v] 563 | for x := 4; x+5 < siz; { 564 | for y := 4; y+5 < siz; { 565 | // don't overwrite timing markers 566 | if (x < 7 && y < 7) || (x < 7 && y+5 >= siz-7) || (x+5 >= siz-7 && y < 7) { 567 | } else { 568 | alignBox(m, x, y) 569 | } 570 | if y == 4 { 571 | y = info.apos 572 | } else { 573 | y += info.astride 574 | } 575 | } 576 | if x == 4 { 577 | x = info.apos 578 | } else { 579 | x += info.astride 580 | } 581 | } 582 | 583 | // Version pattern. 584 | pat := vtab[v].pattern 585 | if pat != 0 { 586 | v := pat 587 | for x := 0; x < 6; x++ { 588 | for y := 0; y < 3; y++ { 589 | p := PVersion.Pixel() 590 | if v&1 != 0 { 591 | p |= Black 592 | } 593 | m[siz-11+y][x] = p 594 | m[x][siz-11+y] = p 595 | v >>= 1 596 | } 597 | } 598 | } 599 | 600 | // One lonely black pixel 601 | m[siz-8][8] = Unused.Pixel() | Black 602 | 603 | return p, nil 604 | } 605 | 606 | // fplan adds the format pixels 607 | func fplan(l Level, m Mask, p *Plan) error { 608 | // Format pixels. 609 | fb := uint32(l^1) << 13 // level: L=01, M=00, Q=11, H=10 610 | fb |= uint32(m) << 10 // mask 611 | const formatPoly = 0x537 612 | rem := fb 613 | for i := 14; i >= 10; i-- { 614 | if rem&(1<>i)&1 == 1 { 624 | pix |= Black 625 | } 626 | if (invert>>i)&1 == 1 { 627 | pix ^= Invert | Black 628 | } 629 | // top left 630 | switch { 631 | case i < 6: 632 | p.Pixel[i][8] = pix 633 | case i < 8: 634 | p.Pixel[i+1][8] = pix 635 | case i < 9: 636 | p.Pixel[8][7] = pix 637 | default: 638 | p.Pixel[8][14-i] = pix 639 | } 640 | // bottom right 641 | switch { 642 | case i < 8: 643 | p.Pixel[8][siz-1-int(i)] = pix 644 | default: 645 | p.Pixel[siz-1-int(14-i)][8] = pix 646 | } 647 | } 648 | return nil 649 | } 650 | 651 | // lplan edits a version-only Plan to add information 652 | // about the error correction levels. 653 | func lplan(v Version, l Level, p *Plan) error { 654 | p.Level = l 655 | 656 | nblock := vtab[v].level[l].nblock 657 | ne := vtab[v].level[l].check 658 | nde := (vtab[v].bytes - ne*nblock) / nblock 659 | extra := (vtab[v].bytes - ne*nblock) % nblock 660 | dataBits := (nde*nblock + extra) * 8 661 | checkBits := ne * nblock * 8 662 | 663 | p.DataBytes = vtab[v].bytes - ne*nblock 664 | p.CheckBytes = ne * nblock 665 | p.Blocks = nblock 666 | 667 | // Make data + checksum pixels. 668 | data := make([]Pixel, dataBits) 669 | for i := range data { 670 | data[i] = Data.Pixel() | OffsetPixel(uint(i)) 671 | } 672 | check := make([]Pixel, checkBits) 673 | for i := range check { 674 | check[i] = Check.Pixel() | OffsetPixel(uint(i+dataBits)) 675 | } 676 | 677 | // Split into blocks. 678 | dataList := make([][]Pixel, nblock) 679 | checkList := make([][]Pixel, nblock) 680 | for i := 0; i < nblock; i++ { 681 | // The last few blocks have an extra data byte (8 pixels). 682 | nd := nde 683 | if i >= nblock-extra { 684 | nd++ 685 | } 686 | dataList[i], data = data[0:nd*8], data[nd*8:] 687 | checkList[i], check = check[0:ne*8], check[ne*8:] 688 | } 689 | if len(data) != 0 || len(check) != 0 { 690 | panic("data/check math") 691 | } 692 | 693 | // Build up bit sequence, taking first byte of each block, 694 | // then second byte, and so on. Then checksums. 695 | bits := make([]Pixel, dataBits+checkBits) 696 | dst := bits 697 | for i := 0; i < nde+1; i++ { 698 | for _, b := range dataList { 699 | if i*8 < len(b) { 700 | copy(dst, b[i*8:(i+1)*8]) 701 | dst = dst[8:] 702 | } 703 | } 704 | } 705 | for i := 0; i < ne; i++ { 706 | for _, b := range checkList { 707 | if i*8 < len(b) { 708 | copy(dst, b[i*8:(i+1)*8]) 709 | dst = dst[8:] 710 | } 711 | } 712 | } 713 | if len(dst) != 0 { 714 | panic("dst math") 715 | } 716 | 717 | // Sweep up pair of columns, 718 | // then down, assigning to right then left pixel. 719 | // Repeat. 720 | // See Figure 2 of http://www.pclviewer.com/rs2/qrtopology.htm 721 | siz := len(p.Pixel) 722 | rem := make([]Pixel, 7) 723 | for i := range rem { 724 | rem[i] = Extra.Pixel() 725 | } 726 | src := append(bits, rem...) 727 | for x := siz; x > 0; { 728 | for y := siz - 1; y >= 0; y-- { 729 | if p.Pixel[y][x-1].Role() == 0 { 730 | p.Pixel[y][x-1], src = src[0], src[1:] 731 | } 732 | if p.Pixel[y][x-2].Role() == 0 { 733 | p.Pixel[y][x-2], src = src[0], src[1:] 734 | } 735 | } 736 | x -= 2 737 | if x == 7 { // vertical timing strip 738 | x-- 739 | } 740 | for y := 0; y < siz; y++ { 741 | if p.Pixel[y][x-1].Role() == 0 { 742 | p.Pixel[y][x-1], src = src[0], src[1:] 743 | } 744 | if p.Pixel[y][x-2].Role() == 0 { 745 | p.Pixel[y][x-2], src = src[0], src[1:] 746 | } 747 | } 748 | x -= 2 749 | } 750 | return nil 751 | } 752 | 753 | // mplan edits a version+level-only Plan to add the mask. 754 | func mplan(m Mask, p *Plan) error { 755 | p.Mask = m 756 | for y, row := range p.Pixel { 757 | for x, pix := range row { 758 | if r := pix.Role(); (r == Data || r == Check || r == Extra) && p.Mask.Invert(y, x) { 759 | row[x] ^= Black | Invert 760 | } 761 | } 762 | } 763 | return nil 764 | } 765 | 766 | // posBox draws a position (large) box at upper left x, y. 767 | func posBox(m [][]Pixel, x, y int) { 768 | pos := Position.Pixel() 769 | // box 770 | for dy := 0; dy < 7; dy++ { 771 | for dx := 0; dx < 7; dx++ { 772 | p := pos 773 | if dx == 0 || dx == 6 || dy == 0 || dy == 6 || 2 <= dx && dx <= 4 && 2 <= dy && dy <= 4 { 774 | p |= Black 775 | } 776 | m[y+dy][x+dx] = p 777 | } 778 | } 779 | // white border 780 | for dy := -1; dy < 8; dy++ { 781 | if 0 <= y+dy && y+dy < len(m) { 782 | if x > 0 { 783 | m[y+dy][x-1] = pos 784 | } 785 | if x+7 < len(m) { 786 | m[y+dy][x+7] = pos 787 | } 788 | } 789 | } 790 | for dx := -1; dx < 8; dx++ { 791 | if 0 <= x+dx && x+dx < len(m) { 792 | if y > 0 { 793 | m[y-1][x+dx] = pos 794 | } 795 | if y+7 < len(m) { 796 | m[y+7][x+dx] = pos 797 | } 798 | } 799 | } 800 | } 801 | 802 | // alignBox draw an alignment (small) box at upper left x, y. 803 | func alignBox(m [][]Pixel, x, y int) { 804 | // box 805 | align := Alignment.Pixel() 806 | for dy := 0; dy < 5; dy++ { 807 | for dx := 0; dx < 5; dx++ { 808 | p := align 809 | if dx == 0 || dx == 4 || dy == 0 || dy == 4 || dx == 2 && dy == 2 { 810 | p |= Black 811 | } 812 | m[y+dy][x+dx] = p 813 | } 814 | } 815 | } 816 | -------------------------------------------------------------------------------- /vendor/odeke-em/qr/web/play.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | QR data layout 7 | 8 | qr/ 9 | upload/ 10 | id.png 11 | id.fix 12 | flag/ 13 | id 14 | 15 | */ 16 | // TODO: Random seed taken from GET for caching, repeatability. 17 | // TODO: Flag for abuse button + some kind of dashboard. 18 | // TODO: +1 button on web page? permalink? 19 | // TODO: Flag for abuse button on permalinks too? 20 | // TODO: Make the page prettier. 21 | // TODO: Cache headers. 22 | 23 | package web 24 | 25 | import ( 26 | "bytes" 27 | "crypto/md5" 28 | "encoding/base64" 29 | "encoding/json" 30 | "fmt" 31 | "html/template" 32 | "image" 33 | "image/color" 34 | _ "image/gif" 35 | _ "image/jpeg" 36 | "image/png" 37 | "io" 38 | "math/rand" 39 | "net/http" 40 | "net/url" 41 | "os" 42 | "sort" 43 | "strconv" 44 | "strings" 45 | "time" 46 | 47 | "odeke-em/appfs/fs" 48 | "odeke-em/gf256" 49 | "odeke-em/qr" 50 | "odeke-em/qr/coding" 51 | "odeke-em/qr/web/resize" 52 | ) 53 | 54 | func runTemplate(c *fs.Context, w http.ResponseWriter, name string, data interface{}) { 55 | t := template.New("main") 56 | 57 | main, _, err := c.Read(name) 58 | if err != nil { 59 | panic(err) 60 | } 61 | style, _, _ := c.Read("style.html") 62 | main = append(main, style...) 63 | _, err = t.Parse(string(main)) 64 | if err != nil { 65 | panic(err) 66 | } 67 | 68 | var buf bytes.Buffer 69 | if err := t.Execute(&buf, &data); err != nil { 70 | panic(err) 71 | } 72 | w.Write(buf.Bytes()) 73 | } 74 | 75 | func isImgName(s string) bool { 76 | if len(s) != 32 { 77 | return false 78 | } 79 | for i := 0; i < len(s); i++ { 80 | if '0' <= s[i] && s[i] <= '9' || 'a' <= s[i] && s[i] <= 'f' { 81 | continue 82 | } 83 | return false 84 | } 85 | return true 86 | } 87 | 88 | func isTagName(s string) bool { 89 | if len(s) != 16 { 90 | return false 91 | } 92 | for i := 0; i < len(s); i++ { 93 | if '0' <= s[i] && s[i] <= '9' || 'a' <= s[i] && s[i] <= 'f' { 94 | continue 95 | } 96 | return false 97 | } 98 | return true 99 | } 100 | 101 | // Draw is the handler for drawing a QR code. 102 | func Draw(w http.ResponseWriter, req *http.Request) { 103 | ctxt := fs.NewContext(req) 104 | 105 | url := req.FormValue("url") 106 | if url == "" { 107 | url = "http://swtch.com/qr" 108 | } 109 | if req.FormValue("upload") == "1" { 110 | upload(w, req, url) 111 | return 112 | } 113 | 114 | t0 := time.Now() 115 | img := req.FormValue("i") 116 | if !isImgName(img) { 117 | img = "pjw" 118 | } 119 | if req.FormValue("show") == "png" { 120 | i := loadSize(ctxt, img, 48) 121 | var buf bytes.Buffer 122 | png.Encode(&buf, i) 123 | w.Write(buf.Bytes()) 124 | return 125 | } 126 | if req.FormValue("flag") == "1" { 127 | flag(w, req, img, ctxt) 128 | return 129 | } 130 | if req.FormValue("x") == "" { 131 | var data = struct { 132 | Name string 133 | URL string 134 | }{ 135 | Name: img, 136 | URL: url, 137 | } 138 | runTemplate(ctxt, w, "qr/main.html", &data) 139 | return 140 | } 141 | 142 | arg := func(s string) int { x, _ := strconv.Atoi(req.FormValue(s)); return x } 143 | targ := makeTarg(ctxt, img, 17+4*arg("v")+arg("z")) 144 | 145 | m := &Image{ 146 | Name: img, 147 | Dx: arg("x"), 148 | Dy: arg("y"), 149 | URL: req.FormValue("u"), 150 | Version: arg("v"), 151 | Mask: arg("m"), 152 | RandControl: arg("r") > 0, 153 | Dither: arg("i") > 0, 154 | OnlyDataBits: arg("d") > 0, 155 | SaveControl: arg("c") > 0, 156 | Scale: arg("scale"), 157 | Target: targ, 158 | Seed: int64(arg("s")), 159 | Rotation: arg("o"), 160 | Size: arg("z"), 161 | } 162 | if m.Version > 8 { 163 | m.Version = 8 164 | } 165 | 166 | if m.Scale == 0 { 167 | if arg("l") > 1 { 168 | m.Scale = 8 169 | } else { 170 | m.Scale = 4 171 | } 172 | } 173 | if m.Version >= 12 && m.Scale >= 4 { 174 | m.Scale /= 2 175 | } 176 | 177 | if arg("l") == 1 { 178 | data, err := json.Marshal(m) 179 | if err != nil { 180 | panic(err) 181 | } 182 | h := md5.New() 183 | h.Write(data) 184 | tag := fmt.Sprintf("%x", h.Sum(nil))[:16] 185 | if err := ctxt.Write("qrsave/"+tag, data); err != nil { 186 | panic(err) 187 | } 188 | http.Redirect(w, req, "/qr/show/"+tag, http.StatusTemporaryRedirect) 189 | return 190 | } 191 | 192 | if err := m.Encode(req); err != nil { 193 | fmt.Fprintf(w, "%s\n", err) 194 | return 195 | } 196 | 197 | var dat []byte 198 | switch { 199 | case m.SaveControl: 200 | dat = m.Control 201 | default: 202 | dat = m.Code.PNG() 203 | } 204 | 205 | if arg("l") > 0 { 206 | w.Header().Set("Content-Type", "image/png") 207 | w.Write(dat) 208 | return 209 | } 210 | 211 | w.Header().Set("Content-Type", "text/html; charset=utf-8") 212 | fmt.Fprint(w, "

") 215 | fmt.Fprintf(w, "
\n", m.Link()) 216 | fmt.Fprintf(w, "
\n") 217 | fmt.Fprintf(w, "
%v
\n", time.Now().Sub(t0)) 218 | } 219 | 220 | func (m *Image) Small() bool { 221 | return 8*(17+4*int(m.Version)) < 512 222 | } 223 | 224 | func (m *Image) Link() string { 225 | s := fmt.Sprint 226 | b := func(v bool) string { 227 | if v { 228 | return "1" 229 | } 230 | return "0" 231 | } 232 | val := url.Values{ 233 | "i": {m.Name}, 234 | "x": {s(m.Dx)}, 235 | "y": {s(m.Dy)}, 236 | "z": {s(m.Size)}, 237 | "u": {m.URL}, 238 | "v": {s(m.Version)}, 239 | "m": {s(m.Mask)}, 240 | "r": {b(m.RandControl)}, 241 | "t": {b(m.Dither)}, 242 | "d": {b(m.OnlyDataBits)}, 243 | "c": {b(m.SaveControl)}, 244 | "s": {s(m.Seed)}, 245 | } 246 | return "/qr/draw?" + val.Encode() 247 | } 248 | 249 | // Show is the handler for showing a stored QR code. 250 | func Show(w http.ResponseWriter, req *http.Request) { 251 | ctxt := fs.NewContext(req) 252 | tag := req.URL.Path[len("/qr/show/"):] 253 | png := strings.HasSuffix(tag, ".png") 254 | if png { 255 | tag = tag[:len(tag)-len(".png")] 256 | } 257 | if !isTagName(tag) { 258 | fmt.Fprintf(w, "Sorry, QR code not found\n") 259 | return 260 | } 261 | if req.FormValue("flag") == "1" { 262 | flag(w, req, tag, ctxt) 263 | return 264 | } 265 | data, _, err := ctxt.Read("qrsave/" + tag) 266 | if err != nil { 267 | fmt.Fprintf(w, "Sorry, QR code not found.\n") 268 | return 269 | } 270 | 271 | var m Image 272 | if err := json.Unmarshal(data, &m); err != nil { 273 | panic(err) 274 | } 275 | m.Tag = tag 276 | 277 | switch req.FormValue("size") { 278 | case "big": 279 | m.Scale *= 2 280 | case "small": 281 | m.Scale /= 2 282 | } 283 | 284 | if png { 285 | if err := m.Encode(req); err != nil { 286 | panic(err) 287 | return 288 | } 289 | w.Header().Set("Cache-Control", "public, max-age=3600") 290 | w.Write(m.Code.PNG()) 291 | return 292 | } 293 | 294 | w.Header().Set("Cache-Control", "public, max-age=300") 295 | runTemplate(ctxt, w, "qr/permalink.html", &m) 296 | } 297 | 298 | func upload(w http.ResponseWriter, req *http.Request, link string) { 299 | // Upload of a new image. 300 | // Copied from Moustachio demo. 301 | f, _, err := req.FormFile("image") 302 | if err != nil { 303 | fmt.Fprintf(w, "You need to select an image to upload.\n") 304 | return 305 | } 306 | defer f.Close() 307 | 308 | i, _, err := image.Decode(f) 309 | if err != nil { 310 | panic(err) 311 | } 312 | 313 | // Convert image to 128x128 gray+alpha. 314 | b := i.Bounds() 315 | const max = 128 316 | // If it's gigantic, it's more efficient to downsample first 317 | // and then resize; resizing will smooth out the roughness. 318 | var i1 *image.RGBA 319 | if b.Dx() > 4*max || b.Dy() > 4*max { 320 | w, h := 2*max, 2*max 321 | if b.Dx() > b.Dy() { 322 | h = b.Dy() * h / b.Dx() 323 | } else { 324 | w = b.Dx() * w / b.Dy() 325 | } 326 | i1 = resize.Resample(i, b, w, h) 327 | } else { 328 | // "Resample" to same size, just to convert to RGBA. 329 | i1 = resize.Resample(i, b, b.Dx(), b.Dy()) 330 | } 331 | b = i1.Bounds() 332 | 333 | // Encode to PNG. 334 | dx, dy := 128, 128 335 | if b.Dx() > b.Dy() { 336 | dy = b.Dy() * dx / b.Dx() 337 | } else { 338 | dx = b.Dx() * dy / b.Dy() 339 | } 340 | i128 := resize.ResizeRGBA(i1, i1.Bounds(), dx, dy) 341 | 342 | var buf bytes.Buffer 343 | if err := png.Encode(&buf, i128); err != nil { 344 | panic(err) 345 | } 346 | 347 | h := md5.New() 348 | h.Write(buf.Bytes()) 349 | tag := fmt.Sprintf("%x", h.Sum(nil))[:32] 350 | 351 | ctxt := fs.NewContext(req) 352 | if err := ctxt.Write("qr/upload/"+tag+".png", buf.Bytes()); err != nil { 353 | panic(err) 354 | } 355 | 356 | // Redirect with new image tag. 357 | // Redirect to draw with new image tag. 358 | http.Redirect(w, req, req.URL.Path+"?"+url.Values{"i": {tag}, "url": {link}}.Encode(), 302) 359 | } 360 | 361 | func flag(w http.ResponseWriter, req *http.Request, img string, ctxt *fs.Context) { 362 | if !isImgName(img) && !isTagName(img) { 363 | fmt.Fprintf(w, "Invalid image.\n") 364 | return 365 | } 366 | data, _, _ := ctxt.Read("qr/flag/" + img) 367 | data = append(data, '!') 368 | ctxt.Write("qr/flag/"+img, data) 369 | 370 | fmt.Fprintf(w, "Thank you. The image has been reported.\n") 371 | } 372 | 373 | func loadSize(ctxt *fs.Context, name string, max int) *image.RGBA { 374 | data, _, err := ctxt.Read("qr/upload/" + name + ".png") 375 | if err != nil { 376 | panic(err) 377 | } 378 | i, _, err := image.Decode(bytes.NewBuffer(data)) 379 | if err != nil { 380 | panic(err) 381 | } 382 | b := i.Bounds() 383 | dx, dy := max, max 384 | if b.Dx() > b.Dy() { 385 | dy = b.Dy() * dx / b.Dx() 386 | } else { 387 | dx = b.Dx() * dy / b.Dy() 388 | } 389 | var irgba *image.RGBA 390 | switch i := i.(type) { 391 | case *image.RGBA: 392 | irgba = resize.ResizeRGBA(i, i.Bounds(), dx, dy) 393 | case *image.NRGBA: 394 | irgba = resize.ResizeNRGBA(i, i.Bounds(), dx, dy) 395 | } 396 | return irgba 397 | } 398 | 399 | func makeTarg(ctxt *fs.Context, name string, max int) [][]int { 400 | i := loadSize(ctxt, name, max) 401 | b := i.Bounds() 402 | dx, dy := b.Dx(), b.Dy() 403 | targ := make([][]int, dy) 404 | arr := make([]int, dx*dy) 405 | for y := 0; y < dy; y++ { 406 | targ[y], arr = arr[:dx], arr[dx:] 407 | row := targ[y] 408 | for x := 0; x < dx; x++ { 409 | p := i.Pix[y*i.Stride+4*x:] 410 | r, g, b, a := p[0], p[1], p[2], p[3] 411 | if a == 0 { 412 | row[x] = -1 413 | } else { 414 | row[x] = int((299*uint32(r) + 587*uint32(g) + 114*uint32(b) + 500) / 1000) 415 | } 416 | } 417 | } 418 | return targ 419 | } 420 | 421 | type Image struct { 422 | Name string 423 | Target [][]int 424 | Dx int 425 | Dy int 426 | URL string 427 | Tag string 428 | Version int 429 | Mask int 430 | Scale int 431 | Rotation int 432 | Size int 433 | 434 | // RandControl says to pick the pixels randomly. 435 | RandControl bool 436 | Seed int64 437 | 438 | // Dither says to dither instead of using threshold pixel layout. 439 | Dither bool 440 | 441 | // OnlyDataBits says to use only data bits, not check bits. 442 | OnlyDataBits bool 443 | 444 | // Code is the final QR code. 445 | Code *qr.Code 446 | 447 | // Control is a PNG showing the pixels that we controlled. 448 | // Pixels we don't control are grayed out. 449 | SaveControl bool 450 | Control []byte 451 | } 452 | 453 | type Pixinfo struct { 454 | X int 455 | Y int 456 | Pix coding.Pixel 457 | Targ byte 458 | DTarg int 459 | Contrast int 460 | HardZero bool 461 | Block *BitBlock 462 | Bit uint 463 | } 464 | 465 | type Pixorder struct { 466 | Off int 467 | Priority int 468 | } 469 | 470 | type byPriority []Pixorder 471 | 472 | func (x byPriority) Len() int { return len(x) } 473 | func (x byPriority) Swap(i, j int) { x[i], x[j] = x[j], x[i] } 474 | func (x byPriority) Less(i, j int) bool { return x[i].Priority > x[j].Priority } 475 | 476 | func (m *Image) target(x, y int) (targ byte, contrast int) { 477 | tx := x + m.Dx 478 | ty := y + m.Dy 479 | if ty < 0 || ty >= len(m.Target) || tx < 0 || tx >= len(m.Target[ty]) { 480 | return 255, -1 481 | } 482 | 483 | v0 := m.Target[ty][tx] 484 | if v0 < 0 { 485 | return 255, -1 486 | } 487 | targ = byte(v0) 488 | 489 | n := 0 490 | sum := 0 491 | sumsq := 0 492 | const del = 5 493 | for dy := -del; dy <= del; dy++ { 494 | for dx := -del; dx <= del; dx++ { 495 | if 0 <= ty+dy && ty+dy < len(m.Target) && 0 <= tx+dx && tx+dx < len(m.Target[ty+dy]) { 496 | v := m.Target[ty+dy][tx+dx] 497 | sum += v 498 | sumsq += v * v 499 | n++ 500 | } 501 | } 502 | } 503 | 504 | avg := sum / n 505 | contrast = sumsq/n - avg*avg 506 | return 507 | } 508 | 509 | func (m *Image) rotate(p *coding.Plan, rot int) { 510 | if rot == 0 { 511 | return 512 | } 513 | 514 | N := len(p.Pixel) 515 | pix := make([][]coding.Pixel, N) 516 | apix := make([]coding.Pixel, N*N) 517 | for i := range pix { 518 | pix[i], apix = apix[:N], apix[N:] 519 | } 520 | 521 | switch rot { 522 | case 0: 523 | // ok 524 | case 1: 525 | for y := 0; y < N; y++ { 526 | for x := 0; x < N; x++ { 527 | pix[y][x] = p.Pixel[x][N-1-y] 528 | } 529 | } 530 | case 2: 531 | for y := 0; y < N; y++ { 532 | for x := 0; x < N; x++ { 533 | pix[y][x] = p.Pixel[N-1-y][N-1-x] 534 | } 535 | } 536 | case 3: 537 | for y := 0; y < N; y++ { 538 | for x := 0; x < N; x++ { 539 | pix[y][x] = p.Pixel[N-1-x][y] 540 | } 541 | } 542 | } 543 | 544 | p.Pixel = pix 545 | } 546 | 547 | func (m *Image) Encode(req *http.Request) error { 548 | p, err := coding.NewPlan(coding.Version(m.Version), coding.L, coding.Mask(m.Mask)) 549 | if err != nil { 550 | return err 551 | } 552 | 553 | m.rotate(p, m.Rotation) 554 | 555 | rand := rand.New(rand.NewSource(m.Seed)) 556 | 557 | // QR parameters. 558 | nd := p.DataBytes / p.Blocks 559 | nc := p.CheckBytes / p.Blocks 560 | extra := p.DataBytes - nd*p.Blocks 561 | rs := gf256.NewRSEncoder(coding.Field, nc) 562 | 563 | // Build information about pixels, indexed by data/check bit number. 564 | pixByOff := make([]Pixinfo, (p.DataBytes+p.CheckBytes)*8) 565 | expect := make([][]bool, len(p.Pixel)) 566 | for y, row := range p.Pixel { 567 | expect[y] = make([]bool, len(row)) 568 | for x, pix := range row { 569 | targ, contrast := m.target(x, y) 570 | if m.RandControl && contrast >= 0 { 571 | contrast = rand.Intn(128) + 64*((x+y)%2) + 64*((x+y)%3%2) 572 | } 573 | expect[y][x] = pix&coding.Black != 0 574 | if r := pix.Role(); r == coding.Data || r == coding.Check { 575 | pixByOff[pix.Offset()] = Pixinfo{X: x, Y: y, Pix: pix, Targ: targ, Contrast: contrast} 576 | } 577 | } 578 | } 579 | 580 | Again: 581 | // Count fixed initial data bits, prepare template URL. 582 | url := m.URL + "#" 583 | var b coding.Bits 584 | coding.String(url).Encode(&b, p.Version) 585 | coding.Num("").Encode(&b, p.Version) 586 | bbit := b.Bits() 587 | dbit := p.DataBytes*8 - bbit 588 | if dbit < 0 { 589 | return fmt.Errorf("cannot encode URL into available bits") 590 | } 591 | num := make([]byte, dbit/10*3) 592 | for i := range num { 593 | num[i] = '0' 594 | } 595 | b.Pad(dbit) 596 | b.Reset() 597 | coding.String(url).Encode(&b, p.Version) 598 | coding.Num(num).Encode(&b, p.Version) 599 | b.AddCheckBytes(p.Version, p.Level) 600 | data := b.Bytes() 601 | 602 | doff := 0 // data offset 603 | coff := 0 // checksum offset 604 | mbit := bbit + dbit/10*10 605 | 606 | // Choose pixels. 607 | bitblocks := make([]*BitBlock, p.Blocks) 608 | for blocknum := 0; blocknum < p.Blocks; blocknum++ { 609 | if blocknum == p.Blocks-extra { 610 | nd++ 611 | } 612 | 613 | bdata := data[doff/8 : doff/8+nd] 614 | cdata := data[p.DataBytes+coff/8 : p.DataBytes+coff/8+nc] 615 | bb := newBlock(nd, nc, rs, bdata, cdata) 616 | bitblocks[blocknum] = bb 617 | 618 | // Determine which bits in this block we can try to edit. 619 | lo, hi := 0, nd*8 620 | if lo < bbit-doff { 621 | lo = bbit - doff 622 | if lo > hi { 623 | lo = hi 624 | } 625 | } 626 | if hi > mbit-doff { 627 | hi = mbit - doff 628 | if hi < lo { 629 | hi = lo 630 | } 631 | } 632 | 633 | // Preserve [0, lo) and [hi, nd*8). 634 | for i := 0; i < lo; i++ { 635 | if !bb.canSet(uint(i), (bdata[i/8]>>uint(7-i&7))&1) { 636 | return fmt.Errorf("cannot preserve required bits") 637 | } 638 | } 639 | for i := hi; i < nd*8; i++ { 640 | if !bb.canSet(uint(i), (bdata[i/8]>>uint(7-i&7))&1) { 641 | return fmt.Errorf("cannot preserve required bits") 642 | } 643 | } 644 | 645 | // Can edit [lo, hi) and checksum bits to hit target. 646 | // Determine which ones to try first. 647 | order := make([]Pixorder, (hi-lo)+nc*8) 648 | for i := lo; i < hi; i++ { 649 | order[i-lo].Off = doff + i 650 | } 651 | for i := 0; i < nc*8; i++ { 652 | order[hi-lo+i].Off = p.DataBytes*8 + coff + i 653 | } 654 | if m.OnlyDataBits { 655 | order = order[:hi-lo] 656 | } 657 | for i := range order { 658 | po := &order[i] 659 | po.Priority = pixByOff[po.Off].Contrast<<8 | rand.Intn(256) 660 | } 661 | sort.Sort(byPriority(order)) 662 | 663 | const mark = false 664 | for i := range order { 665 | po := &order[i] 666 | pinfo := &pixByOff[po.Off] 667 | bval := pinfo.Targ 668 | if bval < 128 { 669 | bval = 1 670 | } else { 671 | bval = 0 672 | } 673 | pix := pinfo.Pix 674 | if pix&coding.Invert != 0 { 675 | bval ^= 1 676 | } 677 | if pinfo.HardZero { 678 | bval = 0 679 | } 680 | 681 | var bi int 682 | if pix.Role() == coding.Data { 683 | bi = po.Off - doff 684 | } else { 685 | bi = po.Off - p.DataBytes*8 - coff + nd*8 686 | } 687 | if bb.canSet(uint(bi), bval) { 688 | pinfo.Block = bb 689 | pinfo.Bit = uint(bi) 690 | if mark { 691 | p.Pixel[pinfo.Y][pinfo.X] = coding.Black 692 | } 693 | } else { 694 | if pinfo.HardZero { 695 | panic("hard zero") 696 | } 697 | if mark { 698 | p.Pixel[pinfo.Y][pinfo.X] = 0 699 | } 700 | } 701 | } 702 | bb.copyOut() 703 | 704 | const cheat = false 705 | for i := 0; i < nd*8; i++ { 706 | pinfo := &pixByOff[doff+i] 707 | pix := p.Pixel[pinfo.Y][pinfo.X] 708 | if bb.B[i/8]&(1<= 128 { 754 | // want white 755 | pval = 0 756 | v = 255 757 | } 758 | 759 | bval := pval // bit value 760 | if pix&coding.Invert != 0 { 761 | bval ^= 1 762 | } 763 | if pinfo.HardZero && bval != 0 { 764 | bval ^= 1 765 | pval ^= 1 766 | v ^= 255 767 | } 768 | 769 | // Set pixel value as we want it. 770 | pinfo.Block.reset(pinfo.Bit, bval) 771 | 772 | _, _ = x, y 773 | 774 | err := targ - v 775 | if x+1 < len(row) { 776 | addDither(pixByOff, row[x+1], err*7/16) 777 | } 778 | if false && y+1 < len(p.Pixel) { 779 | if x > 0 { 780 | addDither(pixByOff, p.Pixel[y+1][x-1], err*3/16) 781 | } 782 | addDither(pixByOff, p.Pixel[y+1][x], err*5/16) 783 | if x+1 < len(row) { 784 | addDither(pixByOff, p.Pixel[y+1][x+1], err*1/16) 785 | } 786 | } 787 | } 788 | } 789 | 790 | for _, bb := range bitblocks { 791 | bb.copyOut() 792 | } 793 | } 794 | 795 | noops := 0 796 | // Copy numbers back out. 797 | for i := 0; i < dbit/10; i++ { 798 | // Pull out 10 bits. 799 | v := 0 800 | for j := 0; j < 10; j++ { 801 | bi := uint(bbit + 10*i + j) 802 | v <<= 1 803 | v |= int((data[bi/8] >> (7 - bi&7)) & 1) 804 | } 805 | // Turn into 3 digits. 806 | if v >= 1000 { 807 | // Oops - too many 1 bits. 808 | // We know the 512, 256, 128, 64, 32 bits are all set. 809 | // Pick one at random to clear. This will break some 810 | // checksum bits, but so be it. 811 | println("oops", i, v) 812 | pinfo := &pixByOff[bbit+10*i+3] // TODO random 813 | pinfo.Contrast = 1e9 >> 8 814 | pinfo.HardZero = true 815 | noops++ 816 | } 817 | num[i*3+0] = byte(v/100 + '0') 818 | num[i*3+1] = byte(v/10%10 + '0') 819 | num[i*3+2] = byte(v%10 + '0') 820 | } 821 | if noops > 0 { 822 | goto Again 823 | } 824 | 825 | var b1 coding.Bits 826 | coding.String(url).Encode(&b1, p.Version) 827 | coding.Num(num).Encode(&b1, p.Version) 828 | b1.AddCheckBytes(p.Version, p.Level) 829 | if !bytes.Equal(b.Bytes(), b1.Bytes()) { 830 | fmt.Printf("mismatch\n%d %x\n%d %x\n", len(b.Bytes()), b.Bytes(), len(b1.Bytes()), b1.Bytes()) 831 | panic("byte mismatch") 832 | } 833 | 834 | cc, err := p.Encode(coding.String(url), coding.Num(num)) 835 | if err != nil { 836 | return err 837 | } 838 | 839 | if !m.Dither { 840 | for y, row := range expect { 841 | for x, pix := range row { 842 | if cc.Black(x, y) != pix { 843 | println("mismatch", x, y, p.Pixel[y][x].String()) 844 | } 845 | } 846 | } 847 | } 848 | 849 | m.Code = &qr.Code{Bitmap: cc.Bitmap, Size: cc.Size, Stride: cc.Stride, Scale: m.Scale} 850 | 851 | if m.SaveControl { 852 | m.Control = pngEncode(makeImage(req, "", "", 0, cc.Size, 4, m.Scale, func(x, y int) (rgba uint32) { 853 | pix := p.Pixel[y][x] 854 | if pix.Role() == coding.Data || pix.Role() == coding.Check { 855 | pinfo := &pixByOff[pix.Offset()] 856 | if pinfo.Block != nil { 857 | if cc.Black(x, y) { 858 | return 0x000000ff 859 | } 860 | return 0xffffffff 861 | } 862 | } 863 | if cc.Black(x, y) { 864 | return 0x3f3f3fff 865 | } 866 | return 0xbfbfbfff 867 | })) 868 | } 869 | 870 | return nil 871 | } 872 | 873 | func addDither(pixByOff []Pixinfo, pix coding.Pixel, err int) { 874 | if pix.Role() != coding.Data && pix.Role() != coding.Check { 875 | return 876 | } 877 | pinfo := &pixByOff[pix.Offset()] 878 | println("add", pinfo.X, pinfo.Y, pinfo.DTarg, err) 879 | pinfo.DTarg += err 880 | } 881 | 882 | func readTarget(name string) ([][]int, error) { 883 | f, err := os.Open(name) 884 | if err != nil { 885 | return nil, err 886 | } 887 | m, err := png.Decode(f) 888 | if err != nil { 889 | return nil, fmt.Errorf("decode %s: %v", name, err) 890 | } 891 | rect := m.Bounds() 892 | target := make([][]int, rect.Dy()) 893 | for i := range target { 894 | target[i] = make([]int, rect.Dx()) 895 | } 896 | for y, row := range target { 897 | for x := range row { 898 | a := int(color.RGBAModel.Convert(m.At(x, y)).(color.RGBA).A) 899 | t := int(color.GrayModel.Convert(m.At(x, y)).(color.Gray).Y) 900 | if a == 0 { 901 | t = -1 902 | } 903 | row[x] = t 904 | } 905 | } 906 | return target, nil 907 | } 908 | 909 | type BitBlock struct { 910 | DataBytes int 911 | CheckBytes int 912 | B []byte 913 | M [][]byte 914 | Tmp []byte 915 | RS *gf256.RSEncoder 916 | bdata []byte 917 | cdata []byte 918 | } 919 | 920 | func newBlock(nd, nc int, rs *gf256.RSEncoder, dat, cdata []byte) *BitBlock { 921 | b := &BitBlock{ 922 | DataBytes: nd, 923 | CheckBytes: nc, 924 | B: make([]byte, nd+nc), 925 | Tmp: make([]byte, nc), 926 | RS: rs, 927 | bdata: dat, 928 | cdata: cdata, 929 | } 930 | copy(b.B, dat) 931 | rs.ECC(b.B[:nd], b.B[nd:]) 932 | b.check() 933 | if !bytes.Equal(b.Tmp, cdata) { 934 | panic("cdata") 935 | } 936 | 937 | b.M = make([][]byte, nd*8) 938 | for i := range b.M { 939 | row := make([]byte, nd+nc) 940 | b.M[i] = row 941 | for j := range row { 942 | row[j] = 0 943 | } 944 | row[i/8] = 1 << (7 - uint(i%8)) 945 | rs.ECC(row[:nd], row[nd:]) 946 | } 947 | return b 948 | } 949 | 950 | func (b *BitBlock) check() { 951 | b.RS.ECC(b.B[:b.DataBytes], b.Tmp) 952 | if !bytes.Equal(b.B[b.DataBytes:], b.Tmp) { 953 | fmt.Printf("ecc mismatch\n%x\n%x\n", b.B[b.DataBytes:], b.Tmp) 954 | panic("mismatch") 955 | } 956 | } 957 | 958 | func (b *BitBlock) reset(bi uint, bval byte) { 959 | if (b.B[bi/8]>>(7-bi&7))&1 == bval { 960 | // already has desired bit 961 | return 962 | } 963 | // rows that have already been set 964 | m := b.M[len(b.M):cap(b.M)] 965 | for _, row := range m { 966 | if row[bi/8]&(1<<(7-bi&7)) != 0 { 967 | // Found it. 968 | for j, v := range row { 969 | b.B[j] ^= v 970 | } 971 | return 972 | } 973 | } 974 | panic("reset of unset bit") 975 | } 976 | 977 | func (b *BitBlock) canSet(bi uint, bval byte) bool { 978 | found := false 979 | m := b.M 980 | for j, row := range m { 981 | if row[bi/8]&(1<<(7-bi&7)) == 0 { 982 | continue 983 | } 984 | if !found { 985 | found = true 986 | if j != 0 { 987 | m[0], m[j] = m[j], m[0] 988 | } 989 | continue 990 | } 991 | for k := range row { 992 | row[k] ^= m[0][k] 993 | } 994 | } 995 | if !found { 996 | return false 997 | } 998 | 999 | targ := m[0] 1000 | 1001 | // Subtract from saved-away rows too. 1002 | for _, row := range m[len(m):cap(m)] { 1003 | if row[bi/8]&(1<<(7-bi&7)) == 0 { 1004 | continue 1005 | } 1006 | for k := range row { 1007 | row[k] ^= targ[k] 1008 | } 1009 | } 1010 | 1011 | // Found a row with bit #bi == 1 and cut that bit from all the others. 1012 | // Apply to data and remove from m. 1013 | if (b.B[bi/8]>>(7-bi&7))&1 != bval { 1014 | for j, v := range targ { 1015 | b.B[j] ^= v 1016 | } 1017 | } 1018 | b.check() 1019 | n := len(m) - 1 1020 | m[0], m[n] = m[n], m[0] 1021 | b.M = m[:n] 1022 | 1023 | for _, row := range b.M { 1024 | if row[bi/8]&(1<<(7-bi&7)) != 0 { 1025 | panic("did not reduce") 1026 | } 1027 | } 1028 | 1029 | return true 1030 | } 1031 | 1032 | func (b *BitBlock) copyOut() { 1033 | b.check() 1034 | copy(b.bdata, b.B[:b.DataBytes]) 1035 | copy(b.cdata, b.B[b.DataBytes:]) 1036 | } 1037 | 1038 | func showtable(w http.ResponseWriter, b *BitBlock, gray func(int) bool) { 1039 | nd := b.DataBytes 1040 | nc := b.CheckBytes 1041 | 1042 | fmt.Fprintf(w, "\n") 1043 | line := func() { 1044 | fmt.Fprintf(w, "\n") 1049 | for i := 0; i < (nd+nc)*8; i++ { 1050 | fmt.Fprintf(w, "> uint(7-i&7) & 1 1052 | if gray(i) { 1053 | fmt.Fprintf(w, " class='gray'") 1054 | } 1055 | fmt.Fprintf(w, ">") 1056 | if v == 1 { 1057 | fmt.Fprintf(w, "1") 1058 | } 1059 | } 1060 | line() 1061 | } 1062 | 1063 | m := b.M[len(b.M):cap(b.M)] 1064 | for i := len(m) - 1; i >= 0; i-- { 1065 | dorow(m[i]) 1066 | } 1067 | m = b.M 1068 | for _, row := range b.M { 1069 | dorow(row) 1070 | } 1071 | 1072 | fmt.Fprintf(w, "
\n", (nd+nc)*8) 1045 | } 1046 | line() 1047 | dorow := func(row []byte) { 1048 | fmt.Fprintf(w, "
\n") 1073 | } 1074 | 1075 | func BitsTable(w http.ResponseWriter, req *http.Request) { 1076 | nd := 2 1077 | nc := 2 1078 | fmt.Fprintf(w, ` 1079 | 1103 | `) 1104 | rs := gf256.NewRSEncoder(coding.Field, nc) 1105 | dat := make([]byte, nd+nc) 1106 | b := newBlock(nd, nc, rs, dat[:nd], dat[nd:]) 1107 | for i := 0; i < nd*8; i++ { 1108 | b.canSet(uint(i), 0) 1109 | } 1110 | showtable(w, b, func(i int) bool { return i < nd*8 }) 1111 | 1112 | b = newBlock(nd, nc, rs, dat[:nd], dat[nd:]) 1113 | for j := 0; j < (nd+nc)*8; j += 2 { 1114 | b.canSet(uint(j), 0) 1115 | } 1116 | showtable(w, b, func(i int) bool { return i%2 == 0 }) 1117 | 1118 | } 1119 | --------------------------------------------------------------------------------